Tuesday, June 24, 2014

Presentation–Site provisioning techniques including site provisioning providers for SharePoint 2013

Recently I did a presentation on site provisioning providers at Sri Lanka SharePoint forum. In that session I described about other site provisioning techniques as well.

10393838_10152318026097530_3673986027299882108_n

There are many site provisioning techniques including Site Definitions, Feature Stapling, Web Templates, Site Provisioning Provider (SPWebProvisioningProvider) etc.. If we need to obtain full control over the provisioning process we may need to select the Site Provisioning Provider approach.

You can find some sample codes I used in demo from this location.

Sunday, June 15, 2014

Set result source and item display template programmatically to SharePoint 2013 search ResultScriptWebPart

Sometimes we need to programmatically set search result source and a custom item display template to a search web part.

Following is a sample code segment to do the needful

  1. //Get the instance of web part
  2. var homePage = web.GetFile("SitePages/Home.aspx");
  3. using (var webPartManager = homePage.GetLimitedWebPartManager(PersonalizationScope.Shared))
  4. {
  5. var recentDocs = webPartManager.WebParts.Cast<WebPart>().FirstOrDefault
  6.         (wp => wp.Title.Equals("Recent Documents")) as ResultScriptWebPart;
  7. var querySettings = new DataProviderScriptWebPart
  8.         {
  9.            PropertiesJson = recentDocs.DataProviderJSON
  10.         };
  11.  
  12. //Get the search service application proxy
  13. var settingsProxy = SPFarm.Local.ServiceProxies.GetValue
  14.         <SearchQueryAndSiteSettingsServiceProxy>();
  15. var searchProxy = settingsProxy.ApplicationProxies.GetValue
  16.         <SearchServiceApplicationProxy>("Search Service Application");
  17. var siteOwner = new SearchObjectOwner(SearchObjectLevel.SPSite, web);
  18.  
  19. //Set the result source. I set the default for demonstration
  20. var siteResultSource = searchProxy.GetResultSourceByName("Local SharePoint Results", siteOwner);
  21.  
  22.  
  23. querySettings.Properties["SourceName"] = siteResultSource.Name;
  24. querySettings.Properties["SourceID"] = siteResultSource.Id;
  25. querySettings.Properties["SourceLevel"] = siteOwner.Level;
  26.  
  27.  
  28. //Set the item display template
  29. recentDocs.ItemTemplateId = "~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Contoso.js";
  30. recentDocs.DataProviderJSON = querySettings.PropertiesJson;
  31.  
  32. webPartManager.SaveChanges(recentDocs);
  33. }
  34. homePage.Update();

That’s all we need to do.

Saturday, June 7, 2014

Programmatically upload and use SharePoint 2013 Item Display Templates

This is the third post of a series of blog posts on SharePoint 2013 search display templates.

In previous posts I explained about Display Templates in general and about creating new Item Display Templates. But in some scenarios we can’t upload display templates manually through mapped network drives or design manager. Following are few scenarios where we require a programmatic approach to deploy search Item Display Templates.

Let’s assume that we have thousands of site collections in our environment and we have a search web part in each home page. Can we manually update each site? It is not feasible, isn’t it.

Another scenario is where we have a custom site template where all web parts, libraries, master pages, etc.. are provisioned when we create the site. In that situation, the expectation is to apply custom Display Templates automatically if available.

Following are the steps to deploy a search Item Display Template programmatically

1. Create the custom Item Display Template

In the previous post I explained about creating a custom Display Template. In this scenario we use a test site collection to create the template.

2. Create Visual Studio solution

We will create an empty SharePoint solution and add a module to host the Display Template

image

We need to deploy only the JavaScript file. But to make the solution complete we will include the html version also in the module. By the way, html file is helpful when we need to do changes in future. In that case we will perform following tasks

  • Do the modification in html file
  • Upload that file in to the test site
  • Get the generated JavaScript file and replace current file in the module with that file
  • Deploy the project

3. Update the Feature

Update the scope of the feature to be “Site”

image 

3. Update the element.xml file

  1. <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  2.   <Module Name="DisplayTemplates" Url="_catalogs/masterpage/Display Templates/Search" RootWebOnly="TRUE">
  3.     <File Path="DisplayTemplates\Item_Contoso.js"
  4.           Url="Item_Contoso.js"
  5.           Type="GhostableInLibrary"
  6.           Level="Published"
  7.           ReplaceContent="TRUE">
  8.       <Property Name="Title" Value="Contoso Item" />
  9.       <Property Name="TargetControlType" Value=";#SearchResults;#Content Web Parts;#" />
  10.       <Property Name="DisplayTemplateLevel" Value="Item" />
  11.       <Property Name="TemplateHidden" Value="FALSE" />
  12.     </File>
  13.   </Module>
  14. </Elements>

