Thursday, 25 April 2013

Provisioning the Content Search web part with a custom Display Template

I hit an interesting issue recently, around provisioning the Content Search web part onto a page with the AllUsersWebPart element. I should say upfront that I’m not (currently) providing an awesome “here’s some code” solution to the problem, but will suggest approaches that should work fine. Mainly I want to raise awareness of the problem at this stage – if you hit it, you’re not going crazy.

Articles in this series:

If you’ve worked with the Content Search web part in SharePoint 2013, you’ll know that you can provide custom JavaScript templates (known as Display Templates) to control how items are displayed. Effectively you’re specifying the HTML which is output. Once the file is in the right place, you can select it in the ‘Display Templates’ section of the Content Search web part properties, like this:

Selecting custom display template

These Display Template files are expected to live in the Master Page Gallery, and if you browse there you can see all the out-of-the-box Display Templates for various bits of SharePoint (Content Search web part, search hover panel, refinement panels etc.). There’s actually a significant difference between sites that have the publishing feature activated, and sites which do not. Specifically, if you browse in a publishing site, you’ll see two sets of files – HTML and JavaScript:

Content Search Display Templates in publishing site

..but if you look at a site without publishing, then you only see JavaScript files:

Content Search Display Templates in non-publishing site

This is one aspect of the “Design Manager” support provided in publishing sites – you get a HTML and a JS file for each template, and you can simply edit HTML file. When you do this, the associated JavaScript display template (which is going to output data in the HTML format you provided) is *generated* from your HTML file. This happens through an event receiver on the Master Page Gallery, and happens fast enough for iterative development to work out fine – as a reasonably hardcore dev, doing this bit in SharePoint Designer works fine for me. However, another option (provided by this support for HTML editing) is using a HTML editor with a design surface (Dreamweaver/Expression Web/Allaire Homesite (!)/whatever) – you can map a drive in Windows Explorer to the Master Page Gallery, to facilitate saving directly to your SharePoint dev environment from your editor.

Provisioning in a Feature - deploy the JavaScript and/or the HTML file?

So, before we get to my actual issue, we have an interesting decision point if we are working against a site with publishing in the "developer/Visual Studio” way. Do we:

  • Provision the HTML file in a Feature, and then “touch” the file somehow (e.g. custom event receiver) to ensure the JavaScript file gets generated?
  • Provision the JavaScript file only

After playing around with both options, my strong view on this is that you should only provision the JavaScript file. By all means, use the HTML support in the core dev phase to obtain the JavaScript file – but when you’re ready to take this to other environments, package it up in Visual Studio, and include it in your farm/sandbox WSP (N.B. none of this really applies to apps). I say this because:

  • You are NOT going to need those HTML files in production – they are not used at runtime. The whole thing is purely to support the design phase!
  • If you need to go back and make changes, then you can either edit the JavaScript file directly (frankly no sweat for most devs) or fire up a publishing site somewhere, use the HTML support, and then drop it back into Visual Studio when done

Touching a file –> to trigger an event receiver –> to generate a file –> which you already have in the development environment == no sense to me :) However, I would say that if you *are* working in a publishing site (e.g. in SPD), always edit the HTML file if it exists – otherwise of course, your changes to the .js file will be overwritten whenever that event receiver executes.

I use XML like the following for this – I found that the key property, ‘HtmlDesignAssociated’ defaults to 0 (zero) but when deploying the .js file only, I like to set it to zero explicitly:

With that out of the way, let’s discuss the problem I see when extending the above - to have a Content Search web part automatically added to a page with AllUsersWebPart element.

Problem - custom Display Template not linked when provisioning Content Search web part using AllUsersWebPart

[Update April 30, 2013 – Ivan Neganov has found a solution to what follows, see the comments at the bottom of the article!]

Hopefully that title was descriptive enough. So the goal is to provision the Display Template in a Feature, but also make use of it – i.e. provision some kind of page which has a pre-configured Content Search web part on it, where the configuration ‘points to’ a custom Display Template. I think this scenario will be pretty common – you’d use it in many places, such as rolling up some news articles with a particular look and feel. The process I used is what any experienced SharePoint developer would probably expect:

  1. Configure the CSWP with the right settings on a page
  2. Export the web part to get the XML
  3. Drop into an AllUsersWebPart element in a Feature
  4. Check the settings all look good
  5. Test

