During a recent project one of the requirements was for a user to be able to search for people based on their last name. To achieve this I put a content editor web part on one of the pages and wrote some html that added links to the search centre appending the search criteria on the URL. The problem with this was the MOSS people search doesn't allow you to easily sort by name. Initially I thought I could append the XSL and use this to sort the results, however this only sorts the results returned on that page. For example if you have 100 results and you are displaying 50 results per page, this is the maximum, then  the sorting will only apply to the first 50 results. At first I thought there would be another way around this as it seemed like a logical thing to be able to do but after much investigation I learned that the best option available was to write a custom page that would pull out all profiles,  sort them and display them my self.

I have broken the tasks required to achieve this into the following steps.

  1. Determine the sort criteria
  2. Connect to the SharePoint site
  3. Execute custom code with elevated permissions
  4. Reconnect to the SharePoint site
  5. Retrieve a list of users
  6. Sort the Users
  7. Bind them to Gridview
  8. Give users access

I will now cover each of these in turn:

Determine the sort criteria

The first step is to determine what the user is trying to search for. As I mentioned earlier in the article the users access this page by clicking on a link in a custom web part that points them to my page passing the search parameters so I need to retrieve this information and assign it to a variable.

                strProfileType = Request.QueryString["ProfileType"];
                strLastName = Request.QueryString["LastName"];

Connect to the SharePoint site

As I have created a separate page out with SharePoint the first step I had to perform was to connect to the site using the credentials of the user accessing the page.

                SPWeb theWeb = new SPSite(strURL).OpenWeb();
                SPUser currentUser = theWeb.CurrentUser;

                using (SPWeb webInUserContext = SPContext.Current.Web)
                {
                    // get current web and site guids from the current context
                    webGuid = webInUserContext.ID;
                    siteGuid = webInUserContext.Site.ID;

                    SPSecurity.CodeToRunElevated elevatedGetUsers = new SPSecurity.CodeToRunElevated(GetUsers);
                    SPSecurity.RunWithElevatedPrivileges(elevatedGetUsers);
                }

Execute custom code with elevated permissions

This section is import as if you don't include it you will find that when you try and loop through the profiles returned by the profile manager in the next section it will give you an error. See code above for example


Reconnect to the SharePoint site

In the GetUser method we now have to re-connect to the site as the impersonated user, which should now have permission to enumerate through the profiles. Otherwise the page would still think it was connected as the user trying to access the page and will say you don't have access.


             // get the site in this context
            SPSite site = new SPSite(siteGuid);
           
            // get the web in this context
            SPWeb web = site.OpenWeb(webGuid);           
            SPSite oPortalSite = new SPSite(strURL);
            ServerContext oContext = ServerContext.GetContext(site);

Retrieve a list of users

My first step in this section was to create a structure to hold the profile information so I could make use of it later on. For this I decided to use a generic list based on the SharePoint UserProfile class this way I can make use of the built in methods to access the information I am interested in. Next I created an instance of the profile manager class supplying the context obtain from above and looped through the list of profiles adding each one to my generic list. In this example I have added some checks to restrict the number of users returned as I am only interested in users whose last name starts with the supplied criteria but you can quite easily change this for your purposes.

            List<UserProfile> oPeopleList = new List<UserProfile>();

            UserProfileManager profileManager = new UserProfileManager(oContext, true);

            strMySiteUrlHost = profileManager.MySiteHostUrl;

            foreach (UserProfile profile in profileManager)
            {             
                if (strLastName != null)
                {
                    if (profile["lastname"].Value != null)
                    {
                        if (profile["lastname"].Value.ToString().ToLower().StartsWith(strLastName.ToLower()))
                        {
                            oPeopleList.Add(profile);
                        }
                    }
                }
            }

Sort the Users

Now I have my list of users I can now perform the sort and in this example I have done so by creating a comparison delegate, which I can use to overwrite the existing sort method. It takes two user profile objects and compares the last name values to determine the order they should be in. Now that I have this generic comparison method I can pass this to the sort method on my generic user profile list and this will sort it by last name.

        /*Comparison delegate used for sorting*/
        static int LastNameComparision(UserProfile x, UserProfile y)
        {
            return x["lastname"].Value.ToString().CompareTo(y["lastname"].Value.ToString());
        }

        oPeopleList.Sort(LastNameComparision);

Bind them to Gridview

The second last step I will cover in this blog was to bind my generic list to my gridview. This is doesn't require much explanation as I’m sure most people will have done this before. My first was to perform a simple check to establish if there are any results, if so I perform the sort as described above then I bind the list to the gridview.

            if (oPeopleList.Count > 0)
            {
                oPeopleList.Sort(LastNameComparision);
                grd_member_search_results.DataSource = oPeopleList;
                grd_member_search_results.DataBind();
            }
            else
            {
                LblError.Visible = true;
                LblError.Text = "Sorry there are no results returned. Please try again";
            }
        
Obviously since we are binding the datasource to the gridview in the code behind you will still need to alter the gridview to pull out the columns you are interested in and perform some formatting to the look and feel.

Give users access

There is one final step that we must take in order for users to be able to access this information and that is to give the network service account 'Manage User Profiles' and ‘Use Personal Features’ permission in the Shared Service Provider. To do this open central admin, click on the shared service provider, select manage permissions then add the user and give them the above permissions. This is key as otherwise you will get an access denied error on the page. I must thank Edin Kapic for this as I spent a long time trying to identify what the error was before finding his blog. You can find more information on this here edinkapic.blogspot.com/2007/08/enumerating-user-profiles.html
Conclusions

These are the basic steps needed to create a new page to perform custom people searching, however you may need to custom this for your own purposes. I hope this was helpful


Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist
posted @ Monday, January 14, 2008 10:50 PM | in MOSS 2007

Comments

No comments posted yet.

Post Comment

Title *
Name *
Email
Url
Comment *  


Please add 4 and 5 and type the answer here: