Deploy SharePoint 2013 Display Template through Visual Studio

We create more and more display templates as soon as we utilize Content By Search Webpart or Search Result Webpart in SharePoint 2013. The recent project that I’ve done includes authoring (internal) site and publishing (external) site through Cross Site Publishing. Since the publishing site is customer facing / external site, we use display templates heavily and I think we have around 90 display templates.

In this article, I would like to show you how to deploy them through Visual Studio. Without further ado, I want to show you my very basic display template:

<html xmlns:mso="urn:schemas-microsoft-com:office:office" xmlns:msdt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882">
<head>
    <title>My Custom Item Template</title>

    <!--[if gte mso 9]><xml>
    <mso:CustomDocumentProperties>
    <mso:TemplateHidden msdt:dt="string">0</mso:TemplateHidden>
    <mso:ManagedPropertyMapping msdt:dt="string">'Link URL'{Link URL}:'Path','Line 1'{Line 1}:'Title', 'ListItemID':'ListItemID', 'owstaxIdCNPKeywords':'owstaxIdCNPKeywords'</mso:ManagedPropertyMapping>
    <mso:MasterPageDescription msdt:dt="string">This Item Display Template will show a small thumbnail icon next to a hyperlink of the item title, with an additional line that is available for a custom managed property.</mso:MasterPageDescription>
    <mso:ContentTypeId msdt:dt="string">0x0101002039C03B61C64EC4A04F5361F385106603</mso:ContentTypeId>
    <mso:TargetControlType msdt:dt="string">;#Content Web Parts;#</mso:TargetControlType>
    <mso:HtmlDesignAssociated msdt:dt="string">1</mso:HtmlDesignAssociated>
    <mso:CrawlerXSLFile msdt:dt="string">/_catalogs/masterpage/MyDisplayTemplates/Server_MyItem.xsl, /_catalogs/masterpage/MyDisplayTemplates/Server_MyItem.xsl</mso:CrawlerXSLFile>
    </mso:CustomDocumentProperties>
    </xml><![endif]-->
</head>

<body>
    <div>
        <!--#_
                var title = $getItemValue(ctx, "Line 1");
                var itemURL = $getItemValue(ctx, "Link URL");
        _#-->
        <a class="mydiv" data-item="_#= $htmlEncode(itemURL) =#_">_#= $htmlEncode(title) =#_</a>
    </div>
</body>
</html>

As you noticed above, I’ve specified CrawlerXSLFile property. Basically this is required for Search Engine Optimization, to allow search crawler to crawl content inside your Content By Search Webpart/ Search Result Webpart. You could read great post from Waldek here. This is same thing like we did when we would like to customize Content Query Webpart in SharePoint 2010.

<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:template match='/'>
		<xsl:apply-templates />
	</xsl:template>

	<xsl:template match='ResultTable'>
		<xsl:apply-templates select='Rows'/>
	</xsl:template>

	<xsl:template match='Rows'>
		<xsl:apply-templates select='Row' />
	</xsl:template>

	<xsl:template match='Row'>
		<xsl:for-each select='*'>
			<xsl:if test="name(.) = 'title'">
			   <xsl:value-of select="." />
			</xsl:if>
		</xsl:for-each>
	</xsl:template>
</xsl:stylesheet>

Now, it come to the deployment part in Visual Studio. Below is the list of steps:
1. Open Visual Studio and Create SharePoint Empty Project as Farm Solution
2. Add New Module, called it DisplayTemplates
3. Add your .html and .xsl display templates into DisplayTemplates Module
4. Edit Elements.xml in DisplayTemplates module

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Module Name="DisplayTemplates" Url="_catalogs/masterPage">
    <File Path="DisplayTemplates\MyItem.html" Url="MyDisplayTemplates/MyItem.html" ReplaceContent="True" Type="GhostableInLibrary" Level="Published"/>
    <File Path="DisplayTemplates\Server_MyItem.xsl" Url="MyDisplayTemplates/Server_MyItem.xsl" Type="GhostableInLibrary" Level="Published" ReplaceContent="TRUE"  />
  </Module>
