09. Mai 2015 · von Gate4 GmbH

Javascript Code Injection für Office365

Möchte man das grundlegende Design seines SharePoint verändern, so war die Anpassung der Master Page bisher das Mittel der Wahl. In Office 365 jedoch funktioniert dies nicht mehr und auch für die On-Premise Variante von SharePoint sollte man sich gut überlegen, ob dies der beste Weg zur Anpassung des Designs ist.
Um Problemen mit SharePoint Updates aus dem Weg zu gehen, führt der von Microsoft für Office 365 unterstützte Weg über das Einfügen von Javascript Code in die Webseite. Code-Injection heißt also das Zauberwort.
Im folgenden Artikel möchte ich zeigen, wie man mit ein wenig Javascript den SharePoint mit eigenem CSS und Javascript auf jeder Page verändern kann.

Mit Code-Injection besteht die Möglichkeit, einen Scriptblock im Header jeder Page einer SharePoint Site unterzubringen. Hierzu nutzt man die UserCustomAction Funktionalität aus dem SharePoint Client Object Model (https://msdn.microsoft.com/en-us/library/microsoft.sharepoint.client.usercustomaction.aspx).  Mit dem SharePoint Javascript Object Model (JSOM) steht der Zugriff auf diese Funktionalitäten auch aus Javascript heraus zur Verfügung.
Mit den UserCustomActions kann man nun einen eigenen Scriptblock im Header jeder Page einfügen, ohne den Code der Seite direkt ändern zu müssen. Die beim SharePoint registrierten Actions werden vom SharePoint beim Ausliefern der Seite eingefügt.
Dies kann man nutzen, um zum Beispiel mittels Javascript und jQuery Elemente der Page zu modifizieren oder auch um ein Custom CSS nachzuladen.

Im folgenden Beispiel möchte ich zeigen, wie man mittels Code-Injection Apps aus dem Office365 Appmenu entfernen und jede Page mit einer Statusnachricht versehen kann.
Hierzu benötigen wir zunächst eine ASPX Seite, auf der wir die Funktionalität mit ein paar Buttons aufrufen können.
MasterPaceCodeInjectionASPX

Der Code hierzu schaut wie folgt aus:

<%@ Page language="C#" MasterPageFile="~masterurl/default.master"    Inherits="Microsoft.SharePoint.WebPartPages.WebPartPage,Microsoft.SharePoint,Version=15.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c" meta:progid="SharePoint.WebPartPage.Document" meta:webpartpageexpansion="full"  %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Import Namespace="Microsoft.SharePoint" %> <%@ Assembly Name="Microsoft.Web.CommandUI, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<asp:Content ID="Main" ContentPlaceHolderID="PlaceHolderMain" runat="server">

	<script type="text/javascript">
          ...
        </script>

	<h2>MasterPage Code Injection</h2><br><br>
	Inject or remove Javascript code from /injection/custom.js into SharePoint MasterPage:<br><br>
    <input id="Button1" type="button" style="width: 150px" value="Inject Javascript Code" onclick="injectJSCode()" />
    <input id="Button2" type="button" style="width: 150px" value="Remove Javascript Code" onclick="removeJSCode()" />
    <br><br><br><br>
	Inject or remove Cascaded Stylesheet (CSS) code from /injection/custom.css into SharePoint MasterPage:<br><br>
    <input id="Button3" type="button" style="width: 150px" value="Inject CSS Code" onclick="injectCSSCode()" />
    <input id="Button4" type="button" style="width: 150px" value="Remove CSS Code" onclick="removeCSSCode()" />  

</asp:Content>

Mit den Buttons werden nun jeweils Javascript Funktionen zum Registrieren oder Entfernen der UserCustomActions aufgerufen.

Der eigentliche, in die MasterPage injizierte Javascript Code zum Ausführen eines Javascripts schaut wie folgt aus:

var headID=document.getElementsByTagName('head')[0];
var newScript=document.createElement('script');
newScript.type='text/javascript';
newScript.src='/injection/custom.js';
headID.appendChild(newScript);

oder für das Nachladen einer CSS Datei:

var headID=document.getElementsByTagName('head')[0];
var newScript=document.createElement('link');
newScript.type='text/css';
newScript.rel='stylesheet';
newScript.href='/injection/custom.css';
headID.appendChild(newScript);

Diesen Code müssen wir nun in einer Stringvariablen speichern und mit Hilfe der SharePoint Client API als eine UserCustomAction registrieren.
Dazu erzeugen wir in der UserCustomActionCollection ein neues Objekt:

var clientContext = new SP.ClientContext();
var newUserCustomAction = clientContext.get_web().get_userCustomActions().add();

Dieser neuen UserCustomAction müssen wir nun einen Identifier, eine Location und natürlich den Javascript Block mitteilen, der mit der Masterpage ausgeliefert und ausgeführt werden soll.

newUserCustomAction.set_description('InjectedJS');  // Name der CustomAction
newUserCustomAction.set_location('ScriptLink');     // Location: ScriptLink
newUserCustomAction.set_scriptBlock(scriptBlock);   // scriptBlock enthält das zu injizierende Javascipt

Abschließend erfolgt ein Update des CustomAction Objekts und ein ExecuteQuery des SharePoint Client Context.

newUserCustomAction.update();
clientContext.executeQueryAsync(Function.createDelegate(this, this.onQueryInjectionSucceeded), Function.createDelegate(this, this.onQueryFailed));

Die Delegates onQueryInjectionSucceeded und onQueryFailed geben einfach nur eine Erfolgs- bzw. Fehlermeldung in einer Alert-Box aus:

// Alertbox InjectionSucceeded
function onQueryInjectionSucceeded(sender, args) {
	alert('Injection succeeded.');
}
// Alertbox QueryFailed
function onQueryFailed(sender, args) {
	alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}

Die kompletten Funktionen zum Injizieren des Javascripts zum Ausführen einer .js Datei oder zum Nachladen einer .css Datei sehen dann wie folgt aus:

// Inject Javascript Code in die SharePoint MasterPage
function injectJSCode() {
	var clientContext = new SP.ClientContext();

	var newUserCustomAction = clientContext.get_web().get_userCustomActions().add();
	newUserCustomAction.set_description('InjectedJS');
	newUserCustomAction.set_location('ScriptLink');

	var scriptBlock = "var headID=document.getElementsByTagName('head')[0];";
	scriptBlock += "var newScript=document.createElement('script');";
	scriptBlock += "newScript.type='text/javascript';"; 
	scriptBlock += "newScript.src='"+ clientContext.get_url() +"/injection/custom.js';";
	scriptBlock += "headID.appendChild(newScript);";
	newUserCustomAction.set_scriptBlock(scriptBlock);
	
	newUserCustomAction.update();
	clientContext.executeQueryAsync(Function.createDelegate(this, this.onQueryInjectionSucceeded), Function.createDelegate(this, this.onQueryFailed));
}

// Inject CSS Code in die SharePoint MasterPage
function injectCSSCode() {
	var clientContext = new SP.ClientContext();

	var newUserCustomAction = clientContext.get_web().get_userCustomActions().add();
	newUserCustomAction.set_description('InjectedCSS');  
	newUserCustomAction.set_location('ScriptLink');

	var scriptBlock = "var headID=document.getElementsByTagName('head')[0];"; 
	scriptBlock += "var newScript=document.createElement('link');"; 
	scriptBlock += "newScript.type='text/css';"; 
	scriptBlock += "newScript.rel='stylesheet';"; 
	scriptBlock += "newScript.href='"+ clientContext.get_url() +"/injection/custom.css';"; 
	scriptBlock += "headID.appendChild(newScript);";	
	newUserCustomAction.set_scriptBlock(scriptBlock);

	newUserCustomAction.update();
	clientContext.executeQueryAsync(Function.createDelegate(this, this.onQueryInjectionSucceeded), Function.createDelegate(this, this.onQueryFailed));
}

Beide Funktionen verweisen auf eine custom.js bzw. custom.css Datei im injection Folder der Website.

Zum Entfernen der Code-Injections müssen wir die jeweilige UserCustomAction wieder aus der UserCustomActionCollection löschen. Dazu muss zunächst die Collection über den ClientContext geladen werden:

// Entfernt Javascript Code aus der SharePoint MasterPage
function removeJSCode(){
	this.clientContext = new SP.ClientContext();
	this.UserCustomActions = clientContext.get_web().get_userCustomActions();
	this.scriptDescription = 'InjectedJS';
	
	clientContext.load(UserCustomActions);
	clientContext.executeQueryAsync(Function.createDelegate(this, this.onQueryRemove), Function.createDelegate(this, this.onQueryFailed));
}

In dem Delegate onQueryRemove kann man nun über die Elemente der Collection iterieren. Die zu löschende CustomAction können wir an ihrem Namen und der Location erkennen. Den Namen geben wir dabei in der Variablen scriptDescription an das Delegate weiter.

// Entfernt eine UserCustom Action aus der SharePoint Page
function onQueryRemove(sender, args) {
	var collectionEnumerator = UserCustomActions.getEnumerator();

	while(collectionEnumerator.moveNext())
	{
	   var userCustomAction = collectionEnumerator.get_current();
	   var description = userCustomAction.get_description();
	   var location = userCustomAction.get_location();
	   if(description == scriptDescription && location == 'ScriptLink')
	   {
		 userCustomAction.deleteObject()
		 clientContext.executeQueryAsync(Function.createDelegate(this, this.onQueryRemoveSucceeded), Function.createDelegate(this, this.onQueryFailed));
		 break;
	   }
	}    
}

Soweit die Funktionalität zum injizieren und entfernen von JavaScript UserCustomAction im SharePoint mittels der SharePoint Client API.

Alles, was wir nun tun müssen, ist, ein custom.js oder ein custom.css bereitzustellen.
In meinem Beispiel möchte ich die OfficeOnline Apps für Word, Excel, PowerPoint und OneNote aus dem App-Menu des Office365 entfernen.
Office365Apps

Dies kann zwar jeder User selber über das Kontextmenu der App machen, aber als Administrator möchte man dies natürlich gerne mit einem mal für alle User erledigen können.
Dies lässt sich auch recht einfach mittels CSS erledigen, indem wir die display Eigenschaft auf none setzen:

/* Example Code for hiding Office365 Apps from Menu 
*/
#O365_AppTile_WordOnline, #O365_AppTile_ExcelOnline, #O365_AppTile_PowerPointOnline, #O365_AppTile_OneNoteOnline {	
	display: none;
}

