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.
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() <= 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 += "<div class='alphaBrowseNavHeader'>" + alpha + "</div>";
html += "<div class='rightActionsWrapper'>";
$(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'> #jobTitle#</span> \
<span class='property'><label>Work:</label> #workPhone#</span> \
<span class='property'><label>Mobile:</label> #mobilePhone#</span> \
<span class='property' sizset='122' sizcache='16'>#accountLink#</span> \
</td> \
<td> \
<span class='property'><label>Business:</label> #company#</span> \
<span class='property'><label>Department:</label> #department#</span> \
<span class='property'><label>Location:</label> #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&NTName=' + accountNameEncoded + '&IsDlg=1' + '"').replace('#ShowOrganizationHierarchyCall#','"' + 'http://[site url]/my/OrganizationView.aspx?ProfileType=User&accountname=' + accountNameEncoded + '"').replace('#ViewRecentContentCall#','"' + 'http://[site URL]/my/personcontent.aspx?accountname=' + accountNameEncoded + '"').replace('#workEmail#',workEmail);
if(workEmail != '')
{
html = html.replace('#accountLink#','<a href=\'mailto:#workEmail#\'>#accountName#</a>');
html = html.replace('#workEmail#', workEmail);
html = html.replace('#accountName#', accountName);
}
else
html = html.replace('#accountLink#',accountName);
});
});
html += "</div>";
$("#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 += "<div class='alphaBrowseNavHeader'>" + alpha + "</div>";
html += "<div class='rightActionsWrapper'>";
$(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'> #jobTitle#</span> \
<span class='property'><label>Work:</label> #workPhone#</span> \
<span class='property'><label>Mobile:</label> #mobilePhone#</span> \
<span class='property' sizset='122' sizcache='16'>#accountLink#</span> \
</td> \
<td> \
<span class='property'><label>Business:</label> #company#</span> \
<span class='property'><label>Department:</label> #department#</span> \
<span class='property'><label>Location:</label> #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&NTName=' + accountNameEncoded + '&IsDlg=1' + '"').replace('#ShowOrganizationHierarchyCall#','"' + 'http://[site url]/my/OrganizationView.aspx?ProfileType=User&accountname=' + accountNameEncoded + '"').replace('#ViewRecentContentCall#','"' + 'http://[site URL]/my/personcontent.aspx?accountname=' + accountNameEncoded + '"').replace('#workEmail#',workEmail);
if(workEmail != '')
{
html = html.replace('#accountLink#','<a href=\'mailto:#workEmail#\'>#accountName#</a>');
html = html.replace('#workEmail#', workEmail);
html = html.replace('#accountName#', accountName);
}
else
html = html.replace('#accountLink#',accountName);
});
});
html += "</div>";
$("#searchResults").empty().append(html);
}
}
<![CDATA[
function escapeHTML (str) {
return str.replace(/</g,'<').replace(/>/g,'>');
}
]]>
</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() <= 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>