This week I am working for a customer who wants to develop a new solution that will allow users to create new SharePoint Online sites, based on a custom web Template, with a single click. After struggling for a few hours trying to find the proper way of achieving this using the SharePoint add-in model, I came up with a very simple solution that allows a user to automate the creation of SharePoint Online sites based on a custom web Template, using calls to the REST API.
We are all familiar with the default out-of-the-box templates (ex: STS#0), but custom web templates are a little different. The first thing you need to know when dealing with custom web templates, is that every one of them are provided a custom ID based on the following naming convention: <GUID>#<Template Name>. For example, assume you were to create a new site template and give it a title of “MasterTemplate”, the given Name for your Template could end up being something like “{2AA91D04-377B-431A-8D23-7424893F5CEB}#MasterTemplate”. The first part of the ID (before the ‘#’) is what we will need to pass to the REST method responsible for creating our new web.
Solution Overview
The solution we will be studying here is made up of two components. The first one will help us retrieve the actual ID of our custom Web Template. The second will be used to actually create the new site, using the retrieved custom Template. All of this will be achieved using a SharePoint-Hosted Add-In and by making REST calls using JavaScript.
For the purpose of this article, I went ahead in SharePoint Online and created a new site, which I’ve modified a bit so that it can be re-used over and over as a Template. I’ve cleaned all web parts from the landing page, and created two custom lists: a task list named “Team Tasks”, and an issue list named “Team Issues to Track”.
I then went ahead and saved this site as a Template. If you don’t see this option in your site settings, make sure you have Scripting enabled for the given site collection (more info at https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f). I named my Template (Help Desk Case).
Now that our custom Web Template is created and registered in our SharePoint Online Site Collection, we need to figure out what its ID is. To achieve this, I went ahead, opened Visual Studio, and created a new SharePoint Add-In. We will be using this Add-In to retrieve the ID of all of our existing Site Templates (including the custom ones), and display them to our users in a drop down list. The idea here is to query tthe following REST endpoint:
/_api/web/getavailablewebtemplates(lcid=1033, doincludecrosslanguage=true)
In the add-in default.aspx page, I have created a new empty DIV element with an ID of “divMain”. This empty div will be used to dynamically generate our drop down list of values contining information about all the available Web Templates. What our JavaScript code will do, is query the host web to retrieve the list of all available Web Templates, loop through each of them and add it to our dynamically generated Drop Down list. The option items in our drop down list will display the Title of each Web Template, but will have a value representing their internal ID.
The code used in the App.js file for our add-in to retrieve that list is the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | 'use strict'; ExecuteOrDelayUntilScriptLoaded(initializePage, "sp.js"); function initializePage() { var hostweburl; var appweburl; var __REQUESTDIGEST; // This code runs when the DOM is ready and creates a context object which is needed to use the SharePoint object model $(document).ready(function () { hostweburl = decodeURIComponent($.getUrlVar("SPHostUrl")); appweburl = decodeURIComponent($.getUrlVar("SPAppWebUrl")); var scriptbase = hostweburl + "/_layouts/15/"; // load the executor script, once completed set the ready variable to true so that $.getScript(scriptbase + "SP.Runtime.js", function () { $.getScript(scriptbase + "SP.js", function () { $.getScript(scriptbase + "SP.RequestExecutor.js", getWebTemplates); } ); } ); }); function getWebTemplates() { var requestURL = appweburl + "/_api/SP.AppContextSite(@target)/web/getavailablewebtemplates(lcid=1033, doincludecrosslanguage=true)?@target='" + hostweburl + "'"; var executor = new SP.RequestExecutor(appweburl); executor.executeAsync({ url: requestURL, type: "GET", headers: { "accept": "application/json;odata=verbose" }, success: function (data) { var jsonObject = JSON.parse(data.body); var results = jsonObject.d.results; var s = $('<select id="ddlTemplate" />'); for(var i = 0; i < results.length; i++) { $('<option />', { value: results[i].Name, text: results[i].Title }).appendTo(s); } s.appendTo('#divMain'); }, error: function (xhr, status, error) { alert(JSON.stringify(xhr)); } }); } } jQuery.extend({ getUrlVars: function () { var vars = [], hash; var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&'); for (var i = 0; i < hashes.length; i++) { hash = hashes[i].split('='); vars.push(hash[0]); vars[hash[0]] = hash[1]; } return vars; }, getUrlVar: function (name) { return jQuery.getUrlVars()[name]; } }); |
Now that we managed to retrieve all site templates for our SharePoint Online Site Collection, we need to work on the piece of our Add-in’s code that will actually go and create the site based on the web Template we’ve selected from our drop down list. To achieve this, we will modify the default.aspx page of our Add-in to include a text box allowing the users to enter a title for their new site, and a button to initiate the site’s creation. The default.aspx code for my solution looks like the following:
1 2 3 4 5 6 7 8 9 10 11 12 | ... <asp:Content ContentPlaceHolderID="PlaceHolderMain" runat="server"> <strong>Title: </strong><input type="text" id="siteTitle" /><br /> <strong>Site Template: </strong> <div id="divMain"> </div> <input type="button" id="btnCreate" value ="Create Site" onclick="createSite" /> </asp:Content> ... |
Now that the visuals are in place, we actually need to connect our button to the action that will create the new site. Based on the .NET markup above, we can see that my button is trying to call a JavaScript function named “createSite”. One very important thing: When calling the REST API to initiate the creation of the new site, you should only pass the associated Web Template ID’s prefix (what is before the ‘#’ sign). For example, in my case, the ID of my Help Desk Case web template is In order to have the onClick event trigger, we need to add the following logic in our App.js file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | function createSite() { var requestURL = appweburl + "/_api/SP.AppContextSite(@target)/web/webinfos/Add?@target='" + hostweburl + "'"; var siteTitle = $('#siteTitle').val(); var siteUrl = $('#siteTitle').val().replace(" ", ""); var templateID = $("#ddlTemplate").val().split('#')[0]; var jsonData = "{ 'parameters': { '__metadata': { 'type': 'SP.WebInfoCreationInformation' }, 'Title': '" + siteTitle + "', 'Url': '" + siteUrl + "', 'WebTemplate': '" + templateID + "'} }"; $.ajax({ url: requestURL, type: "POST", data: jsonData, headers: { "accept": "application/json;odata=verbose", "content-type": "application/json;odata=verbose", "X-RequestDigest": $('#__REQUESTDIGEST').val() }, success: function () { alert("site Created"); }, error: function (xhr, status, error) { alert(JSON.stringify(xhr)); } }); } |
Let’s now compile and deploy our add-in. the user running your add-in should now be presented with a form similar to the picutre below, allowing them to select both out-of-the-box and custom web templates in SharePoint Online, and create new sites with a simple click. Once the site has been successfully created, the user will get a prompt. Off course there is a lot of validation stuff you should take care off yourself if you’d ever want to implement such a solution into production (check that URL doesn’t have characters, etc.).
You can get a copy of the files used in this article Here
Hi Nik!
Wanted to say that Your post is GREAT! I changed something but basically it works like a charm and really helped me in my project! Thank You very much for sharing knowledge!
p.s.: Loaded executor with this snippet:
ExecuteOrDelayUntilScriptLoaded(function() {
var layoutsfolder = _spPageContextInfo.siteAbsoluteUrl + “/_layouts/15/”;
// Load SP.RequestExecutor.js first
jQuery.getScript(layoutsfolder + “SP.RequestExecutor.js”, function() {
console.log(“request executor is now loaded”);
startMyFunction();
});
}, “sp.js”);
Best regards,
Gennady