The key bit of configuration we’re interested in is this line, but it all looks good at this stage:

<property name="ItemTemplateId" type="string">~sitecollection/_catalogs/masterpage/Display Templates/Content Web Parts/Item_Picture3Lines_COB_blue.js</property>

The overall web part XML is as follows (the “ItemTemplateId” is the last property to be listed):

The result is that seemingly all aspects of CSWP configuration take effect, except the custom Display TemplateSo. all other properties are applied, so the search query works fine and shows the correct items - but the look and feel is the out-of-the-box branding supplied by the ‘Item_Picture3Lines.js’ template (i.e. not my ‘Item_Picture3Lines_COB_blue.js’ file). What’s weird is that if you then edit the page, you find that your custom template is indeed available for selection (see first image in this article), and selecting it works just fine and the branding gets applied. At first I assumed this was some sort of issue with the format, or a ‘provisioning sequence’ issue, and played around with some ideas (breaking things into multiple Features with dependencies etc.) – none worked, so it was time to break out Reflector Pro.

After a pretty forensic ‘debugging SP2013 code’ investigation (I had a colleague trace my steps independently, thanks Hrayr!), this looks like a possible bug to me. We also tried environments before and after the March 2013 Public Update, but saw the same results. Essentially the provisioning process eventually calls into some code in the ContentBySearchWebPart class itself, and in particular a method called IsTemplateLocationValid. We see that one check appears to expect the path to start with “~sitecollection/_catalogs/masterpage” (this has been stripped off by earlier SharePoint code, despite the value being specified that way in the XML), so that one fails (click to see larger image – and sorry if you’re not into the Visual Studio ‘Dark’ theme!):

Custom template without sitecollection token

..and the next check expects SPContext to be present. However, it isn’t! I’m not sure why, given the Feature is being activated through the browser in the SharePoint ‘Activate Site Features’ page in my case, but there is definitely no SPContext. So between these two checks, I’d expect one to succeed. I can’t help but think that there is currently a minor bug in SharePoint 2013 - where SPContext is expected to be present in this provisioning process, but for whatever reason it isn’t. I say this partly because I notice that even for out-of-the-box templates, the value comes through stripped of the “~sitecollection” token:

OOTB templates without sitecollection token 2

I also tried using *just* the filename:

<property name="ItemTemplateId" type="string">Item_Picture3Lines_COB_blue.js</property>

..but then the page does not load (blank screen, HTML not output properly) and ULS shows the following runtime error:

Application error when access /SitePages/CSWP_Provisioned.aspx, Error=Cannot make a cache safe URL for "item_picture3lines_cob_provisioned.js", file not found. Please verify that the file exists under the layouts directory. 
at Microsoft.SharePoint.Utilities.SPUtility.MakeBrowserCacheSafeLayoutsUrl(String name, Boolean localizable, Int32 desiredVersion)    

Possible solutions

Broadly I think these are the options:

  1. Manually fix-up any pages where you are adding the Content Search web part using AllUsersWebPart – i.e. edit the page by hand, select the Display Template in the web part properties, then save the page.
  2. On-premises only - run some server-side code (e.g. in a Feature receiver) to fix things up. Use SPLimitedWebPartManager to update the web part properties.
  3. For Office 365 – run some client-side code (JSOM or CSOM) to fix up the web part.

I don’t think any of the code would be hugely challenging to write – I might do it soon if no-one else does :) But for now, I just wanted to highlight the issue. Do you see a different behavior? Please leave a comment and let me know if so!

17 comments:

Ivan Neganov said...

Thanks Chris, this was a very valuable post for us and the timing was great - we were just struggling with the same issue. I was thinking what if we "spoil" the "~sitecollection" token so that it doesn't get resolved too early, and this strategy has worked for us. Simply escaping the tilde for the ~ does the trick. For example:
~sitecollection/_catalogs/masterpage/Display Templates/Content Web Parts/Item_ContentRotatorItem.js

Ivan Neganov said...

