Tuesday, July 21, 2015

How to do a light weight search center using osssearchresults.aspx (part 2/3)

In the first post I went over how to change the default scopes available in the search box drop-downs to ensure we stay within the site and osssearchresults.aspx.

Next up is modifying the UI and hide unwanted controls.

Topics covered in this post:
  • Script injection on osssearchresults.aspx
  • Modify search controls via JSOM at the right place in the page life cycle

Issue 1: Hiding unwanted elements on the page

One issue with osssearchresults.aspx is how the scope drop-down control behaves. If you pick Everything, it will show the right results, but the label will still say Results found in SiteName, which is confusing. As you have the option to change scope in the search box as well, I want to hide the scope control and leave the one in the searchbox. I also want to hide the language preference option.

A search page is built up of search web parts (search box, refiner, results), and it’s all stitched together using JavaScript coming from Search.ClientControls.js

The image below illustrates the use of Srch.ScriptApplicationManager and which properties control which web part on the page.

ScriptApplicationManagerAndParts

Each client side object refers to the web control/web part used and the members of the server side objects can be found in the JSOM objects as well.
To turn off scope drop-down from the search box, the drop-down below the search box as well as the language settings, you would run the command shown below.

ControlsSetting

With the end result being a UI looking like this:

turnedoff

Issue 2: Running script in the page lifecycle

If you view the source of osssearchresults.aspx you will find calls to Sys.Application.add_init which set’s the default values for the search controls to pick up when rendering the display templates.

image

Referencing the load cycle of the page from http://josharepoint.com/2015/06/16/custom-javascript-function-loaded-after-the-ui-has-loaded-in-sharepoint-2013/ we have the following order of events in IE10:
  • ExecuteOrDelayUntilBodyLoaded.
  • Sys.Application.pageLoad.
  • document.ready Jquery.
  • _spBodyOnLoadFunctionNames.
  • _spBodyOnLoadFunction.
  • ExecuteOrDelayUntilScriptLoaded:sp.core.js.
  • SP.SOD.executeFunc: sp.js.
  • Sys.WebForms.PageRequestManager.PageLoaded
The important part here is that we want to register our initialization before Sys.Application.pageLoad. If we did it in the normal ExecuteOrDelayUntilScriptLoaded event, we would be too late, and the first rendering with our changes would not be picked up.

Hence we end up with the following construct to register our code at the right place:

function hideSettingsElementsOSSSearchResults() {
    // custom code
}
ExecuteOrDelayUntilBodyLoaded(function() {
    Sys.Application.add_init(hideSettingsElementsOSSSearchResults);
});

The full code which is also MDS compatible is available at https://github.com/wobba/Scripts/blob/master/mAdcOW.OSSSearchResultOverride.js

Issue 3: Register the script to be run on the page

This is actually not too hard and we will register the script using custom action registration. This ensures the script is loaded on every page without the need of modifying any master page or page layouts.

First I upload the file from my github repo (mAdcOW.OSSSearchResultOverride.js) to the Site Assets library of my site.

image

Next I register the .js file for the site using PowerShell and CSOM.

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime")

# helper methods removed
# see https://github.com/wobba/Scripts/blob/master/RegisterScriptOverride.ps1

Function Add-ScriptLinkAction([Microsoft.SharePoint.Client.ClientContext]$Context,[string]$ScriptSrc,[string]$ScriptBlock,[int]$Sequence,[string]$Name)
{
    $actions = Get-ActionByName -Context $Context -Name $Name
    $actions | ForEach-Object { Delete-Action -UserCustomAction $_  } 

    $action = $Context.Site.UserCustomActions.Add();
    $action.Location = "ScriptLink"
    if($ScriptSrc) {
        $action.ScriptSrc = $ScriptSrc
    }
    if($ScriptBlock) {
        $action.ScriptBlock = $ScriptBlock
    }
    $action.Name = $Name
    $action.Sequence = $Sequence
    $action.Update()
    $Context.ExecuteQuery()
}


$siteUrl = 'https://<yourtentant>.sharepoint.com/sites/<yoursite>'
$context = New-Object Microsoft.SharePoint.Client.ClientContext($siteUrl)
# SPO credentials
$context.Credentials = Get-SPOCredentials -UserName mikael.svenson@puzzlepart.com
# Use ~SiteCollection if you uploaded to the root site 
$scriptUrl = '~Site/SiteAssets/mAdcOW.OSSSearchResultOverride.js'

Add-ScriptLinkAction -Context $context -ScriptSrc $scriptUrl -Sequence 1000 -Name override

The full registration script can be downloaded from https://github.com/wobba/Scripts/blob/master/RegisterScriptOverride.ps1

The end-result is a page hiding the scope and language control, and keeping the scope drop-down in the search box with our custom defined scopes.


image

4 comments:

  1. Hi Mikael

    Is this code for SharePoint Online?
    I get an unauthorized error using SharePointOnlineCredentials in our on-premise installation

    John Legaard

    ReplyDelete
    Replies
    1. Should work regardless - might be that scripts are disabled on your tenant?

      Delete
  2. but SharePointOnlineCredentials uses an email as user credential, should I be able to connect to my local installation by using an email address? shouldn't it be som e like domain\userid??

    ReplyDelete
    Replies
    1. Either skip $context.Credentials if you have access or use $context.Credentials = new-object System.Net.NetworkCredential($username, $password, $domain)

      Delete