Wednesday, February 22, 2012

Select multiple Refiners in the same category (part 1)

The selection of multiple Refiners in the same category is possible out of the box.


For instance if we select the Word refiner in the Result Type category it will show us the following url:

According to the Keyword Query Syntax Reference Multiple Property Restrictions work like this: When you use multiple instances of the same property restriction, matches are based on the union of the property restrictions in the keyword query. This is functionally the same as using the OR Boolean operator.
So if we just add "%20fileextension%3D%22pdf%22" to the URL it will get us Word OR Pdf documents as a result.


The nice thing is that the XSL recognizes both refiners as selected.


Even the deselection of a selected refiner is working.
In this case, if we would deselect the Word refiner, the url is correct:
http://sp2010/SearchCenter/Pages/results.aspx?k=sharepoint&r=fileextension%3D%22pdf%22
But on the other hand if we would deselect the Pdf refiner the logic is broken. When multiple instances of the same property are used -like Word- the url is not correctly processed: the spaces (OR) are replaced by AND. This will show us no results of course.

Another example of selection of multiple refiners is with the Author category. The author can/will show us 2 names: the original author from the document property, and the one that created the document (by upload).


First we select the original author of the document.

Then we select the creator of the document.


The deselection of a selected author is working fine.

Conclusion:
So the selection of multiple refiners in the same category is possible but the UI doesn't provide the full and correct navigation for it.

In part 2 I will try to overcome this.

Tuesday, December 20, 2011

The Ugly, the Bad and the Good (part 4)

Part 4. Revisited

I was still not satisfied with the possibilities I came up with to get the right icon for every filter value in the Refinement panel.
I wanted to be able to add a specific icon to the Refiner values belonging to the Filter category 'Author' for instance.

Therefore I had to promote the ManagedProperty parameter, so its value would be available in the FilterLink template.

