Archive

SharePoint 2013

The search settings for SharePoint sites, site-collections or even for the entire farm can be set through PowerShell. In SP 2010, there were 3 properties which had to be set for a site in order to configure the search box setings: “SRCH_ENH_FTR_URL”, “SRCH_SITE_DROPDOWN_MODE”, “SRCH_TRAGET_RESULTS_PAGE” – for more details, refer to this post. In SharePoint 2013, this has slightly changed, and I have also looked in more depth, to see how the search box works, and how it retrieves its settings.

The search box we see by default in all SharePoint sites is a control registered in the master page by some of the standard SharePoint features. The delegate control id is “SmallSearchInputBox” and three SharePoint features register controls here:

  • ContentLightup, with sequence number 100
  • OSearchBasicFeature, with sequence number 50
  • OSearchEnhancedFeature, with sequence number 25

Considering “SmallSearchInputBox” does not all multiple controls, only the registration with the lowest number gets taken into account. Here is how it looks like:


<?xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
 <Control
 Id="SmallSearchInputBox"
 Sequence="25"
 ControlClass="Microsoft.Office.Server.Search.WebControls.SearchBoxScriptWebPart" ControlAssembly="Microsoft.Office.Server.Search, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c">
 <Property Name="UseSharedSettings">true</Property>
 <Property Name="RenderTemplateId">~sitecollection/_catalogs/masterpage/Display Templates/Search/Control_SearchBox_Compact.js</Property>
 <Property Name="EmitStyleReference">false</Property>
 <Property Name="ServerInitialRender">true</Property>
 <Property Name="TryInplaceQuery">false</Property>
 <Property Name="QueryGroupNamesJson">[&quot;MasterPage&quot;]</Property>
 <Property Name="ChromeType">None</Property>
 <Property Name="ID">SmallSearchInputBox1</Property>
 </Control>
</Elements>

The search box is rendered by a web part called SearchBoxScriptWebPart , which has several properties that can be changed – i.e. the render template uses a script file “Control_SearchBox_Compact.js”, you can change the property to point to your custom file. In order to change the default way the search box is rendered, or maybe use a different search box control, you would have to register a new delegate control with a lower sequence than 25.

But how does the default web part work, and how can we configure it? The web part takes it settings from a settings object: SharedSearchBoxSettings . This configuration can be set at four different levels:

  • Web: It looks for a property of the current web with key “SRCH_SB_SET_WEB”
  • Site-Collection: It looks for a property of the root web with key “SRCH_SB_SET_SITE”
  • Tenant: it looks for a site subscription property with key “SRCH_SB_SET”
  • Search Service Application: it looks in the search service application for the SharedSearchBoxSettings property.

All these properties contain a serialized string of a SharedSearchBoxSettings object: e.g. “{"Inherit":false,"ResultsPageAddress":"http://spdevel.portal.com/search","ShowNavigation":false}“. In a previous post, I have explained how to use PowerShell to update these web properties. The search box web part starts looking for the settings at 1st level, then goes to 2nd level and so on until it finds a configuration. When it finds a configuration, it will not look at the next levels. Because of this, you can overwrite the search box settings at any level.

The search box results address Url can be set at the following levels:

  • SharedSearchBoxSettings: from ResultsPageAddress property
  • Web: from web property “SRCH_ENH_FTR_URL_WEB”
  • Site collection: from root web property “SRCH_ENH_FTR_URL_SITE”
  • Search Service Application: from Search Center Url property

A simple configuration scenario would be to set up a search center for all sites & site-collections in the farm, and to disable the search drop down scopes. In this scenario, you would also have a centralized place for your search settings.. You only need to set the search box settings at the Service Application level, and leave all sites/site-collections to inherit this configuration:


$ssa = Get-SPEnterpriseSearchServiceApplication
#replace {SearchCenterUrl} with the Url of your search center
#settings ShowNavigation to false will not display the search scopes anymore.
$ssa.SharedSearchBoxSettings = "{'Inherit':false,'ResultsPageAddress':'http://{SearchCenterUrl}/pages/results.aspx','ShowNavigation':false}"
$ssa.Update()

Search has been greatly enhanced in SharePoint 2013, and with this, it has also brought some changes in how managed properties are created, and the scenarios where they are supported.

First of all, it should be noted that managed properties are created automatically only for site columns (documentation here). If you are ever wondering why you don’t see a managed property for your column, this should be the first thing you should check. A scenario like this would be if you add a column directly to your list, instead of first creating it as a site column and only after that adding it to your list.

Crawled properties and managed properties are now getting automatically created once the content is crawled. There are a set of rules after which these properties are generated. Full details and explanation about this can be found here . I have also found a blog post that describes how this has evolved and changed from SharePoint 2010.

Things get interesting once you have columns of more complex type, not just text or numbers. I’ll explain this for a simple HTML column (multiple lines of text field with full html support enabled, or publishing HTML). In this case, you should have crawled properties for raw text of this column, but also for the html content of the column. For this scenario, after a full crawl, you should get something like:

  • Crawled property ows_r_MTXT_SiteColumnName mapped to managed property SiteColumnNameOWSMTXT (this contains HTML)
  • Crawled property ows_SiteColumnName unmapped to any managed property (this contains column value as text)

