Create An Easy PDF Preview for Search Results in SharePoint 2013

I saw this question flying around the other day and thought it was kind of interesting.  Someone was asking about the cool new thumbnail previews you get for items in search results in SharePoint 2013.  They really liked how Microsoft Office documents work and wanted to be able to do the same thing with PDF files.  There was an answer, which is a good one, that you could create a custom WOPI extension that would render PDF files using Office Web Apps.  The drawback to that is that it requires someone to do some coding and testing (using full trust code), then deploy it to your OWA farm, then create a new WOPI binding in the SharePoint farm.  Again, an entirely reasonable response if your requirements dictate that.

I, however, representing all that is good about being lazy and cheap, decided to take a different approach to this.  Without digging into the particulars of how all the plumbing works, at the end of the day a search results preview for Office documents are just done via an iFrame.  So why not do the same thing for PDF documents?  Why not indeed – that’s exactly what I did.  Here’s all I did to make this work:

  1. I went to my search center site and download a copy of the Item_PDF.html, which is the out ofthe box display template for a PDF item in SharePoint 2013.  I made the following changes to it:
    1. Changed the Title to “PDF by Steve”
    2. Changed the hoverUrl variable in the javascript to “~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Site_HoverPanel.js”
  2. I saved it as PDF_Default.html and went back to my SharePoint search center site, then navigated to Site Settings…Master pages and page layouts…Display Templates…Search and uploaded it.
  3. I went back to Site Settings and selected Search Result Types in the Site Collection Administration section.
  4. I created a Search Result Type and configured it as follows:
    1. Name:  PDF Viewer
    2. What types of content should match? :  PDF
    3. What should these results look like?  :  PDF by Steve

That’s it – now you’re ready to go.  One thing to point out here – in step 1b I configured the display template to use the hover panel that comes out of the box for sites.  It is configured to use an iFrame and render it nice and pretty, so I didn’t even have to create a hover panel for it.  As a side note I actually DID do that when I was fooling around, but the out of the box stuff looks so much better than what I can do I figured why fight it – graphical beauty ain’t my thing.  Once it’s done I get a nice preview for my PDF documents in search results, which you can see here:

That’s all there is too it.  I’ve attached my display template here, just in case you are as lazy and cheap as I am.  🙂   Hope everyone has a Merry Christmas and Happy New Year.

UPDATE:  I did discover one big limitation with this approach – it only works if your PDF documents are in the same host as your search center, i.e. if they are in the same web application is the easiest example.  So if you have your search center at http://www.foo.com, but your PDFs are in http://www.bar.com then they will not be displayed as preview items.  That’s because of some X-Frame-Options headers that we send down with SharePoint now. If you are following our new recommendations for SharePoint 2013 to use a single web application for your farm then you will be fine, but if you have multiple web applications or are crawling content from non-SharePoint sites then it will not render in the preview.

 

Couple other possible gotchas to be aware of – 1) make sure you have installed the Adobe reader on the machine on which you are viewing the previews and 2) I’ve got at least one report saying this doesn’t work in a non Internet Explorer browser.

 

You can download the attachment here:

Query Rule Conditions in SharePoint 2013 Search

I was putting this information together today for the SharePoint 2013 recordings that will be going up on TechNet early next year, and I thought it would be good information to share here as well.  I’ve had a few posts already about search and query rules.  One of the things that may be a little hard to understand is the conditions that you can create for a query rule.  You actually have a couple of choices before you get there actually – if you decide that you want your query rule to fire every time, then just delete the condition that is added when you create the query rule.  If there are no conditions then your query rule will fire every time.  If you want to use conditions though, then you can add one or more conditions – as soon as we find one that matches, we’ll stop and get onto whatever action you’ve defined for the rule.

The point of this post though is to talk about the conditions and what they mean; it can be a little tough just from looking at the description them, so that’s what I’m hoping to explain here.  These are the conditions you can use in a query rule:

  • Query Matches Keyword Exactly
  • Query Contains Action Term
  • Query Matches Dictionary Exactly
  • Query More Common in Source
  • Result Type Commonly Clicked
  • Advanced Query Text Match

 

Matches keyword exactly is pretty straightforward – you can create a condition where the user’s query exactly matches a specific word or phrase;  you just type in what you’re looking for. 

Action terms can be words that you type into the condition and the query needs to start or end with one of them.  You can also have it look in a term store termset for action words.  The difference between typing in a value for an action term versus keyword, is that keyword must match exactly; action terms can be at the start or end of the query.

For matching a dictionary exactly, you select a termset and the query has to exactly match one of the terms in it.

For query more common in source, this means the condition checks to see if the query is one of the top queries in a specific result source you select.  For example, if a user queries for “Shareholder Presentation” in the default result source, it may be that the query is actually very popular in the Videos result source, where you query against all of the video presentations to your stockholders.  If that’s the case, then you might want to have your query rule also fire the users query against the Videos result source in addition to the source the user is querying.

The result type commonly clicked is similar to query more common in source.  What it means is that in your query rule condition you can select a result type, like videos.  When the query rule looks at the user’s query, if it sees that when people executed that query previously they commonly clicked on videos in the search results, then your condition would return true.  So again – if a query has results that are commonly clicked for a particular result type, then you can use that to have another query execute and return results of that type.

Finally, in the advanced rule you can do all sorts of matching against the user’s query – you can look for a query that contains, starts with or ends with a  particular word or phrase, you can look for matches in termsets, you an use regular expressions, etc. 

 

Hope that helps clarify what these conditions mean and how they are meant to be used.

 

