Sunday, January 24, 2016

Add custom javascript file to all pages in on-premise Sharepoint site collection without modifying masterpage

If you need to inject custom javascript file to all pages in all sub sites in Sharepoint site collection you may edit site master page and system master page and add appropriate <script> tag there. However if you have many site collections or if your site collections don’t use publishing feature it may not be very convenient. There is another way to add custom javascript to all pages: site collection’s custom actions (SPSite.UserCustomActions) with ScriptLink location. Here is PowerShell script which can be used in order to add custom action to each site collection in specified web application with custom javascript reference:

   1: param( 
   2:     [string]$url
   3: )
   4:  
   5: function Add-Custom-Action($site, $actionUrl, $sequence)
   6: {
   7:     Write-Host "Add custom action $actionUrl on" $site.Url -foregroundcolor green
   8:     $actions = $site.UserCustomActions
   9:     $contains = $false
  10:     foreach ($a in $actions)
  11:     {
  12:         if ($a.ScriptSrc.ToLower() -eq $actionUrl.ToLower())
  13:         {
  14:             $contains = $true
  15:             break
  16:         }
  17:     }
  18:  
  19:     if ($contains)
  20:     {
  21:         Write-Host "    Custom action is already added" -foregroundcolor yellow
  22:         return
  23:     }
  24:  
  25:     $action = $site.UserCustomActions.Add()
  26:     $action.Location = "ScriptLink"
  27:     $action.ScriptSrc = $actionUrl
  28:     $action.Sequence = $sequence
  29:     $action.Update()
  30: }
  31:  
  32: Start-Transcript -Path "output.log" -Append -Force -Confirm:$false
  33:  
  34: if (-not $url)
  35: {
  36:     Write-Host "Specify web app url in url parameter" -foregroundcolor red
  37:     return
  38: }
  39:  
  40: $webApp = Get-SPWebApplication $url
  41: $webApp.Sites | ForEach-Object { Add-Custom-Action $_ "/_layouts/15/test/foo.js" 10001 }
  42:  
  43: Stop-Transcript

This script goes through all site collections and adds custom action to each of them. Before to add custom action it checks whether action with specified url is already added and if yes, skips current site collection, so it is safe to call this script many times for the same web app (always create your script with assumption that they may be executed many times for the same site). As result you will have your custom javascript available in all pages in all sub sites in all site collections, including list forms and application layouts pages.

Please note that this script uses basic Sharepoint object model and can be used with on-premise Sharepoint site only. In future posts I will show how to do the same using client object model which can be used in Sharepoint Online.

Update 2016-01-24: see Add custom javascript file to all pages in Sharepoint Online site collection for example of similar script for Sharepoint Online.

2 comments:

  1. Hi,

    I tried this but I cannot get the pages to load when using a ScriptSrc which is not beginning with ~siteCollection like "/Style Library/myLib.js" or "http://myserver.com/Style Library/myLib.js". Do you have an idea? Also put this on SO: http://sharepoint.stackexchange.com/questions/204017/cannotmakebrowsercachesafelayoutsurl-for-scriptlink-in-customaction

    ReplyDelete
  2. hi Ben,
    some time ago I also faced with similar strange issue: looks like Sharepoint somehow checks that file referenced in ScriptSrc really exists. If file not found, pages got broken. In example in this post I used js file from /_layouts subfolder, which as you probably know can be opened in context of any Sharepoint site, i.e. suppose that http://example.com is root site of site collection and http://example.com/test is one of its sub sites, then we will have:
    http://example.com/_layouts/15/test/foo.js
    http://example.com/test/_layouts/15/test/foo.js
    both files will be opened.

    But when you try to add js file into Sharepoint doclib, e.g. to Style library, it will be accessible only by absolute url
    http://example.com/Style library/foo.js.
    So if you use relative url and try to open page on sub site it will try to find it on
    http://example.com/test/Style library/foo.js
    and won't find it here. That's why in last case ~sitecollection is needed.

    ReplyDelete