The tilde escape I just suggested has got converted to the actual tilde character by the blog engine. Trying it again: the escaped path sould be: &#126;sitecollection/_catalogs/masterpage/Display Templates/Content Web Parts/Item_ContentRotatorItem.js

Chris O'Brien said...

@Ivan,

You're right! I've tested, and provisioning works perfectly - excellent find :) If I get chance, I might try and step through in the debugger and add a further comment with some detail on which path the code now takes.

But anyway, many thanks for sharing your finding!

Chris.

Swati Jain said...

Hi Chris,

Like in SharePoint 2010, is it possible to get the artifacts for Webpart and Dispalytemplate using 'Save as Template' and then exporting it to visual studio?

Swati

Chris O'Brien said...

@Swati,

Yes that works fine, but you'll need to edit the XML so that the path to the Display Template uses the tilde workaround above.

HTH,

Chris.

Suhas R.S said...

Nice article on provisioning.
I am facing a problem with Content Search web part with custom display template.
I want to display published page content according to navigation.
I am able to get the content, but since I am displaying HTML i.e. PublishingPageContent type,
for long HTML pages it does hot show me the full page.

I tried creating my own display template and the out-of-box display templates,
but in all the cases it shows me only partial HTML content.

How to create display template that allows to show full HTML content rather than links?

Anonymous said...

great writeup, and the mentioned workaround fixes the issue! However, I keep having problems setting ´NumberOfItems`. On provisioning the WP with a setting other than 3, it is ignored. Any idea what could be the problem?

Thanks in advance!
Roel

Suhas R.S said...

I seemed to have zeroed in on the issue after 'debugging' the display template.
The HTML that I have has < table > markup with 2 rows and 5 columns. The table is wide and covers end to end.
For some reason, the Managed property 'PublishingPageContentOWSHTML' and the corresponding crawled property
which is automatically created for the 'Page Content' Site column does not crawl < table > markup accurately
and does not get content after the table. A bug in SharePoint crawling Publishing HTML content ?

Mike Gorgone said...

Hi Chris,

I'm currently trying to deploy control and item display templates and I followed the steps here:

http://borderingdotnet.blogspot.com/2013/03/how-to-deploy-display-templates-via.html

I created the two files from the OOTB Control_ListWithPaging.html and Item_PictureOnTop.html files

However, after deploying and the feature running "correctly" the html file is there and the .js file is generated (in the Content Web Parts folder) they aren't appearing in the drop down lists for the Web Part.

Any thoughs on why they wouldn't appear?

Chris O'Brien said...

@Mike,

Without seeing what you've done, my hunch would be that you don't have the list item properties set correctly for the display template. In particular, check the 'TargetControlType' setting to ensure you are specifying this is a display template for use with the Content Search web part.

HTH,

Chris.

Esben said...

@Chris: Thanks, this post saved me hours of SharePoint frustration

@Ivan: Thanks, it took me 5 minutes of search/replace to get my display templates working.

vishal singh said...

@Ivan,@Chris Brilliant ! Simply Brlliant.

K@rell said...

Antoher great article Chris!

ChicagoBrian said...

NOTE: Display Templates, like other assets in the Master Page Gallery, must be Published in order for everyone to see them. Once you have completed your edits, return to the Mater Page Gallery and Publish a Major Version of the files so that your users will be able to see them.

Site Settings, Site Collection Administration, Go to top level site settings

From Top Level site - Site Collection Admin - Search Result Types

Link from within the first sentence at top for "crafting a display template in HTML."

julie duprat said...

Thank for your workaround for replacing ~ by &#126 for templateid in cswp params.
It saved my life... Congrats for quality of your post.

julienduprat@hotmail.fr

Cynthia Duke said...


I had my new managed property mapped and it would NOT show up under Link URL and I typed it in and it magically appeared. Something in your article triggered this thought :)

If we ever meet, I either want to give you a hug or buy you a drink. I have been working on CSWP since June and finally found these awesome articles.

Thank you, thank you, thank you!!!!

Kindest Regards,
Cynthia

Chris O'Brien said...

Aw, happy to help Cynthia! Glad to hear you got things working, and thanks for the message.

COB.

P.S. Hugs, drinks or coffee always accepted here ;)