Enable SharePoint 2013 Search REST Queries for Anonymous

Recently in my project, I have to enable / call SharePoint REST Search Queries for Anonymous users. The reason behind that is because the site is public external site and I would like the search box to return search results as suggestions for Anonymous users. There is msdn article to do that step by step, but in this article I would like to share my own experience and knowledge on how did I enable SharePoint 2013 Search REST Queries for Anonymous users.

Pre-Requisites

1. Web Application and Site Collection created with Anonymous Access configured
2. Search Service Application working, Full Crawled completed successfully and Search Setting configured
3. Search Input Box ID must contains “_csr_sbox”

Solution

Without further ado, please find below is the list of steps:

1. Run EnableAnonymousRESTSearch.ps1 and Enter Root Web URL. This PowerShell script will create QueryPropertiesTemplate library in root web and upload QueryParameterTemplate.xml into the library to override QueryProperties element for Anonymous users. It also deactivate ViewFormPagesLockDown Site Collection Feature when activated.

Param(
[string] $url = $(Read-Host -prompt "Enter Site Collection URL: ")
)

# Include SharePoint reference
if ( (Get-PSSnapin -Name Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue) -eq $null )
{
    Add-PsSnapin Microsoft.SharePoint.PowerShell
}

# Get All variables required
$farmID=(Get-SPFarm).Id
$spSite=Get-SPSite $url
$siteColId=$spSite.Id
$topLevelSiteID=$spSite.RootWeb.Id
$rootWeb=$spSite.RootWeb
$featureLockDownFolderName = "ViewFormPagesLockDown"

# Deactivate ViewFormPagesLockDown Site Feature
$featureLockDown = Get-SPFeature -Site $url | Where {$_.DisplayName -eq $featureLockDownFolderName}
if($featureLockDown -ne $null)
{
   Disable-SPFeature $featureLockDown -url $url
   Write-Host "ViewFormPagesLockDown Deactivated ... "
}

# Create QueryPropertiesTemplate Library and Upload QueryParameterTemplate.xml into the library
if($null -ne $rootWeb)
{
	$list=$rootWeb.Lists["QueryPropertiesTemplate"]
		if($null -eq $list)
		{
			$template=$rootWeb.ListTemplates["Document Library"]
			$list=$rootWeb.Lists[$rootWeb.Lists.Add("QueryPropertiesTemplate","QueryPropertiesTemplate",$template)]
		}

	$scriptFile = $MyInvocation.MyCommand.Definition
	[string]$currentSource = Get-Content $scriptFile
	[int]$startScript=$currentSource.LastIndexOf("<?xml ");
	[int]$closeComment=$currentSource.LastIndexOf("#>");
	$xmlFile=[xml]($currentSource.Substring($startScript,$closeComment-$startScript))
	$xmlFile.QueryPropertiesTemplate.QueryProperties.FarmId=$farmID.ToString()
	$xmlFile.QueryPropertiesTemplate.QueryProperties.SiteId=$siteColId.ToString()
	$xmlFile.QueryPropertiesTemplate.QueryProperties.WebId=$topLevelSiteID.ToString()

	$xmlFile.OuterXml | Out-File queryparametertemplate.xml -Encoding UTF8
	$tempFile=Get-Item -LiteralPath "queryparametertemplate.xml"

	$folder = $list.RootFolder
	$stream=$tempFile.OpenRead()
	$file = $folder.Files.Add($folder.Url+"/queryparametertemplate.xml",$stream,$true, "created by script",$false)
	$stream.Close()

	if($null -ne $file)
	{
		Write-Host ("File Created At " + $rootWeb.Url + "/" + $file.Url)
	}
	Write-Host " "
}

<#
<?xml version="1.0" encoding="utf-8"?>
<QueryPropertiesTemplate xmlns="http://www.microsoft.com/sharepoint/search/KnownTypes/2008/08" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <QueryProperties i:type="KeywordQueryProperties">
    <EnableStemming>true</EnableStemming>
    <FarmId>22222222-fa49-4987-b1ea-9fad99e81a0f</FarmId>
    <SiteId>11111111-68f0-41c5-a525-7e7fda7666b3</SiteId>
    <WebId>00000000-8d97-40a2-b07b-cf05e4c85084</WebId>
    <IgnoreAllNoiseQuery>true</IgnoreAllNoiseQuery>
    <KeywordInclusion>AllKeywords</KeywordInclusion>
    <SummaryLength>180</SummaryLength>
    <TrimDuplicates>true</TrimDuplicates>
    <WcfTimeout>120000</WcfTimeout>
    <Properties xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
      <a:KeyValueOfstringanyType>
        <a:Key>_IsEntSearchLicensed</a:Key>
        <a:Value i:type="b:boolean" xmlns:b="http://www.w3.org/2001/XMLSchema">true</a:Value>
      </a:KeyValueOfstringanyType>
      <a:KeyValueOfstringanyType>
        <a:Key>EnableSorting</a:Key>
        <a:Value i:type="b:boolean" xmlns:b="http://www.w3.org/2001/XMLSchema">true</a:Value>
      </a:KeyValueOfstringanyType>
      <a:KeyValueOfstringanyType>
        <a:Key>MaxKeywordQueryTextLength</a:Key>
        <a:Value i:type="b:int" xmlns:b="http://www.w3.org/2001/XMLSchema">4096</a:Value>
      </a:KeyValueOfstringanyType>
      <a:KeyValueOfstringanyType>
        <a:Key>TryCache</a:Key>
        <a:Value i:type="b:boolean" xmlns:b="http://www.w3.org/2001/XMLSchema">true</a:Value>
      </a:KeyValueOfstringanyType>
    </Properties>
    <PropertiesContractVersion>15.0.0.0</PropertiesContractVersion>
    <EnableFQL>false</EnableFQL>
    <EnableSpellcheck>Suggest</EnableSpellcheck>
    <EnableUrlSmashing>true</EnableUrlSmashing>
    <IsCachable>false</IsCachable>
    <MaxShallowRefinementHits>100</MaxShallowRefinementHits>
    <MaxSummaryLength>185</MaxSummaryLength>
    <MaxUrlLength>2048</MaxUrlLength>
    <SimilarType>None</SimilarType>
    <SortSimilar>true</SortSimilar>
    <TrimDuplicatesIncludeId>0</TrimDuplicatesIncludeId>
    <TrimDuplicatesKeepCount>1</TrimDuplicatesKeepCount>
  </QueryProperties>
  <WhiteList xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
    <a:string>RowLimit</a:string>
    <a:string>SortList</a:string>
    <a:string>StartRow</a:string>
    <a:string>RefinementFilters</a:string>
    <a:string>Culture</a:string>
    <a:string>RankingModelId</a:string>
    <a:string>TrimDuplicatesIncludeId</a:string>
    <a:string>ReorderingRules</a:string>
    <a:string>EnableQueryRules</a:string>
    <a:string>HiddenConstraints</a:string>
    <a:string>QueryText</a:string>
    <a:string>QueryTemplate</a:string>
    <a:string>SelectProperties</a:string>
    <a:string>SourceID</a:string>
  </WhiteList>