How to Configure the Global Search Center Url for SharePoint 2013 using PowerShell

I had this question come up today and it was a little bit of work to track down so I thought I would share here.

 

In SharePoint 2013 the Search Service Application has something called a Global Search Center Url.  In the absence of any other configuration, this is where folks will get redirected to run their queries.  However if you are doing a fully scripted install using PowerShell it’s not completely obvious how you set this value.  Here is how you can change it:

$ssa = Get-SPEnterpriseSearchServiceApplication
$ssa.SearchCenterUrl = <newURL>
$ssa.Update()

 

Searching Property Bags in SharePoint 2013

This is one of the 1001 or great new things about search in SharePoint 2013.  In fact if I had the copious free time that would actually be kind of a fun challenge, writing a 1001 new things I like about Search, but I digress.  In this particular case, I’m talking about adding properties to a property bag and getting them indexed and searchable.  Fortunately SharePoint 2013 makes this possible, not only for webs, but even all the way down to an SPList.  How cool is that?  🙂

Here is some PowerShell you can use to get you started with creating an indexed property in an SPWeb property bag:

$web = Get-SPWeb http://localhost
$web.AllProperties[“Prop1”] = “value1”
$web.IndexedPropertyKeys.Add(“Prop1”)
$web.Update()

Next, here’s how you add an indexable property to a list:

$list = $web.Lists[“Announcements”]
$folder = $list.RootFolder
$folder.Properties[“Prop2”] = “Spammers”
$folder.Update()
$list.IndexedRootFolderPropertyKeys.Add(“Prop2”)
$list.Update()

Of course then just run a full crawl afterward to create the crawled property and all that hoo haw so you end up with a managed property when you’re done that is searchable, queryable and retrievable.

 

Integrating People Metadata In Content Searches Using Query Rules in SharePoint 2013

Okay, that’s admittedly kind of a long goofy title, so what is Steve talking about here?  Well, let me phrase it this way – a few days ago I saw a question flying around that went something like this:  I have folks that work for different departments in my organization, like Sales, Finance, etc.  When someone types in a query for something like “sales management pitch” then I would like to be able to also have it show people that work in the Sales department.  Okay, sounds like a pretty cool use of the new Query Rules feature in SharePoint 2013.

To start with, let’s take a step back and look at a related enabling feature we’re going to use here.  In SharePoint 2013, when you do a profile import from Active Directory it automatically populates a special termset in the Managed Metadata Service.  In fact, it actually creates a master group called “People” and populates three termsets:  Department, Job Title and Location (NOTE:  I can’t guarantee that all three of these will still be there when the product ships, it may only end up being two of these).  During the profile import process each term set will be populated with the unique values across all users for each of those properties.  This really helps out a lot for this problem, because when I look at my MMS, I see that in my list of Departments it includes “Sales”, since I have users with that value in their Department attribute in AD:

 

