Friday, June 4, 2010

Displaying real time SharePoint search results alphabetically using search.asmx and jQuery/JS

Scenario:
Ho to display real time SharePoint people search results alphabetically using search.asmx and js

Explanation:
If there is a requirement to display all AD users in people search in SharePoint, the list can grow long and hence it is a good idea to categorize users alphabetically.

People Search Results

Solution:
The below combination of Javascript and XSL can be used to accomplish the above. You can either use your own custom search web part or the OOB search results web part to get the results xml and apply the below XSL to the xml result

The below XSL builds a list of alphabets as links. Upon clicking each link a javascript function is called and names of people that start with the corresponding alphabet are displayed.

<xsl:attribute name="onclick">
GetContacts('<xsl:value-of select="child::node()[position()]"/>');
</xsl:attribute>

Lets get started..

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
  <xsl:output method="html" encoding="UTF-8"/>
  <xsl:template name="Alphabets">
    <Alpha>A</Alpha>
    <Alpha>B</Alpha>
    <Alpha>C</Alpha>
    <Alpha>D</Alpha>
    <Alpha>E</Alpha>
    <Alpha>F</Alpha>
    <Alpha>G</Alpha>
    <Alpha>H</Alpha>
    <Alpha>I</Alpha>
    <Alpha>J</Alpha>
    <Alpha>K</Alpha>
    <Alpha>L</Alpha>
    <Alpha>M</Alpha>
    <Alpha>N</Alpha>
    <Alpha>O</Alpha>
    <Alpha>P</Alpha>
    <Alpha>Q</Alpha>
    <Alpha>R</Alpha>
    <Alpha>S</Alpha>
    <Alpha>T</Alpha>
    <Alpha>U</Alpha>
    <Alpha>V</Alpha>
    <Alpha>W</Alpha>
    <Alpha>X</Alpha>
    <Alpha>Y</Alpha>
    <Alpha>Z</Alpha>
  </xsl:template>
  <xsl:variable name="smallcase" select="'abcdefghijklmnopqrstuvwxyz'"/>
  <xsl:variable name="uppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
  <xsl:key name="keyTitle" match="Result" use="translate(substring(lastname,1,1),'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>
  <xsl:template match="/">
    <xsl:variable name="Rows" select="/Search_Results/All_Results/Result"/>
    <div id="assocDirResultBox" sizset="115" sizcache="16">
      <xsl:variable name="AlphabetNodes">
        <xsl:call-template name="Alphabets"/>
      </xsl:variable>
      <xsl:variable name="AlphabetName">
        <xsl:apply-templates select="msxsl:node-set($AlphabetNodes)/Alpha">
          <xsl:sort select="." order="ascending"/>
        </xsl:apply-templates>
      </xsl:variable>
      <xsl:variable name="TitleNames">
        <xsl:for-each select="$Rows[generate-id(.) = generate-id(key('keyTitle', translate(substring(lastname,1,1),$smallcase,$uppercase))[1])]">
          <xsl:value-of select="translate(substring(lastname,1,1),$smallcase,$uppercase)"/>
        </xsl:for-each>
      </xsl:variable>
      <a name="top_AssosiatesAToZ"></a>
      <div class="alphaBrowseNavBox" sizcache="16" sizset="111">
        <div class="alphaBrowseNav" sizcache="16" sizset="111">
          <xsl:for-each select="msxsl:node-set($AlphabetNodes)/Alpha">
            <xsl:if test="position() &lt;= count(msxsl:node-set($AlphabetNodes)/Alpha)">
              <xsl:variable name="char" select="child::node()[position()]"/>
              <xsl:choose>
                <xsl:when test="contains($TitleNames,$char)">
                  <a>
                    <xsl:attribute name="href">
                      #<xsl:value-of select="child::node()[position()]"/>
                    </xsl:attribute>
                    <xsl:attribute name="onclick">
                      GetContacts('<xsl:value-of select="child::node()[position()]"/>');
                    </xsl:attribute>
                    <xsl:value-of select="child::node()[position()]"/>
                  </a>
                </xsl:when>
                <xsl:otherwise>
                  <span>
                    <xsl:value-of select="child::node()[position()]"/>
                  </span>
                </xsl:otherwise>
              </xsl:choose>
            </xsl:if>
          </xsl:for-each>
        </div>
      </div>
      <div id="searchResults">
      </div>
    </div>
  </xsl:template>
  <!-- A generic function that can be used to replace strings in a given text. Similar to String.Replace of C# -->
  <xsl:template name="ReplaceAllCharsInString">
    <xsl:param name="text"/>
    <xsl:param name="replace"/>
    <xsl:param name="by"/>
    <xsl:choose>
      <xsl:when test="contains($text, $replace)">
        <xsl:value-of select="substring-before($text,$replace)"/>
        <xsl:value-of select="$by"/>
        <xsl:call-template name="ReplaceAllCharsInString">
          <xsl:with-param name="text" select="substring-after($text,$replace)"/>
          <xsl:with-param name="replace" select="$replace"/>
          <xsl:with-param name="by" select="$by"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$text"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

