Display Template Deployment in SharePoint 2013

Hi Guys,

New way to transform search result introduces in SharePoint 2013 which provide a good user experience through the use of display templates. It is far better then using XSLT in SharePoint 2010 to use to customize that display. It is really frustrating while understanding XSLT display templates. The great thing about display templates is that you create them by leveraging skills that many developers already possess: HTML and JavaScript.

The most likely approach to creating display templates is to start with an existing display template HTML file and customize it. It’s not too difficult when you get the hang of the _#= (underscore pound equals) syntax. It’s very important to understand what happens when you edit that HTML file. When you click save SharePoint creates a JavaScript file and associates the two together. (The same thing happens when creating master pages using the design manager but HTML to MASTER file). When you select your display template in the content by search web part it is actually looking for the JavaScript file not the HTML file.

The Dilemma


Putting HTML design files in the SharePoint Master Page Gallery is like playing with the mysterious black box, we never predict what is going to happen with them.

This blog post covers my experiences with provisioning Display Templates to the Master Page Gallery in SharePoint 2013. In order to get more control on the Branding package you should add these design files to your visual studio project. Here are the few very important points about the module attributes which might be used to deploy the files to gallery:

  1. Never Set the Level Attribute of the Display Template to Published:
    When you do this, and you only provision the HTML file, the JavaScript file doesn't gets generated. The JavaScript version only generates for Draft versions of an HTML file.

  2. Make Use of the ReplaceContent="True" Attribute:
    In order to update the HTML file or JavaScript file you need to make use of the ReplaceContent attribute. The file content of your  doesn't get updated if you didn't mention this attribute.

  3. You don't need to Specify Additional Metadata/Properties:
    These properties should all be in the Display Template Head section. Example of the Display Template header section:
    <mso:CustomDocumentProperties>
    <mso:CompatibleManagedProperties msdt:dt="string"></mso:CompatibleManagedProperties>
    <mso:TemplateHidden msdt:dt="string">0</mso:TemplateHidden>
    <mso:MasterPageDescription msdt:dt="string"></mso:MasterPageDescription>
    <mso:ContentTypeId msdt:dt="string">0x0101002039C03B61C64EC4A04F5361F385106601 </mso:ContentTypeId>
    <mso:TargetControlType msdt:dt="string">;#Refinement;#</mso:TargetControlType>
    <mso:HtmlDesignAssociated msdt:dt="string">1</mso:HtmlDesignAssociated>
    <mso:HtmlDesignConversionSucceeded msdt:dt="string">True</mso:HtmlDesignConversionSucceeded>
    <mso:HtmlDesignStatusAndPreview msdt:dt="string">Link to the file, Conversion successful.</mso:HtmlDesignStatusAndPreview>
    </mso:CustomDocumentProperties>

  4. Provision the HTML and the JavaScript Files Together:
    When you try to provision only the HTML file, the first time everything will run smoothly. Your JavaScript file will be created, and metadata is correctly filled in. But from the moment you make any modification to the HTML file in your solution and provision the file again, the HTML file gets updated, but the JavaScript file isn't.The best way that worked for me is to provision the HTML and the JavaScript file together. It seems that the Design Manager also works this way. For the moment a file gets updates, it will package the HTML and JavaScript file.The way I'm doing this right now is like this:<Module Name="_catalogs" Url="_catalogs/masterpage/Display Templates/Content Web Parts" RootWebOnly="TRUE">  <File Url="Control_Custom_Refinement.html" Type="GhostableInLibrary" Level="Draft" ReplaceContent="true"></File>  <File Url="Control_Custom_Refinement.js" Type="GhostableInLibrary" Level="Draft" ReplaceContent="true">    <Property Name="ContentType" Value="Display Template Code" />  </File></Module>

  5. Create a Feature Receiver to Update and Publish your Display Templates:
    When the display templates are provisioned, you will need to do the following two things:Trigger an update > this is needed to update the JavaScript file (so the JavaScript file should definitely have the correctly converted markup);
    The update can be triggered with "item.Update()";
    Publish the file > otherwise they will not be available of site readers.The best way to update and publish your display templates is via a Feature Receiver on the FeatureActivated event. This is what I also used to do in SharePoint 2010 to publish the master page and page layouts when using a Sandboxed Solution.You only need to publish the HTML version, from the moment the HTML file gets published, the engine will automatically published the associated JavaScript file.


On activation, the feature provisions the files, and Master Page Gallery receiver code performs the conversions, but the files are still drafts.For guaranteed usability, the HTML files should be published. This can be done manually, but it can also be dealt with in a feature event receiver. The basis for this is that the HTML file be stamped to the feature by issueing it the Feature ID as a property in Elements.xml:

private string[] folderUrls = { "_catalogs/masterpage/Display Templates/Content Web Parts" };
 

        public override void FeatureActivated(SPFeatureReceiverProperties properties)

        {

            SPSite site = properties.Feature.Parent as SPSite;

            if (site != null)

            {

                SPWeb rootWeb = site.RootWeb;

 

                SPList gallery = site.GetCatalog(SPListTemplateType.MasterPageCatalog);

 

                if (gallery != null)

                {

                    SPListItemCollection folders = gallery.Folders;

                    string featureId = properties.Feature.Definition.Id.ToString();

 

                    foreach (string folderUrl in folderUrls)

                    {

                        SPFolder folder = GetFolderByUrl(folders, folderUrl);

                        if (folder != null)

                        {

                            PublishFiles(folder, featureId);

                        }

                    }

                }

            }

        }

        private static SPFolder GetFolderByUrl(SPListItemCollection folders, string folderUrl)

        {

            if (folders == null)

            {

                throw new ArgumentNullException("folders");

            }

 

            if (String.IsNullOrEmpty(folderUrl))

            {

                throw new ArgumentNullException("folderUrl");

            }

 

            SPFolder folder = null;

 

            SPListItem item = (from SPListItem i

                               in folders

                               where i.Url.Equals(folderUrl, StringComparison.InvariantCultureIgnoreCase)

                               select i).FirstOrDefault();

 

            if (item != null)

            {

                folder = item.Folder;

            }

 

            return folder;

        }

        private static void PublishFiles(SPFolder folder, string featureId)

        {

            if (folder == null)

            {

                throw new ArgumentNullException("folder");

            }

 

            if (String.IsNullOrEmpty(featureId))

            {

                throw new ArgumentNullException("featureId");

            }

 

            SPFileCollection files = folder.Files;

            var drafts = from SPFile f

                                  in files

                                  where String.Equals(f.Properties["FeatureId"] as string, featureId, StringComparison.InvariantCultureIgnoreCase) &&

                                  f.Level == SPFileLevel.Draft

                                  select f;

 

            foreach (SPFile f in drafts)

            {

                f.Publish("");

                f.Update();

            }

        }


Hope that helps you.


Happy SharePointing :)

Comments

Popular posts from this blog

Hide Ribbon on SharePoint 2013 using CSS

Get Comment Count in SharePoint

Configure external site as content sources in sharepoint search