Okay, that’s great, so now let’s go and create a query rule.  As I mentioned in my first post on query rules (http://blogs.technet.com/b/speschka/archive/2012/07/23/using-query-rules-result-types-and-display-templates-for-a-custom-search-sales-report-in-sharepoint-2013.aspx), the first thing we’re going to do is set the condition when my rule should fire.  In this case we’re going to use the Advanced Query Text Match option, and choose the last selection, which is Query contains an entry in this dictionary:.  Now it has a drop down selector, which is unfortunate because it’s a little misleading.  Really what we want to do here is click the Import from taxonomy link that’s directly below it.  When we do that we’ll see a standard MMS picker, and so I just expand my local MMS instance and click the Department termset, because I want my rule to fire when the query text contains a word that is a Department in my organization.

For the next part of my rule, I check all three checkboxes for looking at the query terms.  So if an entire query equals a Department name, or if the query starts with or ends with the name of a Department, then I want my rule to fire.  Finally, for the last part of the rule configuration, if there is a match then I want to assign the match to {subjectTerms}, and the remaining terms to {actionTerms}.  So if we use the example above of searching for “sales management pitch”, then since the query terms starts with “sales”, which is a valid Department value in MMS, my query rule should match – “sales” will be the {subjectTerms} and “management pitch” will be the {actionTerms}.    Here’s what that configuration looks like:

Now when my rule fires, I want to add some folks from the Sales Department to the top of the query results.  So I start working on the next part of my query rule, which is the action I’m going to take.  I click on the Add Result Block link and open up that dialog.  In this case I want to add a Result Block that will display people results, so the first thing I do is click on the Search this Source drop down and change it from Query’s orginal source to Local People Results (System).  I do this first so the query builder will use that source when I modify and test the query.  With that done, based on my matching rule I know that whatever Department has been found will be in the special term “{subjectTerms}”.  In order to use that, I click the Launch Query Builder button so that I can modify the query.  I delete any existing text that’s in the Query text edit area, and then click on the Property filter drop down and click on the –Show All Managed Properties—option.  That repopulates the drop down with all the managed properties, and from that list I select Department.  In the qualifier drop down I change it from “Contains” to “Equals”, and then in the Select value drop down I choose {subjectTerms} – the matched dictionary entry from Department and then click the Add property filter button.   This is what it looks like:

This is shaping up pretty nicely.  Now I want to add a more personal touch to it, and to do that I click on the Sorting tab in the query builder.  In this case instead of just using the default sorting model, since I’m going to be showing people I’m going to sort by social distance.  So not only will I show people in the results, but the list of people will be ordered based on which ones are closest to me in social distance.  To do that I just use the Ranking Model drop down and select People Search Social Distance Model, like this:

Now I can click on the Test tab and try out my new query.  By default you should see zero results, and that’s because it doesn’t have a value for the {subjectTerms} variable.  To test it out with real query criteria click on the Show More link and then type in sales in the {subjectTerms}*: edit box and then click on the Test query button.  I try this with different ranking models to make sure that it’s all working as desired – it is – so I click the OK button to save my query changes.

Now the last thing I want to do for my query rule is display a link at the bottom of my block that someone can click on to find all people that work whatever Department was found.  Before I go any further on that I’m going to tell you something in the interest of full disclosure – I have a picture that I will show in a bit of the final results, and you will see a SHOW MORE link.  For this particular post, I actually combined two pictures – one that had the “SHOW MORE” link and one that did not.  The reason for that is because the “SHOW MORE” link functionality is working inconsistently in beta 2.  All the kinks should be worked out by RTM but for now sometimes it shows up for me, but more often it does not.  I just happened to grab a screenshot on an early set of results I was working on that included it.  I mention that here so that you don’t become overly frustrated if you are working on beta 2 bits and find that sometimes it shows up and sometimes it doesn’t.

That being said…in order to implement this functionality that I described in the previous paragraph, I select the option of “More” link goes to the following URL and typed in the following:  peopleresults.aspx?k=Department:{subjectTerms}.   I also decide to show three matches, and as described above, have them show up at the top of all the search results. So now my complete Result Block dialog looks like this:

Now that it’s done I can go test it out.  So I do a query for “sales management pitch” and here’s what my results look like:

(Note that this is where you see the “SHOW MORE” link I had to paste in at this point).  What is kind of cool is that Rocky shows up in the top of the search results because in my organization I am his Manager, so we see the social distance ranking algorithm at work.  If I hover over any of the people in the results then I can see a list of documents that they’ve recently published:

Finally, when I do click on the SHOW MORE link it takes me to the people search results page, and I’ll see all people that work in the Sales department; when I hover over any one of them I can see what they’ve done, see their profile page, etc.

So that concludes our exercise.  We took a very real customer requirement, and we used the new query rules and profile import features in SharePoint 2013 to create a solution that required no coding at all, and provides a LOT of functionality.  I hope you’ll continue to look at and work with query rules.

Creating Managed Properties in a Site Collection in SharePoint 2013

There are many cool things about search in SharePoint 2013, and one that I think is really going to help growing search verticals for departments and business units is the ability to create managed properties down to the site collection level.  Managed properties are frequently used in search verticals because they give you a way to define custom attributes associated with your data that you want to use for purposes of filtering, reporting, and/or refining.  In SharePoint 2010 and prior versions of SharePoint there were a couple of limitations with how they were used that made them kind of cumbersome:

  • You could only create them at the Search Service Application (SSA) level
  • It required a full crawl of all your content first to create a crawled property, and then a second full crawl of all your content to create a managed property.  This was tough for many organizations.

 

In SharePoint 2013 we open this up and make it a lot more accessible.  You can still create managed properties at the SSA level, but now you can also augment those with managed properties at the site collection level. The whole process starts out like before, creating a crawled property.  Again, a new feature here is that when you create a site column in SharePoint 2013, it is automatically configured to be a crawled property before you ever do your first crawl.  Very cool!  After that you can add your site column to any list or library and start adding content.

Now to create a new managed property you can go into the Site Settings and then Search Schema in the Site Collection Administration section.  From there you can create a new managed property and map it back to the crawled property.  Now one of the big differences between managed properties created in the site collection versus the SSA is that when you create new managed properties in the site collection they have three limitations you should be aware of:

  • They can only be text
  • They cannot be sortable
  • They cannot be refinable

You are not out of luck though!  Out of the box we ship a bunch of managed properties expressly for this purpose.  If you look in the list of managed properties you will see things like RefinableDate00..19, RefinableDecimal, RefinableDouble…RefinableString100, etc.  So when you need a non-string value, or you need something sortable or refinable you can use one of these properties.  Let me explain how it works by way of example.

You create a new site column called FavoriteColor; it’s a Choice column containing Blue, Green and Red and you want to create a new refinable managed property for it.  First you add it to your list or library, and then drop in some content.  Next you’re going to go into Site Settings…Search Schema and scroll down until you find RefinableString00, then edit it.  In the alias field type “Favorite Color”, and in the Mappings to crawled properties field, add a Mapping to the ows_FavoriteColor crawled property then save your changes.  That looks something like this:

 

Now once that’s done we still need to do a full crawl, but wait – there’s a new way to do that in SharePoint 2013 as well.  🙂 We no longer need to do a full crawl of the entire corpus – instead we can ask for a “full crawl” of just a single site, or even a single list.  In this case you would want to go into your list where you are using your FavoriteColor column and then go into the Site Settings.  If you click on the Advanced Settings link you can scroll down until you find the Reindex document library (or Reindex list if you are using a list).  Click that link to configure the list to be crawled.  Now the next time any kind of crawl kicks off from the SSA – incremental or full – it will populate the managed property and you can start using it in your queries, query rules and display templates.

Hope this helps you get started creating these yourself.

Using Query Rules, Result Types and Display Templates for a Custom Search Sales Report in SharePoint 2013

<Standard Disclaimer:  Blog formatting sucks.  Do your eyes a favor – download the attachment, which is the Word document version of this.)

