Thursday, April 26, 2012

Customize Search Results with Custom Links

Adding: View Properties, Edit Document, Show Folder, Permalink, and Email Link

A request came my way to add some custom links to the search results of a site collection.  They wanted to view the properties of documents straight from the search results.  There are a few good blogs posts on this topic, and much of what I will share is derived from their good work.  
Once I got started adding the "View Properties" link.  I realized it would be cool to have a few more links for actions such as:
  • Edit Document
  • Show Folder
  • Permalink
  • Email Link

Step 1: Adding Managed Properties to Search

Anne Stenberg has a great article on the "Basic" property category.  I leveraged her information in building my custom links.  Her article can be found at:

To add the needed managed properties open the Farm Search Administration and select your search service application; then, click on the Metadata Properties.  We are going to need to add a couple to do what we need.

The first metadata property we need to add I called ItemID.  This property maps to the List Item "ID" column found in every SharePoint list.  This property will be used to create the URL for the "View Property" link.

The second metadata property need is a property I called host.  This maps to the Basic:4 property  

The third metadata property needed is my "Permalink" property which is the Document link created as part of the Document ID Service feature (read more: http://msdn.microsoft.com/en-us/library/ee559302.aspx).

These new properties will not be present in the search results until the next full crawl.  You can confirm if they are attached to a given result by viewing the search results XML (http://msdn.microsoft.com/en-us/library/ms546985.aspx).

Step 2: Customizing the Search Core Results web part columns.

If you edit the results.aspx file in a search center site, you will find the primary web part on the page is called Search Core Results.  This web part is easily customizable.  We are going to be changing the properties under the Display Properties section.  In order to customize the look and feel of the results the Use Location Visualization check-box must be unchecked.  

You can then modify the Fetched Properties text-box.  We need to add our three new properties to the list so that the web part can consume them.  Each column is declared by an XML tag that looks like this:

 <Column Name="Title"/>  

To add our three new columns we just add them using the same format and end up with a list of Properties:

 <Columns> <Column Name="WorkId"/> <Column Name="Rank"/> <Column Name="Title"/> <Column Name="Author"/>   
 <Column Name="Size"/> <Column Name="Path"/> <Column Name="Description"/> <Column Name="Write"/>   
 <Column Name="SiteName"/> <Column Name="CollapsingStatus"/> <Column Name="HitHighlightedSummary"/>   
 <Column Name="HitHighlightedProperties"/> <Column Name="ContentClass"/> <Column Name="IsDocument"/>   
 <Column Name="PictureThumbnailURL"/> <Column Name="PopularSocialTags"/> <Column Name="PictureWidth"/>   
 <Column Name="PictureHeight"/> <Column Name="DatePictureTaken"/> <Column Name="ServerRedirectedURL"/>   
 <Column Name="ItemID"/> <Column Name="Host"/> <Column Name="Permalink"/> </Columns>  

Step 3: Customizing the XSL.

The other part we will be customizing is the XSL that controls how the XML search results are rendered.  I am including my XSL code blocks below, but first I will explain what they do.

The first block is added to the results body and is placed just below the result metadata.  This block of code calls a series of custom XSL templates.  Each template renders one of the links.  I have named the templates in a way that it should be self-evident which link they render.  I pass to these templates the parameters needed to render the link (notice, our custom managed properties are referenced, as well as a few default managed properties).  To implement this code, add the following lines to your XSL (using the XSL Editor) below the div tag with the "srch-Metadata2" class:

 <div class=”srch-Metadata2″>  
   <xsl:call-template name=”ViewProperties”>  
    <xsl:with-param name=”Url” select=”url” />  
    <xsl:with-param name=”itemID” select=”itemid” />  
    <xsl:with-param name=”isDocument” select=”isdocument” />  
    <xsl:with-param name=”host” select=”host” />  
   </xsl:call-template>  
   
   <xsl:call-template name=”EditDocument”>  
    <xsl:with-param name=”Url” select=”url” />  
    <xsl:with-param name=”contentclass” select=”contentclass” />  
   </xsl:call-template>  
   
   
   <xsl:call-template name=”ShowFolder”>  
    <xsl:with-param name=”Url” select=”sitename” />  
    <xsl:with-param name=”itemID” select=”itemid” />  
   </xsl:call-template>  
   
   <xsl:call-template name=”ShowPermalink”>  
    <xsl:with-param name=”Permalink” select=”permalink” />  
    <xsl:with-param name=”contentclass” select=”contentclass” />  
   </xsl:call-template>  
   
   <xsl:call-template name=”EmailLink”>  
   <xsl:with-param name=”Permalink” select=”permalink” />  
   <xsl:with-param name=”Url” select=”urlEncoded” />  
   </xsl:call-template>  
 </div>  

The second block of code is the templates.  The most complicated of these templates is the ViewProperties template.  I leaned heavily on the work of other people to build this template and I suggest you visit their sites linked above.  The sites above did have some bugs in their template and so I have made modifications to compensate for those bugs (namely the template did not generate the correct link for a document library with folders).  The rest of the templates are much more straight forward.  To add these templates to the XSL, just before the line that says "<!-- XSL transformation starts here -->" place the following code:

 <xsl:template name="ViewProperties">  
  <xsl:param name="Url" />  
  <xsl:param name="itemID" />  
  <xsl:param name="host" />  
  <xsl:param name="isDocument" />  
  <xsl:if test="string-length($itemID) > 0">  
     <xsl:variable name="UrlLcase" select="translate($Url, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/>  
     <xsl:variable name="hostLcase" select="translate($host, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/>  
     <xsl:choose>  
    <xsl:when test="$isDocument = 'True'">  
        <xsl:variable name="docLib" select="substring-before(substring-after(substring-after($UrlLcase,$hostLcase), '/'), '/')" />  
        <xsl:variable name="viewPropUrl" select="concat($host, '/', $docLib, '/Forms/DispForm.aspx?id=', $itemID)" />  
        <a href="{$viewPropUrl}">View Properties</a>  
        <xsl:text disable-output-escaping="yes">&amp;nbsp;|&amp;nbsp;</xsl:text>  
    </xsl:when>  
    <xsl:when test="$UrlLcase = $hostLcase">  
    </xsl:when>  
    <xsl:otherwise>     
        <xsl:variable name="listLoc" select="substring-before(substring-after(substring-after($UrlLcase,concat($hostLcase,'/lists')), '/'), '/')" />  
        <xsl:variable name="viewPropUrl" select="concat($host, '/lists/', $listLoc, '/DispForm.aspx?id=', $itemID)" />  
        <a href="{$viewPropUrl}">View Properties</a>  
        <xsl:text disable-output-escaping="yes">&amp;nbsp;|&amp;nbsp;</xsl:text>  
    </xsl:otherwise>  
    </xsl:choose>  
  </xsl:if>  
 </xsl:template>  
   
 <xsl:template name="EditDocument">  
   
    <xsl:param name="Url" />  
    <xsl:param name="contentclass" />  
    <xsl:if test="$contentclass='STS_ListItem_DocumentLibrary'">  
       <a href="{$Url}" onclick="return DispEx(this,event,'TRUE','FALSE','FALSE','SharePoint.OpenDocuments.3','0','SharePoint.OpenDocuments','','','','1','0','0','0x7fffffffffffffff')">Edit Document</a>  
        <xsl:text disable-output-escaping="yes">&amp;nbsp;|&amp;nbsp;</xsl:text>  
    </xsl:if>  
 </xsl:template>  
   
 <xsl:template name="ShowFolder">  
    <xsl:param name="Url" />  
    <xsl:param name="itemID" />  
    <xsl:if test="string-length($itemID) > 0">  
       <a href="{$Url}">Show Folder</a>  
        <xsl:text disable-output-escaping="yes">&amp;nbsp;|&amp;nbsp;</xsl:text>  
    </xsl:if>  
 </xsl:template>  
   
 <xsl:template name="ShowPermalink">  
    <xsl:param name="Permalink" />  
    <xsl:param name="contentclass" />  
    <xsl:if test="string-length($Permalink) > 0">  
       <a href="{$Permalink}">Permalink</a>  
        <xsl:text disable-output-escaping="yes">&amp;nbsp;|&amp;nbsp;</xsl:text>  
    </xsl:if>  
 </xsl:template>  
   
 <xsl:template name="EmailLink">  
    <xsl:param name="Permalink" />  
    <xsl:param name="Url" />  
    <xsl:choose>  
    <xsl:when test="string-length($Permalink) > 0">  
       <a href="mailto:?body={$Permalink}">Email Link</a>  
    </xsl:when>  
    <xsl:otherwise>  
       <a href="mailto:?body={$Url}">Email Link</a>  
    </xsl:otherwise>  
    </xsl:choose>  
 </xsl:template>  

Last Thoughts: 

Once you have made the changes in the XSL Editor, apply the changes and save the page.  You should now see results that have an extra line of links like this:



The templates will confirm that the different links apply to each result.  I have found a few minor bugs (for example on mysites the View Properties link still sometimes does not work).  The one thing I would like to add is a source parameter that gets passed in the View Properties link.  This would ensure that if the user clicks close that SharePoint would return to the results page.  If I get it working I will post the tweak.

No comments:

Post a Comment