Matthew McDermott, MVP

General ramblings from a SharePoint MVP about SharePoint and Microsoft technologies related to collaboration, web content management and productivity.

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.xsl. 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.

 

 

Posted by Matthew McDermott on Monday, 1 Sep 2008 05:00
25 Comments | Filed under: Search, Web Publishing
Bookmark this post with:        

Comments

On 08 Sep 2008 11:16, Steve said:

Great series. Really fantastic community content. I have tried implementing this and having a strange problem. My search scope wont include any items where contenttype=Picture. It includes image/jpeg etc but shows zero items for Picture. Any ideas?

On 08 Sep 2008 04:15, Matthew said:

What content types are you seeing in your regular search results? Are you seeing Picture? Are you using Include or Required, you should use include.

On 09 Sep 2008 02:00, Steve said:

Ok, seems like I had to do a full crawl to get the scope rules with content type=Picture to work. Anyway, now I have the full solution setup but the thumbnails for contenttypes of picture are not displaying. They display when they are images from my fileshare. I'm sure it has something to do with the url. The XML results show the url to be It must be because the url is returning a link to the list item not the jpeg image itself? http://knew2n:90/personal/shaund/Shared Pictures/Forms/DispForm.aspx?ID=2&RootFolder=/personal/shaund/Shared Pictures/Profile Pictures

On 09 Sep 2008 03:06, Steve said:

Some more info...my picture contenttypes all have isDocument=0 AT the moment I am using thumbnailurl which is returning a correct link to the actual image and so the images in the results display correctly. Just can't seem to catch the image url when the source is a SharePOint Picture Library. Anyway, thanks again.

On 09 Sep 2008 07:15, Matthew said:

Are you using a JPEG IFilter? If not the results from SharePoint crawls will not return the file link, only the item link. There is another property that you can use, but I need to look it up.

On 09 Sep 2008 05:44, Frank said:

Thanks for shere with us this great information ! Sharepoint and Share file some thing new for me! so ,m looking forward to learn more from You!

On 10 Sep 2008 01:36, Steve said:

Yup, I installed the iFilter you recommended. Images from my file share are working no problem.

On 10 Sep 2008 12:35, Matthew said:

OK, The next step would be to confirm your crawl results. Check your crawl logs and ensure that you are actually crawling the files and not just the items. Double check the IFilter installation too. It sounds like it is not registered for MOSS. (IFilterExplorer will check this for you.) The IFilterShop filter installs correctly, the one from Aiming Tech does not.

On 16 Sep 2008 03:27, Steve said:

Hi again, I installed the XMp iFIlter. I have just tried the iFilterExplorer - could you explain how to check if my xmpfilter is registered for MOSS? It does display in a group and shows the JPEG, GIF, PNG etc.

On 18 Sep 2008 05:58, Matthew said:

In IFilter Explorer choose the MOSS (or Office Server) tab. If the IFilter is registered for the extension it will be listed.

On 22 Sep 2008 03:02, Steve said:

Thanks for all the help Matthew. The iFilter was registered correctly. I haven't made any changes but now it is suddenly wqorking correctly. Very strange. Oh well, looking forward to the next installations :) One feature I have been trying to do with search is to display document results with a link to edit the document's properties. There is no property for doc library name so you have to use substring-after and befores with sitename and url and then append editform.aspx?id=ows_id But I just cant get the sitename+doclib name url. Its easy to strip the filename.docx part off the url, but if the doc lib has folders then the link with editform.aspx wont work for it needs to be at the root level. A post on this if you ever get the time would be great.

On 22 Sep 2008 08:06, Matthew said:

Funny, most people want to avoid the Edit page at all costs. I'll look into it. Remember that if you goal is to get to the Edit page you could filter on list item content types, the url point to the item form, so you could change it to point to the edit form.

On 25 Sep 2008 03:38, Matthew said:

Steve, did you see this for the list item ID? http://sharepoint.microsoft.com/blogs/LKuhn/Lists/Posts/Post.aspx?ID=37 M

On 26 Sep 2008 04:52, Steve said:

Figured the ID property out pretty easily, but thanks for the link. My context is that i have a vault which is a huge collection of collateral documents. I then use search to present the relevant documents on each of my solution sites. So i need the actual document to be returned - with the icon etc. That was easy. I used the designer dataview web part to create a cool presentation. But I want my users to be able to click a link to open the source site of the document (that I have done) and I also want them to be able to view the properties with a simple click off the button as well. You right, edit isnt a great idea, but view properties for this scenario would be great.

On 16 Oct 2008 04:52, Brian Grabowski said:

Good stuff! However I'm getting the same problem as one of you. The search results do not include the Content Type "Picture." I performed a full crawl afterwards and still no dice?

On 17 Oct 2008 10:37, Sadalit said:

FYI - the same thing happened to me that happened to "Steve" - no results at first (broken picture link), then after a few days it was magically working with no further changes. Must be an indexer thing although I was kicking off full crawls each time I tested. My results do not appear in multiple columns the way yours do - just a single column, although I implemented the CSS and copied your code exactly. Any advice?

On 20 Oct 2008 09:40, Matthew said:

Sadalit, not sure what you mean by "multiple columns". Can you clarify weather you are using document libraries, Picture Libraries or File shares?

On 23 Oct 2008 10:55, sadalit said:

Matthew, thanks so much for your response. What I meant was the nice four-across display on the results page which your final screenshot shows. I got it to work today - I believe the problem was that I linked to the "imageresults.css" file but then did not check "Reset all subsites." When I did this just now, I get the look and feel shown in your screenshot. Thank you so much for this excellent four-part article and the code you include - it's a wonderful improvement for SharePoint search.

On 24 Oct 2008 05:24, Steve said:

Sadalit, you can use the dataview web part in the designer to use a GUI to get however many columns you like. There is an article on technet how do do this in the customise search series. Interesting that you received the same behavior as I did :)

On 29 Oct 2008 05:57, MetPrin said:

Steve, you're question to view the properties with a simple click on the search results page is very useful. Did you find the answer?!

On 29 Oct 2008 07:52, Matthew said:

MetPrin, can you give me (and Steve) more detail? Do you want to do a "hide and show" with script or something more substantial?

On 29 Oct 2008 09:36, MetPrin said:

What I want: When I search for something (pictures, documents, listitems), I want to get the results like the normal search results (with pictures I want a thumbnail,...). But I want also an extra link to go straight to the "View properties" page of that item. Now, normally, when I click the link from a document, the document opens. But I want an extra link to go to the properties of that specific item.

On 05 Nov 2008 03:30, Steve said:

MetPetrin...I have all but givenup trying to do it. Dont have the xslt skills. You cant get the documentlibrary name as a property. you can get sitename but you need to remove all folders that the image/document may be inside. so you need to have a strip template that runs until it hits the document library. Matthew its kind of like docname | some property | btn1 | btn2 btn1 can be a simple link to the site url so you can open the site where the result was. I find that pretty useful. btn2 is a link that will take you straight to the edit properites page for the item (or view properties). so url would be http://knew2n/vault/collateral//Forms/EditForm.aspx?ID=55 the "//" is the problemo... :-)

On 03 Dec 2008 11:12, Roberto Lampa said:

First: thank you very much for this detailed tutorial :-) Second: is it possible to apply this tutorial to Microsoft Search Server 2008 Express? Regards

On 03 Dec 2008 03:24, Matthew said:

Roberto, I don't see why not. There is nothing here that is not available in MSSx.

Leave a comment

Name (required)

Url

Email

Comments

Complete this section to post your comment