UPDATE:  a few things have changed for RTM so I am updating this post and adding the artifacts I used to create everything.  Okay, a long title for today’s topic but we’re going to cover a lot of good material.   SharePoint 2013 has a number of outstanding features that allow you to really use and customize search results like never before.  I’m not going to try and give a comprehensive overview of all of them, or even significant details on the components we’re using in this posting because they are capably covered elsewhere.  However I will give you a starting set of search results, give a brief overview of each of these features, and then walk through the process of using them to create a very cool and customized search result that demonstrates how all of these pieces work together.

 

First, let’s start with a view of what the search results look like when I run a query for “sales reports” in my farm (NOTE:  this is a beta 2 build and the final release may look differently):

 

 

This is the out of the box experience with search results, and it looks good.  However, we’ve added these features to help you really enrich the end user searching experience in SharePoint 2013, so let’s add a bit to our scenario here.  We have several divisions throughout our company, and a number of them are responsible for managing sales within their division.  Each division uses a standard Excel template to report their sales activity over time.  In addition each division has one person that is responsible for managing the customer relationships and reporting their sales information.  So how can we use these features in SharePoint to help standardize and bring out important information about the division and its customers?  We’re going to take a multi-prong approach to solving this.

 

Custom Content Type

 

To begin with, we’re going to create a custom content type for everyone that uploads sales reports into SharePoint.  I begin by creating the site columns that I’m going to use – these are the fields that I want to be able to surface in my search sales report.  For our scenario I’ve defined the following site columns that we’ll be using:

 

Site Column Name

Type

Account Manager

Single line of text

Direct Reports

Number

Sales Region

Choice: Northamerica, EMEA, Asia

Top Accounts

Single line of text

Total Accounts

Number

 

Next I created a content type that includes all of these site columns.  I created the content type in the publishing hub site associated with my Managed Metadata Service so that it gets pushed out to all my subscribing sites in the farm, which in my case is all sites in all web applications.

 

The next step, which was manual in my case, was to add this content type to the list of available content types for each division’s site collection, in the document library where they store sales reports (among other things).  Obviously there are ways to automate this, but for brevity’s sake I did it manually.  Okay, so our first step is done – we have a custom content type, it’s deployed, and as sales reports are added to these document libraries, the Account Managers can just identify a sales report when they upload it and fill out the metadata.

 

Managed Properties

 

Now that we have these sales reports populating our sites, we need to create managed properties from the sales report custom site columns. UPDATE: In the example included in the .zip file with this post, I just added site columns and used the managed properties in a single site collection created automatically by SharePoint. There’s nothing really new here in SharePoint 2013 compared to SharePoint 2010 for purposes of this scenario – you need to do a full crawl to create the crawled properties, then create managed properties that are mapped to those crawled properties, then do another full crawl.  I say there’s nothing different in this scenario because we are implementing a farm-wide solution.  However SharePoint 2013 does have a fabulous feature that lets site collection admins mark their site collection, or site, or even list for a full crawl.  This saves you from having to do an enterprise full crawl when you want to do this at a more narrowly scoped level, which is really awesome.  I’ll try and cover that feature in another post – I just wanted to point out that it exists, but isn’t applicable in this case because our solution scope is for the whole farm.

 

Query Rules

 

At this point we have sales reports that are using a custom content type, and we have managed properties that are populated with the metadata values for those sales reports.  The next thing we want to do is to make sure that these items get “noticed” when someone is doing a search for something that should likely include one of these sales reports.  SharePoint 2013 has the perfect feature for this and it’s called Query Rules.  Query rules have three main components:

 

  1. Conditions – the conditions for a query rule determine when a rule will fire.  There are a number of different variations that you can do with rules.  This is just a brief run through of them but the possibilities are HUGE for using these, and in fact you will see a number of these are shipped out of the box.  One other interesting twist – if you want a query rule to fire on EVERY search result, then you just have no conditions.  You can of course do the opposite, and have multiple conditions as well.  You can probably quickly see how you can get lost in the scope of possibilities here.  The conditions you can use for a query rule include:
    1. a query that starts with or ends with a particular word or phrase
    2. a query that contains an action word (words that you define that are typically verbs, like “download”, etc.)
    3. a query contains a word that’s in a managed metadata term set (some very COOL things you can do with that)
    4. a query that is common in another result source, like video results, document results, etc. – it can be whatever because you define and create result sources
    5. the results contain a commonly clicked result type, like discussions, Excel spreadsheets, etc.
    6. advanced rules, where you can go crazy with regular expressions, split the query up into action terms and subject terms (what’s left over after action terms are removed), etc.
  2. Actions – actions are what you are going to do when the conditions for your query rule have been met.  You have three different choices here as well; you can:
    1. Add Promoted Result – this is similar to how Best Bets worked in SharePoint 2010 and Visual Best Bets worked with FAST Search for SharePoint 2010.  You can add a new link that will show at the top of all the search results.  You can choose to have the link rendered as a hyperlink or as a picture – thus the analogy to a Visual Best Bet.
    2. Add Result Block – a result block is where you execute an additional query and return and render the results along with the original search results.  It’s called a block because the results are rendered all together, in a block.  You can have the block show up at the top of all the search results, or you can have it intermingled with the other search results based on rank.  It’s important to note that this DOES NOT MEAN that it does relevancy ranking between the two queries.  It just means that the block as a whole will have a rank along with all the other local search results.  When you click on any item in the result block, that click action is recorded locally and the block as a whole becomes more relevant.  So if items in the block get lots of clicks, then over the time the block will start moving up in the results because it will have greater relevancy that individual results that are not selected as frequently.  There are actually a TON of things you can do to configure your result block, but as I said earlier, I’m not going to deep dive on that here and now.
    3. Changed ranked results by changing the query – this rather verbose option does just what it says.  You can actually go in and change the query that was requested any which way you want.  You can add additional criteria, you can remove terms, you can use XRANK to modify the ranking – the options are pretty wide open here.
  3. Publishing – publishing is what allows you to control if and when a query rule is used.  For example, you may want to create a set of rules that you want to fire on your day after Thanksgiving sale.  However, you don’t want them going off until that day actually arrives.  So you can create the rules, but configure the publishing so that rule is inactive.  Or you can make the rule active, but configure it to not start until a particular date and then end at a later date.

