Wednesday, October 2, 2013

Localizing Content Search Webpart Display Templates in SharePoint 2013

In a previous post, I wrote about a way to parameterize the search query in Content Search Webparts. The method actually resolves some filter criteria in the query text before content is rendered in the CSWP: SharePoint 2013 Content Search Web Part (CSWP) in Variation Sites.

As part of the same task I wanted to localize some hardcoded content rendered in the CSWP. Even though it seemd CSWPs support localization because of the CustomStrings.js javascript reference added in the display template, I could hardly find a reference on how to use it, syntax, etc.

<script>
     $includeLanguageScript(this.url, "~sitecollection/_catalogs/masterpage/Display Templates/Language Files/{Locale}/CustomStrings.js");
</script>

So here is the way. CustomStrings.js by default contains 2 entries and comment at the top asks to add custom localized strings to use in display templates.

// Add your custom localized strings and then include these string dictionaries in your display templates using the $includeLanguageScript function

$registerResourceDictionary("en-ca", {
    "sampleCustomStringId": "Sample custom string",   
    "rf_RefinementTitle_ManagedPropertyName": "Sample Refinement Title for ManagedPropertyName"
});

Therefore the Step 1 is to add localized strings to the CustomStrings.js files and to make sure they are deployed to the master page gallery of the site in the Language files folder. We have to deploy such a file for each culture we want to support.

$registerResourceDictionary("en-ca", {
    "sampleCustomStringId": "Sample custom string",
    "PostedOn": "Posted on",
    "ReadMore": "Read More",
    "rf_RefinementTitle_ManagedPropertyName": "Sample Refinement Title for ManagedPropertyName"
});

Then in the display template we have to make sure CustomStrings.js is referenced using the $includeLanguageScript function.

And lastly, in order to reference a key from the CustomStrings file, we can create variables like:
var readMoreText = $resource("ReadMore");
var postedOnText = $resource("PostedOn");

Defined variables can be used as:
<a href="_#= linkURL =#_" class="btn_more">_#=  readMoreText =#_</a>

Complete body section of the Item Template:
<body>
<!--
 Warning: Do not try to add HTML to this section. Only the contents of the first <div> inside the <body> tag will be used while executing Display Template code. Any HTML that you add to this section will NOT become part of your Display Template.
-->
<script>
    $includeLanguageScript(this.url, "~sitecollection/_catalogs/masterpage/Display Templates/Language Files/{Locale}/CustomStrings.js");
</script>