Here is the complete XSL that can replace the original one. Add this via the XSL Editor of the Refinement Panel webpart:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime">
  <xsl:output method="xml" indent="no"/>
  <xsl:param name="RefinementPanelCaption"></xsl:param>
  <xsl:param name="QueryId"></xsl:param>
  <xsl:param name="TagsCategoryId"></xsl:param>
  <xsl:param name="MetadataServiceUrl"></xsl:param>
  <xsl:param name="IsUIRightToLeft"></xsl:param>
  <xsl:param name="ApplyButtonText"></xsl:param>
  <xsl:param name="CancelButtonText"></xsl:param>
  <xsl:param name="CurrentLcid"></xsl:param>
  <xsl:param name="MoreLinkPanelIDSuffix"></xsl:param>
  <xsl:param name="IdPrefix" />
  <xsl:param name="IsDesignMode">True</xsl:param>
  <xsl:param name="RefineByHeading">Refinement</xsl:param>
  <xsl:param name="IsRTL">False</xsl:param>

  <xsl:template match="FilterPanel">
    <xsl:variable name="FilterCategories" select="FilterCategory" />
    <xsl:variable name="CategoryCount" select="count($FilterCategories)" />
    <xsl:if test="($CategoryCount &gt; 0)">
      <xsl:call-template name="FilterCategory">
        <xsl:with-param name="Categories" select="$FilterCategories" />
      </xsl:call-template>
    </xsl:if>
  </xsl:template>

  <xsl:template name="FilterCategory">
    <xsl:param name="Categories" />
    <xsl:if test="$RefinementPanelCaption != ''">
      <div class="ms-searchref-caption">
        <xsl:value-of select="$RefinementPanelCaption" />
      </div>
    </xsl:if>
    <xsl:for-each select="$Categories">
      <xsl:variable name="FilterCategoryId" select="concat(@Id,$IdPrefix)"/> 
      <xsl:variable name="TagsId" select="concat($TagsCategoryId,$IdPrefix)"/> 
      <xsl:variable name="ColumnId" select="substring(@Id,10)"/> 
      <xsl:variable name="FilterCategoryType" select="@Type"/> 
      <xsl:variable name="ManagedProperty" select="@ManagedProperty"/>
      <xsl:variable name="SSPList" select="substring-before(CustomData/AssociateTermSets, '|')"/>
      <xsl:variable name="TermSetList" select="substring-after(CustomData/AssociateTermSets, '|')"/>
      <xsl:variable name="ShowMoreLink" select="translate(@ShowMoreLink,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/> 
      <xsl:variable name="FreeFormFilterHint" select="@FreeFormFilterHint"/> 
      <xsl:variable name="MoreLinkText" select="@MoreLinkText"/> 
      <xsl:variable name="LessLinkText" select="@LessLinkText"/> 
      <xsl:variable name="ShowCounts" select="@ShowCounts"/> 
      <xsl:variable name="DisplayName" select="@DisplayName"/>   
      <xsl:variable name="EscapedDisplayName" select="CustomData/EscapedDisplayName"/>   
      <xsl:variable name="ShowTaggingControl" select="@ShowTaggingControl"/>   
      <div class="ms-searchref-categoryname">
        <xsl:value-of select="$DisplayName"/>
      </div>
      <ul class="ms-searchref-filters" id="TopFilters_{$FilterCategoryId}">
        <xsl:choose>
          <xsl:when test="$FilterCategoryType = 'Message'">
            <xsl:for-each select="Filters/Filter">
              <xsl:call-template name="FilterMessage">
                <xsl:with-param name="Value" select="Value" />
              </xsl:call-template>
            </xsl:for-each>
          </xsl:when>
          <xsl:otherwise>
            <xsl:call-template name="Filter">
              <xsl:with-param name="Filters" select="Filters/Filter" />
              <xsl:with-param name="ShowCounts" select="$ShowCounts" />
    <!-- HW: Begin -->
    <xsl:with-param name="ManagedProperty" select="$ManagedProperty" />
    <!-- HW: End -->
            </xsl:call-template>
          </xsl:otherwise>
        </xsl:choose>
      </ul>
        <xsl:if test="$ShowMoreLink='TRUE'">
          <xsl:variable name="MoreFilters" select="MoreFilters/Filter" />
          <xsl:choose>
            <xsl:when test="$FilterCategoryId and ($FilterCategoryId != '') and ($FilterCategoryType = 'Microsoft.Office.Server.Search.WebControls.ManagedPropertyFilterGenerator')">
              <xsl:variable name="MoreDivId" select="concat('RefinementMore_', $QueryId, $FilterCategoryId)" />
              <xsl:variable name="LessDivId" select="concat('RefinementLess_', $QueryId, $FilterCategoryId)" />
              <xsl:if test="$MoreFilters != ''">
                <a class="ms-searchref-more" href="javascript:{{}}" onclick="SearchEnsureSOD();ToggleRefMoreLessFilters(this, true);">
                  <div class="ms-searchref-morelink">
                    <xsl:value-of select="$MoreLinkText" />
                    <xsl:text disable-output-escaping="yes">&amp;nbsp;</xsl:text>
                    <img src="/_layouts/images/more_arrow.png" class="ms-searchref-moreicon" />
                  </div>
                </a>
                <ul class="ms-searchref-filters" id="MoreFilters_{$FilterCategoryId}" style="display:none">
                  <xsl:call-template name="Filter">
                    <xsl:with-param name="Filters" select="$MoreFilters" />
                    <xsl:with-param name="ShowCounts" select="$ShowCounts" />
     <!-- HW: Begin -->
     <xsl:with-param name="ManagedProperty" select="$ManagedProperty" />
     <!-- HW: End -->
                  </xsl:call-template>
                </ul>
                <a class="ms-searchref-more" href="javascript:{{}}" onclick="SearchEnsureSOD();ToggleRefMoreLessFilters(this, false);" style="display:none">
                  <div class="ms-searchref-morelink">
                    <xsl:value-of select="$LessLinkText" />
                    <xsl:text disable-output-escaping="yes">&amp;nbsp;</xsl:text>
                    <img src="/_layouts/images/less_arrow.png" class="ms-searchref-moreicon" />
                  </div>
                </a>
              </xsl:if>
            </xsl:when>
            <xsl:when test="$FilterCategoryId and ($FilterCategoryId != '') and ($FilterCategoryType = 'Microsoft.Office.Server.Search.WebControls.TaxonomyFilterGenerator')">
              <xsl:variable name="IsTagsColumn" select="$FilterCategoryId = $TagsId" />
              <xsl:if test="$MoreFilters != ''">
                <a id="{$MoreLinkPanelIDSuffix}_{$FilterCategoryId}" href="javascript:{{}}" class="ms-searchref-more" onclick="SearchEnsureSOD();RenderTaggingControl('{$FilterCategoryId}', {$IsTagsColumn}, '{$SSPList}', '{$TermSetList}', '{$MetadataServiceUrl}', {$CurrentLcid}, '{$MoreLinkPanelIDSuffix}', '{$EscapedDisplayName}', {$ShowTaggingControl}, {$IsUIRightToLeft});">
                  <div class="ms-searchref-morelink">
                    <xsl:value-of select="$MoreLinkText" />
                    <xsl:text disable-output-escaping="yes">&amp;nbsp;</xsl:text>
                    <img src="/_layouts/images/more_arrow.png" class="ms-searchref-moreicon" />
                  </div>
                </a>
                <div style="display:none">
                  <ul class="ms-searchref-filters" id="MoreFilters_{$FilterCategoryId}">
                    <xsl:call-template name="Filter">
                      <xsl:with-param name="Filters" select="MoreFilters/Filter" />
                      <xsl:with-param name="ShowCounts" select="$ShowCounts" />
                    </xsl:call-template>
                  </ul>
                  <div id="TaxonomyMoreControl_{$FilterCategoryId}">
                    <input id="MetadataHiddenInput_{$FilterCategoryId}" type="hidden" />
                    <div id="TaggingControl_{$FilterCategoryId}" class="ms-taxonomy">
                        <img alt="" src="/_layouts/images/blank.gif" height="0" width="0"/>
                    </div>
                    <img style="display:none" src="/_layouts/images/RTE2FIND.gif" class="ms-searchref-taxapply" align="right" alt="{$ApplyButtonText}" onclick="SearchEnsureSOD();var link=GetTaxonomyApplyFilterUrl('{$FilterCategoryId}','{$ColumnId}');if (link!='')window.location=link;" />
                  </div>
                </div>
                <a style="display:none" class="ms-searchref-more" href="javascript:{{}}" onclick="SearchEnsureSOD();ToggleTaxonomyLessFilters(this);">
                  <div class="ms-searchref-morelink">
                    <xsl:value-of select="$LessLinkText" />
                    <xsl:text disable-output-escaping="yes">&amp;nbsp;</xsl:text>
                    <img src="/_layouts/images/less_arrow.png" class="ms-searchref-moreicon" />
                  </div>
                </a>
              </xsl:if>
            </xsl:when>
            <xsl:when test="$FilterCategoryId and ($FilterCategoryId != '') and ($FilterCategoryType = 'Message')">
            </xsl:when>
            <xsl:otherwise>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:if>
      <div class="ms-searchref-catseparator">&#160;</div>
    </xsl:for-each>
  </xsl:template>

  <xsl:template name="Filter">
    <xsl:param name="Filters" />
    <xsl:param name="ShowCounts" />
 <!-- HW: Begin -->
 <xsl:param name="ManagedProperty" />
 <!-- HW: End -->
    <xsl:for-each select="$Filters">
      <xsl:variable name="Selection" select="Selection" />
      <xsl:choose>
        <xsl:when test="($Selection = 'Selected')">
          <xsl:call-template name="FilterLink">
            <xsl:with-param name="Url" select="Url" />
            <xsl:with-param name="UrlTooltip" select="Tooltip" />
            <xsl:with-param name="Value" select="Value" />
            <xsl:with-param name="FilterSelection" select="'ms-searchref-selected ms-searchref-removable'" />
            <xsl:with-param name="ShowCounts" select="$ShowCounts" />
            <xsl:with-param name="Count" select="Count" />
            <xsl:with-param name="Percentage" select="Percentage" />
            <xsl:with-param name="Indentation" select="Indentation" />
   <!-- HW: Begin -->
   <xsl:with-param name="ManagedProperty" select="$ManagedProperty" />
   <!-- HW: End -->
          </xsl:call-template>
        </xsl:when>
        <xsl:when test="($Selection = 'Implied')">
          <xsl:call-template name="FilterLink">
            <xsl:with-param name="Url" select="Url" />
            <xsl:with-param name="UrlTooltip" select="Tooltip" />
            <xsl:with-param name="Value" select="Value" />
            <xsl:with-param name="FilterSelection" select="'ms-searchref-selected'" />
            <xsl:with-param name="ShowCounts" select="$ShowCounts" />
            <xsl:with-param name="Count" select="Count" />
            <xsl:with-param name="Percentage" select="Percentage" />
            <xsl:with-param name="Indentation" select="Indentation" />
   <!-- HW: Begin -->
   <xsl:with-param name="ManagedProperty" select="$ManagedProperty" />
   <!-- HW: End -->
          </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
          <xsl:call-template name="FilterLink">
            <xsl:with-param name="Url" select="Url" />
            <xsl:with-param name="UrlTooltip" select="Tooltip" />
            <xsl:with-param name="Value" select="Value" />
            <xsl:with-param name="FilterSelection" select="'ms-searchref-unselected'" />
            <xsl:with-param name="ShowCounts" select="$ShowCounts" />
            <xsl:with-param name="Count" select="Count" />
            <xsl:with-param name="Percentage" select="Percentage" />
            <xsl:with-param name="Indentation" select="Indentation" />
   <!-- HW: Begin -->
   <xsl:with-param name="ManagedProperty" select="$ManagedProperty" />
   <!-- HW: End -->
          </xsl:call-template>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each>
  </xsl:template>

  <xsl:template name="FilterLink">
    <xsl:param name="Url"/>
    <xsl:param name="UrlTooltip"/>
    <xsl:param name="Value"/>
    <xsl:param name="FilterSelection"/>
    <xsl:param name="ShowCounts"/>
    <xsl:param name="Count"/>
    <xsl:param name="Percentage"/>
    <xsl:param name="Indentation"/>
 <!-- HW: Begin -->
 <xsl:param name="ManagedProperty" />
 <!-- HW: End -->
    <xsl:variable name="SecureUrl">
      <xsl:call-template name="GetSecureUrl">
        <xsl:with-param name="Url" select="$Url" />
      </xsl:call-template>
    </xsl:variable>
    <li class="ms-searchref-filter {$FilterSelection}">
      <xsl:if test="($Indentation = '1')">
        <span class="ms-searchref-indenticon">&#8627;&#160;</span>
      </xsl:if>
   <!-- HW: Begin -->
      <xsl:choose>
        <xsl:when test="starts-with($Value, 'Any ')">
          <img align="absmiddle" src="/_layouts/images/asterisk.png" border="0" />
        </xsl:when>
        <xsl:when test="$ManagedProperty = 'SiteName'">
          <a href="http://{$Value}" target="_blank" title="Open site in new window"><img align="absmiddle" src="/_layouts/images/SharePointFoundation16.png" border="0" /></a>
        </xsl:when>
        <xsl:when test="ddwrt:MapToIcon('', $Value) != 'icgen.gif'">
          <img align="absmiddle" src="/_layouts/images/{ddwrt:MapToIcon('', $Value)}" border="0" />
        </xsl:when>
        <xsl:when test="$Value = 'Word'">
          <img align="absmiddle" src="/_layouts/images/icdocx.png" border="0" />
        </xsl:when>
        <xsl:when test="$Value = 'Excel'">
          <img align="absmiddle" src="/_layouts/images/icxlsx.png" border="0" />
        </xsl:when>
        <xsl:when test="$Value = 'PowerPoint'">
          <img align="absmiddle" src="/_layouts/images/icpptx.gif" border="0" />
        </xsl:when>
        <xsl:when test="$Value = 'Webpage'">
          <img align="absmiddle" src="/_layouts/images/icaspx.gif" border="0" />
        </xsl:when>
  <xsl:when test="$ManagedProperty = 'Author'">
    <a href="http://sp2010/SearchCenter/Pages/peopleresults.aspx?k={$Value}" target="_blank" title="Search for user profile"><img align="absmiddle" src="/_layouts/images/ACA16.gif" border="0" /></a>
        </xsl:when>
        <xsl:otherwise>
          <img align="absmiddle" src="/_layouts/images/{$Value}.gif" onError="this.src='/_layouts/images/bullet.gif';" border="0" />
        </xsl:otherwise>
      </xsl:choose>
      <xsl:text> </xsl:text>
   <!-- HW: End -->
      <a class="ms-searchref-filterlink" href="{$SecureUrl}" title="{$RefineByHeading}: {$UrlTooltip}">
        <xsl:value-of select="Value"/>
      </a>
      <xsl:choose>
        <xsl:when test="($ShowCounts = 'Count') and ($Count != '')">
          <span class="ms-searchref-count">
            <xsl:if test="$IsRTL = 'True'">&#x200f;</xsl:if> 
            (<xsl:value-of select="Count"/>)
          </span>
        </xsl:when>
        <xsl:when test="($ShowCounts = 'Percentage') and ($Percentage != '')">
          <span class="ms-searchref-count">
            <xsl:if test="$IsRTL = 'True'">&#x200f;</xsl:if> 
            (<xsl:value-of select="format-number($Percentage, '0%')"/>)
          </span>
        </xsl:when>
      </xsl:choose>
    </li>
  </xsl:template>

  <xsl:template name="FilterMessage">
    <xsl:param name="Value"/>
    <xsl:param name="FilterSelection"/>
    <li class="ms-searchref-filtermsg">
      <span class="ms-searchref-filterlink ms-searchref-msg">
        <xsl:value-of select="Value"/>
      </span>
    </li>
  </xsl:template>

  <xsl:template name="GetSecureUrl">
    <xsl:param name="Url"/>
    <xsl:choose>
      <xsl:when test="$IsDesignMode = 'True'">
        <xsl:value-of select="$Url" />
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="ddwrt:EnsureAllowedProtocol($Url)" />
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