The same logic applies to Url columns, User columns, Lookup columns etc. Depending on what you are trying to achieve, you might use the crawled property with column value as text, or the crawled property that contains the unformatted column value.

This sounds great so far, right? And it is, except for one really usual scenario! The common way of provisioning site columns in a solution when building a custom application, with custom columns, content types, lists etc. is declarative (xml and CAML schema). There are other ways to do this (server-side API, client-side API, SharePoint UI) but declarative would be the most common way of doing it in 2007 and 2010.

The strange thing that happens when you provision your custom site columns declarative in a farm solution in SharePoint 2013 is that the managed properties don’t get automatically created. It’s not only the managed properties that don’t get automatically created, but the crawled property that contains the unformatted column value isn’t created also. What you get is only a single crawled property for you column – ows_SiteColumnName. It contains only the column value as text, and it doesn’t have a managed property pair. i have found a blog post on this topic that contains a nice summary which shows when the managed properties are created depending on your creation approach.

Even if you don’t plan to use your custom site columns in search in the beginning, maybe in the future you will also want to support this. Therefore, for all scenarios, I would currently drop the declarative provisioning of site columns in SharePoint 2013 and go for server-side/client-side API. You can still use the xml and CAML schema, and take advantage of the SPFieldCollection.AddFieldAsXml method.

The new refinement panel for SharePoint 2013 search is build completely different from its predecessor in 2010, and follows the new pattern of showing data using display templates. This makes it really easy to customize and can be done by anyone with html/js/css knowledge, without any previously required xsl knowledge.

The default display templates for refinement panels (Filter_Default.js, Filter_MultiValue.js) use a method for retrieving the title of a search managed property.

Srch.Refinement.getRefinementTitle(ctx.RefinementControl);

This goes through the following steps to find the title for the refinement:

  • It looks in currently loaded js resource files. If the managed property key is found, the result is returned. I have described here how to use js resource files for display templates in SharePoint 2013.
  • It looks in the Search default resource files – these can be found in {15}/Resources with the name pattern Srch.resources.{culture}.resx
  • if nothing is found, returns the name of the managed property

The managed property key it looks for is in the format “rf_RefinementTitle_{ManagedProperty}”. Therefore, if the managed property it currently displays is “LastModifiedTime” it will look for a resource with key “rf_RefinementTitle_LastModifiedTime”.

Standard title for last managed property "LastModifiedTime"                                                                                       Custom title for the same managed property

Considering these steps, you can localize new managed properties in your search center, but you can also change the titles for the existing ones. You can use the default “CustomStrings.js” resource file for the new managed properties titles, or you can create your own js resource file, but you must make sure that the file is loaded in the page. Some display templates automatically load the default resource file, but otherwise you can add a Content Editor web part to the page to load your resource file.

$includeLanguageScript(this.url, "~sitecollection/_catalogs/masterpage/Display Templates/Language Files/MyCustomResourceFile.js");

Managed metadata navigation is a new feature in SharePoint 2013 that allows the user to configure the navigation on a site collection based on a term set in the term store. This brings a lot of benefits for the site administrators, as they can define SEO-friendly URL’s, and they can update the navigation anytime using the term store management tool. There are several blog posts out there that show how it can be configured, so I will skip this part.

Although it is a very powerful feature, it still has some limitations. They can be seen especially when you have an Information Architecture that spans across a lot of site collections and maybe across web applications, and you would like to use the same navigation in all places. When you try to bind the same navigation term set to a new site, you will get a message that says the term set is already bound to a different site, and if you choose to proceed, the navigation settings for the other site will be broken.

message_attaching

Considering you have proceeded, on the initial site, the navigation will not work anymore, and you will receive an error message.

error_message_navigation

I have searched for different resolutions on this topic and  I have found two solutions on how to overcome it:

  • Create a new term set, and pin it to the source term set. The new term set is a copy of the source term set, and it can be used in a new site collection. This technique is explained in more detail here. However, if you have a lot of site collections, this can lead to a very large number of term groups and term sets in the term store.
  • Create a custom control (server-side), or a script (client-side JavaScript) that reads from a term set in the term store, and renders the navigation as you want it. You can find a sample for this here, and it also includes source code and explanation for it. if you are using this, you will have to recreate the Html and JavaScript for the navigation, that is by default rendered by the AspMenu control (this might become tricky if there are multiple dynamic levels for the navigation).

Another solution would be to create a custom navigation provider that retrieves the navigation nodes from a managed metadata navigation term set. I find this solution an easy way to overcome the limitation because you only have to retrieve the navigation nodes, and you can leave the rendering to the default AspMenu control. It is a highly reusable solution, which requires no change when it is used in different projects for different customers. The drawback – you need a farm solution to be able to create a custom navigation provider.