The javascript/jQuery functions:
function GetContacts(alpha) 
  {
   var qry = "SELECT Department,WorkPhone,OfficeNumber,AboutMe,PictureURL,WorkEmail,WebSite,BaseOfficeLocation,LastName,FirstName,Company,MobilePhone,PreferredName,AccountName,UserProfile_GUID, JobTitle,Size, Rank, Path, Title, Description, Write  FROM SCOPE() WHERE (\"scope\"='Associates') AND (\"DAV:contentclass\"='urn:content-class:SPSPeople') AND (\"LastName\" LIKE '" + alpha + "%') ORDER BY LastName";

   var queryXML = 
    <![CDATA["<QueryPacket xmlns='urn:Microsoft.Search.Query' Revision='1000'> \
    <Query domain='QDomain'> \
     <SupportedFormats><Format>urn:Microsoft.Search.Response.Document.Document</Format></SupportedFormats> \
     <Context> \
      <QueryText type='MSSQLFT' language='en-us'>#qry#</QueryText> \
     </Context> \
    <SortByProperties><SortByProperty name='Rank' direction='Descending' order='1'/></SortByProperties> \
     <Range><StartAt>1</StartAt><Count>2000</Count></Range> \
     <EnableStemming>false</EnableStemming> \
     <TrimDuplicates>true</TrimDuplicates> \
     <IgnoreAllNoiseQuery>true</IgnoreAllNoiseQuery> \
     <ImplicitAndBehavior>true</ImplicitAndBehavior> \
     <IncludeRelevanceResults>true</IncludeRelevanceResults> \
     <IncludeSpecialTermResults>true</IncludeSpecialTermResults> \
     <IncludeHighConfidenceResults>true</IncludeHighConfidenceResults> \
    </Query></QueryPacket>"]]>;
   
   queryXML = queryXML.replace('#qry#', qry);
   
   var soapEnv =
    <![CDATA["<soap:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'> \
      <soap:Body> \
     <Query xmlns='urn:Microsoft.Search'> \
       <queryXml>" + escapeHTML(queryXML) + "</queryXml> \
     </Query> \
      </soap:Body> \
    </soap:Envelope>"]]>;   

   $.blockUI({ message: '<p>Please wait while the contacts are being loaded...</p>' });
    
   $.ajax({
    url: window.location.protocol + "//" + window.location.host + "/_vti_bin/search.asmx",
    type: "POST",
    dataType: "xml",
    data: soapEnv,
    complete: processResult,
    contentType: "text/xml; charset=\"utf-8\""
   });
   
   function processResult(xData, status) {
    var html = "";
    html += "&lt;div class='alphaBrowseNavHeader'&gt;" + alpha + "&lt;/div&gt;";
    html += "&lt;div class='rightActionsWrapper'&gt;";
    $(xData.responseXML).find("QueryResult").each(function() {
     var x = $("<xml>" + $(this).text() + "</xml>");

     /* Find value of each property */ 
     x.find("Document").each(function() {
      var department = '', workPhone = '', pictureUrl = '', workEmail = '', location = '', lastName = '', firstName = '', company = '', mobilePhone = '', preferredname = '', accountName = '', accountNameEncoded = '', jobTitle = '', responsibility = '';
      $(this).find("Property").each(function() {
       switch($(this).find("Name").text())
       {
        case 'DEPARTMENT':
         department = $(this).find("Value").text();
         break;
        case 'WORKPHONE':
         workPhone = $(this).find("Value").text();
         break;          
        case 'PICTUREURL':
         pictureUrl = $(this).find("Value").text();
         break;          
        case 'WORKEMAIL':
         workEmail = $(this).find("Value").text();
         break;          
        case 'BASEOFFICELOCATION':
         location = $(this).find("Value").text();
         break;          
        case 'LASTNAME':
         lastName = $(this).find("Value").text();
         break;          
        case 'FIRSTNAME':
         firstName = $(this).find("Value").text();
         break;          
        case 'COMPANY':
         company = $(this).find("Value").text();
         break;          
        case 'MOBILEPHONE':
         mobilePhone = $(this).find("Value").text();
         break;          
        case 'PREFERREDNAME':
         preferredname = $(this).find("Value").text();
         break;          
        case 'ACCOUNTNAME':
         accountName = $(this).find("Value").text();
         accountNameEncoded = accountName.replace('\\','%5C');
         break;          
        case 'JOBTITLE':
         jobTitle = $(this).find("Value").text();
         break;  
        case 'RESPONSIBILITY':
         responsibility = $(this).find("Value").text();
         break;  
        default:
         break;
       }
      })
      
      /* If no picture is available, display the temp place holder image */
      if(pictureUrl == '')
      {
       pictureUrl = '/Style%20Library/Images/temp_placeholder.gif';
      }          
      
      var title = $("Title", $(this)).text();
      var url = $("Action>LinkUrl", $(this)).text();
      var description = $("Description", $(this)).text()
       
      html += 
      <![CDATA["<div class='rightActionsBox'> \
       <a href='"+ url +"'><img alt='Photo of " + preferredname + "' border='0' width='158' class='rightActionsBoxImage' src='"+pictureUrl+"' /></a> \
       <div class='rightActionsCopy' sizset='122' sizcache='16'> \
        <a href='"+ url +"'><span class='name'>" + preferredname + "</span></a> \
        <table class='assocDirResultTable'> \
         <tbody> \
          <tr> \
           <td> \
            <span class='property title'>&#160;#jobTitle#</span> \
            <span class='property'><label>Work:</label>&#160;#workPhone#</span> \
            <span class='property'><label>Mobile:</label>&#160;#mobilePhone#</span> \
            <span class='property' sizset='122' sizcache='16'>#accountLink#</span> \
           </td> \
           <td> \
            <span class='property'><label>Business:</label>&#160;#company#</span> \
            <span class='property'><label>Department:</label>&#160;#department#</span> \
            <span class='property'><label>Location:</label>&#160;#location#</span> \
           </td> \
           <td class='last'><label>Ask me about</label> \
            <span class='property'>#responsibility#</span> \
           </td> \
          </tr> \
         </tbody> \
        </table> \
       </div> \
       <ul class='rightActions' sizset='123' sizcache='16'> \
        <li class='iconCal' sizset='123' sizcache='16'> \
         <a href='#' onclick='ShowDialog(#AddToColleageCall#, null, AddToColleaguesDialogCallback);return false;'>Add As Colleague</a> \
        </li> \
        <li class='iconPeople' sizset='124' sizcache='16'> \
         <a href='#' onclick='ShowDialog(#ShowOrganizationHierarchyCall#, null, ShowOrganizationHierarchyDialogCallback);return false;'>Browse In Organization Chart</a> \
        </li> \
        <li class='iconFlagGreen' sizset='125' sizcache='16'> \
         <a href='#' onclick='ShowDialog(#ViewRecentContentCall#, null, ViewRecentContentDialogCallback);return false;'>View Recent Content</a> \
        </li> \
       </ul> \
      </div>"]]>
      html = html.replace('#jobTitle#', jobTitle).replace('#workPhone#', workPhone).replace('#mobilePhone#', mobilePhone).replace('#company#', company).replace('#department#', department).replace('#location#', location).replace('#responsibility#', responsibility).replace('#AddToColleageCall#','"' + 'http://[Site URL]/my/_layouts/QuickLinksDialogForm.aspx?Mode=Person&amp;NTName=' + accountNameEncoded + '&amp;IsDlg=1' + '"').replace('#ShowOrganizationHierarchyCall#','"' + 'http://[site url]/my/OrganizationView.aspx?ProfileType=User&amp;accountname=' + accountNameEncoded + '"').replace('#ViewRecentContentCall#','"' + 'http://[site URL]/my/personcontent.aspx?accountname=' + accountNameEncoded + '"').replace('#workEmail#',workEmail);
      if(workEmail != '')
      {
       html = html.replace('#accountLink#','&lt;a href=\'mailto:#workEmail#\'&gt;#accountName#&lt;/a&gt;');
       html = html.replace('#workEmail#', workEmail);
       html = html.replace('#accountName#', accountName);
      }
      else 
       html = html.replace('#accountLink#',accountName);
     });
    });
    html += "&lt;/div&gt;"; 
    $("#searchResults").empty().append(html);
   }
  } 