The result is a Refinement Panel with icons, where a hyperlink is added to the icons of the Filter categories 'Site' and 'Author':
- Site: Open site in new window
- Author: hyperlink to a people search results page with a TOP Federated Results webpart, that includes the local people search and an internet people search via the api of linkedin, pipl, etc.
Replace the url in row 253 with the url of your own search center.


Example of custom refiner with custom icons (with same file name as the value).


Wish list:
- Display avatar of the authors: due to the lack of e-mail address and MD5 encoding this is difficult to achieve;
- Show user profile on hoover with javascript/jQuery: be my guest. => There is a solution for this in SP2013/Online made by Elio Struyf: People refinement made easy by adding profile pictures.


I managed to make it possible to select multiple Refiners via the UI, just by extending the XSL of the Refinement Panel and adding a little javascript.

Saturday, December 17, 2011

From Russia with love

Tip:

There are two Russian vendors that offer free SharePoint tools.

RebelSearch Document Preview for SharePoint Search 2010
Install this on your WFE server(s) and you will have a preview option of the search results.
Be aware that it runs under the farm service account, and that is considered a security risk by SharePoint itself.

HarePoint Workflow Scheduler , that allows automatic execution of SharePoint workflows ​by schedule.
HarePoint Workflow Extensions Free Set of Activities, with a lot of String functions (finally available in SPD!).
Together with Virto Workflow Activities Kit that offers a free rollup of activities from Codeplex, these are a very useful extension of the workflow activities in SharePoint Designer.

