Microsoft SharePoint Products and Technologies Resource Kit

 

This chapter is taken from Microsoft SharePoint Products and Technologies Resource Kit published by Microsoft Press; ISBN 073561881X; copyright Microsoft Press 2005; all rights reserved. The author, Bill English, MCSE, Microsoft Certified Trainer, is a Microsoft MVP for Microsoft SharePoint Products and Technologies, a member of the Microsoft SharePoint Beta team, and the author and coauthor of eight computer books. The Microsoft SharePoint teams develop and support Microsoft Office SharePoint Portal Server 2003 and Microsoft Windows SharePoint Services.

No part of these chapters may be reproduced, stored in a retrieval system, or transmitted in any form or by any mean—electronic, electrostatic, mechanical, photocopying, recording or otherwise—without the prior written permission of the publisher, except in the case of brief quotations embodied in critical articles or reviews.

Chapter 36: Building Applications for Microsoft Office SharePoint Portal Server 2003

Contents

SharePoint Portal Server Web Services
    Visual Studio .NET Setup
    AreaService Web Service
    Search Web Service
    UserProfileService Web Service
Summary

Sometimes you will want to create a remote client application that interfaces with your portal site functionality and data outside of the Web interface provided by Microsoft. Perhaps you will want to tap into the search capabilities of Microsoft Office SharePoint Portal Server 2003, or you will want to create an alternative means of managing your portal site areas. In any case, SharePoint Portal Server Web services represent another major advantage to using Microsoft SharePoint Products and Technologies. These Web services allow remote clients to register with the portal site, retrieve and manipulate areas and subareas, retrieve user profile information, and search the SharePoint Portal Server index of resources. Third parties or corporate developers could develop remote client applications that provide SharePoint Portal Server functionality and data in venues other than the portal site.

SharePoint Portal Server Web Services

There are three Web services that SharePoint Portal Server 2003 exposes to the remote client developer: the Area Web service (AreaService), the User Profile Web service (UserProfileService), and the Query Web service (search). Each Web service is described in the following sections along with an example of how to use it. The next section describes how to create and configure a new Microsoft Windows–based application in C# that each example assumes as its starting point.

Note   Of course, the SharePoint Portal Server API for developers is more extensive than these three Web services. Microsoft Windows SharePoint Services object model (and its numerous associated Web services) is covered in detail in Chapter 33, "The Windows SharePoint Services Object Model," and the SharePoint Portal Server 2003 object model is covered in detail in Chapter 34, "The SharePoint Portal Server Object Model Introduction."

Visual Studio .NET Setup