Okay, that was the “brief” rundown on what query rules are, so how will we use them here? 

UPDATE: this query rule was in beta 2 but did not make it into RTM.  You can manually create a rule like it by using an Advanced Text Match and selecting Query contains one of these phrases:, then adding this:  analysis;cube;dashboard;dashboards;data;database;report;reports;sales.  Check Start of and End Of query matches checkboxes, and uncheck Entire query matches exactly.   Select the last radio button to Assign match to {actionTerms}, unmatched items to {subjectTerms}.  For the Action, add a result block that issues a query for the {subjectTerms} against the Local Reports and Data Results result source.  Create the rule for the Local SharePoint Results result source.

Well it turns out that there is a query rule that ships out of the box that is going to take care of this for us.  I actually made the rule inactive when I took the screenshot above so you could understand the impact of it better.  In our case, we want queries in which someone might want to see a sales report to have our division sales report pop up and be noticed.  So, go into your Search site collection, Site Settings, and click on the Query Rules link.  In the Select a source dropdown, click on it and select Local Reports and Data Results; you will see a query rule called Reports and Data. UPDATE: Select the Local SharePoint Results result source if you are creating your own rule as described above.  Click on it and select View from the drop down menu to see how the rule is constructed.  It works like this:

  • Condition – the condition for the rule is that the query contains one of these words at the start or end of the query:  analysis;cube;dashboard;dashboards;data;database;report;reports;sales.  Notice that you see both reports and sales in there.  So if someone does a query for “sales report” then this query rule will execute.  That sounds perfect – if someone searches for “sales report” then we DO want them to see our division sales reports.  As part of this rule it assigns the match to the action terms, and everything else is assigned to the subject terms.  In this case it assigns “reports” to the action term, and “sales” to the subject term.
  • Action – the action for this rule is just running another query based ONLY on the subject terms.  So based on the condition above it will run a separate query just for “sales”.  It’s going to add a block that is always returned on top of all the other results.  BUT…it’s ONLY going to search for sales in the Local Reports and Data Results source.  This is a result source that also ships out of the box, and effectively just returns Excel documents (file extension ends with .XLSX, XLS, etc.).  Great, so it’s query to run query for “sales” only against Excel documents.
  • Publishing – the rule is active with no start or end date restrictions, so it always fires.

 

I re-enabled that query rule, so now this is what the results look like when I do a query for sales reports:

 

 

Cool, this is getting better – the query rule has kicked in and now we’re getting our documents based on our custom sales report content type showing up in the top of the results – that’s great!  However, we still aren’t showing any of the metadata about the documents and that’s what really brings this all together.

UPDATE: To simplify this process, you should do the following:

  1. Copy the query rule I described above.
  2. For the Action, instead of issuing a query for {subjectTerms} against the Local Reports and Data Results result, change the query criteria to ContentType:”Sales Report”. 
  3. Save this new query rule, and make the other one inactive.

When you’re done you have one active query rule that retrieves only items that are based on our custom Sales Report content type.

Display Templates

 

In SharePoint 2010 if you wanted to render a particular item differently, it was a fairly arduous process of going in and modifying one HUGE chunk of XSLT in the core results web part.  You got to practice your awesome XSLT skills and try and find the right place in that enormous document where to insert your custom code.  Overall, it was a less than joyful experience.

 

In SharePoint 2013 – queue the music – we have display templates and what an improvement they are.   No longer do you need to carry your XSLT Zen about you, now you can create your custom display code as straight HTML – yahoo!  In fact, for this posting I actually used Adobe’s Dreamweaver CS6 to create the “code” for my custom display template.  So, how do display templates work? 

 

With a display template, you have a few different things to keep track of:

 

  1. Managed properties – you need to tell it what managed properties you need retrieved at query time.  Those properties you can then use in your HTML, using a method I’ll describe below.
  2. External JS and CSS – if you have any javascript or CSS files you want used with your display template then you can externalize them and add them to your display template.
  3. Inline JS – you can also use inline JS in your display template.  You just need to make sure that it is below the first <div> in your display template – also more on that below.
  4. HTML – this is where you create the actual HTML for your display template that will render the results.

 

For our display template, we are going to pull together the attributes from the custom sales report content type we created so that we can have it shown in the search results like this:

 

 

Let’s get started – to begin with, you will pretty much always want to start with an existing display template when you are building a new one.  If you go into your search center site collection, Site Settings, then click on Master pages and page layouts link.  When you get into the library, click on the Display Templates folder, then click on the Search folder.  In there you will find all of the display templates that we ship out of the box.  You will likely see a number of files that have the same name, but either a .html or .js suffix.  All you care about are the .html files – the .js files are generated automatically when you upload an .html file.  NOTE:  If you do not see the HTML files, then you are probably not working in a search center site.  If you want to do this in a non-search site for some reason, enable the Publishing feature in the site collection.  In this case, since the display template is for Excel files I started out with the Item_Excel.htm file.  I downloaded a copy locally, renamed it SalesReport.html, and then opened it in Dreamweaver.

 