I will not post the entire code for this here, but I will only highlight the main ideas that are needed for this:

  •  Create the custom navigation provider. You will have to create a class that inherits from PortalSiteMapProvider or from SiteMapProvider. Code sample for this can be found on msdn.
  • Register the navigation provider with a web application feature, and by making changes to web.config file. Code sample for this can be found on msdn.
  • Change the master page to use your navigation control, or deploy an elements file with a control element that enables the use of your navigation provider. Code sample for this can be found on msdn.
  • I recommend using the cache for the navigation data, once you retrieve it from the term store. You can use the web application cache, or the distributed cache service. I prefer the later one, as  it works the same way across multiple front-ends.
  • When you retrieve the data from the navigation term set, the key method is NavigationTermSet.GetAsResolvedByWeb , as it returns the navigation term set object, as if it would be attached to that web.
  • Once you have the NavigationTermSet object, you can find all its children NavigationTerm . From there, you can work with everything that you can define in the term store management tool for that term.

I would have liked SharePoint 2013 to ship with such a navigation provider OOB, but, if not, the effort to implement it is not really large, and I believe it is definitely worth it.

In many scenarios where you have to build up a complex Information Architecture in SharePoint, you will also have to set the Search Settings for each site or site collection to point to your search center and your custom results page.

Search Settings Page

Search Settings for a site in SharePoint 2013 UI

These settings are stored in the property bag of the site. For SharePoint 2010, this blog post shows the property names, and how they can be set using Powershell. In SharePoint 2013 the search settings have become a little bit more complex, therefore we need a new script to set them. Below you will find two powershell functions: one that sets the search settings for the entire site collection and one just for a specific web.

function SetSearchSettingsSite($siteUrl,$searchCenterUrl,$resultPagesUrl)
{
 $site = Get-SPSite $siteUrl
 $site.AllWebs | ForEach-Object {
 $web = $_
 $web.AllProperties["SRCH_SB_SET_WEB"] = '{"Inherit":false,"ResultsPageAddress":"'+$resultPagesUrl+'","ShowNavigation":false}'
 $web.AllProperties["SRCH_ENH_FTR_URL_WEB"] = $searchCenterUrl
 $web.Update()
 }
}
function SetSearchSettingsWeb($siteUrl,$searchCenterUrl,$resultPagesUrl)
{
 $web = Get-SPWeb $siteUrl
 $web.AllProperties["SRCH_SB_SET_WEB"] = '{"Inherit":false,"ResultsPageAddress":"'+$resultPagesUrl+'","ShowNavigation":false}'
 $web.AllProperties["SRCH_ENH_FTR_URL_WEB"] = $searchCenterUrl
 $web.Update()
}
SetSearchSettingsSite http://spdevel.portal.com/ http://spdevel.portal.com/search http://spdevel.portal.com/search/Pages/Results.aspx

The second part of the search settings has been integrated in a single web property – “SRCH_SB_SET_WEB”, as a representation of a SharedSearchBoxSettings object. This script only sets the search settings at site-level. If you want to configure the search settings at site-collection level or at farm-level, it would be slightly different. At site-collection level, you would need to set the root web property “SRCH_SB_SET_SITE”, and for farm-level, you would need to set the SharedSearchBoxSettings and SearchCenterUrl properties of the Search Service Application. For more details, please see this post.

In a different blog post I have explained the basics of working with publishing navigation in SharePoint 2013. The OOB navigation offers two options: “structural navigation” and “managed metadata navigation”.

Managed metadata navigation is bound to a term set, that provides the navigation nodes for your site (more details on how to configure it here). If you want to change this, or you are simply looking for a script that will set this up in new environments, here it is:

Add-PSSnapIn Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue

$assembly = [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Publishing")

function UpdateNavigation($url,$termStoreName,$termGroupName,$termSetName){
 $web= Get-SPWeb $url
 $site = $web.Site
 $navSettings = New-Object Microsoft.SharePoint.Publishing.Navigation.WebNavigationSettings($web)
 $taxSession = Get-SPTaxonomySession -Site $site
 $termStore = $taxSession.TermStores[$termStoreName]
 $termGroup = $termStore.Groups[$termGroupName]
 $termSet = $termGroup.TermSets[$termSetName]
 #Quick Launch
 $navSettings.CurrentNavigation.Source = 2
 $navSettings.CurrentNavigation.TermStoreId = $termStore.Id
 $navSettings.CurrentNavigation.TermSetId = $termSet.Id
 #Global Navigation
 $navSettings.GlobalNavigation.Source = 2
 $navSettings.GlobalNavigation.TermStoreId = $termStore.Id
 $navSettings.GlobalNavigation.TermSetId = $termSet.Id

$navSettings.AddNewPagesToNavigation = $false
 $navSettings.CreateFriendlyUrlsForNewPages = $false
 $navSettings.Update()
 Write-Host "Navigation updated succesfully for site $url"
}
#Sample usage: UpdateNavigation http://spdevel.portal.com/ "Managed Metadata Service" "My Navigation Group" "Navigation"

An important limitation of this managed metadata navigation is the fact that a term set can only be bound to a single site. You can find more on this topic post.