public static TokenParser ProcessThemes(Tenant tenant, ProvisioningTenant provisioningTenant, TokenParser parser, PnPMonitoredScope scope, ProvisioningMessagesDelegate messagesDelegate) { if (provisioningTenant.Themes != null && provisioningTenant.Themes.Any()) { var themes = tenant.GetAllTenantThemes(); tenant.Context.Load(themes); tenant.Context.ExecuteQueryRetry(); foreach (var theme in provisioningTenant.Themes) { var parsedName = parser.ParseString(theme.Name); if (themes.FirstOrDefault(t => t.Name == parsedName) != null) { if (theme.Overwrite) { var parsedPalette = parser.ParseString(theme.Palette); messagesDelegate?.Invoke($"Overwriting existing theme {parsedName}", ProvisioningMessageType.Progress); var palette = JsonConvert.DeserializeObject <Dictionary <string, string> >(parsedPalette); var tenantTheme = new TenantTheme() { Name = parsedName, Palette = palette, IsInverted = theme.IsInverted }; tenant.UpdateTenantTheme(parsedName, JsonConvert.SerializeObject(tenantTheme)); tenant.Context.ExecuteQueryRetry(); } else { messagesDelegate?.Invoke($"Skipped processing theme {parsedName} as it already exists and Overwrite is set to false", ProvisioningMessageType.Progress); } } else { var parsedPalette = parser.ParseString(theme.Palette); messagesDelegate?.Invoke($"Processing theme {parsedName}", ProvisioningMessageType.Progress); var palette = JsonConvert.DeserializeObject <Dictionary <string, string> >(parsedPalette); var tenantTheme = new TenantTheme() { Name = parsedName, Palette = palette, IsInverted = theme.IsInverted }; tenant.AddTenantTheme(parsedName, JsonConvert.SerializeObject(tenantTheme)); tenant.Context.ExecuteQueryRetry(); } } } return(parser); }
internal static TokenParser ProcessSiteScripts(Tenant tenant, ProvisioningTenant provisioningTenant, FileConnectorBase connector, TokenParser parser, PnPMonitoredScope scope, ProvisioningMessagesDelegate messagesDelegate) { if (provisioningTenant.SiteScripts != null && provisioningTenant.SiteScripts.Any()) { var existingScripts = tenant.GetSiteScripts(); tenant.Context.Load(existingScripts); tenant.Context.ExecuteQueryRetry(); foreach (var siteScript in provisioningTenant.SiteScripts) { var parsedTitle = parser.ParseString(siteScript.Title); var parsedDescription = parser.ParseString(siteScript.Description); var parsedContent = parser.ParseString(System.Text.Encoding.UTF8.GetString(ConnectorFileHelper.GetFileBytes(connector, parser.ParseString(siteScript.JsonFilePath)))); var existingScript = existingScripts.FirstOrDefault(s => s.Title == parsedTitle); messagesDelegate?.Invoke($"Processing site script {parsedTitle}", ProvisioningMessageType.Progress); if (existingScript == null) { TenantSiteScriptCreationInfo siteScriptCreationInfo = new TenantSiteScriptCreationInfo { Title = parsedTitle, Description = parsedDescription, Content = parsedContent }; var script = tenant.CreateSiteScript(siteScriptCreationInfo); tenant.Context.Load(script); tenant.Context.ExecuteQueryRetry(); parser.AddToken(new SiteScriptIdToken(null, parsedTitle, script.Id)); } else { if (siteScript.Overwrite) { var existingId = existingScript.Id; existingScript = Tenant.GetSiteScript(tenant.Context, existingId); tenant.Context.ExecuteQueryRetry(); existingScript.Content = parsedContent; existingScript.Title = parsedTitle; existingScript.Description = parsedDescription; tenant.UpdateSiteScript(existingScript); tenant.Context.ExecuteQueryRetry(); var existingToken = parser.Tokens.OfType <SiteScriptIdToken>().FirstOrDefault(t => t.GetReplaceValue() == existingId.ToString()); if (existingToken != null) { parser.Tokens.Remove(existingToken); } parser.AddToken(new SiteScriptIdToken(null, parsedTitle, existingId)); } } } } return(parser); }
public static TokenParser ProcessWebApiPermissions(Tenant tenant, ProvisioningTenant provisioningTenant, TokenParser parser, PnPMonitoredScope scope, ProvisioningMessagesDelegate messagesDelegate) { if (provisioningTenant.WebApiPermissions != null && provisioningTenant.WebApiPermissions.Any()) { messagesDelegate?.Invoke("Processing WebApiPermissions", ProvisioningMessageType.Progress); var servicePrincipal = new SPOWebAppServicePrincipal(tenant.Context); //var requests = servicePrincipal.PermissionRequests; var requestsEnumerable = tenant.Context.LoadQuery(servicePrincipal.PermissionRequests); var grantsEnumerable = tenant.Context.LoadQuery(servicePrincipal.PermissionGrants); tenant.Context.ExecuteQueryRetry(); var requests = requestsEnumerable.ToList(); foreach (var permission in provisioningTenant.WebApiPermissions) { var parsedScope = parser.ParseString(permission.Scope); var parsedResource = parser.ParseString(permission.Resource); var request = requests.FirstOrDefault(r => r.Scope.Equals(parsedScope, StringComparison.InvariantCultureIgnoreCase) && r.Resource.Equals(parsedResource, StringComparison.InvariantCultureIgnoreCase)); while (request != null) { if (grantsEnumerable.FirstOrDefault(g => g.Resource.Equals(parsedResource, StringComparison.InvariantCultureIgnoreCase) && g.Scope.ToLower().Contains(parsedScope.ToLower())) == null) { var requestToApprove = servicePrincipal.PermissionRequests.GetById(request.Id); tenant.Context.Load(requestToApprove); tenant.Context.ExecuteQueryRetry(); try { requestToApprove.Approve(); tenant.Context.ExecuteQueryRetry(); } catch (Exception ex) { messagesDelegate?.Invoke(ex.Message, ProvisioningMessageType.Warning); } } requests.Remove(request); request = requests.FirstOrDefault(r => r.Scope.Equals(parsedScope, StringComparison.InvariantCultureIgnoreCase) && r.Resource.Equals(parsedResource, StringComparison.InvariantCultureIgnoreCase)); } } } return(parser); }
public static TokenParser ProcessThemes(Tenant tenant, ProvisioningTenant provisioningTenant, TokenParser parser, PnPMonitoredScope scope, ProvisioningMessagesDelegate messagesDelegate) { if (provisioningTenant.Themes != null && provisioningTenant.Themes.Any()) { foreach (var theme in provisioningTenant.Themes) { var parsedName = parser.ParseString(theme.Name); var parsedPalette = parser.ParseString(theme.Palette); messagesDelegate?.Invoke($"Processing theme {parsedName}", ProvisioningMessageType.Progress); var palette = JsonConvert.DeserializeObject <Dictionary <string, string> >(parsedPalette); var tenantTheme = new TenantTheme() { Name = parsedName, Palette = palette, IsInverted = theme.IsInverted }; tenant.UpdateTenantTheme(parsedName, JsonConvert.SerializeObject(tenantTheme)); tenant.Context.ExecuteQueryRetry(); } } return(parser); }
public static TokenParser ProcessUserProfiles(Tenant tenant, ProvisioningTenant provisioningTenant, TokenParser parser, PnPMonitoredScope scope, ProvisioningMessagesDelegate messagesDelegate) { if (provisioningTenant.SPUsersProfiles != null && provisioningTenant.SPUsersProfiles.Any()) { messagesDelegate?.Invoke("Processing User Profiles", ProvisioningMessageType.Progress); foreach (var profile in provisioningTenant.SPUsersProfiles) { string parsedUser; if (!string.IsNullOrEmpty(profile.TargetUser)) { parsedUser = parser.ParseString(profile.TargetUser); } else { parsedUser = parser.ParseString(profile.TargetGroup); } PeopleManager peopleManager = new PeopleManager(tenant.Context); try { // Currently only supports setting Single Valued property // We don't have a way at the moment to set Multi-valued property foreach (var props in profile.Properties) { peopleManager.SetSingleValueProfileProperty($"i:0#.f|membership|{parsedUser}", props.Key, parser.ParseString(props.Value)); } tenant.Context.ExecuteQueryRetry(); } catch (Exception ex) { scope.LogError($"Error processing user profile for {parsedUser}. Skipped due to error: ${ex.Message}"); } } } return(parser); }
public static TokenParser ProcessApps(Tenant tenant, ProvisioningTenant provisioningTenant, FileConnectorBase connector, TokenParser parser, PnPMonitoredScope scope, ProvisioningTemplateApplyingInformation applyingInformation, ProvisioningMessagesDelegate messagesDelegate) { if (provisioningTenant.AppCatalog != null && provisioningTenant.AppCatalog.Packages.Count > 0) { var rootSiteUrl = tenant.GetRootSiteUrl(); tenant.Context.ExecuteQueryRetry(); using (var context = ((ClientContext)tenant.Context).Clone(rootSiteUrl.Value, applyingInformation.AccessTokens)) { var web = context.Web; Uri appCatalogUri = null; try { appCatalogUri = web.GetAppCatalog(); } catch (System.Net.WebException ex) { if (ex.Response != null) { var httpResponse = ex.Response as System.Net.HttpWebResponse; if (httpResponse != null && httpResponse.StatusCode == HttpStatusCode.Unauthorized) { // Ignore any security exception and simply keep // the AppCatalog URI null } else { throw ex; } } else { throw ex; } } if (appCatalogUri != null) { var manager = new AppManager(context); foreach (var app in provisioningTenant.AppCatalog.Packages) { AppMetadata appMetadata = null; if (app.Action == PackageAction.Upload || app.Action == PackageAction.UploadAndPublish) { var appSrc = parser.ParseString(app.Src); var appBytes = ConnectorFileHelper.GetFileBytes(connector, appSrc); var hash = string.Empty; using (var memoryStream = new MemoryStream(appBytes)) { hash = CalculateHash(memoryStream); } var exists = false; var appId = Guid.Empty; using (var appCatalogContext = ((ClientContext)tenant.Context).Clone(appCatalogUri, applyingInformation.AccessTokens)) { // check if the app already is present var appList = appCatalogContext.Web.GetListByUrl("AppCatalog"); var camlQuery = new CamlQuery { ViewXml = string.Format(appExistsQuery, hash) }; var items = appList.GetItems(camlQuery); appCatalogContext.Load(items, i => i.IncludeWithDefaultProperties()); appCatalogContext.ExecuteQueryRetry(); if (items.Count > 0) { exists = true; appId = Guid.Parse(items[0].FieldValues["UniqueId"].ToString()); } } var appFilename = appSrc.Substring(appSrc.LastIndexOf('\\') + 1); if (!exists) { messagesDelegate?.Invoke($"Processing solution {app.Src}", ProvisioningMessageType.Progress); appMetadata = manager.Add(appBytes, appFilename, app.Overwrite, timeoutSeconds: 500); } else { messagesDelegate?.Invoke($"Skipping existing solution {app.Src}", ProvisioningMessageType.Progress); appMetadata = manager.GetAvailable().FirstOrDefault(a => a.Id == appId); } if (appMetadata != null) { parser.AddToken(new AppPackageIdToken(web, appFilename, appMetadata.Id)); parser.AddToken(new AppPackageIdToken(web, appMetadata.Title, appMetadata.Id)); } } if (app.Action == PackageAction.Publish || app.Action == PackageAction.UploadAndPublish) { if (appMetadata == null) { appMetadata = manager.GetAvailable() .FirstOrDefault(a => a.Id == Guid.Parse(parser.ParseString(app.PackageId))); } if (appMetadata != null) { manager.Deploy(appMetadata, app.SkipFeatureDeployment); } else { scope.LogError("Referenced App Package {0} not available", app.PackageId); throw new Exception($"Referenced App Package {app.PackageId} not available"); } } if (app.Action == PackageAction.Remove) { var appId = Guid.Parse(parser.ParseString(app.PackageId)); // Get the apps already installed in the site var appExists = manager.GetAvailable()?.Any(a => a.Id == appId); if (appExists.HasValue && appExists.Value) { manager.Remove(appId); } else { messagesDelegate?.Invoke($"App Package with ID {appId} does not exist in the AppCatalog and cannot be removed!", ProvisioningMessageType.Warning); } } } } else { messagesDelegate?.Invoke($"Tenant app catalog doesn't exist. ALM step will be skipped!", ProvisioningMessageType.Warning); } } } return(parser); }
public static TokenParser ProcessSiteDesigns(Tenant tenant, ProvisioningTenant provisioningTenant, TokenParser parser, PnPMonitoredScope scope, ProvisioningMessagesDelegate messagesDelegate) { if (provisioningTenant.SiteDesigns != null && provisioningTenant.SiteDesigns.Any()) { var existingDesigns = tenant.GetSiteDesigns(); tenant.Context.Load(existingDesigns); tenant.Context.ExecuteQueryRetry(); foreach (var siteDesign in provisioningTenant.SiteDesigns) { var parsedTitle = parser.ParseString(siteDesign.Title); var parsedDescription = parser.ParseString(siteDesign.Description); var parsedPreviewImageUrl = parser.ParseString(siteDesign.PreviewImageUrl); var parsedPreviewImageAltText = parser.ParseString(siteDesign.PreviewImageAltText); messagesDelegate?.Invoke($"Processing site design {parsedTitle}", ProvisioningMessageType.Progress); var existingSiteDesign = existingDesigns.FirstOrDefault(d => d.Title == parsedTitle); if (existingSiteDesign == null) { TenantSiteDesignCreationInfo siteDesignCreationInfo = new TenantSiteDesignCreationInfo() { Title = parsedTitle, Description = parsedDescription, PreviewImageUrl = parsedPreviewImageUrl, PreviewImageAltText = parsedPreviewImageAltText, IsDefault = siteDesign.IsDefault, }; switch ((int)siteDesign.WebTemplate) { case 0: { siteDesignCreationInfo.WebTemplate = "64"; break; } case 1: { siteDesignCreationInfo.WebTemplate = "68"; break; } } if (siteDesign.SiteScripts != null && siteDesign.SiteScripts.Any()) { List <Guid> ids = new List <Guid>(); foreach (var siteScriptRef in siteDesign.SiteScripts) { ids.Add(Guid.Parse(parser.ParseString(siteScriptRef))); } siteDesignCreationInfo.SiteScriptIds = ids.ToArray(); } var design = tenant.CreateSiteDesign(siteDesignCreationInfo); tenant.Context.Load(design); tenant.Context.ExecuteQueryRetry(); if (siteDesign.Grants != null && siteDesign.Grants.Any()) { foreach (var grant in siteDesign.Grants) { var rights = (TenantSiteDesignPrincipalRights)Enum.Parse(typeof(TenantSiteDesignPrincipalRights), grant.Right.ToString()); tenant.GrantSiteDesignRights(design.Id, new[] { grant.Principal }, rights); } tenant.Context.ExecuteQueryRetry(); } parser.AddToken(new SiteDesignIdToken(null, design.Title, design.Id)); } else { if (siteDesign.Overwrite) { var existingId = existingSiteDesign.Id; existingSiteDesign = Tenant.GetSiteDesign(tenant.Context, existingId); tenant.Context.ExecuteQueryRetry(); existingSiteDesign.Title = parsedTitle; existingSiteDesign.Description = parsedDescription; existingSiteDesign.PreviewImageUrl = parsedPreviewImageUrl; existingSiteDesign.PreviewImageAltText = parsedPreviewImageAltText; existingSiteDesign.IsDefault = siteDesign.IsDefault; switch ((int)siteDesign.WebTemplate) { case 0: { existingSiteDesign.WebTemplate = "64"; break; } case 1: { existingSiteDesign.WebTemplate = "68"; break; } } tenant.UpdateSiteDesign(existingSiteDesign); tenant.Context.ExecuteQueryRetry(); var existingToken = parser.Tokens.OfType <SiteDesignIdToken>().FirstOrDefault(t => t.GetReplaceValue() == existingId.ToString()); if (existingToken != null) { parser.Tokens.Remove(existingToken); } parser.AddToken(new SiteScriptIdToken(null, parsedTitle, existingId)); if (siteDesign.Grants != null && siteDesign.Grants.Any()) { var existingRights = Tenant.GetSiteDesignRights(tenant.Context, existingId); tenant.Context.Load(existingRights); tenant.Context.ExecuteQueryRetry(); foreach (var existingRight in existingRights) { Tenant.RevokeSiteDesignRights(tenant.Context, existingId, new[] { existingRight.PrincipalName }); } foreach (var grant in siteDesign.Grants) { var rights = (TenantSiteDesignPrincipalRights)Enum.Parse(typeof(TenantSiteDesignPrincipalRights), grant.Right.ToString()); tenant.GrantSiteDesignRights(existingId, new[] { parser.ParseString(grant.Principal) }, rights); } tenant.Context.ExecuteQueryRetry(); } } } } } return(parser); }
internal static TokenParser ProcessStorageEntities(Tenant tenant, ProvisioningTenant provisioningTenant, TokenParser parser, PnPMonitoredScope scope, ProvisioningTemplateApplyingInformation applyingInformation, ProvisioningMessagesDelegate messagesDelegate) { if (provisioningTenant.StorageEntities != null && provisioningTenant.StorageEntities.Any()) { var rootSiteUrl = tenant.GetRootSiteUrl(); tenant.Context.ExecuteQueryRetry(); using (var context = ((ClientContext)tenant.Context).Clone(rootSiteUrl.Value, applyingInformation.AccessTokens)) { var web = context.Web; Uri appCatalogUri = null; try { appCatalogUri = web.GetAppCatalog(); } catch (System.Net.WebException ex) { if (ex.Response != null) { var httpResponse = ex.Response as System.Net.HttpWebResponse; if (httpResponse != null && httpResponse.StatusCode == HttpStatusCode.Unauthorized) { // Ignore any security exception and simply keep // the AppCatalog URI null } else { throw ex; } } else { throw ex; } } if (appCatalogUri != null) { using (var appCatalogContext = context.Clone(appCatalogUri, applyingInformation.AccessTokens)) { foreach (var entity in provisioningTenant.StorageEntities) { var key = parser.ParseString(entity.Key); var value = parser.ParseString(entity.Value); var description = parser.ParseString(entity.Description); var comment = parser.ParseString(entity.Comment); appCatalogContext.Web.SetStorageEntity(key, value, description, comment); } appCatalogContext.Web.Update(); appCatalogContext.ExecuteQueryRetry(); } } else { messagesDelegate?.Invoke($"Tenant app catalog doesn't exist. Provisioning of storage entities will be skipped!", ProvisioningMessageType.Warning); } } } return(parser); }
public static TokenParser ProcessO365GroupSettings(Tenant tenant, ProvisioningTenant provisioningTenant, TokenParser parser, PnPMonitoredScope scope, ProvisioningMessagesDelegate messagesDelegate) { if (provisioningTenant.Office365GroupsSettings != null && provisioningTenant.Office365GroupsSettings.Properties.Any()) { messagesDelegate?.Invoke("Processing Office 365 Group Settings", ProvisioningMessageType.Progress); bool siteClassificationSettingsExists = false; if (PnPProvisioningContext.Current != null) { string accessToken = string.Empty; try { // Get a fresh Access Token for every request accessToken = PnPProvisioningContext.Current.AcquireToken(GraphHelper.MicrosoftGraphBaseURI, "Directory.ReadWrite.All"); if (accessToken != null) { try { var siteClassificationSettings = tenant.GetSiteClassificationsSettings(accessToken); siteClassificationSettingsExists = true; } catch (Exception ex) { // Tenant classification doesn't exist, just swallow the exception. } if (siteClassificationSettingsExists) { // Tenant classification exists, update the necessary values for Group Settings. try { string directorySettingTemplatesUrl = $"{GraphHttpClient.MicrosoftGraphV1BaseUri}groupSettings"; var directorySettingTemplatesJson = GraphHttpClient.MakeGetRequestForString(directorySettingTemplatesUrl, accessToken); var directorySettingTemplates = JsonConvert.DeserializeObject <DirectorySettingTemplates>(directorySettingTemplatesJson); // Retrieve the setinngs for "Group.Unified" var unifiedGroupSetting = directorySettingTemplates.Templates.FirstOrDefault(t => t.DisplayName == "Group.Unified"); if (unifiedGroupSetting != null) { var props = provisioningTenant.Office365GroupsSettings.Properties; foreach (var v in unifiedGroupSetting.SettingValues) { var item = props.Where(p => p.Key == v.Name).FirstOrDefault(); if (!string.IsNullOrEmpty(item.Key)) { v.Value = parser.ParseString(item.Value); } } string updateDirectorySettingUrl = $"{GraphHttpClient.MicrosoftGraphV1BaseUri}groupSettings/{unifiedGroupSetting.Id}"; var updateDirectorySettingResult = GraphHttpClient.MakePatchRequestForString( updateDirectorySettingUrl, content: new { templateId = unifiedGroupSetting.Id, values = from v in unifiedGroupSetting.SettingValues select new { name = v.Name, value = v.Value }, }, contentType: "application/json", accessToken: accessToken); } else { throw new ApplicationException("Missing DirectorySettingTemplate for \"Group.Unified\""); } } catch (Exception ex) { scope.LogError($"Error occurred processing O365 Group settings ${ex.Message}"); } } else { // Tenant classification doesn't exist, create the necessary template for Group Settings. try { string directorySettingTemplatesUrl = $"{GraphHttpClient.MicrosoftGraphV1BaseUri}groupSettingTemplates"; var directorySettingTemplatesJson = GraphHttpClient.MakeGetRequestForString(directorySettingTemplatesUrl, accessToken); var directorySettingTemplates = JsonConvert.DeserializeObject <DirectorySettingTemplates>(directorySettingTemplatesJson); // Retrieve the setinngs for "Group.Unified" var unifiedGroupSetting = directorySettingTemplates.Templates.FirstOrDefault(t => t.DisplayName == "Group.Unified"); if (unifiedGroupSetting != null) { var props = provisioningTenant.Office365GroupsSettings.Properties; foreach (var v in unifiedGroupSetting.SettingValues) { var item = props.Where(p => p.Key == v.Name).FirstOrDefault(); if (!string.IsNullOrEmpty(item.Key)) { v.Value = parser.ParseString(item.Value); } else { // Set default value because null is not supported // It only accepts entire collection and not individual properties v.Value = v.DefaultValue; } } string updateDirectorySettingUrl = $"{GraphHttpClient.MicrosoftGraphV1BaseUri}groupSettings"; var updateDirectorySettingResult = GraphHttpClient.MakePostRequestForString( updateDirectorySettingUrl, content: new { templateId = unifiedGroupSetting.Id, values = from v in unifiedGroupSetting.SettingValues select new { name = v.Name, value = v.Value }, }, contentType: "application/json", accessToken: accessToken); } else { throw new ApplicationException("Missing DirectorySettingTemplate for \"Group.Unified\""); } } catch (Exception ex) { scope.LogError($"Error occurred processing O365 Group settings ${ex.Message}"); } } } } catch (Exception ex) { scope.LogError($"Error occurred processing O365 Group settings ${ex.Message}"); } } } return(parser); }
/// <summary> /// Actual implementation of the apply templates /// </summary> /// <param name="web"></param> /// <param name="template"></param> /// <param name="provisioningInfo"></param> /// <param name="calledFromHierarchy"></param> /// <param name="tokenParser"></param> internal void ApplyRemoteTemplate(Web web, ProvisioningTemplate template, ProvisioningTemplateApplyingInformation provisioningInfo, bool calledFromHierarchy = false, TokenParser tokenParser = null) { using (var scope = new PnPMonitoredScope(CoreResources.Provisioning_ObjectHandlers_Provisioning)) { #if !ONPREMISES || SP2016 || SP2019 web.Context.DisableReturnValueCache = true; #endif ProvisioningProgressDelegate progressDelegate = null; ProvisioningMessagesDelegate messagesDelegate = null; ProvisioningSiteProvisionedDelegate siteProvisionedDelegate = null; if (provisioningInfo != null) { if (provisioningInfo.OverwriteSystemPropertyBagValues == true) { scope.LogInfo(CoreResources.SiteToTemplateConversion_ApplyRemoteTemplate_OverwriteSystemPropertyBagValues_is_to_true); } progressDelegate = provisioningInfo.ProgressDelegate; if (provisioningInfo.ProgressDelegate != null) { scope.LogInfo(CoreResources.SiteToTemplateConversion_ProgressDelegate_registered); } messagesDelegate = provisioningInfo.MessagesDelegate; if (provisioningInfo.MessagesDelegate != null) { scope.LogInfo(CoreResources.SiteToTemplateConversion_MessagesDelegate_registered); } siteProvisionedDelegate = provisioningInfo.SiteProvisionedDelegate; } else { // When no provisioning info was passed then we want to execute all handlers provisioningInfo = new ProvisioningTemplateApplyingInformation(); provisioningInfo.HandlersToProcess = Handlers.All; } // Check if scope is present and if so, matches the current site. When scope was not set the returned value will be ProvisioningTemplateScope.Undefined if (template.Scope == ProvisioningTemplateScope.RootSite) { if (web.IsSubSite()) { scope.LogError(CoreResources.SiteToTemplateConversion_ScopeOfTemplateDoesNotMatchTarget); throw new Exception(CoreResources.SiteToTemplateConversion_ScopeOfTemplateDoesNotMatchTarget); } } var currentCultureInfoValue = System.Threading.Thread.CurrentThread.CurrentCulture.LCID; if (!string.IsNullOrEmpty(template.TemplateCultureInfo)) { int cultureInfoValue = System.Threading.Thread.CurrentThread.CurrentCulture.LCID; if (int.TryParse(template.TemplateCultureInfo, out cultureInfoValue)) { System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(cultureInfoValue); } else { System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(template.TemplateCultureInfo); } } // Check if the target site shares the same base template with the template's source site var targetSiteTemplateId = web.GetBaseTemplateId(); if (!String.IsNullOrEmpty(targetSiteTemplateId) && !String.IsNullOrEmpty(template.BaseSiteTemplate)) { if (!targetSiteTemplateId.Equals(template.BaseSiteTemplate, StringComparison.InvariantCultureIgnoreCase)) { var templatesNotMatchingWarning = String.Format(CoreResources.Provisioning_Asymmetric_Base_Templates, template.BaseSiteTemplate, targetSiteTemplateId); scope.LogWarning(templatesNotMatchingWarning); messagesDelegate?.Invoke(templatesNotMatchingWarning, ProvisioningMessageType.Warning); } } // Always ensure the Url property is loaded. In the tokens we need this and we don't want to call ExecuteQuery as this can // impact delta scenarions (calling ExecuteQuery before the planned update is called) web.EnsureProperty(w => w.Url); List <ObjectHandlerBase> objectHandlers = new List <ObjectHandlerBase>(); if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.RegionalSettings)) { objectHandlers.Add(new ObjectRegionalSettings()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.SupportedUILanguages)) { objectHandlers.Add(new ObjectSupportedUILanguages()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.AuditSettings)) { objectHandlers.Add(new ObjectAuditSettings()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.SitePolicy)) { objectHandlers.Add(new ObjectSitePolicy()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.SiteSecurity)) { objectHandlers.Add(new ObjectSiteSecurity()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Features)) { objectHandlers.Add(new ObjectFeatures()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.TermGroups)) { objectHandlers.Add(new ObjectTermGroups()); } // Process 3 times these providers to handle proper ordering of artefact creation when dealing with lookup fields // 1st. create fields, content and list without lookup fields if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Fields) || provisioningInfo.HandlersToProcess.HasFlag(Handlers.Lists)) { objectHandlers.Add(new ObjectField(FieldAndListProvisioningStepHelper.Step.ListAndStandardFields)); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.ContentTypes)) { objectHandlers.Add(new ObjectContentType(FieldAndListProvisioningStepHelper.Step.ListAndStandardFields)); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Lists)) { objectHandlers.Add(new ObjectListInstance(FieldAndListProvisioningStepHelper.Step.ListAndStandardFields)); } // 2nd. create lookup fields (which requires lists to be present if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Fields) || provisioningInfo.HandlersToProcess.HasFlag(Handlers.Lists)) { objectHandlers.Add(new ObjectField(FieldAndListProvisioningStepHelper.Step.LookupFields)); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.ContentTypes)) { objectHandlers.Add(new ObjectContentType(FieldAndListProvisioningStepHelper.Step.LookupFields)); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Lists)) { objectHandlers.Add(new ObjectListInstance(FieldAndListProvisioningStepHelper.Step.LookupFields)); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Files)) { objectHandlers.Add(new ObjectFiles()); } // 3rd. Create remaining objects in lists (views, user custom actions, ...) if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Lists)) { objectHandlers.Add(new ObjectListInstance(FieldAndListProvisioningStepHelper.Step.ListSettings)); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Fields) || provisioningInfo.HandlersToProcess.HasFlag(Handlers.Lists)) { objectHandlers.Add(new ObjectListInstanceDataRows()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Workflows)) { objectHandlers.Add(new ObjectWorkflows()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Pages)) { objectHandlers.Add(new ObjectPages()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.PageContents)) { objectHandlers.Add(new ObjectPageContents()); } #if !ONPREMISES if (!calledFromHierarchy && provisioningInfo.HandlersToProcess.HasFlag(Handlers.Tenant)) { objectHandlers.Add(new ObjectTenant()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.ApplicationLifecycleManagement)) { objectHandlers.Add(new ObjectApplicationLifecycleManagement()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Pages)) { objectHandlers.Add(new ObjectClientSidePages()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.SiteHeader)) { objectHandlers.Add(new ObjectSiteHeaderSettings()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.SiteFooter)) { objectHandlers.Add(new ObjectSiteFooterSettings()); } #endif if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.CustomActions)) { objectHandlers.Add(new ObjectCustomActions()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Publishing)) { objectHandlers.Add(new ObjectPublishing()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.ComposedLook)) { objectHandlers.Add(new ObjectComposedLook()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.SearchSettings)) { objectHandlers.Add(new ObjectSearchSettings()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.PropertyBagEntries)) { objectHandlers.Add(new ObjectPropertyBagEntry()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.WebSettings)) { objectHandlers.Add(new ObjectWebSettings()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Theme)) { objectHandlers.Add(new ObjectTheme()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Navigation)) { objectHandlers.Add(new ObjectNavigation()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.ImageRenditions)) { objectHandlers.Add(new ObjectImageRenditions()); } objectHandlers.Add(new ObjectLocalization()); // Always add this one, check is done in the handler if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.ExtensibilityProviders)) { objectHandlers.Add(new ObjectExtensibilityHandlers()); } // Only persist template information in case this flag is set: this will allow the engine to // work with lesser permissions if (provisioningInfo.PersistTemplateInfo) { objectHandlers.Add(new ObjectPersistTemplateInfo()); } var count = objectHandlers.Count(o => o.ReportProgress && o.WillProvision(web, template, provisioningInfo)) + 1; progressDelegate?.Invoke("Initializing engine", 1, count); // handlers + initializing message) if (tokenParser == null) { tokenParser = new TokenParser(web, template); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.ExtensibilityProviders)) { var extensibilityHandler = objectHandlers.OfType <ObjectExtensibilityHandlers>().First(); extensibilityHandler.AddExtendedTokens(web, template, tokenParser, provisioningInfo); } int step = 2; // Remove potentially unsupported artifacts var cleaner = new NoScriptTemplateCleaner(web); if (messagesDelegate != null) { cleaner.MessagesDelegate = messagesDelegate; } template = cleaner.CleanUpBeforeProvisioning(template); CallWebHooks(template, tokenParser, ProvisioningTemplateWebhookKind.ProvisioningStarted); foreach (var handler in objectHandlers) { if (handler.WillProvision(web, template, provisioningInfo)) { if (messagesDelegate != null) { handler.MessagesDelegate = messagesDelegate; } if (handler.ReportProgress && progressDelegate != null) { progressDelegate(handler.Name, step, count); step++; } CallWebHooks(template, tokenParser, ProvisioningTemplateWebhookKind.ObjectHandlerProvisioningStarted, handler); tokenParser = handler.ProvisionObjects(web, template, tokenParser, provisioningInfo); CallWebHooks(template, tokenParser, ProvisioningTemplateWebhookKind.ObjectHandlerProvisioningCompleted, handler); } } // Notify the completed provisioning of the site web.EnsureProperties(w => w.Title, w => w.Url); siteProvisionedDelegate?.Invoke(web.Title, web.Url); CallWebHooks(template, tokenParser, ProvisioningTemplateWebhookKind.ProvisioningCompleted); System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(currentCultureInfoValue); } }