Self-Service Site Provisioning using Apps for SharePoint 2013
NOTE: Site Provisioning using apps has been better addressed by myself and others in the community on the Office AMS project published on codeplex. You are highly encouraged to reference that material over this. I would also discourage the use of Autohosted apps for this type of customization. Thanks! |
I’ve always been a big fan of self-service site provisioning in SharePoint. That process is even better in SharePoint 2013 (including SharePoint Online), with the ability to specify a custom site creation form. Owning the provisioning process is incredibly powerful for farm/tenant administrators to configure the options and outputs of site provisioning. By leveraging the new app model, we can customize the provision process in SharePoint Online to activate specific features, capabilities, and branding to our sites. This post will explore the process for delivering custom site creation using an app for SharePoint. The video below demonstrates the solution outlined in this post.
[View:https://www.youtube.com/watch?v=WTN0zQN4enc]
Self-Service Sites in SharePoint 2013
Self-service provisioning is improved in SharePoint 2013 by allowing an administrator to specify a custom creation form. This form can take over the provisioning process and will be the cornerstone of the solution outlined below. Without implementing a custom form, the default form only allows a user to specify a title. The default output will be a Team site created as a sub-site at a specified location. Again, if we want to capture additional details or provide different options, we must introduce a custom creation form.
Default Create a Site form in SharePoint 2013
The Solution
Because the app needs the ability to create sub-sites and site collections anywhere in the tenancy, it will need FullControl permission on the entire tenancy. The app will also need to make app-only calls to SharePoint, so it can work with tenant objects or sites outside the context. Both these settings can be configured in the Permissions tab of the AppManifest.xml.
AppManifest Permissions for out App
NOTE: You should typically avoid requesting tenancy permissions in your apps…especially with FullControl. It is a best practice for apps to request the minimum permissions they need to function. The “tenancy” permission scope is in place specifically for scenarios like provisioning. Tenancy-scoped apps will typically be developed in-house, as I would be REALLY surprised if the Office Store accepted an app requesting Tenant FullControl permissions. |
Our app will enable administrators to dynamically configure site creation settings using a Library in the app web. I’m using a library instead of a list, so each option can be displayed in the site creation form with an icon. Each row in the Library will represent a site provisioning option for an end-user. The columns to support this include the following:
- Title – the title of the configuration option (ex: Small Team Site)
- Site Template – the WebTemplate name to be used in provisioning (ex: STS#0 for Team Site)
- Base Path – the absolute URL where this option will get provisioned (ex: https://tenant/teams/)
- Site Type – choice of “Subsite” or “Site Collection”
- MasterPage URL – url of the masterpage file to apply (leave blank for no branding)
- Storage Maximum Limit – the storage quota in MB (only applicable for site collections)
- UserCode Maximum Limit – the user code quota in points (only applicable for site collections)
Library Configuration in App Project
I leveraged a module in the solution to pre-load a few provisioning options, but an administrator could easily edit these options or add new options through the library.
Module Elements.xml to pre-load provisioning options
<?xml version="1.0" encoding="utf-8"?><Elements xmlns="https://schemas.microsoft.com/sharepoint/"> <Module Name="AppAssets"> <File Path="AppAssets\Blog.png" Url="SSConfig/Blog.png" ReplaceContent="TRUE"> <Property Name="Title" Value="Blog" Type="string"></Property> <Property Name="SiteTemplate" Value="BLOG#0" Type="string"></Property> <Property Name="BasePath" Value="https://richdizzcom.sharepoint.com/sites/Blogs/" Type="string"></Property> <Property Name="SiteType" Value="Subsite" Type="string"></Property> <Property Name="MasterPageUrl" Value="/sites/Blogs/_catalogs/masterpage/DallasMTC.com.master" Type="string"></Property> <Property Name="StorageMaximumLevel" Value="100" Type="string"></Property> <Property Name="UserCodeMaximumLevel" Value="300" Type="string"></Property> </File> <File Path="AppAssets\Community.png" Url="SSConfig/Community.png" ReplaceContent="TRUE"> <Property Name="Title" Value="Community" Type="string"></Property> <Property Name="SiteTemplate" Value="COMMUNITY#0" Type="string"></Property> <Property Name="BasePath" Value="https://richdizzcom.sharepoint.com/sites/Communities/" Type="string"></Property> <Property Name="SiteType" Value="Subsite" Type="string"></Property> <Property Name="MasterPageUrl" Value="/sites/Communities/_catalogs/masterpage/DallasMTC.com.master" Type="string"></Property> <Property Name="StorageMaximumLevel" Value="100" Type="string"></Property> <Property Name="UserCodeMaximumLevel" Value="300" Type="string"></Property> </File> <File Path="AppAssets\Project.png" Url="SSConfig/Project.png" ReplaceContent="TRUE"> <Property Name="Title" Value="Project" Type="string"></Property> <Property Name="SiteTemplate" Value="PROJECTSITE#0" Type="string"></Property> <Property Name="BasePath" Value="https://richdizzcom.sharepoint.com/teams/" Type="string"></Property> <Property Name="SiteType" Value="Site Collection" Type="string"></Property> <Property Name="MasterPageUrl" Value="https://richdizzcom.sharepoint.com/_catalogs/masterpage/DallasMTC.com.master" Type="string"></Property> <Property Name="StorageMaximumLevel" Value="100" Type="string"></Property> <Property Name="UserCodeMaximumLevel" Value="300" Type="string"></Property> </File> <File Path="AppAssets\Publishing.png" Url="SSConfig/Publishing.png" ReplaceContent="TRUE"> <Property Name="Title" Value="Publishing" Type="string"></Property> <Property Name="SiteTemplate" Value="BLANKINTERNET#0" Type="string"></Property> <Property Name="BasePath" Value="https://richdizzcom.sharepoint.com/sites/" Type="string"></Property> <Property Name="SiteType" Value="Site Collection" Type="string"></Property> <Property Name="MasterPageUrl" Value="https://richdizzcom.sharepoint.com/_catalogs/masterpage/DallasMTC.com.master" Type="string"></Property> <Property Name="StorageMaximumLevel" Value="100" Type="string"></Property> <Property Name="UserCodeMaximumLevel" Value="300" Type="string"></Property> </File> <File Path="AppAssets\Team.png" Url="SSConfig/Team.png" ReplaceContent="TRUE"> <Property Name="Title" Value="Team" Type="string"></Property> <Property Name="SiteTemplate" Value="STS#0" Type="string"></Property> <Property Name="BasePath" Value="https://richdizzcom.sharepoint.com/teams/" Type="string"></Property> <Property Name="SiteType" Value="Site Collection" Type="string"></Property> <Property Name="MasterPageUrl" Value="https://richdizzcom.sharepoint.com/_catalogs/masterpage/DallasMTC.com.master" Type="string"></Property> <Property Name="StorageMaximumLevel" Value="100" Type="string"></Property> <Property Name="UserCodeMaximumLevel" Value="300" Type="string"></Property> </File> </Module></Elements> |
Our app will deliver a site creation form that will query the library to display site creation options.
PageLoad and btnCreate Events
private List<SSConfig> configList;private const string SHAREPOINT_PID = "00000003-0000-0ff1-ce00-000000000000";private const string TENANT_ADMIN_URL = "https://richdizzcom-admin.sharepoint.com";protected void Page_Load(object sender, EventArgs e){ //get SharePoint context var spContext = Util.ContextUtil.Current; using (var clientContext = TokenHelper.GetClientContextWithContextToken(spContext.ContextDetails.AppWebUrl, spContext.ContextDetails.ContextTokenString, Request.Url.Authority)) { //populate the badges control List list = clientContext.Web.Lists.GetByTitle("SSConfig"); CamlQuery query = new CamlQuery() { ViewXml = "<View><ViewFields><FieldRef Name='Title' /><FieldRef Name='SiteTemplate' /><FieldRef Name='BasePath' /><FieldRef Name='SiteType' /><FieldRef Name='MasterPageUrl' /><FieldRef Name='StorageMaximumLevel' /><FieldRef Name='UserCodeMaximumLevel' /></ViewFields></View>" }; var items = list.GetItems(query); clientContext.Load(items, i => i.IncludeWithDefaultProperties(j => j.DisplayName)); clientContext.ExecuteQuery(); configList = items.ToList(spContext.ContextDetails.AppWebUrl, "SSConfig"); } if (!this.IsPostBack) { //bind repeater repeaterTemplate.DataSource = configList; repeaterTemplate.DataBind(); //configure buttons based on display type if (Page.Request["IsDlg"] == "1") btnCancel.Attributes.Add("onclick", "javascript:closeDialog();return false;"); else btnCancel.Click += btnCancel_Click; }} protected void btnCancel_Click(object sender, EventArgs e){ Response.Redirect(Page.Request["SPHostUrl"]);} protected void btnCreate_Click(object sender, EventArgs e){ //get the selected config SSConfig selectedConfig = configList.FirstOrDefault(i => i.Title.Equals(hdnSelectedTemplate.Value)); if (selectedConfig != null) { string webUrl = ""; if (selectedConfig.SiteType.Equals("Site Collection", StringComparison.CurrentCultureIgnoreCase)) webUrl = CreateSiteCollection(selectedConfig); else webUrl = CreateSubsite(selectedConfig); //redirect to new site ClientScript.RegisterStartupScript(typeof(Default), "RedirectToSite", "navigateParent('" + webUrl + "');", true); }} |
Once the user submits the site creation form, the app will provision differently based on site type (Subsite or Site Collection). One thing that is common between the two methods is the need to execute app-only calls, since we will likely be provisioning with a different context from where the form is hosted (ex: site collection will require the context of the tenant administration site). The TokenHelper contains a GetAppOnlyAccessToken method to get the access token for a specific site that is different from the context of the form.
To provision a sub-site, we need to establish context with the site that will host our new sub-site (ie – the parent site). To apply a brand to a sub-site, I’m requiring the master page to exist in the Master Page Gallery of the root web in the site collection. That way, I can just set the MasterUrl and CustomMasterUrl after the new sub-site is provisioned.
Code to create sub-site
private string CreateSubsite(SSConfig selectedConfig){ string webUrl = selectedConfig.BasePath + txtUrl.Text; //create subsite var parentSite = new Uri(selectedConfig.BasePath); //static for my tenant var token = TokenHelper.GetAppOnlyAccessToken(SHAREPOINT_PID, parentSite.Authority, null).AccessToken; using (var clientContext = TokenHelper.GetClientContextWithAccessToken(parentSite.ToString(), token)) { var properties = new WebCreationInformation() { Url = txtUrl.Text, Title = txtTitle.Text, Description = txtDescription.Text, WebTemplate = selectedConfig.SiteTemplate, UseSamePermissionsAsParentSite = false }; //create and load the new web Web newWeb = clientContext.Web.Webs.Add(properties); clientContext.Load(newWeb, w => w.Title); clientContext.ExecuteQuery(); //TODO: set additional owners //apply the masterpage to the site (if applicable) if (!String.IsNullOrEmpty(selectedConfig.MasterUrl)) { newWeb.MasterUrl = selectedConfig.MasterUrl; newWeb.CustomMasterUrl = selectedConfig.MasterUrl; } /**************************************************************************************/ /* Placeholder area for updating additional settings and features on the new site */ /**************************************************************************************/ //update the web with the new settings newWeb.Update(); clientContext.ExecuteQuery(); } return webUrl;} |
Provisioning a site collection is a little more complex. First we need to establish context with tenant administration site and use a Tenant object for creation. The Tenant object can be found in the Microsoft.Online.SharePoint.Client.Tenant assembly which comes with the SharePoint Online Management Shell download. Provisioning the site collection can take a while to complete, so this occurs asynchronously using the SpoOperation class. We can leverage this to wait until the creation operation is complete before trying to apply the master page. Applying a master page to a site collection is a little more complex. The master page needs to live within the site collection, so our solution will download the master page from the location referenced in the configuration and upload it into the Master Page Gallery of the new site collection before setting MasterUrl and CustomMasterUrl on the site. This requires that scripts and styles in the master page are referenced using absolute paths. Pulling down and applying an entire design package would likely be a more elegant, but was overkill for this proof of concept.
Code to create site collection
private byte[] GetMasterPageFile(string masterUrl){ byte[] mpBytes = null; //get the siteurl of the masterpage string siteUrl = masterUrl.Substring(0, masterUrl.IndexOf("/_catalogs")); var siteUri = new Uri(siteUrl); //static for my tenant var token = TokenHelper.GetAppOnlyAccessToken(SHAREPOINT_PID, siteUri.Authority, null).AccessToken; using (var clientContext = TokenHelper.GetClientContextWithAccessToken(siteUri.ToString(), token)) { string relativeMasterUrl = masterUrl.Substring(8); relativeMasterUrl = relativeMasterUrl.Substring(relativeMasterUrl.IndexOf("/")); File file = clientContext.Web.GetFileByServerRelativeUrl(relativeMasterUrl); var stream = file.OpenBinaryStream(); clientContext.ExecuteQuery(); using (stream.Value) { mpBytes = new Byte[stream.Value.Length]; stream.Value.Read(mpBytes, 0, mpBytes.Length); } } return mpBytes;} private string CreateSiteCollection(SSConfig selectedConfig){ string webUrl = ""; //create site collection using the Tenant object var tenantAdminUri = new Uri(TENANT_ADMIN_URL); //static for my tenant var token = TokenHelper.GetAppOnlyAccessToken(SHAREPOINT_PID, tenantAdminUri.Authority, null).AccessToken; using (var clientContext = TokenHelper.GetClientContextWithAccessToken(tenantAdminUri.ToString(), token)) { var tenant = new Tenant(clientContext); webUrl = String.Format("{0}{1}", selectedConfig.BasePath, txtUrl.Text); var properties = new SiteCreationProperties() { Url = webUrl, Owner = "ridize@richdizzcom.onmicrosoft.com", Title = txtTitle.Text, Template = selectedConfig.SiteTemplate, StorageMaximumLevel = Convert.ToInt32(selectedConfig.StorageMaximumLevel), UserCodeMaximumLevel = Convert.ToDouble(selectedConfig.UserCodeMaximumLevel) }; SpoOperation op = tenant.CreateSite(properties); clientContext.Load(tenant); clientContext.Load(op, i => i.IsComplete); clientContext.ExecuteQuery(); //check if site creation operation is complete while (!op.IsComplete) { //wait 30seconds and try again System.Threading.Thread.Sleep(30000); op.RefreshLoad(); clientContext.ExecuteQuery(); } } //get the newly created site collection var siteUri = new Uri(webUrl); //static for my tenant token = TokenHelper.GetAppOnlyAccessToken(SHAREPOINT_PID, siteUri.Authority, null).AccessToken; using (var clientContext = TokenHelper.GetClientContextWithAccessToken(siteUri.ToString(), token)) { var newWeb = clientContext.Web; clientContext.Load(newWeb); clientContext.ExecuteQuery(); //update description newWeb.Description = txtDescription.Text; //TODO: set additional site collection administrators //apply the masterpage to the site (if applicable) if (!String.IsNullOrEmpty(selectedConfig.MasterUrl)) { //get the the masterpage bytes from it's existing location byte[] masterBytes = GetMasterPageFile(selectedConfig.MasterUrl); string newMasterUrl = String.Format("{0}{1}/_catalogs/masterpage/ssp.master", selectedConfig.BasePath, txtUrl.Text); //upload to masterpage gallery of new web and set List list = newWeb.Lists.GetByTitle("Master Page Gallery"); clientContext.Load(list, i => i.RootFolder); clientContext.ExecuteQuery(); FileCreationInformation fileInfo = new FileCreationInformation(); fileInfo.Content = masterBytes; fileInfo.Url = newMasterUrl; Microsoft.SharePoint.Client.File masterPage = list.RootFolder.Files.Add(fileInfo); string relativeMasterUrl = newMasterUrl.Substring(8); relativeMasterUrl = relativeMasterUrl.Substring(relativeMasterUrl.IndexOf("/")); //we can finally set the masterurls on the newWeb newWeb.MasterUrl = relativeMasterUrl; newWeb.CustomMasterUrl = relativeMasterUrl; } /**************************************************************************************/ /* Placeholder area for updating additional settings and features on the new site */ /**************************************************************************************/
//update the web with the new settings newWeb.Update(); clientContext.ExecuteQuery(); } return webUrl;}
|
Because our site creation form will be launch in the “Start a Site” dialog, our app needs to be able to communicate back with the SharePoint page that hosts the dialog. This communication is necessary to make the dialog page visible, resize the dialog, close the dialog, and navigate away from the dialog. This cross-domain communication is achieved using the HTML5 postMessage API, where SharePoint “listens” for MakePageVisible, Resize, CloseDialog, and NavigateParent messages from the page displayed within the dialog IFRAME. Below are the scripts to implement this messaging, which I hunted down through thousands of line of javascript.
postMessage scripts for interacting with dialog parent
//Makes the page visible in the dialogfunction MakeSSCDialogPageVisible() { var dlgMadeVisible = false; try { var dlg = window.top.g_childDialog; if (Boolean(window.frameElement) && Boolean(window.frameElement.makeVisible)) { window.frameElement.makeVisible(); dlgMadeVisible = true; } } catch (ex) { } if (!dlgMadeVisible && Boolean(top) && Boolean(top.postMessage)) { var message = "MakePageVisible"; top.postMessage(message, "*"); }} //Resizes the dialog for the page sizefunction UpdateSSCDialogPageSize() { var dlgResized = false; try { var dlg = window.top.g_childDialog; if (!fIsNullOrUndefined(dlg)) { dlg.autoSize(); dlgResized = true; } } catch (ex) { } if (!dlgResized && Boolean(top) && Boolean(top.postMessage)) { var message = "PageWidth=450;PageHeight=480"; top.postMessage(message, "*"); }} //postMessage to SharePoint for closing dialogfunction closeDialog() { var target = parent.postMessage ? parent : (parent.document.postMessage ? parent.document : undefined); target.postMessage('CloseDialog', '*');} //postMessage to close the dialog and navigate from the page to a specified urlfunction navigateParent(url) { var target = parent.postMessage ? parent : (parent.document.postMessage ? parent.document : undefined); target.postMessage('NavigateParent=' + url, '*');} |
In order for our site creation form to launch from the “add site” link on the “sites” page, we need to configure it as the “Start a Site” form in the tenant administration site. This field is limited to 255 characters, so we will likely need to remove some of the standard tokens that are passed to our app. At minimum, the URL MUST include SPHostUrl and SPAppWebUrl parameters. Our app will leverage these to get context and access tokens from SharePoint.
"Start a Site" configuration in SharePoint admin center
Our app should now be ready for primetime! Here is a screenshot of our custom app launched from the “Start a Site” dialog and in full-screen mode.
Our custom provisioning app launched from the "Create a Site" link
The fullscreen version of the app
Final Thoughts
I hope this post helped illustrate the power of delivering self-service site creation and how the app model can take it to the next level. I see this and “App Stapling” as the primary two patterns for pushing capability/features into sites and site collections in SharePoint Online. Please note that the provided code is not production ready and also references on of my tenants.
Solution code: https://sdrv.ms/XSBX7n
Comments
Anonymous
April 04, 2013
Just in time! I just found out how to use the tenant CSOM. This example is great! What type of project is SelfServiceProvisioning, I can't open it in my VS2012?Anonymous
April 05, 2013
It uses the "App for SharePoint" template that is part of the new Office/SharePoint tooling. You can get it here: aka.ms/OfficeDevToolsForVS2012 You will also need the SharePoint Online Management Shell if you want to provision site collections...you can find that here: www.microsoft.com/.../details.aspx Hope that helps...let me know if you have any other questions!Anonymous
April 05, 2013
I was using 11.0.50901.0, that's why I could open the example! 11.0.60226.0 works great!Anonymous
April 15, 2013
Great post and very interesting as I'm just now getting up to speed with all you can do in Office 365. Where do you find the SharePoint product id (SHAREPOINT_PID) that you reference in your code example? Thanks!Anonymous
April 15, 2013
Kevin...that SHAREPOINT_PID is the same for ALL SharePoint online tenants: 00000003-0000-0ff1-ce00-000000000000 If you are interested, you can find it as part of the "Microsoft.SharePoint" app identifier here: https://[tenant].sharepoint.com/_layouts/15/appprincipals.aspxAnonymous
May 07, 2013
Hi, I have spoken to alot of people and consultants and were unable to get any assistance on how to setup SP 2013 for multiple tenants. Have you got any guidance with regards to this? BTW - Love this app.Anonymous
May 16, 2013
The comment has been removedAnonymous
June 09, 2013
Very interesting article. Is there a way to automatically deploy an app on personal sites on O365. This would be great to change the default look end feel for end users.Anonymous
July 22, 2013
The comment has been removedAnonymous
July 22, 2013
In addition to my previous post i deleted the Web Site prerequisites and then i can update the app. Only then the default.aspx is looping in the var spContext = Util.ContextUtil.Current; Section in the default.aspx.cs get { ContextUtil spContext = null; if (HttpContext.Current.Request.Cookies["SPContext"] != null) { JavaScriptSerializer serializer = new JavaScriptSerializer(); spContext = new ContextUtil((SPContext)serializer.Deserialize(HttpContext.Current.Request.Cookies["SPContext"].Value, typeof(SPContext))); } if (spContext == null || !spContext.IsValid) spContext = new ContextUtil(HttpContext.Current.Request); if (spContext.IsValid) return spContext; else { HttpContext.Current.Response.Redirect(GetRedirectUrl()); << return null; } } The page hangs upon 'HttpContext.Current.Response.Redirect(GetRedirectUrl());' because the context cannot be filled??Anonymous
July 23, 2013
The comment has been removedAnonymous
September 10, 2013
I'm getting "token request failed" exceptions at oauth2Response = client.Issue(AcsMetadataParser.GetStsUrl(targetRealm), oauth2Request) as OAuth2AccessTokenResponse; in tokenhelper.cs when I attempt to provision a site. Would you have any idea what I'm doing wrong? This is obviously a configuration issue, but the only changes that I've made have been to swap out your tenant with my own.Anonymous
September 11, 2013
@ Jim Crowley - Do you have the Realm configured in your web.config?Anonymous
September 15, 2013
ive gotten the app to deploy, but sites are not being created. how can i find out my realm id?Anonymous
September 16, 2013
The comment has been removedAnonymous
October 20, 2013
The comment has been removedAnonymous
October 20, 2013
The comment has been removedAnonymous
October 22, 2013
The comment has been removedAnonymous
October 30, 2013
I am needing help RE the Realm ID and the Client ID required in the Web.config Using steps from this comment section I got the string that "@john" mentioned. In the web.config file I see: So where do I add the string after the @ sign? as "ClientId" value? What about the value of "ClientSecret" <appSettings> <add key="ClientId" value="" /> <add key="ClientSecret" value="" /> </appSettings>Anonymous
October 30, 2013
OK, I am pretty lost at this point. After updating the project code to work with my tenant, I can upload the app to the catalog and / or I can deploy via VS 2012 to my developer site. The app will not create any sites or site collections. When I "Start Debugging" from VS 2012 my dev site shows the app as available and when I click "Trust It" the browser chugs along with the message "Working on it...". It has been "Working on it.." for 15 minutes now... Nothing else ever happens. I am sure my misunderstanding / misconfiguration is very basic, and I have not idea what it is or how to correct it. Anyone who could point me in the right direction? Thanks and Cheers :0(Anonymous
October 30, 2013
The comment has been removedAnonymous
November 01, 2013
@RicharddiZerega Thanks for the reply I know you are very busy. My issue(s) are resolved. I can now create sub sites and site collections as well. One minor issue I will be working on soon is that after a sub site is created the form just posts and renders "Default.aspx" again. I am going to develop a "Thank You" confirmation message as well... Also, after a Site Collection is created if "Default.aspx" is left open it errors out after a few minutes... Cheers,Anonymous
November 06, 2013
BTW, I learned about your blog while at DevConn 2013 OK, One last question / issue. I want to have more than 5 options and when I add any more images to the "AppAssets" folder and update the Elements.xml File to reflect the additional site provisioning choices. I can never get more than 5 options to appear on Default.aspx I can see that the images in the "AppAssets" folder populate (move) over to the SSConfig Document Library. No matter what I do the images I add to AppAssets are left behind in the "AppAssets" folder so do not move to the SSConfig library and do not appear as options on Default.aspx I really want 7 options and I realize I need to update the UI on Defualt.aspx to be wider as well. How do I add more than 5 images and related config settings to SSConfig? Please advise. Thanks in advance for all your help I know you must be very busy... CheersAnonymous
November 06, 2013
Hey I found my error in the Elements.xml file. Really dumb error... CheersAnonymous
November 06, 2013
The comment has been removedAnonymous
November 08, 2013
The comment has been removedAnonymous
November 10, 2013
SpaceGhost904, I began to get the same error. However, now the platform seems to have been fixed, and this works again.Anonymous
November 10, 2013
I experienced exactly the same error when creating subsites SpaceGhost904 & Safka.. I'll test if it runs again this afternoon & report back here.Anonymous
November 10, 2013
nope, still experiencing the same error. And I can also not load my web templatesAnonymous
November 10, 2013
Ok, it's now 15:33 here and as of 10 minutes ago I'm not experiencing anymore server errors. That's a week of work wasted while figuring what was going wrong not counting evening and weekend work! Glad the 'problem' fixed itself. I also get the templates now so my code functions as it's supposed to now. Glad I checked back on this blog :) To the author of the article: Great article, really found the material I needed for my project, can't thank you enough :)Anonymous
November 12, 2013
OK, Probably a stupid Newbie thing. I have been searching and attempting to resolve this issue for days now. Cannot debug "Self-Service Site Provisioning App" (Via Start >> Debug) in Visual Studio. The app loads in "/_layouts/15/AppInv.aspx" and I can click "Trust"... After that the app just endless loops "Working on it"... I am getting the error below over and over again: A first chance exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll An exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll but was not handled in user code A first chance exception of type 'Microsoft.IdentityModel.Tokens.AudienceUriValidationFailedException' occurred in SelfServiceProvisioningWeb.dll A first chance exception of type 'Microsoft.IdentityModel.Tokens.AudienceUriValidationFailedException' occurred in SelfServiceProvisioningWeb.dllAnonymous
November 12, 2013
Not sure how to fix that SpaceGhost since I'm only using some of his subsite provisioning code. Have you tried using some breakpoints? And see how far you get? Have you enabled visual studio to break on uncaught exceptions? BTW: I've got the [ServerException: Value does not fall within the expected range.] a few more times today, very randomly..it's back to normal now. I suspect they are making changes trying to fix something for their tenant portal. It's been ongoing for about a week now..Anonymous
November 12, 2013
with 'they' I mean whoever's working BTS @ microsoft. It's bad that such a thing happens on such a platform. My app is going in production in about a month or so. I can only hope this occurence doesn't keep repeating itself.Anonymous
November 13, 2013
The comment has been removedAnonymous
November 17, 2013
Are there any possibilities for uploading a custom solution (wsp) before applying the site template and then use a custom web template?Anonymous
November 18, 2013
@Pieterjan Spoelders Yes I have attempted to set break point. Point is never hit. I will attempt to "enable visual studio to break on uncaught exceptions" next... ThanksAnonymous
November 18, 2013
@Pieterjan Spoelders Yes, I am using VS 2012 and it looks like "break on uncaught exceptions" was enabled already by default. Anyway, had intention of making this App Production ready but tabling that as I cannot even debug it. ThanksAnonymous
November 18, 2013
@Serge ARADJ - you can only provision site collections server-side and in O365. However, you can provision subsites. Don't let "cloud-hosted" freak you out...cloud-hosted apps can be hosted anywhere...including another IIS machine in your data center. Utlimately cloud-hosted = server-side code without running on the SharePoint farm. @Evert - I haven't tried that scenario, but it seems feasible. @SpaceGhost904 - I'm not sure why you are unable to hit a breakpoint. You might remove all the aync processing for now and do everything on the main thread. To do this, you will likely want to increase timeouts for site collections to finish provisioning. -RZAnonymous
November 19, 2013
I can set breakpoints, but, I cannot get past what happens after I click the "Trust" button. In other words the App will not even load during a debug session. So, removing any code would not help as I am not able to even start the provisioning process at all. To debug am I supposed to be able to click start debugging (green arrow or F5) or is there some other configuration / setting I am supposed to do? Please help as I have a deadline and not being able to debug is very time consuming frustrating and a blocking issue. Thanks in advance for ANY guidance you or anyone else can provide... sure I am missing something very very basic. I lowered the permissions for the app to web and fullcontrol and even tried just write thinking my tenant just did not want to grant full tenant control. Am I not waiting long enough for the tenant to "Trust" the app? Still getting" An exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll but was not handled in user code A first chance exception of type 'Microsoft.IdentityModel.Tokens.AudienceUriValidationFailedException' occurred in SelfServiceProvisioningWeb.dll A first chance exception of type 'Microsoft.IdentityModel.Tokens.AudienceUriValidationFailedException' occurred in SelfServiceProvisioningWeb.dll A first chance exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll An exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll but was not handled in user code A first chance exception of type 'Microsoft.IdentityModel.Tokens.AudienceUriValidationFailedException' occurred in SelfServiceProvisioningWeb.dll A first chance exception of type 'Microsoft.IdentityModel.Tokens.AudienceUriValidationFailedException' occurred in SelfServiceProvisioningWeb.dll A first chance exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll An exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll but was not handled in user code A first chance exception of type 'Microsoft.IdentityModel.Tokens.AudienceUriValidationFailedEAnonymous
November 19, 2013
@RicharddiZerega Sorry I made the previous post. Close to being totally confused by my situation. Is it possible that something is wrong with our tenant? I am going to explain to our organization that App Model development is currently immature and we should wait a period of time to let it "mature"... Thanks and CheersAnonymous
November 19, 2013
@Pieterjan Spoelders Good Day, Hey after you start debugging in VS 2012 does the "_layouts/15/AppRedirect.aspx?" page load with the "Trust It" button displayed? Does Default.aspx load after you click the "Trust it" button. I am so focused on understanding this, step by step, if that is what it takes, I am just going to keep hacking away on my own personal time... CheersAnonymous
November 19, 2013
The comment has been removedAnonymous
November 19, 2013
@RicharddiZerega Thanks so much yes that would really help. I will shoot an email. Thanks so much again. I can customize the declarative parts of the app in the mean time. Cheers,Anonymous
November 19, 2013
OK, finally got a breakpoint to be hit by setting it in “SelfServiceProvisioningweb.TokenHelper”. After I “Start Debugging” and then click the “Trust it” button on "/_layouts/15/AppInv.aspx" The code just keeps hitting the if statement below over and over and over again until I “Stop Debugging”. if (!validationSuccessful) { throw new AudienceUriValidationFailedException( String.Format(CultureInfo.CurrentCulture, ""{0}" is not the intended audience "{1}"", String.Join(";", acceptableAudiences), token.Audience)); } return token; I am so new to App Model development this is not telling me much. Hope it means something to anyone here. Thanks so much.Anonymous
November 19, 2013
Me too facing the debug issue problem. Did anybody found the solution?Anonymous
November 26, 2013
I'm quite sure something goes wrong with your validation then. Are you sure you added the realm property to your web.config file like so: <appSettings>...<add key="Realm" value="realmvaluegoeshere"> </appSettings>?Anonymous
November 26, 2013
The comment has been removedAnonymous
November 28, 2013
The comment has been removedAnonymous
December 11, 2013
When i click on create button the modal window just reloads the page again. No site/sub site is created. Even the textbox clear the values. But this works absolutely fine in normal window mode. Any help appreciatedAnonymous
December 11, 2013
Hi all, The images from the template selection section don't render correctly. Anyone an idea how to fix this? ThxAnonymous
December 12, 2013
@Raghu - have you tried debugging...can you hit a breakpoint? I have not seen the issue you described, but it might be IE Security Zone related if you only see it with IE @Sveng - I am almost 100% sure your issue with images is IE Security Zone related. I suspect images will display in Chrome/Firefox. To fix, try adding the remote web url (or https://*o365apps.net) to trusted sites in IE. -richdizzAnonymous
December 12, 2013
The comment has been removedAnonymous
December 12, 2013
The comment has been removedAnonymous
December 12, 2013
The comment has been removedAnonymous
December 12, 2013
The comment has been removedAnonymous
December 13, 2013
That's actually a really good idea Sveng...I might use that myself :) IE Security Zones are kryptonite these days...Anonymous
December 15, 2013
Adding the url https://*.o365apps.net as trusted sites resolved my postback issue as mentioned earlier. But I cannot ask all the users in the organization to follow the same procedure. Is there any alternative method to achieve this.Anonymous
December 16, 2013
Hi, I am getting web.config error. How to resolve it. Thanks.Anonymous
January 06, 2014
Adding the url https://*.o365apps.net as trusted sites resolved my postback issue as mentioned earlier. But I cannot ask all the users in the organization to follow the same procedure. Is there any alternative method to achieve this.Anonymous
January 09, 2014
The comment has been removedAnonymous
January 10, 2014
This example is great. Is there are an API method to enable "external sharing" for site collections ?Anonymous
January 13, 2014
got it, SiteProperties site = tenant.GetSitePropertiesByUrl(url, true); site.SharingCapability = SharingCapabilities.ExternalUserSharingOnly; Requires latest client dlls or the KB2817619 SDK updateAnonymous
January 22, 2014
I have 2 major issues, I cannot figure out how to add users correctly to the sites being created and the images will not show unless i view the library first then go back to the app. Can anyone help with these?Anonymous
January 23, 2014
Hi Brandon, Code to add users private void AddUsersAsAdmin(ClientContext clientContext) { try { var users = hdnAdministratorLogins.Value.Split('!'); for (int i = 0; i < users.Length; i++) { string login = users[i].ToString(); login = login.Replace("!", ""); User user = clientContext.Web.EnsureUser(login); Web web = clientContext.Web; Group ownerGrp = web.AssociatedOwnerGroup; ownerGrp.Users.AddUser(user); clientContext.ExecuteQuery(); } } catch { Microsoft.SharePoint.Client.Utilities.Utility.LogCustomRemoteAppError(ErrorLogContext, new Guid("{6b3b9061-14e7-423f-a747-7a5613348a98}"), "Error in AddUsersAsAdmin"); ErrorLogContext.ExecuteQuery(); throw; } }Anonymous
January 23, 2014
Im getting this error in error console of SAFARI BROWSER(5.1.7) when i open this app through my site link Blocked a frame with origin "https://763a65f1-26af-4fed-81c5-320da421cfc5.o365apps.net" from accessing a frame with origin "https://test-my.sharepoint.com". Protocols, domains, and ports must match. (x3). If i open it in normal mode window first and than through my site, it is working fine. But directly in my site it is not working Please help me. This is deployed in production.Anonymous
January 29, 2014
I am a bit confused how this should work with SharePoint Online as there is no page load event to play with. Am I missing something, or is this only a solution for on-prem deployments?Anonymous
January 30, 2014
I could really use some help with this. I am sure I am doing something silly. I am able to deploy this app to our SPO environment, and I am able to fill out the new site form. After I fill out the page to create a new Blog site (for example), I am able to see the new subsite listed in the site contents. If I try and go to the new subsite I simply just get "Sorry, something went wrong" with no further details and no way to get any additional details. It is so frustrating that something so simple has taken two days of my time so far.Anonymous
January 31, 2014
Anybody experience the subsites being created but not redirected to the new site just back to the form? Thanks!Anonymous
February 04, 2014
Hello, I created this App as an auto-hosted app in an Office365 developer site. I have granted it Full control on the Tenant. However, when I try to create a site collection I still get an access denied when executing the query after Tenant.CreateSite. What am I missing here? Kind Regards, JurgenAnonymous
February 05, 2014
The comment has been removedAnonymous
February 06, 2014
I am able to search users but it keeps saying user search error after i create the site it makes the site, but does not add users to the owners group....can anyone help?Anonymous
February 06, 2014
If anyone is having issues with the icons displaying you can use the friendlier URL and it seems to work fine. One way to get that is go to the settings and you will see the URL at the top. Remove 'SSConfig/Forms/AllItems.aspx' or whatever you called your list from the URL and the remaining string is the link you want to use to access the self service app.Anonymous
February 17, 2014
The comment has been removedAnonymous
February 17, 2014
Terry - I'd highly recommend looking at the template solution Vesa Juvonen has blogged about. Vesa and I are combining some of the IP of our respective solutions and hoping to put out a consolidated solution on codeplex in the future: blogs.msdn.com/.../site-provisioning-techniques-and-remote-provisioning-in-sharepoint-2013.aspx -RZAnonymous
March 12, 2014
The comment has been removedAnonymous
March 18, 2014
The comment has been removedAnonymous
April 15, 2014
@Mohan: SiteProperties site = tenant.GetSitePropertiesByUrl(url, true); site.SharingCapability = SharingCapabilities.ExternalUserSharingOnly; The above code is not working even after installing latest dlls and KB2817619 SDK update. Could you please share the location of dll?Anonymous
April 15, 2014
------------ IMPORTANT NOTE ------------ Site provisioning has been enhanced and can be found at http://officeams.codeplex.com ------------ IMPORTANT NOTE ------------Anonymous
June 11, 2014
The comment has been removedAnonymous
June 26, 2014
The comment has been removedAnonymous
June 26, 2014
Zuas - Office AMS has (or soon will have) a sample of subsite provisioning client-side. Check out for it at http://officeams.codeplex.com/Anonymous
June 29, 2014
The comment has been removedAnonymous
July 07, 2014
The comment has been removedAnonymous
September 04, 2014
In the step Start a site, the site settings does not save my changes to the URL. I Click "Use the form at this URL" paste in the URL of the App, but it does not save it. I don´t get any error.Anonymous
October 26, 2014
I do agree with others asking for : SharePoint Provider Hosted! and bad news : autohosted not support anymore, so all the solution presented there is just deprecated :( msdn.microsoft.com/.../dn722449(v=office.15).aspx So could you do the same with Provider Hosted and still declarative SharePoint artifact ? i m quite sure it s not possible, need to do all with JSOM or CSOMAnonymous
November 10, 2014
Hi and tnx for great App! Is there possible to get this working with SharePoint Hosted as Hosting Type?Anonymous
December 15, 2014
Hi, I am getting access denied error while creating site collection even though I checked the " App Only call option" .Access denied. You do not have permission to perform this action or access this resource. Please let me know on thisAnonymous
January 10, 2015
Any idea on this social.technet.microsoft.com/.../how-to-custom-site-template-in-sharepoint-o365Anonymous
March 11, 2015
The comment has been removedAnonymous
November 05, 2015
The comment has been removedAnonymous
January 21, 2016
Hi, I am not able to download the sample code from one drive.Can you please help me here.