</Elements>

5. Ensure Display Template Module included in your Feature

 DisplayTemplate1

6. Add Feature Receiver

using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Utilities;
using System;
using System.Runtime.InteropServices;
using System.Xml;

namespace DisplayTemplatesDemo.Features.Feature1 {
    /// <summary>
    /// This class handles events raised during feature activation, deactivation, installation, uninstallation, and upgrade.
    /// </summary>
    /// <remarks>
    /// The GUID attached to this class may be used during packaging and should not be modified.
    /// </remarks>

    [Guid("4d50c166-1095-4d7c-a4c1-61b35687cbf5")]
    public class Feature1EventReceiver : SPFeatureReceiver {
        /// <summary>
        /// Feature activated, we just change the item title of display template to force SharePoint generates the associated .js file
        /// </summary>
        /// <param name="properties"></param>
        public override void FeatureActivated(SPFeatureReceiverProperties properties) 
        {
            try {
                var site = (SPSite)properties.Feature.Parent;
                var web = site.RootWeb;

                //get all display templates
                string allDisplayTemplates = getAllDisplayTemplates(site.RootWeb, properties.Definition);
                //get the master pages gallery
                var gallery = web.GetCatalog(SPListTemplateType.MasterPageCatalog);

                foreach (var displayTemplate in allDisplayTemplates.Split(new char[] {','}))
                {
                    var url = SPUtility.ConcatUrls(gallery.RootFolder.Url, "CNPGallery/Display Templates");
                    url = SPUtility.ConcatUrls(url, displayTemplate);
                    //get the file
                    var fileOrFolder = web.GetFileOrFolderObject(url);

                    if (fileOrFolder != null && fileOrFolder is SPFile) {
                        var file = (SPFile)fileOrFolder;
                        if (file.Exists) {
                            //determine if the gallery requires files to be checked out before editing
                            //if so, check this one out
                            if (gallery.ForceCheckout)
                                file.CheckOut();

                            //make a simple change
                            file.Item["Title"] = (file.Item.Title != null ? file.Item.Title : displayTemplate);
                            file.Item.Update();

                            //if check out required, check it in
                            if (gallery.ForceCheckout)
                                file.CheckIn(string.Empty);
                            //if the gallery has minor versioning enabled, publish a major
                            if (gallery.EnableMinorVersions)
                                file.Publish(string.Empty);
                            //if the gallery required approval, approve it
                            if (gallery.EnableModeration)
                                file.Approve(string.Empty);
                        }
                    }
                }
            }
            catch (Exception ex) {
                //deactivate the feature
                var site = (SPSite)properties.Feature.Parent;
                site.Features.Remove(properties.Feature.DefinitionId);

                SPUtility.TransferToErrorPage(ex.Message);
            }
        }

        private string getAllDisplayTemplates(SPWeb web, SPFeatureDefinition definition)
        {
            string result = string.Empty;
            try
            {
                string wpcatalogUrl = SPUrlUtility.CombineUrl(web.Url, "_catalogs/masterpage");

                // Get the Feature.xml for the feature
                XmlDocument featureXml = new XmlDocument();
                featureXml.LoadXml(definition.GetXmlDefinition(web.Locale).OuterXml);

                XmlNamespaceManager nsMgr = new XmlNamespaceManager(featureXml.NameTable);
                nsMgr.AddNamespace("sp", "http://schemas.microsoft.com/sharepoint/");

                // Get Location attribute of each ElementManifest inside ElementManifests inside the Feature
                foreach (XmlNode locationNode in featureXml.SelectNodes("/sp:Feature/sp:ElementManifests/sp:ElementFile/@Location", nsMgr))
                {
                    if (result != string.Empty)
                        result += ",";
                    result += locationNode.Value.Replace("Templates\\", string.Empty);
                }
            }
            catch(Exception ex)
            {
                result = string.Empty;
            }
            return result;
        }
    }
}

7. Finally the solution look like below

 DisplayTemplate2

8. Build and Deploy the solution

Advertisements

Tagged: , , , , , , ,

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: