The customization of the search experience in MOSS is crucial in many situations. You may need to add additional metadata to your display results, to modify the search result page layout or to add additional metadata properties to your advanced search page. In the paragraphs below I am going to demonstrate a simple scenario of MOSS search experience customization.

In the first part of this example I am going to create a tabular layout of my search results page for an e-protocol SharePoint list. I am going to demonstrate the way to add additional metadata to the search results page, to modify the layout using SharePoint Designer and in the second part to modify the advanced search page to include more options to query.

Before I start customizing my search results page I need to populate some custom columns, I created to my e-protocol SharePoint list. Each time SharePoint crawls the farm to create indexed content, it adds any custom column (metadata property) it finds in Lists, Document Libraries and Sites, to its metadata properties database. But for those properties to become manageable, MOSS administrator needs to create a “managed property” record for each of the metadata he needs to manage. Notice that each “managed property” can map to one or more metadata properties. For example, if I want to create a managed property for the ProtocolID metadata, I would create a single map for this property. This could become available to my search results page and advanced search queries. Now consider the following scenario: I want to create a single managed property to query and display all kinds of calling number information across my contacts lists. I should create a single managed property which maps to multiple metadata properties: mobile phone, home phone, company phone, etc.

I am going to create seven (7) managed properties for my Protocol Application List. In order to create these managed properties I need to navigate to the Search Administration Page of my SSP:

  • From there I navigate to the metadata properties in the side menu and I select new managed property.
  • I type an appropriate name for my new managed property (ex ProtID)
  • I select the appropriate type of this property (ex Text)
    image
  • I add the appropriate mapping by selecting a crawled metadata property to map to this managed property (ex ows_PID)
    image_0
  • I Press the OK button

At this point I repeat the same procedure for the next six (6) managed properties I want to create:
image_1

From that point, I want to modify my search results page. In my search results page I want to display, in a tabular format, the ProtPID, ProtSubject, ProtINOUT, ProtDocumentDate, ProtNTOriginDestination and ProtEXTOriginDestination properties I have created in the previous steps. SharePoint server/services return the search results in an XML format. The visual presentation of this XML format is done by XSLT and CSS. So, in order to manipulate the visual representation of those results I have to extract their raw XML form.

To extract the raw XML form of my search results:

  • I navigate to my search center web site
  • I submit a search term in the search box (ex Test)
  • In the search results page I navigate to the site actions menu and I select the edit page menu item
    image_2
  • I select the edit menu of the Search Core Results WebPart and the Modify Shared Web Part option
    image_3
  • In the Search Core Results –> Data View Properties –> I press XSL Editor..
    image_4
  • I replace all code with the following:
    <xsl:stylesheet version=”1.0″ xmlns:xsl=”http://www.w3.org/1999/XSL/Transform” >
    <xsl:output method=”xml” version=”1.0″ encoding=”UTF-8″ indent=”yes” />
    <xsl:template match=”/”>
    <xmp><xsl:copy-of select=”*”/></xmp>
    </xsl:template>
    </xsl:stylesheet>
  • I press the apply Button

Notice the changes to the core results. A raw XML format of the results is displayed. But wait a minute… Where is the managed properties I created earlier? The answer is nowhere. I need to tell the WebPart to add those properties to the XML results:

  • In the Results Query Options of the Search Core Results properties, press the … icon in the Selected Columns option
    image_5
  • Notice the format of and the changes I made to the bottom of the XML:
    <root xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”>
    <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=”ProtOurPID”/>
    <Column Name=”ProtSubject”/>
    <Column Name=”ProtINOUT”/>
    <Column Name=”ProtDocumentDate”/>
    <Column Name=”ProtINTOriginDestination”/>
    <Column Name=”ProtEXTOriginDestination”/>

    </Columns>
    </root>
  • Before the closing Columns statement </Columns> I added 6 of my managed properties in the form of: <Column Name=”MyManagedProperty” />
  • I press the OK Button

