SharePoint Image Search (Part 4)

Introduction

This is Part 4 of a 4 part series on how to create an image search center on a MOSS site. My goal is to create a search results page similar to the Microsoft Live Search Images page. In Part 1 I explored the out of the box search experience for images and walked through the setup required to get the indexer to crawl image file types. In Part 2 I installed the XMP IFilter and demonstrated the capabilities related to Managed Properties. In Part 3 I detailed how to configure Crawled Properties and create a Pictures search scope. Then we configured a basic search results page. We concluded the post with an issue: How do I make my search results sexy?

Search Results Configuration

The Search Core Results Web part can be edited to display our results. The challenge is that the edit experience of the web part itself is not very good. I prefer to create a new XSL file and reference that file from the Search Core Results Web part XLS Link property.

  1. Begin the process by opening the root of your publishing site with SharePoint Designer. Browse to the Style Library/XSL Style Sheets folder and create a new HTML file. Right click the new file and rename it to pictureresults.xsl. Double click the file to edit it.

  2. Return to the Picture Search results page and edit the Search Core Results Web part. Click the XSL Editor button and select and copy the contents of the entire edit dialog to the clipboard, paste this text over the default text in the pictureresults.xsl file you opened in SharePoint Designer. (Important: your file should now contain everything that was in the Search Core Results dialog and NOTHING else.)

  3. Save the file and check it in.
  4. In the SharePoint Designer file browser right click the pictureresults.xsl file and choose Properties…

  5. Copy the Title text to the clipboard and return to the search results page. Close the XSL Edit dialog if it is still open. Paste the file title into the XSL Link property ensuring that you prefix the path with a slash as shown here:
  6. Click OK to save your changes to the Search Results page and publish the page. Your page should function correctly without any errors. At this point I usually edit the XSL to ensure that I have everything configured correctly.

CSS and XSL for Image Display

At this point it is only a matter of editing the XSL to display all of the property values that we want to return and use XSL and CSS to make it pretty. When I start on a project like this I always mock up the page with regular HTML and CSS then transfer the working results to my XSL page, in this case my HTML test page looks like this:

CSS First

  1. Begin by creating a new style sheet in the /Style Library called pictureresults.css. Paste in the following CSS code:

    .result
    {
        float:left;
        vertical-align:middle;
        padding: 25px 15px 25px 15px;    
        border: 1px gray solid;
        margin: 10px;
        height: 250px;
        width: 182px;
    }
     
    .picture
    {
        height:175px;
        text-align:center;
        line-height:125px;
        width: 182px;
    }
     
    .properties
    {
        font-family: verdana;
        font-size: 0.9em;
        width:180px;
        height:60px;
        border: 1px gray solid;
        padding: 2px;
    }
     
    .picture img
    {
        vertical-align:middle;
        border: none;
    }
     
    .banner
    {
        background-color:aqua;
        width: 182px;
        height:20px;
        visibility: hidden;
    }

  2. Save and check in the file.
  3. Browse to the site settings page for your search site and choose Master Page.
  4. Edit the Alternate Style Sheet setting to reference your new style sheet.

    (I know that there are a lot of different ways to do this, for this demo I found this to be the simplest.)

  5. Return to your search page and run a simple search, at this point you should not notice anything different.