Complete Code:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
 <xsl:output method="html" encoding="UTF-8"/>
 <xsl:template name="Alphabets">
  <Alpha>A</Alpha>
  <Alpha>B</Alpha>
  <Alpha>C</Alpha>
  <Alpha>D</Alpha>
  <Alpha>E</Alpha>
  <Alpha>F</Alpha>
  <Alpha>G</Alpha>
  <Alpha>H</Alpha>
  <Alpha>I</Alpha>
  <Alpha>J</Alpha>
  <Alpha>K</Alpha>
  <Alpha>L</Alpha>
  <Alpha>M</Alpha>
  <Alpha>N</Alpha>
  <Alpha>O</Alpha>
  <Alpha>P</Alpha>
  <Alpha>Q</Alpha>
  <Alpha>R</Alpha>
  <Alpha>S</Alpha>
  <Alpha>T</Alpha>
  <Alpha>U</Alpha>
  <Alpha>V</Alpha>
  <Alpha>W</Alpha>
  <Alpha>X</Alpha>
  <Alpha>Y</Alpha>
  <Alpha>Z</Alpha>
 </xsl:template>
 <xsl:variable name="smallcase" select="'abcdefghijklmnopqrstuvwxyz'"/>
 <xsl:variable name="uppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
 <xsl:key name="keyTitle" match="Result" use="translate(substring(lastname,1,1),'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>
 <xsl:template match="/">
  <script type="text/javascript">
  function AddToColleaguesDialogCallback(dialogResult, returnValue)
  {
   if (dialogResult == 1)
   {
    alert('Contact has been added as your colleague.');
   }
  }

  function ShowOrganizationHierarchyDialogCallback(dialogResult, returnValue)
  {
  }
  
  function ViewRecentContentDialogCallback(dialogResult, returnValue)
  {
  }
  
  function ULSX84(){var o=new Object;o.ULSTeamName="SharePoint Portal Server";o.ULSFileName="portal.js";return o;}
  
  function ShowDialog(e,b,c)
  {
   ULSX84:;
   var a=[];
   if(b!=null)
    a[0]=b;
   else 
    a=null;
   var d={width:750,height:500};
   SP.UI.ModalDialog.commonModalDialogOpen(e,d,c,a)
  }
 
  $(document).ajaxStop($.unblockUI); 
 
  $(document).ready(function(){
   GetContacts('A');
  });
 
  function GetContacts(alpha) 
  {
   var qry = "SELECT Department,WorkPhone,OfficeNumber,AboutMe,PictureURL,WorkEmail,WebSite,BaseOfficeLocation,LastName,FirstName,Company,MobilePhone,PreferredName,AccountName,UserProfile_GUID, JobTitle,Size, Rank, Path, Title, Description, Write  FROM SCOPE() WHERE (\"scope\"='Associates') AND (\"DAV:contentclass\"='urn:content-class:SPSPeople') AND (\"LastName\" LIKE '" + alpha + "%') ORDER BY LastName";

   var queryXML = 
    <![CDATA["<QueryPacket xmlns='urn:Microsoft.Search.Query' Revision='1000'> \
    <Query domain='QDomain'> \
     <SupportedFormats><Format>urn:Microsoft.Search.Response.Document.Document</Format></SupportedFormats> \
     <Context> \
      <QueryText type='MSSQLFT' language='en-us'>#qry#</QueryText> \
     </Context> \
    <SortByProperties><SortByProperty name='Rank' direction='Descending' order='1'/></SortByProperties> \
     <Range><StartAt>1</StartAt><Count>2000</Count></Range> \
     <EnableStemming>false</EnableStemming> \
     <TrimDuplicates>true</TrimDuplicates> \
     <IgnoreAllNoiseQuery>true</IgnoreAllNoiseQuery> \
     <ImplicitAndBehavior>true</ImplicitAndBehavior> \
     <IncludeRelevanceResults>true</IncludeRelevanceResults> \
     <IncludeSpecialTermResults>true</IncludeSpecialTermResults> \
     <IncludeHighConfidenceResults>true</IncludeHighConfidenceResults> \
    </Query></QueryPacket>"]]>;
   
   queryXML = queryXML.replace('#qry#', qry);
   
   var soapEnv =
    <![CDATA["<soap:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'> \
      <soap:Body> \
     <Query xmlns='urn:Microsoft.Search'> \
       <queryXml>" + escapeHTML(queryXML) + "</queryXml> \
     </Query> \
      </soap:Body> \
    </soap:Envelope>"]]>;   

   $.blockUI({ message: '<p>Please wait while the contacts are being loaded...</p>' });
    
   $.ajax({
    url: window.location.protocol + "//" + window.location.host + "/_vti_bin/search.asmx",
    type: "POST",
    dataType: "xml",
    data: soapEnv,
    complete: processResult,
    contentType: "text/xml; charset=\"utf-8\""
   });
   
   function processResult(xData, status) {
    var html = "";
    html += "&lt;div class='alphaBrowseNavHeader'&gt;" + alpha + "&lt;/div&gt;";
    html += "&lt;div class='rightActionsWrapper'&gt;";
    $(xData.responseXML).find("QueryResult").each(function() {
     var x = $("<xml>" + $(this).text() + "</xml>");

     /* Find value of each property */ 
     x.find("Document").each(function() {
      var department = '', workPhone = '', pictureUrl = '', workEmail = '', location = '', lastName = '', firstName = '', company = '', mobilePhone = '', preferredname = '', accountName = '', accountNameEncoded = '', jobTitle = '', responsibility = '';
      $(this).find("Property").each(function() {
       switch($(this).find("Name").text())
       {
        case 'DEPARTMENT':
         department = $(this).find("Value").text();
         break;
        case 'WORKPHONE':
         workPhone = $(this).find("Value").text();
         break;          
        case 'PICTUREURL':
         pictureUrl = $(this).find("Value").text();
         break;          
        case 'WORKEMAIL':
         workEmail = $(this).find("Value").text();
         break;          
        case 'BASEOFFICELOCATION':
         location = $(this).find("Value").text();
         break;          
        case 'LASTNAME':
         lastName = $(this).find("Value").text();
         break;          
        case 'FIRSTNAME':
         firstName = $(this).find("Value").text();
         break;          
        case 'COMPANY':
         company = $(this).find("Value").text();
         break;          
        case 'MOBILEPHONE':
         mobilePhone = $(this).find("Value").text();
         break;          
        case 'PREFERREDNAME':
         preferredname = $(this).find("Value").text();
         break;          
        case 'ACCOUNTNAME':
         accountName = $(this).find("Value").text();
         accountNameEncoded = accountName.replace('\\','%5C');
         break;          
        case 'JOBTITLE':
         jobTitle = $(this).find("Value").text();
         break;  
        case 'RESPONSIBILITY':
         responsibility = $(this).find("Value").text();
         break;  
        default:
         break;
       }
      })
      
      /* If no picture is available, display the temp place holder image */
      if(pictureUrl == '')
      {
       pictureUrl = '/Style%20Library/Images/temp_placeholder.gif';
      }          
      
      var title = $("Title", $(this)).text();
      var url = $("Action>LinkUrl", $(this)).text();
      var description = $("Description", $(this)).text()
       
      html += 
      <![CDATA["<div class='rightActionsBox'> \
       <a href='"+ url +"'><img alt='Photo of " + preferredname + "' border='0' width='158' class='rightActionsBoxImage' src='"+pictureUrl+"' /></a> \
       <div class='rightActionsCopy' sizset='122' sizcache='16'> \
        <a href='"+ url +"'><span class='name'>" + preferredname + "</span></a> \
        <table class='assocDirResultTable'> \
         <tbody> \
          <tr> \
           <td> \
            <span class='property title'>&#160;#jobTitle#</span> \
            <span class='property'><label>Work:</label>&#160;#workPhone#</span> \
            <span class='property'><label>Mobile:</label>&#160;#mobilePhone#</span> \
            <span class='property' sizset='122' sizcache='16'>#accountLink#</span> \
           </td> \
           <td> \
            <span class='property'><label>Business:</label>&#160;#company#</span> \
            <span class='property'><label>Department:</label>&#160;#department#</span> \
            <span class='property'><label>Location:</label>&#160;#location#</span> \
           </td> \
           <td class='last'><label>Ask me about</label> \
            <span class='property'>#responsibility#</span> \
           </td> \
          </tr> \
         </tbody> \
        </table> \
       </div> \
       <ul class='rightActions' sizset='123' sizcache='16'> \
        <li class='iconCal' sizset='123' sizcache='16'> \
         <a href='#' onclick='ShowDialog(#AddToColleageCall#, null, AddToColleaguesDialogCallback);return false;'>Add As Colleague</a> \
        </li> \
        <li class='iconPeople' sizset='124' sizcache='16'> \
         <a href='#' onclick='ShowDialog(#ShowOrganizationHierarchyCall#, null, ShowOrganizationHierarchyDialogCallback);return false;'>Browse In Organization Chart</a> \
        </li> \
        <li class='iconFlagGreen' sizset='125' sizcache='16'> \
         <a href='#' onclick='ShowDialog(#ViewRecentContentCall#, null, ViewRecentContentDialogCallback);return false;'>View Recent Content</a> \
        </li> \
       </ul> \
      </div>"]]>
      html = html.replace('#jobTitle#', jobTitle).replace('#workPhone#', workPhone).replace('#mobilePhone#', mobilePhone).replace('#company#', company).replace('#department#', department).replace('#location#', location).replace('#responsibility#', responsibility).replace('#AddToColleageCall#','"' + 'http://[Site URL]/my/_layouts/QuickLinksDialogForm.aspx?Mode=Person&amp;NTName=' + accountNameEncoded + '&amp;IsDlg=1' + '"').replace('#ShowOrganizationHierarchyCall#','"' + 'http://[site url]/my/OrganizationView.aspx?ProfileType=User&amp;accountname=' + accountNameEncoded + '"').replace('#ViewRecentContentCall#','"' + 'http://[site URL]/my/personcontent.aspx?accountname=' + accountNameEncoded + '"').replace('#workEmail#',workEmail);
      if(workEmail != '')
      {
       html = html.replace('#accountLink#','&lt;a href=\'mailto:#workEmail#\'&gt;#accountName#&lt;/a&gt;');
       html = html.replace('#workEmail#', workEmail);
       html = html.replace('#accountName#', accountName);
      }
      else 
       html = html.replace('#accountLink#',accountName);
     });
    });
    html += "&lt;/div&gt;"; 
    $("#searchResults").empty().append(html);
   }
  } 
  
  <![CDATA[
  function escapeHTML (str) {
   return str.replace(/</g,'&lt;').replace(/>/g,'&gt;');
  }
  ]]>
  </script>
  <xsl:variable name="Rows" select="/Search_Results/All_Results/Result"/>
  <div id="assocDirResultBox" sizset="115" sizcache="16">
   <xsl:variable name="AlphabetNodes">
    <xsl:call-template name="Alphabets"/>
   </xsl:variable>
   <xsl:variable name="AlphabetName">
    <xsl:apply-templates select="msxsl:node-set($AlphabetNodes)/Alpha">
     <xsl:sort select="." order="ascending"/>
    </xsl:apply-templates>
   </xsl:variable>
   <xsl:variable name="TitleNames">
    <xsl:for-each select="$Rows[generate-id(.) = generate-id(key('keyTitle', translate(substring(lastname,1,1),$smallcase,$uppercase))[1])]">
     <xsl:value-of select="translate(substring(lastname,1,1),$smallcase,$uppercase)"/>
    </xsl:for-each>
   </xsl:variable>
   <a name="top_AssosiatesAToZ"></a>
   <div class="alphaBrowseNavBox" sizcache="16" sizset="111">
    <div class="alphaBrowseNav" sizcache="16" sizset="111">
     <xsl:for-each select="msxsl:node-set($AlphabetNodes)/Alpha">
      <xsl:if test="position() &lt;= count(msxsl:node-set($AlphabetNodes)/Alpha)">
       <xsl:variable name="char" select="child::node()[position()]"/>
       <xsl:choose>
        <xsl:when test="contains($TitleNames,$char)">
         <a>
          <xsl:attribute name="href">
           #<xsl:value-of select="child::node()[position()]"/>
          </xsl:attribute>
          <xsl:attribute name="onclick">
           GetContacts('<xsl:value-of select="child::node()[position()]"/>');
          </xsl:attribute>          
          <xsl:value-of select="child::node()[position()]"/>
         </a>
        </xsl:when>
        <xsl:otherwise>
         <span>
          <xsl:value-of select="child::node()[position()]"/>
         </span>
        </xsl:otherwise>
       </xsl:choose>
      </xsl:if>
     </xsl:for-each>
    </div>
   </div>
   <div id="searchResults">
   </div>
  </div>
 </xsl:template>
 <!-- A generic function that can be used to replace strings in a given text. Similar to String.Replace of C# -->
 <xsl:template name="ReplaceAllCharsInString">
  <xsl:param name="text"/>
  <xsl:param name="replace"/>
  <xsl:param name="by"/>
  <xsl:choose>
   <xsl:when test="contains($text, $replace)">
    <xsl:value-of select="substring-before($text,$replace)"/>
    <xsl:value-of select="$by"/>
    <xsl:call-template name="ReplaceAllCharsInString">
     <xsl:with-param name="text" select="substring-after($text,$replace)"/>
     <xsl:with-param name="replace" select="$replace"/>
     <xsl:with-param name="by" select="$by"/>
    </xsl:call-template>
   </xsl:when>
   <xsl:otherwise>
    <xsl:value-of select="$text"/>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>
</xsl:stylesheet>

5 comments:

  1. Hi

    I've tried to modify the core result webpart on the people search result page. But when I add your XML, the result is, that only the of letters from A-Z are displayed, but nothing else. :(
    There is a javascript error on the page, but only points to the line, that place the webpart on the result page :(
    Could you please point me in the right direction, or let me know what I am doing wrong.

    Regards,
    Michael Caspersen

    ReplyDelete
  2. Can u tell me what the JS error says?

    ReplyDelete
  3. Line 1012, Char 3, Error: Object expected, Code: 0

    When I do view source, line 1012 contains:
    function ShowOrganizationHierarchyDialogCallback(dialogResult, returnValue)

    I couldn't paste the html code, so not much information i'm afraid :(

    Regards Michael

    ReplyDelete
  4. I am facing same problem of javascript when I used this xsl in people search result page.
    exception come on below line
    $(document).ajaxStop($.unblockUI);
    object expected.kindly help what is the cause of this problem

    ReplyDelete
  5. Please make sure that you have installed the AJAX library and pointing to it properly.

    ReplyDelete