Now, I have my Raw XML results with my managed properties included. To manipulate their layout using Sharepoint Designer, I have to save those results as an XML file and import it as a data source:

  • I copy the raw XML format of the search results (from <All_Results> to </All_results>) into notepad and I save the file as RawXMLResults.xml to c:
  • I open my SharePoint site and I temporarily save the RawXMLResults.xml to one of my document libraries
  • In SharePoint Designer I create a new ASPX page
    image_6
  • In the Data View menu I select the Insert Data View Option…
    image_7
  • In the Data Source Library (Right Pane in SharePoint Designer) I expand the XML Files, and I select the Add an XML File… hyperlink:
    image_8
  • In the new window I select the Source tab and I browse for the RawXMLResults.xml file I saved in a previous step to one of my document libraries
  • After pressing OK the new XML file appears in the list of my XML Datasources
  • By selecting the file, a drop down menu appears. I click the Show Data Option
    image_9
  • Having selected the data source webpart in my ASPX Page
    image_10
  • I select the columns I want to add to my layout and from the Insert Selected Fields as…  menu, I select the Multiple Item View
    image_11
  • Note that the order I selected the columns, is the order that is displayed in my table
  • After making the appropriate modifications to my layout, I need to copy the XSLT code  and paste it back to my Search Core Results WebPart. I copy the code from the <xsl:stylesheet … to the </xsl:stylesheet> element.
    image_12
  • My final step is to paste the code to the XSL Editor… in my Search Core Results WebPart properties to see the effect.
    image_4

Personally I prefer to manipulate XSLT by using external file references instead of pasting the code directly to the WebPart. If you want to achieve the same, do the following:

  • Create a document library to host your XSL files (ex Transformations)
  • Instead of pasting the code from Sharepoint Designer directly to the WebPart, paste it to notepad and save the file as something.xsl
  • Upload something.xsl to your document library
  • In the XSL Editor… of the Search Core Results WebPart Reference to the file by using the sample code below:
    <?xml version=”1.0″ encoding=”ISO-8859-1″?>
    <xsl:stylesheet version=”1.0″
    xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”><xsl:import href=”/Transformations/something.xsl“/><xsl:template match=”/”>
    <xsl:apply-imports/>
    </xsl:template>

    </xsl:stylesheet>

A last thing. In order to be able to sort results by relevance / by date and to display the predefined text in case there are no results to return back, you have to copy the following code from the shipped results XSLT to your custom one:

<xsl:param name=”ResultsBy” />
<xsl:param name=”ViewByUrl” />
<xsl:param name=”ViewByValue” />
<xsl:param name=”IsNoKeyword” />
<xsl:param name=”IsFixedQuery” />
<xsl:param name=”ShowActionLinks” />
<xsl:param name=”MoreResultsText” />
<xsl:param name=”MoreResultsLink” />
<xsl:param name=”CollapsingStatusLink” />
<xsl:param name=”CollapseDuplicatesText” />
<xsl:param name=”AlertMeLink” />
<xsl:param name=”AlertMeText” />
<xsl:param name=”SrchRSSText” />
<xsl:param name=”SrchRSSLink” />
<xsl:param name=”ShowMessage” />
<xsl:param name=”IsThisListScope” />
<xsl:param name=”DisplayDiscoveredDefinition” select=”True” />
<xsl:param name=”NoFixedQuery” />
<xsl:param name=”NoKeyword” />
<xsl:param name=”NoResults” />
<xsl:param name=”NoResults1″ />
<xsl:param name=”NoResults2″ />
<xsl:param name=”NoResults3″ />
<xsl:param name=”NoResults4″ />
<xsl:param name=”DefinitionIntro” /><!– When there is keywory to issue the search –>
<xsl:template name=”dvt_1.noKeyword”>
<span class=”srch-description”>
<xsl:choose>
<xsl:when test=”$IsFixedQuery”>
<xsl:value-of select=”$NoFixedQuery” />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select=”$NoKeyword” />
</xsl:otherwise>
</xsl:choose>
</span>
</xsl:template><!– When empty result set is returned from search –>
<xsl:template name=”dvt_1.empty”>
<div class=”srch-sort”>
<xsl:if test=”$AlertMeLink and $ShowActionLinks”>
<span class=”srch-alertme” > <a href =”{$AlertMeLink}” id=”CSR_AM1″ title=”{$AlertMeText}”><img style=”vertical-align: middle;” src=”/_layouts/images/bell.gif” alt=”” border=”0″/><xsl:text disable-output-escaping=”yes”>&amp;nbsp;</xsl:text><xsl:value-of select=”$AlertMeText” /></a>
</span>
</xsl:if>

<xsl:if test=”string-length($SrchRSSLink) &gt; 0 and $ShowActionLinks”>
<xsl:if test=”$AlertMeLink”>
|
</xsl:if>
<a type=”application/rss+xml” href =”{$SrchRSSLink}” title=”{$SrchRSSText}” id=”SRCHRSSL”><img style=”vertical-align: middle;” border=”0″ src=”/_layouts/images/rss.gif” alt=””/><xsl:text disable-output-escaping=”yes”>&amp;nbsp;</xsl:text><xsl:value-of select=”$SrchRSSText”/></a>
</xsl:if>
</div>
<br/> <br/>