XSL for Image Display

  1. Open the pictureresults.xsl file and locate the result template, it begins with

    <xsl:template match="Result">

    Select the entire template and replace it with the following templates:

     
        <xsl:template match="Result">
         <xsl:variable name="url" select="url"/>
        
            <div id="result{id}" class="result">
                <div id="image{id}" class="picture" >
                    <xsl:call-template name="DisplayThumbnail">
                        <xsl:with-param name="str" select="url"/>
                        <xsl:with-param name="height" select="pictureheight" />
                        <xsl:with-param name="width" select="picturewidth" />
                    </xsl:call-template>
                    </div>
                <div id="props{id}" class="properties">
                    <xsl:call-template name="DisplaySite">
                        <xsl:with-param name="title" select="title"/>
                        <xsl:with-param name="url" select="sitename" />
                        <xsl:with-param name="isdocument" select="isdocument"/>
                    </xsl:call-template>
                    <xsl:call-template name="DisplayTitle">
                        <xsl:with-param name="str" select="title"/>
                        <xsl:with-param name="url" select="url"/>
                    </xsl:call-template>
                    
                    <xsl:call-template name="DisplayDim">
             <xsl:with-param name="height" select="pictureheight" />
             <xsl:with-param name="width" select="picturewidth" />
             </xsl:call-template>
                    <xsl:call-template name="DisplaySize">
             <xsl:with-param name="size" select="size" />
             </xsl:call-template>
                </div>
            </div>
        
        </xsl:template>
        
        <xsl:template name="DisplayThumbnail">
            <xsl:param name="str" />
            <xsl:param name="height" />
            <xsl:param name="width" />
            <xsl:if test='string-length($str) &gt; 0'>
                <a>
                    <xsl:attribute name="href"><xsl:value-of select="$str" /></xsl:attribute>
                <img>
                    <xsl:attribute name="src">
                        <xsl:value-of select="$str" />
                    </xsl:attribute>
                    <xsl:choose>
                        <xsl:when test="string-length($height) = 0">
                            <xsl:attribute name="height">
                                125px
                            </xsl:attribute>
                            <xsl:attribute name="width">
                                150px
                            </xsl:attribute>
                        </xsl:when>
                        <xsl:when test="$width &gt; 150 and $width &gt;= $height">
                        <xsl:attribute name="width">
                            <xsl:value-of select="(150 div $width) * $width" />px
                        </xsl:attribute>
                        <xsl:attribute name="height">
                            <xsl:value-of select="(150 div $width) * $height" />px
                        </xsl:attribute>
                    </xsl:when>
                    <xsl:when test="$height &gt; 150 and $width &lt; $height">
                        <xsl:attribute name="width">
                            <xsl:value-of select="(150 div $height) * $width" />
                        </xsl:attribute>
                        <xsl:attribute name="height">
                            <xsl:value-of select="(150 div $height) * $height" />
                        </xsl:attribute>
                    </xsl:when>
                </xsl:choose>
                </img></a>
            </xsl:if>
        </xsl:template>
        
        <xsl:template name="DisplaySite">
            <xsl:param name="title"/>
            <xsl:param name="url" />
            <xsl:param name="isdocument" />
            <xsl:if test='$isdocument = 1'>
                <xsl:if test='string-length($url) &gt; 0'>
                    <xsl:choose>
                        <xsl:when test="starts-with($url, 'file://')">
                         <xsl:element name="a">
                             <xsl:attribute name="href">
                                    <xsl:call-template name="strip">
                                        <xsl:with-param name="relfile"><xsl:value-of select="url"/></xsl:with-param>
                                    </xsl:call-template>    
                                </xsl:attribute>
                                <img src="/_layouts/images/folder.gif" alt="Open file location" style="border:none; vertical-align:bottom;"/>
                            </xsl:element>
                     </xsl:when>
                        <xsl:when test="starts-with($url, 'http://')">
                 <xsl:element name="a"><xsl:attribute name="href">
                         <xsl:call-template name="strip">
                                <xsl:with-param name="relfile"><xsl:value-of select="url"/></xsl:with-param>
                            </xsl:call-template>
                         </xsl:attribute><xsl:attribute name="target">blank</xsl:attribute><img src="/_layouts/images/cat.gif" alt="Open file location" style="border:none; vertical-align:bottom;"/></xsl:element>
                 </xsl:when>
                    </xsl:choose>
                    &#160;
                </xsl:if>
            </xsl:if>
        </xsl:template>
        <xsl:template name="strip">
            <xsl:param name="reldir"/>
            <xsl:param name="relfile"/>
            <xsl:choose>
                <xsl:when test="contains($relfile, '/')">
                    <xsl:call-template name="strip">
                        <xsl:with-param name="relfile">
         <xsl:value-of select="substring-after($relfile,'/')"/>
         </xsl:with-param>
                        <xsl:with-param name="reldir">
         <xsl:value-of select="concat($reldir, substring-before($relfile,'/'), '/')"/>
         </xsl:with-param>
                    </xsl:call-template>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="$reldir"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:template>
     
        <!-- Display the formated Dimensions of the image -->
        <xsl:template name="DisplayDim">
            <xsl:param name="height" />
            <xsl:param name="width" />        
            <xsl:if test='string-length($height) &gt; 0'>
         <br/>dimensions: <xsl:value-of select="$width" />x<xsl:value-of select="$height" />
         </xsl:if>
        </xsl:template>
     
        <xsl:template name="DisplayTitle">
            <xsl:param name="str" />
            <xsl:param name="url" />
            <xsl:if test='string-length($str) &gt; 0'>
                <span style="width=110px; height:1.5em; overflow:hidden;"><a href="{$url}" title="View: {$str}"><xsl:value-of select="$str" /></a></span>
            </xsl:if>
        </xsl:template>

     

  2. Finally, locate the DisplaySize template and replace it with the following:

    <!-- The size attribute for each result is prepared here -->
    <xsl:template name="DisplaySize">
    <xsl:param name="size" />
    <xsl:if test='string-length($size) &gt; 0'>
    <xsl:if test="number($size) &gt; 0">
    <br/>size:
    <xsl:choose>
    <xsl:when test="round($size div 1024) &lt; 1"><xsl:value-of select="$size" /> Bytes</xsl:when>
    <xsl:when test="round($size div (1024 *1024)) &lt; 1"><xsl:value-of select="round($size div 1024)" />KB</xsl:when>
    <xsl:otherwise><xsl:value-of select="round($size div (1024 * 1024))"/>MB</xsl:otherwise>
    </xsl:choose>
    </xsl:if>
    </xsl:if>
    </xsl:template>

  3. Save your XSL file and return to the Picture Search page.
  4. Execute a property search like "fileextension:jpg". Your results should look like the following:

How does it work?

Each component of the results display use the following templates.

  1. Each result row is wrapped in a Result DIV from the main template
  2. Each image is wrapped in an Image DIV.
  3. The images themselves are generated with the DisplayThumbnail template. The math for resizing the images is based on this great post by Jason Addington. This is why I spent so much time on determining the height and width of the images earlier in the article.
  4. The site (or folder) icon is displayed depending on the source of the image as detected by the DisplaySite template.
  5. The properties are displayed in a separate Properties DIV. Each property is displayed using a separate template.

Wrap it up

The goal of this series was to demonstrate many of the common techniques required to deliver a search project with SharePoint. The series has demonstrated the flexibility of configuration available to administrators for enhancing the end user search experience. You can download the XSL and CSS files from this article from the CodePlex Project: SharePoint Search XSL Examples. Now I know that my results don’t pop-out like they do on Microsoft Live Search Images, but that is just a scripting trick, I’ll get to it later.

 

 

About Matthew McDermott