Next I add my managed properties.   There is a tag called mso:ManagedPropertyMapping where you put in the managed properties your display template requires.  I just added mine to the end of the list, using the same format as the rest of the managed properties – it looks like this:

UPDATE: Just an FYI – these managed property names assume you create them yourself in the Search Service Application.  In the example display template included in the .zip file with this post, it uses  the managed property names
you get when you just add site columns and let SharePoint created the managed properties in a single site collection for you automatically.

<mso:ManagedPropertyMapping msdt:dt=”string”>’Title’:’Title’,’Author’:’Author’,’Size’:’Size’,’Path’:’Path’,’Description’:’Description’,’LastModifiedTime’:’LastModifiedTime’,’CollapsingStatus’:’CollapsingStatus’,’DocId’:’DocId’,’HitHighlightedSummary’:’HitHighlightedSummary’,’HitHighlightedProperties’:’HitHighlightedProperties’,’FileExtension’:’FileExtension’,’ViewsLifeTime’:’ViewsLifeTime’,’ParentLink’:’ParentLink’,’ViewsRecent’:’ViewsRecent’,’FileType’:’FileType’,’IsContainer’:’IsContainer’,’ServerRedirectedURL’:’ServerRedirectedURL’,’ServerRedirectedEmbedURL’:’ServerRedirectedEmbedURL’,’ServerRedirectedPreviewURL’:’ServerRedirectedPreviewURL’, ‘AccountManager’:’AccountManager’, ‘SalesRegion’:’SalesRegion’, ‘TotalAccounts’:’TotalAccounts’, ‘TopAccounts’:’TopAccounts’, ‘DirectReports’:’DirectReports’, ‘ContentType’:’ContentType'</mso:ManagedPropertyMapping>

 

As you can see, I added AccountManager, SalesRegion, etc. – all of the properties I had created for my custom content type.  After that I scrolled down to where it says this:

 

<div id=”_#= $htmlEncode(itemId) =#_” name=”Item” data-displaytemplate=”ExcelItem” class=”ms-srch-item” onmouseover=”_#= ctx.CurrentItem.csr_ShowHoverPanelCallback =#_” onmouseout=”_#= ctx.CurrentItem.csr_HideHoverPanelCallback =#_”>

                                                                _#=ctx.RenderBody(ctx)=#_                                                     

                <div id=”_#= $htmlEncode(hoverId) =#_” class=”ms-srch-hover-outerContainer”></div>

</div>

 

And I deleted everything between the outer DIV tags (which I’ve highlighted in red above).   Now I can get down to writing my HTML.  Before I do that, there are three things you should know before you start:

 

  1. You will probably find it easiest to keep a <style> tag in the <head> section of your display template.  For reasons I won’t go into now, those style tags will be discarded when your display template is rendered, so you ultimately need to copy out your style tags and put them in a separate CSS file.  However, while you’re actually creating your HTML you can use and modify your style tags to make sure they reflect the look and feel that you want your display template to have.
  2. As mentioned above, you can add what I called inline javascript, which really means that you can add javascript code as long as it’s in the correct location.  The correct location for a display template means that it must be placed after the first DIV tag in your display template.  Also, it needs to go between an opening and closing tag set that looks like this:  <!–#_ and _#–>.  I’ll explain more about custom tags below, but suffice to say if you do not have your javascript between these tags it will not execute.  One thing that is VERY cool about this though is that you can use variables you define in your inline javascript when you create the HTML that will be output.  I’ll explain that more in the next item.
  3. To emit a managed property or variable from any inline javascript you’ve created, they need to be enclosed in an opening and closing tag set that looks like this:  _#= and =#_.   The managed properties are all available in an object called ctx.CurrentItem.  For example, to emit the AccountManager managed property, I would add this tag:  _#= ctx.CurrentItem.AccountManager =#_.  If I had javascript that looked like this:  var foo = “The current Account Manager is “ + ctx.CurrentItem.AccountManager + “.”;, then to emit “foo” I would add this tag:  _#= foo =#_.  This gives you complete flexibility to add or process data to work for your display template.

 

Okay, now that you understand the mechanisms for creating the HTML, here is what I used to create the display template I showed above; in the head tag I added these style attributes:

 

<style>

.ReportDiv

{

                float:left;

}

.ReportText

{

                font-family: Verdana, Geneva, sans-serif;

                font-size: 12px;

}

.ReportHeading

{

                font-weight: bold;

}

.LogoImg

{

                margin-top: 15px;

                width: 118px;

                height: 101px;

}

.MasterDiv

{

                float:left;

                height: 215px;

                width: 342px;

                background-repeat: no-repeat;

                background-image: url(http://sps/sites/search/PublishingImages/SalesReportBackground.PNG);

                padding: 15px;

}

.DetailsContainer

{

                background-color:#CCC;

}

</style>

 

The main thing worth noting here is really just that the image I use for a background in the account manager details is one that I’ve already uploaded to my site; everything else should be fairly self-explanatory. UPDATE: I’ve highlighted these elements in the post to make sure you understand that in order to get this to work in your environment, you need to a) upload the image and b) update the SalesReport.css and SalesReport.html files with the location where you added them. Again, by having this style tag in my <head> section I can see exactly what the layout is going to like while I’m designing it in Dreamweaver.