Wednesday, October 19, 2011

The poor man's interface (use case 1)

How to connect from SAP to SharePoint to show related documents

Use case:
In SAP we have the registration of objects, and we want to link relevant documents or forms to these objects. Instead of the options SAP DMS, Web Dynpro or Adobe forms, we want to make use of SharePoint for this purpose.
So related documents are stored in SharePoint. But how to get to them, how to upload them and link them to the SAP objects?


Solution:
With some modest customization of SAP it is possible to present an option (under the button) to open a browser with a constructed url based on the metadata from SAP. For example: http://sp2010/objectinfo/default.aspx?ObjectID=obj000333
The first part of the url is static:"http://sp2010/objectinfo/default.aspx?ObjectID=", and the ID of the SAP object is added: "obj000333".
This url will connect to the default page of a Document Center site in SharePoint.


What should the user experience be like?
First of all we want to show a selection of documents and pictures based on the ID of the SAP object. This can be done with the help of a Query String (URL) Filter webpart that gets the value of the Query String Parameter: ObjectID. Then we connect to send the filter values to the Documents and Pictures webpart.


We should also be able to add new documents or pictures to the libraries and have the object ID added to them automatically after upload. With the help of javascript we are able to store the Object ID and current user ID as session variables, so the values can be used after upload to modify the properties of the (new) documents and pictures. This is one of the Off-The-Shelf scripts that I have described in an earlier post.