4. Deploy the solution

Finally we will deploy the solution to make the template available for search web parts

image

Following is the link to the source code

Monday, June 2, 2014

Create SharePoint 2013 Item Display Template overriding RenderBody

This is the second post of a series of blog posts on SharePoint 2013 search display templates.
I’ve given an introduction on Display Templates in previous post, Where I mentioned different types and their relationships. In this post I’ll discuss on Item Display Templates.
If we navigate to the mapped network drive for Display Templates (“<Site URL>_catalogs/masterpage/Display Templates/Search”) we can find available Display Templates.  Let’s take Item_Word.html file as the example.
image
As shown in above diagram, Item Display Template has following main areas
1. Title
This is the title of the Display Template which will be shown in the settings area of the web part.
2. CustomDocumentProperies
This contains some properties about the Display Template itself. Most important of them is the ManagedPropertyMapping section. Within that section we can include any managed property those are accessed within the Item Display Template.
3. RenderBody (ctx)
As you can see from the diagram, “ctx” object is filled with data (e.g.: Icon, HoverUrl, etc..). Then we will call the RenderBody method which will call Item_CommonItem_Body template to generate the search result item.
By using above facts we can create our own Display Templates. As an example we will use following scenario.
Below is an out of the box search web part. We will create a new Display Template just to remove the path component
image
We need to follow the steps given below.
1. Create new template based on existing item template (e.g.: Item_Word)
image
2.Open the Item_Contoso.html and change Titles accordingly
image
3. Test the Display Template with a web part
image
It is working as expected. Now we need to customize the template.
Creating a “Result Type” is not mandatory as we want this Display Template to be available on this web part only.
4. Override RenderBody (ctx)
Since I need to remove the path (URL component displayed in green color) from our web part, I have two options to consider
  • Remove the managed property “Path” from our template
  • Remove the HTML component that renders the path.