<span class=”srch-description” id=”CSR_NO_RESULTS”>
<xsl:value-of select=”$NoResults” />

<ol>
<li><xsl:value-of select=”$NoResults1″ /></li>
<li><xsl:value-of select=”$NoResults2″ /></li>
<li><xsl:value-of select=”$NoResults3″ /></li>
<li><xsl:value-of select=”$NoResults4″ /></li>
</ol>
</span>
</xsl:template>

<!– Main body template. Sets the Results view (Relevance or date) options –>
<xsl:template name=”dvt_1.body”>
<div class=”srch-results”>
<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=”$AlertMeLink”>
|
<span class=”srch-alertme” > <a href =”{$AlertMeLink}” id=”CSR_AM2″ title=”{$AlertMeText}”><img style=”vertical-align: middle;” src=”/_layouts/images/bell.gif” alt=”” border=”0″/><xsl:text disable-output-escaping=”yes”>&amp;nbsp;</xsl:text><xsl:value-of select=”$AlertMeText” /></a>
</span>
</xsl:if>
<xsl:if test=”string-length($SrchRSSLink) &gt; 0″>
|
<a type=”application/rss+xml” href =”{$SrchRSSLink}” title=”{$SrchRSSText}” id=”SRCHRSSL”><img style=”vertical-align: middle;” border=”0″ src=”/_layouts/images/rss.gif” alt=””/><xsl:text disable-output-escaping=”yes”>&amp;nbsp;</xsl:text><xsl:value-of select=”$SrchRSSText”/></a>
</xsl:if>
</div>
<br /><br />
</xsl:if>
<xsl:apply-templates />

</div>
<xsl:call-template name=”DisplayMoreResultsAnchor” />
</xsl:template>

<!—Your Results Template Goes Here –>  

<!—Your Results Template Goes Here –>

<!– The “view more results” for fixed query –>
<xsl:template name=”DisplayMoreResultsAnchor”>
<xsl:if test=”$MoreResultsLink”>
<a href=”{$MoreResultsLink}” id=”CSR_MRL”>
<xsl:value-of select=”$MoreResultsText”/>
</a>
</xsl:if>
</xsl:template>

<xsl:template match=”All_Results/DiscoveredDefinitions”>
<xsl:variable name=”FoundIn” select=”DDFoundIn” />
<xsl:variable name=”DDSearchTerm” select=”DDSearchTerm” />
<xsl:if test=”$DisplayDiscoveredDefinition = ‘True’ and string-length($DDSearchTerm) &gt; 0″>
<script language=”javascript”>
function ToggleDefinitionSelection()
{
var selection = document.getElementById(“definitionSelection”);
if (selection.style.display == “none”)
{
selection.style.display = “inline”;
}
else
{
selection.style.display = “none”;
}
}
</script>
<div>
<a href=”#” onclick=”ToggleDefinitionSelection(); return false;”>
<xsl:value-of select=”$DefinitionIntro” /><b><xsl:value-of select=”$DDSearchTerm”/></b></a>
<div id=”definitionSelection” class=”srch-Description” style=”display:none;”>
<xsl:for-each select=”DDefinitions/DDefinition”>
<br/>
<xsl:variable name=”DDUrl” select=”DDUrl” />
<xsl:value-of select=”DDStart”/>
<b>
<xsl:value-of select=”DDBold”/>
</b>
<xsl:value-of select=”DDEnd”/>
<br/>
<xsl:value-of select=”$FoundIn”/>
<a href=”{$DDUrl}”>
<xsl:value-of select=”DDTitle”/>
</a>
</xsl:for-each>
</div>
</div>
</xsl:if>
</xsl:template>

<!– XSL transformation starts here –>
<xsl:template match=”/”>
<xsl:if test=”$AlertMeLink”>
<input type=”hidden” name=”P_Query” />
<input type=”hidden” name=”P_LastNotificationTime” />
</xsl:if>
<xsl:choose>
<xsl:when test=”$IsNoKeyword = ‘True'” >
<xsl:call-template name=”dvt_1.noKeyword” />
</xsl:when>
<xsl:when test=”$ShowMessage = ‘True'”>
<xsl:call-template name=”dvt_1.empty” />
</xsl:when>
<xsl:otherwise>
<xsl:call-template name=”dvt_1.body”/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>