Finally we want to show an input box with the Object ID as default value, and the manual input can be used to change the filter value on the spot.



When the documents are stored in more than one location it's better to fire a search query from SAP.
Add the ObjectID as managed property to your search administration, so it will be indexed and can be used as keyword in the search query.
The url should be like: http://sp2010/SearchCenter/Pages/results.aspx?k=ObjectID%3A%22obj000333%22

Monday, October 17, 2011

Return to the context of the contextual search

The contextual search is integrated in the standard search center of SharePoint 2010. So we have one search experience, that can be modified.
The scope of the contextual search is added to the search query url by the parameters cs (contextual scope: This Site/This List) and u (url of the site/list).


The masterpage of the search center doesn't provide any navigation. And other portals are not part of the navigation within the Home site. But it would be nice to be able to go back to where we started the search, no matter where we came from. This can be done with some simple Html and javascript.
Add a HTML Form Web Part to the left zone of the search results page and paste the following code into the source editor:


<br/>&#160;&#160;&#160;<a href="/"><img class="ms-rtePosition-4" alt="Go Home" src="/_layouts/images/hhome.png" border="0"/></a>&#160;<font color="#0072bc"><a href="/">Go Home</a>&#160; </font><a href="#" onClick="javascript:window.location.replace(GetUrlKeyValue('u'));"><img class="ms-rtePosition-4" alt="Go Back" src="/_layouts/images/back.gif" border="0"/></a>&#160;<a href="#" onClick="javascript:window.location.replace(GetUrlKeyValue('u'));">Go Back</a><br/><br/>

This results in two options: go home, or go back to the context.
When there is no context, you will return to the default page of the search center.