Next let’s look at the HTML itself that is used to create the display template:

            <div>

                <div class=”ReportDiv”>

                                <img class=”LogoImg” src=”http://sps/sites/search/PublishingImages/SalesReportLogo.PNG” />

                </div>

                <div class=”MasterDiv”>

                   

                    <!– Title and link to item –>

                    <a href=”_#= ctx.CurrentItem.Path =#_” class=”ReportText”>_#= ctx.CurrentItem.Title =#_</a>

                    <br/><br/>

 

                    <!– Account Manager –>

                    <span class=”ReportHeading ReportText”>

                      Account Manager:

                    </span>

                    <span class=”ReportText”>

                      _#= ctx.CurrentItem.AccountManager =#_

                    </span><br/>

                   

                    <!– Sales Region –>

                    <span class=”ReportHeading ReportText”>

                      Sales Region:

                    </span>

                    <span class=”ReportText”>

                      _#= ctx.CurrentItem.SalesRegion =#_

                    </span><br/>

                   

                    <!– Total Accounts –>

                    <span class=”ReportHeading ReportText”>

                      Total Accounts:

                    </span>

                    <span class=”ReportText”>

                      _#= ctx.CurrentItem.TotalAccounts =#_

                    </span><br/>

                   

                    <!– Top Accounts –>

                    <span class=”ReportHeading ReportText”>

                      Top Accounts:

                    </span>

                    <span class=”ReportText”>

                      _#= ctx.CurrentItem.TopAccounts =#_

                    </span><br/>

                   

                    <!– Direct Reports –>

                    <span class=”ReportHeading ReportText”>

                      Direct Reports:

                    </span>

                    <span class=”ReportText”>

                      _#= ctx.CurrentItem.DirectReports =#_

                    </span><br/>

                   

                    <!–  Hit Highlighted Text –>

                    <br/>

                    <span class=”ReportHeading ReportText”>

                                Details:

                    </span>

                    <br/>

                    <span class=”DetailsContainer ReportText”>

                                _#= Srch.U.processHHXML(ctx.CurrentItem.HitHighlightedSummary) =#_

                    </span>

                </div>

            </div>        

 

I’ll skip talking about the formatting features of this content and just focus on the data.  You can see where I’ve plugged in the managed properties throughout, using the _#= and =#_ tags.  You’ll see that it is pure token substitution so I can even use it directly in my <a> href attribute above (as opposed to doing this with XSLT, which is far more involved).  The rest of the fields are just plugged into the appropriate DIV or SPAN tag for formatting.  The one “special” case shown here is for the HitHighlightedSummary, which is using a built in processing component that comes with search.  At this time I don’t have a complete list of components and methods and what they do, but I add this one only because if you don’t use it, your summary will not be hit highlighted.  So make sure you use this method for that purpose.

 

Now that I’ve got everything working, there is one other thing I need to do to my markup.  I copy everything between my <style> tags and put them in a new CSS document called SalesReport.css.  In my master page gallery, in the /Display Templates/Search folder, I created another folder called styles.  I uploaded my SalesReport.css file into that directory.  To use it now in my display template I have to add a new script tag.  It should be below the <body> tag, and above the first <div> tag.  I use a SharePoint javascript function called  $includeCSS to get my CSS file pulled down for the display template.  So my opening markup looks like this:

 

<body>

<script>

                                $includeCSS(this.url, “./styles/SalesReport.css”);

</script>

 

<div id=”Item_SalesReport”>

 

UPDATE: Once you’ve made these changes, you need to save your display template and upload it to the /Display Templates/Search folder.  If you are uploading the SalesReport.html that is included with this post, you MUST be sure that when you check the file in you change the content type from Document to Item Display Template; otherwise it cannot be used.

All right, so we are code complete now, but how do we get our custom display template to be used? 

Result Types

Result Types are the way in which you get display templates invoked, based on a set of rules.  The rules are pretty straightforward to use – you can have them fire for a specific pre-defined type of content, like an email, a PDF, a Word document, a SharePoint Wiki, etc.  In addition to that you can only also only have them used when the query is for a specific result source.  Finally, you can also choose any managed property as criteria for the rule, with any common comparison.  For example, you could have a result type rule only fire when the AccountManager field contains “Vice President” if you wanted to have a different display type used for your VPs.  In our case, we want our result type to fire only when the content type equals “Sales Report”.  So we add a condition for our result type that says when ContentType equals “Sales Report”.

 

UPDATE: I was hoping this would work for RTM but in fact it does not.  You have two options here:  1) you can change the equality condition to “Contains” instead of “Equals”.  This is because the content type string actually ends up being a long string, the last part of which is “Sales Report” in our case.  Alternatively, if you only wanted it used with a single query rule, then in the query rule properties you can select the display template to be used for
displaying items.

As you can see we could even add multiple values for the match.  We can also add multiple properties, that get AND’d together.  For this scenario ContentType is all we need.  Once we’ve defined the conditions, we can configure the Action for the rule.  For a result type, the Action is just a drop down with all of the available display templates.  All I need to do is to pick my Sales Report display template from the dropdown, and click the Save button to create my result type.  Again, if you’re not sure how to create result types then take a look at all of the result types that come out of the box in SharePoint.  You can pick up lots of good ideas for new result types by looking at some of the existing ones.

 

Bringing It All Together

Now we have all the pieces in place – a custom content type that captures the metadata we want, some managed properties to extract it out and put it in the index, a query rule that ensures our sales report content pops up to the top of the list when someone queries for “sales report”, a custom display template that shows off the metadata we captured in a really unique and useful format that doesn’t look remotely close to a typical search result, and a result type that makes sure our display template is used when our custom content type is returned in search results.  The final results when we’re finished now look like this:

 

 

