4

I have the need to customize the site creation process in SharePoint Online. Is this possible via an app using JavaScript CSOM? If so can anyone point me in the right direction.

8

Yes it's possible. If you're new to JavaScript, I recommend learning deferred's (promises) as they hold the trick to controlling the async firing order. Here is a sample to get you started:

Here is a namespaced function that gets the id of a custom site template based on the template's name and then spins up a new site under the root based on that template (I omitted the errorHandler function for brevity, although this response is far from brief).

"Use strict";

var NAPP = window.NAPP || {};
NAPP.Automation = NAPP.Automation || {};

NAPP.Automation.Site = function () {
var createSite = function () {
    return $.Deferred(function (def) {  
        var templateName = "My Custom Template Name";
        var templateID = "";

        getWebTemplateID(templateName);
        function getWebTemplateID(templateName) {
            var hostweburl = decodeURIComponent(getQueryStringParameter("SPHostUrl"));
            this.context = SP.ClientContext.get_current();
            var appContextSite = new SP.AppContextSite(this.context, hostweburl);
            this.web = appContextSite.get_web();
            var webTemp = this.web.getAvailableWebTemplates(1033, false);
            this.context.load(webTemp);
            this.context.executeQueryAsync(Function.createDelegate(this, gotWebTemplate), Function.createDelegate(this, errorHandler));
            function gotWebTemplate() {
                var enumerator = webTemp.getEnumerator();
                while (enumerator.moveNext()) {
                    var currentItem = enumerator.get_current();
                    var currID = currentItem.get_name();
                    var currTitle = currentItem.get_title();
                    if (currTitle === templateName) {
                        templateID = currID;
                        finishSiteCreation();
                    }
                }
            }
        }

        function finishSiteCreation() {
            siteName = $("#companyName").val(); // these come from a custom form I have on the default.aspx page of the app
            siteTitle = $("#siteName").val();

            $("#progressWrap").empty().append("<div>Creating subsite...</div>");
            var hostweburl = decodeURIComponent(getQueryStringParameter("SPHostUrl"));
            this.context = SP.ClientContext.get_current();
            var appContextSite = new SP.AppContextSite(this.context, hostweburl);
            this.web = appContextSite.get_web();

            var WCI = new SP.WebCreationInformation();
            WCI.set_webTemplate(templateID);
            WCI.set_description($("#companyDesc").val());
            WCI.set_title(siteName);
            WCI.set_url(siteTitle);
            WCI.set_language(1033);
            WCI.set_useSamePermissionsAsParentSite("false");
            this.newSite = this.web.get_webs().add(WCI);

            this.context.load(this.newSite);
            this.context.executeQueryAsync(
                Function.createDelegate(this, function () {
                    $("#progressWrap").append("<div class='sxsTxt'>" + siteName+ " Site Created</div>");
                    def.resolve(this.newSite);
                }),
                Function.createDelegate(this, function (sender, args) { def.reject(sender, args); })
            );
        }
    });
};
return { createSite: createSite };
}();

It's important to note that the function returns a deferred object which signals that it's done and is ready for another. (Thank you sir; may I have another?)

And here is a function to create a group in that new site (spWeb object is passed down through the promise chaining in the initialization which I'll show later)

"use strict";

var NAPP = window.NAPP || {};
NAPP.Automation = NAPP.Automation || {};

NAPP.Automation.Groups = function () {
var createGroups = function (spWeb, groupName, permissionLevel) {
    return $.Deferred(function (def) {
        this.context = SP.ClientContext.get_current();
        this.groupCollection = spWeb.get_siteGroups();

        var permissionGroup = new SP.GroupCreationInformation();
        permissionGroup.set_title(groupName);
        permissionGroup.set_description("description");
        var oPermissionsGroup = this.groupCollection.add(permissionGroup);

        var rollDef = spWeb.get_roleDefinitions().getByName(permissionLevel);
        var collBind = SP.RoleDefinitionBindingCollection.newObject(this.context);
        collBind.add(rollDef);

        var assignments = spWeb.get_roleAssignments();
        var roleAssignment = assignments.add(oPermissionsGroup, collBind);

        this.context.load(oPermissionsGroup);
        this.context.executeQueryAsync(
            Function.createDelegate(this, function () {
                $("#progressWrap").append("<div class='sxsTxt'>" + groupName + " Group Created</div>");
                def.resolve(oPermissionsGroup);
            }),
            Function.createDelegate(this, function (sender, args) {def.reject(sender, args);})
        );
    })
};
return { createGroups: createGroups };
}();

and here is how I call them to fire in the right order using promises on button click

    $("#someButton").click(function () {
    var spWebPromise = NAPP.Automation.Site.createSite();
    $.when(spWebPromise)// begin promise chaining
        .then(function (spWeb) { return createSiteGroups(spWeb);})
        .done(function (spWeb) {$("#progressWrap").append("<div class='sxsTxt3'>All done!</div>"); })
        .fail(function (sender, args) { $("#progressWrap").append("<div style='color:red;'>Error: " + args.get_message() + "</div>"); });
});

Since I needed to create multiple groups for each site, I stuck the actual call to the namespace for the create groups inside it's own promise returning function so that multiple groups would be created asynchronously (when block notates all functions contained therein need to finish before the promise is fulfilled).

// promise firing order helpers
function createSiteGroups(spWeb) {
return $.Deferred(function (def) {
    $.when(
        NAPP.Automation.Groups.createGroups(spWeb, "Approver", "Read"),
        NAPP.Automation.Groups.createGroups(spWeb, "External Members", "Read"),
        NAPP.Automation.Groups.createGroups(spWeb, "Internal Members", "Edit"),
        NAPP.Automation.Groups.createGroups(spWeb, "Owners", "Full Control"),
        NAPP.Automation.Groups.createGroups(spWeb, "Read Only", "Read")
    )
    .done(function () {
        $("#progressWrap").append("<div class='sxsTxt2'>--- Groups Creation Complete ---</div>");
        def.resolve(spWeb);
    })
    .fail(function (sender, args) { def.reject(sender, args); });
});
}
| improve this answer | |
  • G'day Josey, thanks for the reply that awesome ! I have been investigating using REST as well. What do you think REST vs CSOM? – Daniel Sep 5 '13 at 1:06
  • For Create/Read/Update/Delete operations, REST is the way to go. Especially if you're trying to upload documents (REST allows a 2gb file upload size while CSOM only allows for 1.5mb). But for anything else, I'm beginning to prefer CSOM(JSOM). – Josey Sep 5 '13 at 13:55
  • @Josey I Have done similar kinda requirement, also site provision(created) successfully but one unnecessary error comes out "Unexpected response from server" is this Microsoft change their policy now? – Amit Koyani Jul 20 '19 at 12:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.