Dies wird als custom.css nun in einem Ordner mit dem Namen injection im SharePoint bzw. im Office365 abgelegt. Dies kann man z.B. bequem mit dem SharePoint Designer erledigen.

Wenn wir nun auf unserer MasterPageCodeInjection.aspx den Button zum injizieren des CSS drücken, wird der Code zum Nachladen der CSS Datei injiziert und nach einem Reload sind die Apps aus dem Office365 Appmenu verschwunden.
MasterPageCodeInjection2

Für das Anzeigen einer Statusnachricht legen wir in der custom.js ein kleines Javascript ab:

SP.SOD.executeOrDelayUntilScriptLoaded(function(){
  var strStatusId = SP.UI.Status.addStatus("Information : ", "Hallo Welt!", true);
  SP.UI.Status.setStatusPriColor(strStatusId, "yellow");
}, 'sp.js');

Dieser Code erzeugt, nachdem die sp.js Datei geladen wurde, eine Statusbar, welche nun auf jeder Page erscheint:
MasterPageCodeInjection3



Diesen Blogeintrag bewerten:

5 Stimmen mit durchschnittlich 3.8/5 Punkten

Haben Sie Fragen zu diesem Artikel oder brauchen Sie Unterstützung?

Nehmen Sie mit uns Kontakt auf!

