Overview
This article describes how to use Microsoft Office SharePoint Server (MOSS) People Search to display department-level contacts in a simple-to-read grid format.
This solution arose from a consulting engagement. The client’s portal taxonomy included many corporate departments. Most departments wanted to display member’s contact information on their site’s main page. The client’s first impulse was to use a Contacts web part. That is not usually the best approach, however. Instead, leverage the People Search Core Results web part.
The solution
Here is the desired result:

To achieve that result, we use the following SharePoint features and technology:
1. People Search Core Results web part
2. XSL
3. Visual Studio (optional but recommended)
This solution does not require any programming (at least in a C#/VB.NET sense). It does not require SharePoint Designer.
Overall, we follow these steps:
1. Locate the page where we want to add the department contacts.
2. Edit the page and add a People Search Core Results web part where appropriate.
3. Modify the web part for look and feel and query parameters.
4. Modify the web part’s XSL to display the data in a grid format (best done in visual studio, but any text editor will do).
Add a People Search Core Results Web Part
Locate the page where you want to show the department contacts and edit the page.
Click “add a web part” and select “People Search Core Results” from the gallery:

Modify Fixed Keyword Query
After it has been added, modify the web part and expand the “Fixed Keyword Query” section as shown:

This tells the web part to “feed itself” so to speak. Instead of looking for the user to enter search terms, it instead uses whatever we enter into the “Fixed Keyword Query”. In the example above, we tell the web part to run a query that returns any person whose department equals “IT” (for information technology).
See here (http://msdn2.microsoft.com/en-us/library/ms467796.aspx) for the MSDN documentation on keyword queries to learn more options that might be useful here.
Modify Results Query Options
Even though we’ve just specified a fixed keyword query on the web part, we need to tell it to use the results of fixed keyword query we just defined as opposed to the results of some other query. Do this as shown:

Enjoy the Intermediate Results
At this point, we have a fully functioning embedded people search that only shows contact information for those users whose department equals the literal value “IT”. MOSS presents the contacts using the same layout you get when you do a people search from anywhere (i.e. picture of the user, specially formatted contact details, etc). For a department level contacts list, we want a much more minimalist result.
You can cross-check your work. Use advanced search to search for People whose department = “IT”. The results should be identical.
Modify the XSL to Generate a Grid Layout
Luckily for us, the People Search Core Results (and its partner, the simple Search Core Results), unlike the Content Query Web Part, uses just one XSL “file” for transforming query results to HTML. As a result, we can make straight forward changes to generate the output we want.
I’ve included XSL that generates the minimalist layout at the end of the article. There are two key facets to it.
First, at its root, it generates an html table, as in: <table><tr><td> … </td></tr></table>
Second, It uses the <xsl:sort> directive.
This is the key bit of XSL:
<table cellpadding="1" cellspacing="5" width="100%">
<xsl:for-each select="All_Results/Result">
<xsl:sort select="preferredname"/>
<!-- Emit <tr> and <td> tags for the columns you want to show -->
</xsl:for-each>
</table>
We are manually iterating through the search result set (“ALL_Results/Result”) and telling the parser to sort via “preferredname” (<xsl:sort select=”preferredname”/>).
It should be obvious how to sort by another field, such as email address.
Use Visual Studio
I always find it easiest to use visual studio to craft my XML. When properly configured (as described here, http://paulgalvin.spaces.live.com/blog/cns!1CC1EDB3DAA9B8AA!132.entry), visual studio provides Intellisense support for the XSL itself.
Benefits to This Approach
Organizations derive at least two benefits from this approach.
First, department members are often managed in active directory. Active directory drives People search via the full and incremental user profile import process (a shared service in MOSS). Therefore, the information is already centrally managed from a data input perspective. If we can trust active directory to provide good information (which is admittedly a stretch for some organizations), there is no reason to add a new redundant source of contact details via a Contacts web part.
Second, this approach provides a high visibility feedback mechanism to active directory managers. End users will see their name and title and other profile data on their department main page (or wherever their contact data is displayed) and in a manner more public than the user’s My Site. If that information is incorrect, they have a good incentive to notify AD managers who can clean up those users’ AD data. In some cases, it may be easier to manage incorrect AD data this way rather than attempting a brute force, top-down project to clean up AD.
As AD data is cleaned, it becomes more valuable and can be used in other areas of SharePoint (e.g. via Colleagues), My Sites and even other applications in your enterprise that leverage active directory.
Other Resources
If you found this article to be interesting, the following related resources may also be of interest:
· XSL Primer (http://www.whitefrost.com/documents/html/technical/xsl/xslPrimer.html): I find myself referring to this site on a fairly regular basis.
· This MSDN forum posting (http://forums.microsoft.com/forums/ShowPost.aspx?PostID=2683595&SiteID=1) includes some questions and answers on this technique.
· Display Content Query Web Part results in a grid (http://paulgalvin.spaces.live.com/blog/cns!1CC1EDB3DAA9B8AA!491.entry)
· Embed hyperlinks into the XSL (http://paulgalvin.spaces.live.com/blog/cns!1CC1EDB3DAA9B8AA!301.entry).
XSL
The following XSL replaces the default provided by the web part and displays a minimalist grid format. It could be trimmed further.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:param name="ResultsBy" />
<xsl:param name="ViewByUrl" />
<xsl:param name="ViewByValue" />
<xsl:param name="IsNoKeyword" />
<xsl:param name="IsFixedQuery" />
<xsl:param name="MoreResultsText" />
<xsl:param name="MoreResultsLink" />
<xsl:param name="CollapsingStatusLink" />
<xsl:param name="CollapseDuplicatesText" />
<xsl:param name="MeHeaderValue" />
<xsl:param name="CollHeaderValue" />
<xsl:param name="CollOfCollHeaderValue" />
<xsl:param name="EveryoneHeaderValue" />
<xsl:param name="AddToMyColleaguesText" />
<xsl:param name="SrchRSSText" />
<xsl:param name="SrchRSSLink" />
<xsl:param name="ShowMessage" />
<xsl:param name="ShowActionLinks" />
<!-- When there is keyword to issue the search -->
<xsl:template name="dvt_1.noKeyword">
<span class="ms-sbplain">
<xsl:choose>
<xsl:when test="$IsFixedQuery">
Please set the 'Fixed Query' property for the webpart.
</xsl:when>
<xsl:otherwise>
Enter one or more words to search for in the search box.
</xsl:otherwise>
</xsl:choose>
</span>
</xsl:template>
<!-- When empty result set is returned from search -->
<xsl:template name="dvt_1.empty">
<xsl:if test="$ShowActionLinks">
<div class="srch-sort">
<xsl:if test="string-length($SrchRSSLink) > 0">
<a type="application/rss+xml" href ="{$SrchRSSLink}" title="{$SrchRSSText}" id="SRCHRSSL">
<img border="0" src="/_layouts/images/rss.gif" alt=""/>
<xsl:text disable-output-escaping="yes">&nbsp;</xsl:text>
<xsl:value-of select="$SrchRSSText"/>
</a>
</xsl:if>
</div>
</xsl:if>
<br/>
<br/>
<span class="ms-sbplain" id="CSR_NO_RESULTS">
No contacts were found for this department. Ensure that the fixed keyword query component to this
People Search Core Results is properly defined and that the Cross Web Part Query is correct.
</span>
</xsl:template>
<!-- Main body template. Sets the Results view (Relevance or date) options -->
<xsl:template name="dvt_1.body">
<xsl:if test="$ShowActionLinks">
<div class="srch-sort">
<xsl:value-of select="$ResultsBy" />
<xsl:if test="$ViewByUrl">
|
<a href ="{$ViewByUrl}" id="CSR_RV" title="{$ViewByValue}">
<xsl:value-of select="$ViewByValue" />
</a>
</xsl:if>
<xsl:if test="string-length($SrchRSSLink) > 0">
|
<a type="application/rss+xml" href ="{$SrchRSSLink}" title="{$SrchRSSText}" id="SRCHRSSL">
<img border="0" src="/_layouts/images/rss.gif" alt=""/>
<xsl:text disable-output-escaping="yes">&nbsp;</xsl:text>
<xsl:value-of select="$SrchRSSText"/>
</a>
</xsl:if>
</div>
<div style="margin-top: 15px;"></div>
<br />
<br />
</xsl:if>
<table cellpadding="1" cellspacing="5" width="100%">
<xsl:for-each select="All_Results/Result">
<!-- Wrap all of our grid output logic inside an <xsl:sort> directive. -->
<xsl:sort select="preferredname"/>
<!--
Put column headers here if you want. I took a more minimalist
approach since the column information is self-evident.
-->
<tr>
<td>
<xsl:value-of select="preferredname"/> <!-- This is usually Last Name, First Name -->
</td>
<td>
<xsl:variable name="email" select="workemail"/>
<a href="mailto:{$email}">
<xsl:value-of select="workemail"/>
</a>
</td>
<td>
<xsl:value-of select="jobtitle" />
</td>
<td>
<xsl:value-of select="workphone" />
</td>
<td>
<xsl:value-of select="officenumber" />
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
<xsl:template name="HitHighlighting">
<xsl:param name="hh" />
<xsl:apply-templates select="$hh"/>
</xsl:template>
<xsl:template match="ddd">
…
</xsl:template>
<xsl:template match="c0">
<b>
<xsl:value-of select="."/>
</b>
</xsl:template>
<xsl:template match="c1">
<b>
<xsl:value-of select="."/>
</b>
</xsl:template>
<xsl:template match="c2">
<b>
<xsl:value-of select="."/>
</b>
</xsl:template>
<xsl:template match="c3">
<b>
<xsl:value-of select="."/>
</b>
</xsl:template>
<xsl:template match="c4">
<b>
<xsl:value-of select="."/>
</b>
</xsl:template>
<xsl:template match="c5">
<b>
<xsl:value-of select="."/>
</b>
</xsl:template>
<xsl:template match="c6">
<b>
<xsl:value-of select="."/>
</b>
</xsl:template>
<xsl:template match="c7">
<b>
<xsl:value-of select="."/>
</b>
</xsl:template>
<xsl:template match="c8">
<b>
<xsl:value-of select="."/>
</b>
</xsl:template>
<xsl:template match="c9">
<b>