Since managed property “Path” is referred by other places, it is not the best practice to remove it. So I will remove the HTML component that renders the path. How can I find the source of that HTML?
As I mentioned in my earlier post, when a Display Template calls RenderBody method it will call Item_CommonItem_Body template. The rendering part happens there. But can we directly modify that template according to our requirement?
It’s not the correct approach as that template is referred by all Item Display Templates. Instead I will copy necessary code segment from Item_CommonItem_Body template and insert that in our template
image
5. Find the code that renders the path and remove that component
Following is the code that renders the path.
image
We will remove _#= truncatedUrl =#_ element and save the template. If we need we can do necessary modifications as well. Final result looks like below
image
Following is the final Display Template
  1. <html xmlns:mso="urn:schemas-microsoft-com:office:office" xmlns:msdt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882">
  2.   <head>
  3.     <title>Contoso Item</title>
  4.  
  5.     <!--[if gte mso 9]><xml>
  6. <mso:CustomDocumentProperties>
  7. <mso:TemplateHidden msdt:dt="string">0</mso:TemplateHidden>
  8. <mso:MasterPageDescription msdt:dt="string">Displays a result tailored for a Microsoft Word document.</mso:MasterPageDescription>
  9. <mso:ContentTypeId msdt:dt="string">0x0101002039C03B61C64EC4A04F5361F385106603</mso:ContentTypeId>
  10. <mso:TargetControlType msdt:dt="string">;#SearchResults;#</mso:TargetControlType>
  11. <mso:HtmlDesignAssociated msdt:dt="string">1</mso:HtmlDesignAssociated>
  12. <mso:ManagedPropertyMapping msdt:dt="string">'Title':'Title','Path':'Path','Description':'Description','EditorOWSUSER':'EditorOWSUSER','LastModifiedTime':'LastModifiedTime','CollapsingStatus':'CollapsingStatus','DocId':'DocId','HitHighlightedSummary':'HitHighlightedSummary','HitHighlightedProperties':'HitHighlightedProperties','FileExtension':'FileExtension','ViewsLifeTime':'ViewsLifeTime','ParentLink':'ParentLink','FileType':'FileType','IsContainer':'IsContainer','SecondaryFileExtension':'SecondaryFileExtension','DisplayAuthor':'DisplayAuthor','ServerRedirectedURL':'ServerRedirectedURL','SectionNames':'SectionNames','SectionIndexes':'SectionIndexes','ServerRedirectedEmbedURL':'ServerRedirectedEmbedURL','ServerRedirectedPreviewURL':'ServerRedirectedPreviewURL'</mso:ManagedPropertyMapping>
  13. <mso:HtmlDesignConversionSucceeded msdt:dt="string">True</mso:HtmlDesignConversionSucceeded>
  14. <mso:HtmlDesignStatusAndPreview msdt:dt="string">http://sp13:8080/sites/4750/_catalogs/masterpage/Display%20Templates/Search/Item_Contoso.html, Conversion successful.</mso:HtmlDesignStatusAndPreview>
  15. </mso:CustomDocumentProperties>
  16. </xml><![endif]-->
  17.   </head>
  18.   <body>
  19.     <div id="Item_Contoso">
  20.       <!--#_
  21.         if(!$isNull(ctx.CurrentItem) && !$isNull(ctx.ClientControl)){
  22.             var id = ctx.ClientControl.get_nextUniqueId();
  23.             var itemId = id + Srch.U.Ids.item;
  24.             var hoverId = id + Srch.U.Ids.hover;
  25.             var hoverUrl = "~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Word_HoverPanel.js";
  26.             $setResultItem(itemId, ctx.CurrentItem);
  27.             ctx.CurrentItem.csr_Icon = Srch.U.getIconUrlByFileExtension(ctx.CurrentItem);
  28.             ctx.CurrentItem.csr_OpenApp = "word";
  29.             ctx.currentItem_ShowHoverPanelCallback = Srch.U.getShowHoverPanelCallback(itemId, hoverId, hoverUrl);
  30.             ctx.currentItem_HideHoverPanelCallback = Srch.U.getHideHoverPanelCallback();
  31. _#-->
  32.       <div id="_#= $htmlEncode(itemId) =#_" name="Item" data-displaytemplate="ContosoItem" style="padding: 0; margin: 0;" class="ms-srch-item" onmouseover="_#= ctx.currentItem_ShowHoverPanelCallback =#_" onmouseout="_#= ctx.currentItem_HideHoverPanelCallback =#_">
  33.  
  34.         <!--#_
  35.         var id = ctx.CurrentItem.csr_id;
  36.         var title = Srch.U.getHighlightedProperty(id, ctx.CurrentItem, "Title");
  37.         if ($isEmptyString(title)) {title = $htmlEncode(ctx.CurrentItem.Title)}
  38.         var appAttribs = "";
  39.         if (!$isEmptyString(ctx.CurrentItem.csr_OpenApp)) { appAttribs += "openApp=\"" + $htmlEncode(ctx.CurrentItem.csr_OpenApp) + "\"" };
  40.         if (!$isEmptyString(ctx.CurrentItem.csr_OpenControl)) { appAttribs += " openControl=\"" + $htmlEncode(ctx.CurrentItem.csr_OpenControl) + "\"" };
  41.         var showHoverPanelCallback = ctx.currentItem_ShowHoverPanelCallback;
  42.         if (Srch.U.n(showHoverPanelCallback)) {
  43.             var itemId = id + Srch.U.Ids.item;
  44.             var hoverId = id + Srch.U.Ids.hover;
  45.             var hoverUrl = "~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Default_HoverPanel.js";
  46.             showHoverPanelCallback = Srch.U.getShowHoverPanelCallback(itemId, hoverId, hoverUrl);
  47.         }
  48.         var displayPath = Srch.U.getHighlightedProperty(id, ctx.CurrentItem, "Path");
  49.         if ($isEmptyString(displayPath)) {displayPath = $htmlEncode(ctx.CurrentItem.Path)}
  50.         var url = ctx.CurrentItem.csr_Path;
  51.         if($isEmptyString(url)){
  52.             var useWACUrl = !$isEmptyString(ctx.CurrentItem.ServerRedirectedURL);
  53.             if(ctx.ScriptApplicationManager && ctx.ScriptApplicationManager.states){
  54.                 useWACUrl = (useWACUrl && !ctx.ScriptApplicationManager.states.openDocumentsInClient);
  55.             }
  56.             if(useWACUrl)
  57.             {
  58.                 url = ctx.CurrentItem.ServerRedirectedURL;
  59.             } else {
  60.                 url = ctx.CurrentItem.Path;
  61.             }        
  62.         }
  63.         ctx.CurrentItem.csr_Path = url;
  64.         var pathLength = ctx.CurrentItem.csr_PathLength;
  65.         if(!pathLength) {pathLength = Srch.U.pathTruncationLength}
  66.  
  67.         var maxTitleLengthInChars = Srch.U.titleTruncationLength;
  68.         var termsToUse = 2;
  69.         if(ctx.CurrentItem.csr_PreviewImage != null)
  70.         {
  71.             maxTitleLengthInChars = Srch.U.titleTruncationLengthWithPreview;
  72.             termsToUse = 1;
  73.         }
  74.  
  75.         var clickType = ctx.CurrentItem.csr_ClickType;
  76.         if(!clickType) {clickType = "Result"}        
  77. _#-->
  78.         <div id="_#= $htmlEncode(id + Srch.U.Ids.body) =#_" class="ms-srch-item-body" onclick="_#= showHoverPanelCallback =#_">
  79.           <!--#_
  80.             if (!$isEmptyString(ctx.CurrentItem.csr_Icon)) {
  81. _#-->
  82.           <div class="ms-srch-item-icon">
  83.             <img id="_#= $htmlEncode(id + Srch.U.Ids.icon) =#_" onload="this.style.display='inline'" src="_#= $urlHtmlEncode(ctx.CurrentItem.csr_Icon) =#_" />
  84.           </div>
  85.           <!--#_
  86.             }
  87.             var titleHtml = String.format('<a clicktype="{0}" id="{1}" href="{2}" class="ms-srch-item-link" title="{3}" onfocus="{4}" {5}>{6}</a>',
  88.                                           $htmlEncode(clickType), $htmlEncode(id + Srch.U.Ids.titleLink), $urlHtmlEncode(url), $htmlEncode(ctx.CurrentItem.Title),
  89.                                           showHoverPanelCallback, appAttribs, Srch.U.trimTitle(title, maxTitleLengthInChars, termsToUse));
  90. _#-->
  91.           <div id="_#= $htmlEncode(id + Srch.U.Ids.title) =#_" class="ms-srch-item-title">
  92.             <h3 class="ms-srch-ellipsis">
  93.               _#= titleHtml =#_
  94.             </h3>
  95.           </div>
  96.           <!--#_
  97.             if (!$isEmptyString(ctx.CurrentItem.HitHighlightedSummary)) {
  98. _#-->
  99.           <div id="_#= $htmlEncode(id + Srch.U.Ids.summary) =#_" class="ms-srch-item-summary">_#= Srch.U.processHHXML(ctx.CurrentItem.HitHighlightedSummary) =#_</div>
  100.           <!--#_
  101.             }
  102.             var truncatedUrl = Srch.U.truncateHighlightedUrl(displayPath, pathLength);
  103. _#-->
  104.           <div id="_#= $htmlEncode(id + Srch.U.Ids.path) =#_" tabindex="0" class="ms-srch-item-path" title="_#= $htmlEncode(ctx.CurrentItem.Path) =#_" onblur="Srch.U.restorePath(this, '_#= $scriptEncode(truncatedUrl) =#_', '_#= $scriptEncode(ctx.CurrentItem.Path) =#_')" onclick="Srch.U.selectPath('_#= $scriptEncode(ctx.CurrentItem.Path) =#_', this)" onkeydown="Srch.U.setPath(event, this, '_#= $scriptEncode(ctx.CurrentItem.Path) =#_', '_#= $scriptEncode(truncatedUrl) =#_')" >
  105.  
  106.           </div>
  107.         </div>
  108.         <!--#_
  109.         if (!$isEmptyString(ctx.CurrentItem.csr_PreviewImage))
  110.         {
  111.             var altText = Srch.Res.item_Alt_Preview;
  112.             if(!$isEmptyString(ctx.CurrentItem.csr_PreviewImageAltText)){
  113.                 altText = ctx.CurrentItem.csr_PreviewImageAltText;
  114.             }
  115.  
  116.             var onloadJS = "var container = $get('" + $scriptEncode(id + Srch.U.Ids.preview) + "'); if(container){container.style.display = 'inline-block';}" +
  117.                            "var path = $get('" + $scriptEncode(id + Srch.U.Ids.path) + "'); if (path) { Srch.U.ensureCSSClassNameExist(path, 'ms-srch-item-preview-path');}" +
  118.                            "var body = $get('" + $scriptEncode(id + Srch.U.Ids.body) + "'); if (body) { Srch.U.ensureCSSClassNameExist(body, 'ms-srch-item-summaryPreview');}";
  119.  
  120.             var previewHtml = String.format('<a clicktype="{0}" href="{1}" class="ms-srch-item-previewLink" {2}><img class="ms-srch-item-preview" src="{3}" alt="{4}" onload="{5}" /></a>',
  121.                                         $htmlEncode(clickType), $urlHtmlEncode(url), appAttribs, $urlHtmlEncode(ctx.CurrentItem.csr_PreviewImage), $htmlEncode(altText), onloadJS);
  122. _#-->
  123.           <div id="_#= $htmlEncode(id + Srch.U.Ids.preview) =#_"class="ms-srch-item-previewContainer">
  124.           _#= previewHtml =#_
  125.         </div>
  126.         <!--#_
  127.         }
  128. _#-->
  129.  
  130.  
  131.  
  132.  
  133.         <div id="_#= $htmlEncode(hoverId) =#_" class="ms-srch-hover-outerContainer"></div>
  134.       </div>
  135.       <!--#_
  136.         }
  137. _#-->
  138.     </div>
  139.   </body>
  140. </html>
That’s all we need to do.