Use the following steps to create a remote client (a C# Windows–based application) in Microsoft Visual Studio .NET that will demonstrate the functionality that is available using the SharePoint Portal Server 2003 Web services.

  1. Using Visual Studio .NET, create a new C# Windows–based application from the application templates.

  2. Right-click the References folder, and then click Add Web Reference.

  3. In the Start Browsing for Web services dialog box, click the link for the Web services on the local computer. (For simplicity, this example assumes that you are using a pristine, local installation of SharePoint Portal Server 2003; if SharePoint Portal Server is installed on another server you will need to type in the fully qualified URL to the Web service on that server.)

  4. In the Services column, click AreaService. If prompted, enter your credentials.

  5. Once the methods are retrieved, type AreaWebService into the Web reference name text box, and then click Add Reference. This will close the dialog box and create a proxy to the Web service that you can use in code.

  6. Follow steps 2 through 5 for both search and UserProfileService, naming them SearchWebService and UserProfileWebService, respectively. At the end of this step, there will be three items listed in the Web References folder (shown in Figure 36-1) of the project's Solution Explorer.

    Web services in the Web Reference folder

    Figure 36-1. Web services in the Web Reference folder

  7. From the Toolbox, drag a button, text box, tree view, and list box onto Form1 using the default name assigned to them by Visual Studio .NET: button1, textBox1, treeView1, and listBox1, respectively.

  8. Double-click button1 to get to the button1_Click function in the code behind. You will be repeatedly replacing code in this function as you explore the SharePoint Portal Server 2003 Web services.

  9. Double-click treeView1 to get the treeView1_AfterSelect function in the code behind. You will also place code in this function.

The following C# Windows–based application examples should run locally on your development computer using your default NTLM credentials accessing the local installation of SharePoint Portal Server 2003 from the MyComputer zone. Failing that approach, or to access a server running SharePoint Portal Server 2003 elsewhere, you will need to either implement Code Access Security (CAS) as described in Chapter 34, "The SharePoint Portal Server Object Model Introduction," Chapter 35, "Building Applications Using Windows SharePoint Services Data," and Chapter 37, "Using Visual Studio .NET to Create Web Parts," to establish trust for the C# Windows–based application or to set up anonymous access to your portal site (only recommended for exploring what can be done with these Web services). You are now ready to begin working with the SharePoint Portal Server 2003 Web services.

AreaService Web Service

You created a proxy to the SharePoint Portal Server 2003 AreaService Web service named AreaWebService in Visual Studio .NET Setup. Let's begin our exploration of this Web service by retrieving the characteristics of the home area and its subareas in a default installation of SharePoint Portal Server 2003. Clicking a node in the tree view will populate the list box with the characteristics of that area. Start by creating two private functions that perform activities that will be called from multiple places.

Create a getUserCredentials function to simplify the way you authenticate to the Web services. If SharePoint Portal Server 2003 is installed locally on your computer, you can use the URL that you use to access your portal (typically https://localhost/) for the URI as shown in the code; otherwise, use the same URL referenced by your Web services.

private System.Net.NetworkCredential getUserCredentials()
{
 //1. Return a network credential from the credential cache
 //   for the currently authenticated user.
 //   This assumes integrated security is in use and that the
 //   currently authenticated user has sufficient rights.
 return 
   System.Net.CredentialCache.DefaultCredentials.GetCredential(
   new Uri("https://localhost/"), "NTLM");
}

Create a populateListbox function to simplify the way you authenticate to the Web services.

private void populateListBox(AreaWebService.AreaData area)
{
 //1. Empty the listbox
 listBox1.Items.Clear();

 //2. Put the Area name in the text box
 textBox1.Text = area.AreaName + " Area";

 //3. Add each characteristic of the area to the listbox
 listBox1.Items.Add("AppearanceDate:" + area.AppearanceDate);
 listBox1.Items.Add("AreaID:        " + area.AreaID);
 listBox1.Items.Add("Bool1:         " + area.Bool1);
 listBox1.Items.Add("Bool2:         " + area.Bool2);
 listBox1.Items.Add("Bool3:         " + area.Bool3);
 listBox1.Items.Add("CreationDate:  " + area.CreationDate);
 listBox1.Items.Add("Datetime1:     " + area.Datetime1);
 listBox1.Items.Add("Depth:         " + area.Depth);
 listBox1.Items.Add("Description:   " + area.Description);
 listBox1.Items.Add("ExpirationDate:" + area.ExpirationDate);
 listBox1.Items.Add("HonorOrder:    " + area.HonorOrder);
 listBox1.Items.Add("InheritUrl:    " + area.InheritUrl);
 listBox1.Items.Add("Int1:          " + area.Int1);
 listBox1.Items.Add("Int2:          " + area.Int2);
 listBox1.Items.Add("Int3:          " + area.Int3);
 listBox1.Items.Add("IsPublicNav:   " + area.IsPublicNav);
 listBox1.Items.Add("LargeIconUrl:  " + area.LargeIconUrl);
 listBox1.Items.Add("LastModified:  " + area.LastModified);
 listBox1.Items.Add("ListingCount:  " + area.ListingCount);
 listBox1.Items.Add("Navigation:    " + area.Navigation);
 listBox1.Items.Add("NText1:        " + area.NText1);
 listBox1.Items.Add("NVarChar1:     " + area.NVarChar1);
 listBox1.Items.Add("NVarChar2:     " + area.NVarChar2);
 listBox1.Items.Add("NVarChar3:     " + area.NVarChar3);
 listBox1.Items.Add("NVarChar4:     " + area.NVarChar4);
 listBox1.Items.Add("Order:         " + area.Order);
 listBox1.Items.Add("Owner:         " + area.Owner);
 listBox1.Items.Add("OwnerEmail:    " + area.OwnerEmail);
 listBox1.Items.Add("OwnerGuid:     " + area.OwnerGuid);
 listBox1.Items.Add("OwnerPicture:  " + area.OwnerPicture);
 listBox1.Items.Add("ParentID:      " + area.ParentID);
 listBox1.Items.Add("SmallIconUrl:  " + area.SmallIconUrl);
 listBox1.Items.Add("Synonyms:      " + area.Synonyms);
 listBox1.Items.Add("System:        " + area.System);
 listBox1.Items.Add("UrlNavigation: " + area.UrlNavigation);
 listBox1.Items.Add("UrlOverride:   " + area.UrlOverride);
 listBox1.Items.Add("WebUrl:        " + area.WebUrl);
}

Place the following code in the button1_Click function:

private void button1_Click(object sender, System.EventArgs e)
{
 //1. Instantiate the proxy for the AreaService Web Service
 AreaWebService.AreaService areaWS =
   new AreaWebService.AreaService();

 //2. Set credentials for authentication to the Web service
 areaWS.Credentials = getUserCredentials();

 //3. Establish a variable for the home area data
 AreaWebService.AreaData homeArea =
   areaWS.GetAreaData(areaWS.GetHomeAreaID());

 //4. Add the home area and its subareas to the treeview
 TreeNode node = treeView1.Nodes.Add(homeArea.AreaName + " Area");
 node.Tag = homeArea.AreaID;
 foreach(Guid areaGuid in areaWS.GetSubAreas(homeArea.AreaID))
 {
   //5. Establish a variable for each home subarea and
   //   add it to the treeview as a child of the home area
   AreaWebService.AreaData area = areaWS.GetAreaData(areaGuid);
   TreeNode childNode = node.Nodes.Add(area.AreaName + " Area");
   childNode.Tag = area.AreaID;
 }

 //6. Expand the treeview node
 node.Toggle();

 //7. Default listBox nodes to characteristics of the home area
 populateListBox(homeArea);
}

Finally, place the following code in the treeView1_AfterSelect function:

private void treeView1_AfterSelect(object sender,
 System.Windows.Forms.TreeViewEventArgs e)
{
 //1. Instantiate the proxy for the AreaService Web Service
 AreaWebService.AreaService areaWS =
   new AreaWebService.AreaService();

 //2. Set credentials for authentication to the Web service
 areaWS.Credentials = getUserCredentials();

 //3. Establish a variable for the clicked node's area data
 AreaWebService.AreaData clickedArea =
   areaWS.GetAreaData(new Guid(e.Node.Tag.ToString()));

 //4. Populate the list box using the area data
 populateListBox(clickedArea);
}

Press F5 in Visual Studio .NET to compile and run the application. Click the button1 button in the resulting dialog box to populate the list box as shown in Figure 36-2.

The values in the list box represent the home area and its subareas. Clicking a node in the list box will result in its characteristics filling the list box.

News area returned by the AreaService Web service

Figure 36-2. News area returned by the AreaService Web service

Most of the characteristics have their roots in the familiar tabbed Change Settings pages for an area in SharePoint Portal Server 2003. The fields might differ in name somewhat, for instance the Title field is called AreaName in the Web service and the Start Date is called AppearanceDate in the Web service. There are also a few fields that seem unfamiliar. For instance, Bool1, Bool2, Bool3, Datetime1, Int1, Int2, Int3, NText, NVarChar1, NVarChar2, NVarChar3, and NVarChar4 are all user-defined values that programmers can set and get that accept various evident datatypes.

If the ListingCount were greater than 0 for an area, you could use the GetAreaListings method of the AreaService Web service to get a list of area listing GUIDs. Subsequently, you could use the GetAreaListingData method to get and set a subset of the area listing's characteristics.

Of course, you can use CreateArea and CreateAreaListing to generate new areas and area listings. You can also use DeleteArea (if not a system area like the home page) and DeleteAreaListing to remove areas and area listings. There are examples of these functions in the Microsoft SharePoint Products and Technologies 2003 Software Development Kit (SDK).

There are also many Web service methods that start with Begin and have associated methods that start with End. These methods can be used to complete that functionality asynchronously.

Search Web Service

You created a proxy to the SharePoint Portal Server 2003 search Web service named SearchWebService in Visual Studio .NET Setup. This Web service lets us query the SharePoint Portal Server 2003 search index for resources that match a pattern or query. A simple string of search terms can be used to query the search index. By default, results of this type of query are returned in order of relevance rank, descending from most relevant to least relevant. Booleans and wildcards are not currently supported out of the box with the default search constructor Web Part by SharePoint Portal Server 2003. The Microsoft SQL Syntax for Full-text Search (MSSQLFT) dialect can also be used to query the search index. Use an MSSQLFT query with the SQL SELECT response format to specify different sort orders.

You will use the same set of controls for this sample application as you did for the first one. However, they will all perform different functions. The text box will be the string that you want to search for, the button will initiate the search, the tree view will contain our search results including a hyperlink to the resource, and the list view will just be used for providing feedback to the user.

As with the AreaService Web service, you need to provide credentials to the search Web service. So, you will use the getUserCredentials function created earlier in this chapter to help with this functionality.

Place the following code in the button1_Click function:

private void button1_Click(object sender, System.EventArgs e)
{
 //1. Setup a variable containing the XML required for querying
 //   the Web service. Double quotes in the XML must be escaped
 //   with a backslash. The SEARCHSTRING will be replaced later with
 //   the actual string of keywords that the user wants
 //   to search for.
 string queryString =
   "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
   "<QueryPacket xmlns=\"urn:Microsoft.Search.Query\" " +
       "Revision=\"1000\">" +
     "<Query domain=\"QDomain\">" +
       "<SupportedFormats>" +
         "<Format>" +
           "urn:Microsoft.Search.Response.Document.Document" +
         "</Format>" +
       "</SupportedFormats>" +
       "<Context>" +
         "<QueryText language=\"en-US\" type=\"STRING\">" +
           "SEARCHSTRING" +
         "</QueryText>" + 
       "</Context>" +
       "<Range>" +
         "<StartAt>1</StartAt>" +
         "<Count>9</Count>" +
       "</Range>" +
     "</Query>" +
   "</QueryPacket>";

 //2. Instantiate the proxy to the SearchWebService Web Service
 SearchWebService.QueryService queryWS =
   new SearchWebService.QueryService();

 //3. Set credentials for authentication to the Web service
 queryWS.Credentials = getUserCredentials();

 //4. Clear the list box and treeview from previous searches
 treeView1.Nodes.Clear();
 listBox1.Items.Clear();

 //5. Verify that the search Web service status is ONLINE
 if(queryWS.Status().Equals("ONLINE"))
 {
   //6. Replace the search string in the XML with the string
   //   of keywords that the user wants to search for.
   string query = queryString.Replace(
     "SEARCHSTRING",
     textBox1.Text);

   //7. Submit the query returning a dataset
   DataSet ds = queryWS.QueryEx(query);

   //8. Process the results of the query, one row in the dataset
   //   for each resource found by the search
   foreach(DataRow row in ds.Tables["table"].Rows)
   {
     //9. We will use the link to the resource and its title
     string hrefLink =
       row["DAV%3ahref"].ToString();
     string displayTitle=
row["urn%3aschemas.microsoft.com%3afulltextqueryinfo%3adisplaytitle"]
   .ToString();

     //10. The last modified date, content length, description,
     //    rank, sourceGroup, and sdid1 (search ID)
     //    are returned from the default search
     //    but are not used in this example
     string getLastModified =
       row["DAV%3agetlastmodified"].ToString();
     string getContentLength =
       row["DAV%3agetcontentlength"].ToString();
     string description =
row["urn%3aschemas.microsoft.com%3afulltextqueryinfo%3adescription"]
   .ToString();
     string rank =
row["urn%3aschemas.microsoft.com%3afulltextqueryinfo%3arank"]
   .ToString();
     string sourceGroup =
row["urn%3aschemas.microsoft.com%3afulltextqueryinfo%3asourcegroup"]
   .ToString();
     string sdid1 =
row["urn%3aschemas.microsoft.com%3afulltextqueryinfo%3asdid1"]
   .ToString();

     //11. Add each resource into the treeview
     TreeNode node = treeView1.Nodes.Add(displayTitle);

     //12. Save the URL with the node
     node.Tag = hrefLink;
   }

   //13. Determine if any resources were found by the search
   if (treeView1.Nodes.Count &gt; 0)
   {
     //14. Message the user to click on any item to view it
     listBox1.Items.Add("Click on any item to view it.");
   }
   else
   {
     //15. Message the user that no resources were returned
     listBox1.Items.Add("No resources were returned.");
   }
 }
 else
 {
   //16. Message the user if search is not ONLINE
   //    Currently, the only other value is OFFLINE
   listBox1.Items.Add("Search is currently "
     + queryWS.Status());
 }
}

Notice that in line 1 of the code we set up a variable containing the XML required for querying the Web service. To do an MSSQLFT search instead of a STRING search, just change type=\"STRING\" to type=\"MSSQLFT\". But if you do that, the text box needs to contain a query rather than just a string when the button is clicked.

In line 7 of the code, you submit the query XML using QueryEx to return a dataset. Using Query rather than QueryEx will return an xmlNode with basically the same data. The dataset is just a little easier to work with, even though the names of the fields are long. Speaking of the field names, notice that %3a is the escape sequence for a colon. Mentally substituting those three characters when looking at the preceding code will make the field names easier to understand.

Finally, place the following code in the treeView1_AfterSelect function:

private void treeView1_AfterSelect(object sender,
 System.Windows.Forms.TreeViewEventArgs e)
{
 //1. Setup a variable representing a latent process with
 //   IE (iexplore.exe) as the application and the URL
 //   in the clicked node's tag as the document to open
 System.Diagnostics.ProcessStartInfo iexploreStartInfo = new
   System.Diagnostics.ProcessStartInfo(
     "iexplore.exe", e.Node.Tag.ToString());

 //2. Starting the process opens IE to the URL
 System.Diagnostics.Process iexplore = 
   System.Diagnostics.Process.Start(iexploreStartInfo);
}

Replace the text in the text box with the keywords you want to search for. In our example, you searched for the word "microsoft". Click the button. If searching a brand new portal site, there should be a number of resources found (typically five), as shown in Figure 36-3.

Click any resource listed in the tree view and your browser will open directly to that resource's URL.

Resources returned using the search Web service

Figure 36-3. Resources returned using the search Web service

By default, all portal site sources are searched. To get a list of the sources that are available to be searched, you can use the GetPortalSearchInfo method of the search Web service.

Again, there are many Web service methods that start with Begin and have associated methods that start with End. These methods can be used to complete that functionality asynchronously.

UserProfileService Web Service

You created a proxy to the SharePoint Portal Server 2003 User Profile Web service named UserProfileWebService in Visual Studio .NET Setup. Basically, this Web service lets you retrieve user profiles and their schema using a variety of different user IDs: GUID, index, and user account name. GUID and index will be useful when SharePoint Portal Server provides those tokens to us as the ID for a user.

Again, you will use the same set of controls for this sample application as you did for the both the AreaService Web Service and the search Web Service examples. For this example, allow the user to initially type an account name into the text box. When the user clicks the button, you will attempt to retrieve the account name specified in the text box from SharePoint Portal Server 2003. You will save all successful searches in the tree view and present all profile details in the list view. If the user's profile is not found, you will present a message in the list view indicating that status. If the user clicks on a previously successful account in the tree view, you will re-retrieve the profile using the GUID that you saved when the profile was initially retrieved.

As with the previous Web services, you need to provide credentials to the UserProfileService Web service. So, you will use the getUserCredentials function created earlier in this chapter to help with this functionality.

Place the following code in the button1_Click function:

private void button1_Click(object sender, System.EventArgs e)
{
 //1. Establish a proxy to the UserProfileService Web Service
 UserProfileWebService.UserProfileService userProfileWS =
   new UserProfileWebService.UserProfileService();

 //2. Set credentials for authentication to the Web service
 userProfileWS.Credentials = getUserCredentials();

 //3. Clear the list box on each request
 listBox1.Items.Clear();

 try
 {
   //4. Give names to the PropertyData array indexes
   int userProfile_GUID = 0;
   int accountName = 1;

   //5. Retrieve the PropertyData array for account name
   //   provided by the user in the text box
   UserProfileWebService.PropertyData[] userData =
     userProfileWS.GetUserProfileByName(textBox1.Text);

   //6. If successful, add account name to the treeview
   TreeNode node = treeView1.Nodes.Add(
     userData[accountName].Value);

   //7. Save the GUID with the node
   node.Tag = userData[userProfile_GUID].Value;

   //8. For every property in the profile
   foreach(UserProfileWebService.PropertyData userProperty
             in userData)
   {
     //9. Add the name and value to the listbox
     listBox1.Items.Add(userProperty.Name + ": "
       + userProperty.Value);
   }
 }
 catch
 {
   //10. Message the user if the account name is not found
   listBox1.Items.Add("Account Name Not Found");
 }
}

Finally, place the following code in the treeView1_AfterSelect function:

private void treeView1_AfterSelect(object sender, 
  System.Windows.Forms.TreeViewEventArgs e)
{
 //1. Establish a proxy to the UserProfileService Web Service
 UserProfileWebService.UserProfileService userProfileWS =
   new UserProfileWebService.UserProfileService();

 //2. Set credentials for authentication to the Web service
 userProfileWS.Credentials = getUserCredentials();

 //3. Clear the list box on each request
 listBox1.Items.Clear();

 //4. Show the clicked accountName in the text box
 textBox1.Text = e.Node.Text;

 try
 {
   //5. Retrieve the PropertyData array for GUID saved when
   //   the user was first retrieved
   UserProfileWebService.PropertyData[] userData =
     userProfileWS.GetUserProfileByGuid(
     new Guid(e.Node.Tag.ToString()));

   //6. For every property in the profile
   foreach(UserProfileWebService.PropertyData userProperty
             in userData)
   {
     //7. Add the name and value to the listbox
     listBox1.Items.Add(userProperty.Name + ": "
       + userProperty.Value);
   }
 }
 catch
 {
   //8. Message the user if the account name is not found
   listBox1.Items.Add("Account Name Not Found");
 }
}

Type an account name into the text box in the format domain\username or just a username, and click the button. If the account is found, it will be added to the tree view of previous successful retrievals and its properties will be presented in the list box, shown in Figure 36-4.

Profile properties returned by UserProfileService Web service

Figure 36-4. Profile properties returned by UserProfileService Web service

Clicking any previously retrieved profile in the tree view will re-retrieve that profile using the GUID stored in the tree view node's tag property.

Summary

The examples provided in this chapter are rudimentary and somewhat crude. However, they demonstrate the capabilities available to a remote client accessing SharePoint Portal Server 2003 Web services.

We covered all three Web services provided by SharePoint Portal Server 2003. The AreaService Web service allowed us to manage nearly every aspect of a SharePoint Portal Server 2003 area. The search Web service gave us a window into the resources indexed by SharePoint Portal Server 2003, and the UserProfileService Web service lets us retrieve profile information using various user IDs.

The next chapter describes how you can use Visual Studio .NET to create Web Parts.