That concludes this introduction to some of the very cool features in SharePoint 2013 for presenting search results.  Hopefully you have a feel for the power in these features and can find many useful and interesting ways to use them.

You can download the attachment here:

Debugging Display Templates

Right after I published my last post on using custom display templates, of course one of the first questions I got was great – how do I debug them?  Well there are a couple of ways that I’ve found to debug these:

  1. In your display template add your own javascript after the first div tag and put in a debugger; statement.  Note that you MUST uncheck the option in IE to disable script debugging and restart the browser.  This is really cool because you can break in Visual Studio and get all your variables and query values:

 

 

 

  1. The second way is a little more “hard-coded”, so I don’t like it as much but so far it works well.  You need to:
    1. Press F12 to bring up the IE Developer window
    2. Click on the Script tab
    3. In the drop down with the list of script files select clientrenderer.js
    4. Look for the CoreRenderWorker function; I normally find it by going down to the second to last line of script and pressing the END key.
    5. Click and highlight the first line of code inside that function; it should be something like “var a;”
    6. Right-click on it and select the Insert Breakpoint from the menu.
    7. Click the Start Debugging button.
    8. Go back to the browser and execute your query
    9. When the debugger breaks in, click on the Locals tab on the right side of the window, and then click the plus sign next to the “c” variable to expand it.
    10. You can look at all of the variables in there, but generally you will want to click the Play button in the debugger and continue.  Each time a new set of code is loaded the “c” variable collapses, and that’s your clue that you should go back and expand it again to see what data it contains.  I generally find that you need to click the Play button 3 to 5 times, until an object called “CurrentItem” appears under the “c” variable.  That represents a single search result and allows you to peruse all of the values for the managed properties that you requested.  Super useful and does not require Visual Studio.

 

When Do You Need to Install a Custom Claims Provider for Search in SharePoint 2010

We’ve been having a few good (meaning “interesting”) discussions lately about custom claims providers and search.  As it turns out, there are instances when you need to install your custom claim provider on a search box (“box” being something I’ll define down below) in order to get security trimming working correctly in your search results.  When this applies though is different depending on whether you are using FAST Search 2010 or SharePoint Search 2010.

First a little explanation is probably useful here.  Every time a user makes a query, the claims in the user token are decoded.  However the SiteData Web Service, which is what the crawler uses to retrieve security information about content, always returns encoded claims.  So how do we reconcile that?

In FAST Search 2010, the claims are always decoded before they are stored by the FAST Search 2010 indexer.  We do this because the FAST Query servers don’t have the SharePoint bits installed, so they can’t encode the claims.  Remember the user claims come over decoded, so in order to match the encoded claims the SiteData Web Service returns, the user claims would have to be encoded to do a comparison.  Since we can’t install a custom claim provider on a FAST Query server we have to decode the claims that we get from the SiteData Web Service, and in order to do that any custom claims provider you are using must be installed in the FAST Content SSA.  Doing so allows us to decode the claims, store the claims decoded, and then when the decoded claims for a user are presented we can do a comparison.  So in the case of FAST, we are concerned with using any custom claims providers at crawl time.

For SharePoint Search 2010 its kind of the opposite problem.  SharePoint expects that it will be installed everywhere, so it works on the premise that at query time, it can encode the claims from the user so that a comparison can be made to the ACLs that are stored for the content.  Where you will find this breaks down though is in the scenario where you have not deployed the custom claims provider to servers that are running the query processor, also known as the Query and Site Settings Service.  In most cases, you install your custom claims provider on all servers in your farm – WFEs as well as application servers.  The query processor needs this custom claims provider installed in order to encode the claims.  So if everything is running in a single farm and you install the custom claims provider on all servers, you should be good.  The situation that came up recently (and that prompted this post) is a scenario where you have a separate services farm and you are consuming SharePoint Search services from that farm.  In that case you need to make sure that any custom claims providers are installed on every server in the services farm that is running the Query and Site Settings Service.  If you do not do that you will find that your users’ custom claims cannot be evaluated, and as a result they will typically see no search results returned.

This was an interesting scenario and took some great troubleshooting by a cast of characters here…especially calling out my brilliant little brother Luca for helping us get over the final hurdle here, as well as Sanjeev and Michael P.  Thanks all for helping us understand this better.

Troubleshooting Tip for Debugging Crawl Issues in SharePoint 2010

I recently came across a very nice troubleshooting methodology when I was trying to debug some authentication issues that were occurring during a SharePoint 2010 crawl.  I was getting some errors and also having difficulty getting the information I needed out of the crawl log to some other issues that were occurring.  Strangely enough, enter Fiddler to the rescue (www.fiddler2.com).

 

I’m sure most everyone is familiar with Fiddler so I won’t bother explaining it here.  The trick though was to get it to capture what was happening during the crawl.  I found a very slick way to do this is to configure Fiddler as a reverse proxy for the crawl account.  The instructions for configuring Fiddler as a reverse proxy can be found here:  http://www.fiddler2.com/Fiddler/help/reverseproxy.asp.  The way I used it was as follows:

 

  1. Log into my crawl server as the crawl account.
  2. Configure Fiddler to be a reverse proxy as described above.
  3. Start Fiddler
  4. Start a new crawl.

 

I had isolated my trouble sites into a separate content source.  So once I followed these steps I was able to see each request from the crawler to that content source, how it was authenticating, and what was happening.  Overall it proved very useful in understanding much more clearly what was going on during the crawl of those sites.