Wir unterstützen Sie gerne bei Ihren Vorhaben!


2 Kommentare zu “Javascript Code Injection für Office365”

Der Blogeintrag ist super hilfreich, nur leider stoße ich mit dem Code auf ein Problem. Die JS oder CSS Files werden zwar schön per UserCustomAction registriert, aber sobald ich die Homepage verlasse und zB die SiteSettings aufrufe werden die Skripte nicht mehr geladen.
Was mache ich hier falsch?? Ich brauche die UserCustomAction auf allen Pages in einer SiteCollection

Hallo ChrMac,

der Artikel ist leider schon etwas älter und durch das „Modern UX“ von Office365 hat sich auch die Art und Weise geändert wie Anpassungen getätigt werden. Bedauerlicherweise sind die UserCustomAction nicht länger überall supported bzw. werden nach und nach verschwinden. Als Alternative ist das SharePoint Framework (SPFx) gekommen, mit dem du die Anpassungen durchführen kannst. Dieses läuft sowohl in der Office 365 Cloud als auch OnPremise ab SharePoint 2016 Feature Pack 2.

Mehr Informationen unter:
https://docs.microsoft.com/en-us/sharepoint/dev/spfx/sharepoint-framework-overview

Viele Grüße
Jörg

Schreibe einen Kommentar zu ChrMac

Kontakt.
Lassen Sie sich von uns beraten
Wir freuen uns über Ihr Interesse an unseren Leistungen. Hinterlassen Sie
uns Ihren Namen, Ihre Telefonnummer und E-Mail Adresse – wir melden
uns schnellstmöglich bei Ihnen.
Kontakt aufnehmen