<!--
    Use the div below to author your Display Template. Here are some things to keep in mind:
    * Surround any JavaScript logic as shown below using a "pound underscore" (#_ ... _#) token inside a comment.

    * Use the values assigned to your variables using an "underscore pound equals"
    (_#= ... =#_) token.
-->

<div id="TwoLines">
<!--#_
var monthname = new Array("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
var encodedId = $htmlEncode(ctx.ClientControl.get_nextUniqueId() + "_2lines_");

var linkURL = $getItemValue(ctx, "Link URL");
linkURL.overrideValueRenderer($urlHtmlEncode);
var iconURL = Srch.ContentBySearch.getIconSourceFromItem(ctx.CurrentItem);

var line1 = $getItemValue(ctx, "Line 1");
var line2 = $getItemValue(ctx, "Line 2");
line1.overrideValueRenderer($contentLineText);
line2.overrideValueRenderer($contentLineText);

var containerId = encodedId + "container";
var pictureLinkId = encodedId + "pictureLink";
var pictureId = encodedId + "picture";
var dataContainerId = encodedId + "dataContainer";
var line1LinkId = encodedId + "line1Link";
var line1Id = encodedId + "line1";
var line2Id = encodedId + "line2";

var readMoreText = $resource("ReadMore");
var postedOnText = $resource("PostedOn");
_#-->
 <div id="_#= containerId =#_" data-displaytemplate="Item2Lines">
  <div class="NewsRight" id="_#= dataContainerId =#_">
   <div id="_#= line1Id =#_"> _#= postedOnText =#_
    <label>_#= monthname[new Date(line1).getMonth()] + " " + new Date(line1).getDate()+ ", "+new Date(line1).getFullYear() =#_</label>
   </div>
              
<!--#_
if(!line2.isEmpty)
{
_#-->
   <div class="Award_Itm_Link" id="_#= line2Id =#_" >
    <a href="_#= linkURL =#_" title="_#= $htmlEncode(line1.defaultValueRenderer(line1)) =#_" id="_#= line1LinkId =#_">_#= line2 =#_</a>
   </div>
<!--#_
}
_#-->
   <a href="_#= linkURL =#_" class="btn_more">_#= readMoreText =#_</a>   
  </div>
  <br class="clearfloat" />
 </div>
</div>
</body>

Tuesday, September 3, 2013

Generate XML Sitemap using PowerShell – SharePoint 2013 Search Engine Sitemap

From SEO perspective there are a number of things that we can do to improve the ranking of our site content. One of them is XML sitemaps. By submitting an XML Sitemap to search engines we can help them discover content on our website.

Unlike SharePoint 2010, SharePoint 2013 provides native support for generating XML Sitemaps. In order to generate an XML Sitemap for the site:
  1. First we have to enable the “Search Engine Sitemap” feature from Site Collection Features. This will register the Site Collection with the “Search Engine Sitemap job” Timer Job.
  2. Run “Search Engine Sitemap job” Timer Job, which anyway by default runs daily and generates an XML Sitemap for every Site Collection that has been registered with it.

In addition to generate XML Sitemap, Search Engine Sitemap job also modifies the robots.txt file. In there, it adds the link for the XML Sitemap file so that it can be easily discovered by search engines.

We can automate the generation of XML sitemap in deployment using PowerShell as follows:

function GenerateXmlSitemap([string] $siteCollectionUrl)
{
 try
 {
   $site = Get-SPSite $siteCollectionUrl
   $rootWeb = $site.RootWeb
   $id = [Guid](“7310C4BB-1407-4B73-96A1-2A88D034E039”);

   #1. Activate “Search Engine Sitemap” feature from Site Collection Features    
   Enable-SPFeature –identity “77fc9e13-e99a-4bd3-9438-a3f69670ed97” –URL $siteCollectionUrl –EA SilentlyContinue
             
   #2. Run “Search Engine Sitemap job”         
   $site.AddWorkItem([System.Guid]::Empty, [System.DateTime]::Now.ToUniversalTime(), $id, $rootWeb.ID, $site.ID, 1, $false,[System.Guid]::Empty, [System.Guid]::Empty, $rootWeb.CurrentUser.ID, $null, [System.String]::Empty, [System.Guid]::Empty, $false);
                    
   $webApplication = $site.WebApplication;
   $searchEngineSitemapJob = $webApplication.JobDefinitions |where{ $_.Name –match “SPSitemapJobDefinition” };

   runjob($searchEngineSitemapJob);
 }
 catch
 {
   write-host “Error: “+ $_.Exception.Message –ForegroundColor Red
 }
}

function runJob($Job)
{
  write-host “Last execution time was:” $Job.LastRunTime

  [Array] $JobHistoryList=$Job.HistoryEntries

  Start-SPTimerJob $Job
  Write-Host “Running Timer Job:” –NoNewline
  Write-Host $Job.DisplayName –NoNewline –ForegroundColor Yellow
        
  $count = $JobHistoryList.count
  $countNext = $count+1

  while ( $countNext –ne $count)
  {
    [Array] $JobHistoryList=$Job.HistoryEntries
    $count = $JobHistoryList.count
    Write-Host “=” –NoNewline
    sleep 2
  }
  Write-Host “”

  if ($Job.status –ne “Succeeded”)
  {
    Write-Host –f yellow $JobHistoryList[0].status
    Write-Host –f yellow $JobHistoryList[0].ErrorMessage
  }
  else
  {
    Write-Host –f Green $JobHistoryList[0].status
  }
}

GenerateXmlSitemap –siteCollectionUrl http://prasadsp2013/


Important:
  • For the XML Sitemap to be generated, anonymous access must be enabled on the Default Zone of the Site Collection for which it’s generating the XML Sitemap.
  • Also the XML Sitemap generation process uses Search to generate the XML Sitemap. Therefore crawl the site and verify that the pages have been indexed is important.

Find JobDefinition Name to use in PowerShell:

Go to the Central Administration -> Monitoring -> Review job definitions and find required job definition. Then by hover the mouse pointer over the job name we can get the job id.


Use URL decoder to decode the job id and using following PowerShell script we can get the job name.