1

I would like to know if it's possible to have a SP 2013 Hosted app that injects a piece of Javascript that gets executed on every page load.

For the sake of simplicity, imagine that I want to create an App that on every page load of the SP Site displays a alert('Hello world!');

I don't want to have a Remote Web, pure and simple Hosted App that can be added by anyone simply by picking it from the SP Store.

Is this possible?

Thanks!

AlexCode
  • 4,055
  • 4
  • 33
  • 46

2 Answers2

1

You can inject the javascript using a custom action script link as @AlexCode suggests but the app will require web - full control permissions. I can't remember where I adapted this code from while I was looking into add-in development. Also this is for POC only you should probably look to make it more robust before using it in a live environment.

App.js contents

(function(undefined) {
    "use strict";
    var actions, web, context, hostContext, actionDescription;
    console.log('running function');
    // getQueryStringParameter: method to retrieve query string parameter values
    var getQueryStringParameter = function(param) {

            var params = document.URL.split('?')[1].split('&');
            var length = params.length;
            for (var i = 0; i < length; i = i + 1) {
                var singleParam = params[i].split('=');
                if (singleParam[0] == param) {
                    return singleParam[1];
                }
            }
        };

    // inject: method to return as a string the js that will be ran by the custom action
    var inject = function() {
            debugger;
            var scriptToRun;

            scriptToRun += '(function (){' +
                    'var elem = document.getElementsByTagName("head")[0];' +                    
                    'var script = document.createElement("script");' +
                    'script.appendChild(document.createTextNode(alert("hello world")));' +                  
                    'elem.appendChild(script);' +
                '}());';

            return scriptToRun;
        };

    var success = function() {
        alert('Done');
    }

    var fail = function() {
        alert('Failed');
    }

    // unprovision: removes the custom action and the JavaScript file
    var unprovision = function() {
            context = SP.ClientContext.get_current();
            hostContext = new SP.AppContextSite(context, decodeURIComponent(getQueryStringParameter('SPHostUrl')));
            // load the custom actions from the host web
            actions = hostContext.get_web().get_userCustomActions();
            context.load(actions);
            web = hostContext.get_web();
            context.load(web);
            context.executeQueryAsync(unprovisionEx, fail);
        };

    // unprovisionEx: method to remove the custom action
    var unprovisionEx = function() {
            var enumerator = actions.getEnumerator();
            var removeThese = [];
            // find the custom action
            while (enumerator.moveNext()) {
                var action = enumerator.get_current();
                if (action.get_description() == actionDescription && action.get_location() == 'ScriptLink') {
                    // add it to a temporary array (we cannot modify an enumerator while enumerating)
                    removeThese.push(action);
                }
            }
            // do the actual removal of the custom action
            var length = removeThese.length;
            for (var i = 0; i < length; i++) {
                removeThese[i].deleteObject();
                delete removeThese[i];
            }

            context.executeQueryAsync(success, fail);
        };


    // provisionScriptLink: method that adds the custom action
    var provisionScriptLink = function() {
            var enumerator = actions.getEnumerator();
            var removeThese = [];
            // check if the custom action already exists, if it does then remove it before adding the new one
            while (enumerator.moveNext()) {
                var action = enumerator.get_current();
                if (action.get_description() == actionDescription && action.get_location() == 'ScriptLink') {
                    removeThese.push(action);
                }
            }

            var length = removeThese.length;
            for (var i = 0; i < length; i++) {
                removeThese[i].deleteObject();
                delete removeThese[i];
            }

            // create the custom action
            var newAction = actions.add();
            // the 'description' is what we'll use to uniquely identify our custom action
            newAction.set_description(actionDescription);
            newAction.set_location('ScriptLink');
            newAction.set_scriptBlock(inject());

            newAction.update();
            context.executeQueryAsync(success, fail);
        };

    // provision: starts with uploading the JavaScript file to the host we, once done it will continue with the provisionScriptLink() method
    var provision = function() {
            context = SP.ClientContext.get_current();
            hostContext = new SP.AppContextSite(context, decodeURIComponent(getQueryStringParameter('SPHostUrl')));
            // load the custom actions from the host web
            actions = hostContext.get_web().get_userCustomActions();
            context.load(actions);
            web = hostContext.get_web();
            context.load(web);

            context.executeQueryAsync(provisionScriptLink, fail);
        };


    document.getElementById("add").onclick = provision;

}());

Default.apsx content

<%-- The following 4 lines are ASP.NET directives needed when using SharePoint components --%>

<%@ Page Inherits="Microsoft.SharePoint.WebPartPages.WebPartPage, Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" MasterPageFile="~masterurl/default.master" Language="C#" %>

<%@ 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" %>
<%@ Register TagPrefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<%-- The markup and script in the following Content element will be placed in the <head> of the page --%>
<asp:Content ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">

    <script type="text/javascript" src="/_layouts/15/sp.runtime.js"></script>
    <script type="text/javascript" src="/_layouts/15/sp.js"></script>

    <!-- Add your CSS styles to the following file -->
    <link rel="Stylesheet" type="text/css" href="../Content/App.css" />

</asp:Content>

<%-- The markup in the following Content element will be placed in the TitleArea of the page --%>
<asp:Content ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea" runat="server">
    Page Title
</asp:Content>

<%-- The markup and script in the following Content element will be placed in the <body> of the page --%>
<asp:Content ContentPlaceHolderID="PlaceHolderMain" runat="server">

    <div>
       <button type="button" value="add" name="add" id="add">Add</button>
    </div>

</asp:Content>

<asp:Content ContentPlaceHolderID="PlaceHolderUtilityContent" runat="server">
    <!-- Add your JavaScript to the following file -->
    <script type="text/javascript" src="../Scripts/App.js"></script>
</asp:Content>
Aquila Sands
  • 1,471
  • 1
  • 20
  • 28
0

You can provide a custom master page to the host site from app site via javascript. Anyway the host site must use the new master page.

You can see this article for more info

Max
  • 6,821
  • 3
  • 43
  • 59
  • If I understood it well, that's not an option. I, as a SP Hosted App provider, have no control over the Host Web and I can't force anyone to do that. I believe the way is through a CustomAction ScriptLink but I'm not sure how. – AlexCode Oct 28 '15 at 10:24