</QueryPropertiesTemplate>
#>

2. Create QuerySuggestion.js java script file. The java script will utilize AutoComplete interaction from jquery-ui and call Search Query REST to populate search results in the AutoComplete drop down.

$(function () {
    // If Anonymous Users
    if (_spPageContextInfo.systemUserKey === undefined) {
        var siteurl = _spPageContextInfo.webAbsoluteUrl;

        // Get all search input boxes
        $("input[id*='_csr_sbox']").each(function () {
            var searchinput = $(this);
            searchinput.data("old_value", searchinput.val());

            // Bind Cut, Paste and KeyDown Event Listener to current search input box
            searchinput.bind("paste cut keydown", function (e) {
                var that = this;
                setTimeout(function () {
                    // If search input box not empty
                    if ($(that).val()) {
                        $(that).data("old_value", $(that).val());

                        // Query Suggestion using AutoComplete
                        searchinput.autocomplete({
                            minLength: 0,
                            source: function (request, response) {
                                // Call Search REST API and get search result for query suggestion
                                $.ajax({
                                    url: siteurl + "/_api/search/query?querytext='" + $(that).val() + "*'&QueryTemplatePropertiesUrl='spfile://webroot/queryparametertemplate.xml'",
                                    method: "GET",
                                    headers: { "Accept": "application/json; odata=verbose" },
                                    success: function (data) {
                                        response($.map(data.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results, function (item) {
                                            return {
                                                fields: getFields(item.Cells.results)
                                            }
                                        }));
                                    },
                                    error: function (data) {
                                        alert("Query Suggestion Error: Fail to call Search REST API");
                                    }
                                });
                            },
                            focus: function (event, ui) { // If Query Suggestion get focused
                                searchinput.val(ui.item.fields.Title);
                                return false;
                            },
                            select: function (event, ui) { // If Query Suggestion get selected
                                searchinput.val(ui.item.fields.Title);
                                return false;
                            }
                        })
                        .data("ui-autocomplete")._renderItem = function (ul, item) { // render Query suggestion with <ul> and <li>
                            return $("<li></li>")
                                .data("ui-autocomplete-item", item)
                                .append(item.fields.Title)
                                .appendTo(ul);
                        };
                    }
                }, 200);
            })
        });
    }
});

// getFields function: to return all properties to look like json result
function getFields(results) {
    r = {};
    for (var i = 0; i < results.length; i++) {
        if (results[i] != undefined && results[i].Key != undefined) {
            r[results[i].Key] = results[i].Value;
        }
    }
    return r;
}

3. Place javascripts and css in Style Library Root Web:
– jquery.js to /Style Library/QuerySuggestion/js/jquery.js
– jquery-ui.min.js to /Style Library/QuerySuggestion/js/jquery-ui.min.js
– QuerySuggestion.js to Style Library/QuerySuggestion/js/QuerySuggestion.js
– jquery-ui.min.css to Style Library/QuerySuggestion/css/jquery-ui.min.css
4. Update and Publish custom MasterPage to include below: (under existing jquery script element)

<script src="/Style Library/CNP.CWE.QuerySuggestion/js/jquery.js" type="text/javascript"></script>
<script src="/Style Library/CNP.CWE.QuerySuggestion/js/jquery-ui.min.js" type="text/javascript"></script>
<script src="/Style Library/CNP.CWE.QuerySuggestion/js/CNP.CWE.QuerySuggestion.js" type="text/javascript"></script>
<link href="/Style Library/CNP.CWE.QuerySuggestion/css/jquery-ui.min.css" rel="stylesheet" type="text/css"/>

I’ve got the PowerShell and Javascripts from my reference below with bit of modification from me.
QuerySuggestion

References

Advertisements

Tagged: , , , , , , , , ,

Leave a Reply

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

WordPress.com Logo

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

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s

%d bloggers like this: