private void ExtractLibraries(ProvisioningTemplate template, ProvisioningTemplateCreationInformation creationInformation, PnPMonitoredScope scope) { scope.LogInfo("ContentProvisioniningExtensibilityHandler.ExtractLibraries"); var libraries = template.Lists.Where(p => SupportedLibraryTemplateTypes.Contains(p.TemplateType)); foreach (var library in libraries) { scope.LogInfo(String.Format("ContentProvisioniningExtensibilityHandler.ExtractLibraries {0}", library.Url)); var splibrary = web.GetListByUrl(library.Url); var libraryFolder = splibrary.RootFolder; splibrary.EnsureProperty(p => p.RootFolder); var sourceFiles = GetFiles(libraryFolder); clientContext.ExecuteQueryRetry(); var formsPath = String.Format("{0}/Forms", libraryFolder.ServerRelativeUrl); var mPath = String.Format("{0}/_m/", libraryFolder.ServerRelativeUrl); var wPath = String.Format("{0}/_w/", libraryFolder.ServerRelativeUrl); var tPath = String.Format("{0}/_t/", libraryFolder.ServerRelativeUrl); sourceFiles = sourceFiles.Where(p => !p.ServerRelativeUrl.StartsWith(formsPath) && !p.ServerRelativeUrl.StartsWith(mPath) && !p.ServerRelativeUrl.StartsWith(wPath) && !p.ServerRelativeUrl.StartsWith(tPath) ); var featureTemplate = ProvisioningTemplate.GetPublishingFeatureBaseTemplate(); foreach (var sourceFile in sourceFiles) { if (!featureTemplate.IsFeatureNativeFile(sourceFile.Name)) { ExtractFile(sourceFile, template, creationInformation, scope); } } } }
// This function will be triggered based on the schedule you have set for this WebJob public static void VerifyDocLib() { var url = ConfigurationManager.AppSettings["SpUrl"].ToString(); var user = ConfigurationManager.AppSettings["SpUserName"].ToString(); var pw = ConfigurationManager.AppSettings["SpPassword"].ToString(); using (PnPMonitoredScope p = new PnPMonitoredScope()) { p.LogInfo("Gathering connect info from web.config"); p.LogInfo("SharePoint url is " + url); p.LogInfo("SharePoint user is " + user); try { ClientContext cc = new ClientContext(url); cc.AuthenticationMode = ClientAuthenticationMode.Default; cc.Credentials = new SharePointOnlineCredentials(user, GetPassword(pw)); using (cc) { if (cc != null) { string listName = "Site Pages"; p.LogInfo("This is what a Monitored Scope log entry looks like in PNP, more to come as we try to validate a lists existence."); p.LogInfo("Connect to Sharepoint"); ListCollection lists = cc.Web.Lists; IEnumerable <List> results = cc.LoadQuery <List>(lists.Where(lst => lst.Title == listName)); p.LogInfo("Executing query to find the Site Pages library"); cc.ExecuteQuery(); p.LogInfo("Query Executed successfully."); List list = results.FirstOrDefault(); if (list == null) { p.LogError("Site Pages library not found."); } else { p.LogInfo("Site Pages library found."); ListItemCollection items = list.GetItems(new CamlQuery()); cc.Load(items, itms => itms.Include(item => item.DisplayName)); cc.ExecuteQueryRetry(); string pgs = "These pages were found, " + string.Join(", ", items.Select(x => x.DisplayName)); p.LogInfo(pgs); } } } } catch (System.Exception ex) { p.LogInfo("We had a problem: " + ex.Message); } } }
// This function will be triggered based on the schedule you have set for this WebJob public static void VerifyDocLib() { var url = ConfigurationManager.AppSettings["SpUrl"].ToString(); var user = ConfigurationManager.AppSettings["SpUserName"].ToString(); var pw = ConfigurationManager.AppSettings["SpPassword"].ToString(); using (PnPMonitoredScope p = new PnPMonitoredScope()) { p.LogInfo("Gathering connect info from web.config"); p.LogInfo("SharePoint url is " + url); p.LogInfo("SharePoint user is " + user); try { ClientContext cc = new ClientContext(url); cc.AuthenticationMode = ClientAuthenticationMode.Default; cc.Credentials = new SharePointOnlineCredentials(user, GetPassword(pw)); using (cc) { if (cc != null) { string listName = "Site Pages"; p.LogInfo("This is what a Monitored Scope log entry looks like in PNP, more to come as we try to validate a lists existence."); p.LogInfo("Connect to Sharepoint"); ListCollection lists = cc.Web.Lists; IEnumerable<List> results = cc.LoadQuery<List>(lists.Where(lst => lst.Title == listName)); p.LogInfo("Executing query to find the Site Pages library"); cc.ExecuteQuery(); p.LogInfo("Query Executed successfully."); List list = results.FirstOrDefault(); if (list == null) { p.LogError("Site Pages library not found."); } else { p.LogInfo("Site Pages library found."); ListItemCollection items = list.GetItems(new CamlQuery()); cc.Load(items, itms => itms.Include(item => item.DisplayName)); cc.ExecuteQueryRetry(); string pgs = "These pages were found, " + string.Join(", ", items.Select(x => x.DisplayName)); p.LogInfo(pgs); } } } } catch (System.Exception ex) { p.LogInfo("We had a problem: " + ex.Message); } } }
/// <summary> /// Retrieves a file as a byte array from the connector. If the file name contains special characters (e.g. "%20") and cannot be retrieved, a workaround will be performed /// </summary> internal static void ProcessCdns(Tenant tenant, ProvisioningTenant provisioningTenant, TokenParser parser, PnPMonitoredScope scope, ProvisioningMessagesDelegate messagesDelegate) { if (provisioningTenant.ContentDeliveryNetwork != null) { if (provisioningTenant.ContentDeliveryNetwork.PublicCdn != null || provisioningTenant.ContentDeliveryNetwork.PrivateCdn != null) { var publicCdnEnabled = tenant.GetTenantCdnEnabled(SPOTenantCdnType.Public); var privateCdnEnabled = tenant.GetTenantCdnEnabled(SPOTenantCdnType.Private); tenant.Context.ExecuteQueryRetry(); var publicCdn = provisioningTenant.ContentDeliveryNetwork.PublicCdn; if (publicCdn != null) { if (publicCdnEnabled.Value != publicCdn.Enabled) { scope.LogInfo($"Public CDN is set to {(publicCdn.Enabled ? "Enabled" : "Disabled")}"); tenant.SetTenantCdnEnabled(SPOTenantCdnType.Public, publicCdn.Enabled); tenant.Context.ExecuteQueryRetry(); } if (publicCdn.Enabled) { if (!publicCdn.NoDefaultOrigins) { tenant.CreateTenantCdnDefaultOrigins(SPOTenantCdnType.Public); tenant.Context.ExecuteQueryRetry(); } ProcessOrigins(tenant, publicCdn, SPOTenantCdnType.Public, parser, scope); ProcessPolicies(tenant, publicCdn, SPOTenantCdnType.Public, parser, scope); } } var privateCdn = provisioningTenant.ContentDeliveryNetwork.PrivateCdn; if (privateCdn != null) { if (privateCdnEnabled.Value != privateCdn.Enabled) { scope.LogInfo($"Private CDN is set to {(privateCdn.Enabled ? "Enabled" : "Disabled")}"); tenant.SetTenantCdnEnabled(SPOTenantCdnType.Private, privateCdn.Enabled); tenant.Context.ExecuteQueryRetry(); } if (privateCdn.Enabled) { if (!privateCdn.NoDefaultOrigins) { tenant.CreateTenantCdnDefaultOrigins(SPOTenantCdnType.Private); tenant.Context.ExecuteQueryRetry(); } ProcessOrigins(tenant, privateCdn, SPOTenantCdnType.Private, parser, scope); ProcessPolicies(tenant, privateCdn, SPOTenantCdnType.Private, parser, scope); } } } } }
private static void ProcessCdns(Web web, ProvisioningTenant provisioningTenant, TokenParser parser, PnPMonitoredScope scope) { if (provisioningTenant.ContentDeliveryNetwork != null) { var webUrl = web.Url; var uri = new Uri(webUrl); if (!uri.Host.Contains("-admin.")) { var splittedHost = uri.Host.Split(new[] { '.' }); webUrl = $"{uri.Scheme}://{splittedHost[0]}-admin.{string.Join(".", splittedHost.Skip(1))}"; } using (var tenantContext = web.Context.Clone(webUrl)) { var tenant = new Tenant(tenantContext); var publicCdnEnabled = tenant.GetTenantCdnEnabled(SPOTenantCdnType.Public); var privateCdnEnabled = tenant.GetTenantCdnEnabled(SPOTenantCdnType.Private); tenantContext.ExecuteQueryRetry(); var publicCdn = provisioningTenant.ContentDeliveryNetwork.PublicCdn; if (publicCdn != null) { if (publicCdnEnabled.Value != publicCdn.Enabled) { scope.LogInfo($"Public CDN is set to {(publicCdn.Enabled ? "Enabled" : "Disabled")}"); tenant.SetTenantCdnEnabled(SPOTenantCdnType.Public, publicCdn.Enabled); tenantContext.ExecuteQueryRetry(); } if (publicCdn.Enabled) { ProcessOrigins(tenant, publicCdn, SPOTenantCdnType.Public, parser, scope); ProcessPolicies(tenant, publicCdn, SPOTenantCdnType.Public, parser, scope); } } var privateCdn = provisioningTenant.ContentDeliveryNetwork.PrivateCdn; if (privateCdn != null) { if (privateCdnEnabled.Value != privateCdn.Enabled) { scope.LogInfo($"Private CDN is set to {(publicCdn.Enabled ? "Enabled" : "Disabled")}"); tenant.SetTenantCdnEnabled(SPOTenantCdnType.Private, privateCdn.Enabled); tenantContext.ExecuteQueryRetry(); } if (privateCdn.Enabled) { ProcessOrigins(tenant, publicCdn, SPOTenantCdnType.Public, parser, scope); ProcessPolicies(tenant, publicCdn, SPOTenantCdnType.Public, parser, scope); } } } } }
private void SleepAndWake(int taskNumber) { using (PnPMonitoredScope p = new PnPMonitoredScope(string.Format("PnP Sleeping Monitored Scope {0}", taskNumber))) { for (int i = 0; i < 10; i++) { p.LogInfo(string.Format("Starting sleep for 2 seconds, iteration {0}", i)); System.Threading.Thread.Sleep(2000); p.LogInfo("Ending sleep"); } } }
private void ApplyProvisioningTemplate(ProvisioningTemplate template, string siteCollectionUrl) { using (PnPMonitoredScope Log = new PnPMonitoredScope("ApplyProvisioningTemplate")) { using (var siteContext = AppOnlyContextProvider.GetAppOnlyContext(siteCollectionUrl)) { Site site = siteContext.Site; Web web = site.RootWeb; siteContext.Load(site, s => s.Url); siteContext.Load(web, w => w.Url); siteContext.ExecuteQueryRetry(); try { var applyingInfo = new ProvisioningTemplateApplyingInformation { ProgressDelegate = (message, step, total) => { Log.LogInfo($"{step}/{total} Provisioning {message}"); } }; web.ApplyProvisioningTemplate(template, applyingInfo); } catch (Exception exception) { Log.LogError($"Error occured while applying template {template.DisplayName}: {exception.Message}"); } } } }
private static Principal GetPrincipal(Web web, TokenParser parser, PnPMonitoredScope scope, IEnumerable <Group> groups, Model.RoleAssignment roleAssignment) { var parsedRoleDefinition = parser.ParseString(roleAssignment.Principal); Principal principal = groups.FirstOrDefault(g => g.LoginName.Equals(parsedRoleDefinition, StringComparison.OrdinalIgnoreCase)); if (principal == null) { if (parsedRoleDefinition.Contains("#ext#")) { principal = web.SiteUsers.FirstOrDefault(u => u.LoginName.Equals(parsedRoleDefinition)); if (principal == null) { scope.LogInfo($"Skipping external user {parsedRoleDefinition}"); } } else { try { principal = web.EnsureUser(parsedRoleDefinition); web.Context.ExecuteQueryRetry(); } catch (Exception ex) { scope.LogWarning(ex, "Failed to EnsureUser {0}", parsedRoleDefinition); } } } principal.EnsureProperty(p => p.Id); return(principal); }
private void ApplyProvisioningTemplate(ProvisioningTemplate template, Web web) { using (PnPMonitoredScope Log = new PnPMonitoredScope("ApplyProvisioningTemplate")) { web.Context.Load(web); web.Context.ExecuteQueryRetry(); try { var applyingInfo = new ProvisioningTemplateApplyingInformation { ProgressDelegate = (message, step, total) => { Log.LogInfo($"{step}/{total} Provisioning {message}"); } }; web.ApplyProvisioningTemplate(template, applyingInfo); } catch (Exception exception) { Log.LogError($"Error occured while applying template {template.DisplayName}: {exception.Message}"); } } }
public static void ProcessPolicies(Tenant tenant, CdnSettings cdnSettings, SPOTenantCdnType cdnType, TokenParser parser, PnPMonitoredScope scope) { var isDirty = false; var rawPolicies = tenant.GetTenantCdnPolicies(cdnType); tenant.Context.ExecuteQueryRetry(); var policies = ParsePolicies(rawPolicies); if (!string.IsNullOrEmpty(cdnSettings.IncludeFileExtensions)) { var parsedValue = parser.ParseString(cdnSettings.IncludeFileExtensions); if (policies.FirstOrDefault(p => p.Key == SPOTenantCdnPolicyType.IncludeFileExtensions).Value != parsedValue) { scope.LogInfo($"Setting IncludeFileExtensions policy to {parsedValue}"); tenant.SetTenantCdnPolicy(cdnType, SPOTenantCdnPolicyType.IncludeFileExtensions, parsedValue); isDirty = true; } } if (!string.IsNullOrEmpty(cdnSettings.ExcludeRestrictedSiteClassifications)) { var parsedValue = parser.ParseString(cdnSettings.ExcludeRestrictedSiteClassifications); if (policies.FirstOrDefault(p => p.Key == SPOTenantCdnPolicyType.ExcludeRestrictedSiteClassifications).Value != parsedValue) { scope.LogInfo($"Setting ExcludeRestrictSiteClassifications policy to {parsedValue}"); tenant.SetTenantCdnPolicy(cdnType, SPOTenantCdnPolicyType.ExcludeRestrictedSiteClassifications, parsedValue); isDirty = true; } } if (!string.IsNullOrEmpty(cdnSettings.ExcludeIfNoScriptDisabled)) { var parsedValue = parser.ParseString(cdnSettings.ExcludeIfNoScriptDisabled); if (policies.FirstOrDefault(p => p.Key == SPOTenantCdnPolicyType.ExcludeIfNoScriptDisabled).Value != parsedValue) { scope.LogInfo($"Setting ExcludeIfNoScriptDisabled policy to {parsedValue}"); tenant.SetTenantCdnPolicy(cdnType, SPOTenantCdnPolicyType.ExcludeIfNoScriptDisabled, parsedValue); isDirty = true; } } if (isDirty) { tenant.Context.ExecuteQueryRetry(); } }
private static void ProcessCdns(Web web, ProvisioningTenant provisioningTenant, TokenParser parser, PnPMonitoredScope scope) { if (provisioningTenant.ContentDeliveryNetwork != null) { using (var tenantContext = web.Context.Clone(web.GetTenantAdministrationUrl())) { var tenant = new Tenant(tenantContext); var publicCdnEnabled = tenant.GetTenantCdnEnabled(SPOTenantCdnType.Public); var privateCdnEnabled = tenant.GetTenantCdnEnabled(SPOTenantCdnType.Private); tenantContext.ExecuteQueryRetry(); var publicCdn = provisioningTenant.ContentDeliveryNetwork.PublicCdn; if (publicCdn != null) { if (publicCdnEnabled.Value != publicCdn.Enabled) { scope.LogInfo($"Public CDN is set to {(publicCdn.Enabled ? "Enabled" : "Disabled")}"); tenant.SetTenantCdnEnabled(SPOTenantCdnType.Public, publicCdn.Enabled); tenantContext.ExecuteQueryRetry(); } if (publicCdn.Enabled) { ProcessOrigins(tenant, publicCdn, SPOTenantCdnType.Public, parser, scope); ProcessPolicies(tenant, publicCdn, SPOTenantCdnType.Public, parser, scope); } } var privateCdn = provisioningTenant.ContentDeliveryNetwork.PrivateCdn; if (privateCdn != null) { if (privateCdnEnabled.Value != privateCdn.Enabled) { scope.LogInfo($"Private CDN is set to {(publicCdn.Enabled ? "Enabled" : "Disabled")}"); tenant.SetTenantCdnEnabled(SPOTenantCdnType.Private, privateCdn.Enabled); tenantContext.ExecuteQueryRetry(); } if (privateCdn.Enabled) { ProcessOrigins(tenant, publicCdn, SPOTenantCdnType.Public, parser, scope); ProcessPolicies(tenant, publicCdn, SPOTenantCdnType.Public, parser, scope); } } } } }
public static void ProcessOrigins(Tenant tenant, CdnSettings cdnSettings, SPOTenantCdnType cdnType, TokenParser parser, PnPMonitoredScope scope) { if (cdnSettings.Origins != null && cdnSettings.Origins.Any()) { var origins = tenant.GetTenantCdnOrigins(cdnType); tenant.Context.ExecuteQueryRetry(); foreach (var origin in cdnSettings.Origins) { switch (origin.Action) { case OriginAction.Add: { var parsedOriginUrl = parser.ParseString(origin.Url); if (!origins.Contains(parsedOriginUrl)) { scope.LogInfo($"Adding {parsedOriginUrl} to {cdnType} CDN"); tenant.AddTenantCdnOrigin(cdnType, parsedOriginUrl); } break; } case OriginAction.Remove: { var parsedOriginUrl = parser.ParseString(origin.Url); if (origins.Contains(parsedOriginUrl)) { scope.LogInfo($"Removing {parsedOriginUrl} to {cdnType} CDN"); tenant.RemoveTenantCdnOrigin(cdnType, parsedOriginUrl); } break; } } tenant.Context.ExecuteQueryRetry(); } } }
private static void AddUserToGroup(Web web, Group group, IEnumerable <User> members, PnPMonitoredScope scope, TokenParser parser) { if (members.Any()) { scope.LogDebug("Adding users to group {0}", group.Title); try { foreach (var user in members) { var parsedUserName = parser.ParseString(user.Name); scope.LogDebug("Adding user {0}", parsedUserName); if (parsedUserName.Contains("#ext#")) { var externalUser = web.SiteUsers.FirstOrDefault(u => u.LoginName.Equals(parsedUserName)); if (externalUser == null) { scope.LogInfo($"Skipping external user {parsedUserName}"); } else { group.Users.AddUser(externalUser); } } else { try { var existingUser = web.EnsureUser(parsedUserName); web.Context.ExecuteQueryRetry(); group.Users.AddUser(existingUser); } catch (Exception ex) { scope.LogWarning(ex, "Failed to EnsureUser {0}", parsedUserName); } } } web.Context.ExecuteQueryRetry(); } catch (Exception ex) { scope.LogError(CoreResources.Provisioning_ObjectHandlers_SiteSecurity_Add_users_failed_for_group___0_____1_____2_, group.Title, ex.Message, ex.StackTrace); throw; } } }
public void PnPMonitoredScopeNestingTest() { using (var ctx = TestCommon.CreateClientContext()) { using (var scope1 = new PnPMonitoredScope("1.0")) { Assert.IsTrue(scope1.CorrelationId != Guid.Empty); Assert.IsTrue(scope1.Parent == null); scope1.LogInfo("Logging from scope 1"); using (var scope1_1 = new PnPMonitoredScope("1.1")) { Assert.IsTrue(scope1_1.Parent != null); scope1_1.LogInfo("Logging from scope 1.1"); using (var scope1_1_1 = new PnPMonitoredScope("1.1.1")) { Assert.IsTrue(scope1_1_1.Parent != null && scope1_1_1.Parent != scope1); scope1_1_1.LogInfo("Logging from scope 1.1.1"); } using (var scope1_1_2 = new PnPMonitoredScope("1.1.2")) { Assert.IsTrue(scope1_1_2.Parent != null && scope1_1_2.Parent == scope1_1); using (var scope1_1_2_1 = new PnPMonitoredScope("1.1.2.1")) { Assert.IsTrue(scope1_1_2_1.Parent != null && scope1_1_2_1.Parent == scope1_1_2); } scope1_1_2.LogInfo("Logging from scope 1.1.2"); } } } using (var topScope2 = new PnPMonitoredScope("1.0")) { topScope2.LogInfo("Test"); } } }
public override TokenParser ProvisionObjects(Web web, ProvisioningTemplate template, TokenParser parser, ProvisioningTemplateApplyingInformation applyingInformation) { using (var scope = new PnPMonitoredScope(this.Name)) { if (template.SitePolicy != null) { if (web.GetSitePolicyByName(template.SitePolicy) != null) // Site Policy Available? { web.ApplySitePolicy(template.SitePolicy); scope.LogInfo(CoreResources.Provisioning_ObjectHandlers_SitePolicy_PolicyAdded, template.SitePolicy); } else { scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_SitePolicy_PolicyNotFound, template.SitePolicy); } } } return(parser); }
public override TokenParser ProvisionObjects(Web web, ProvisioningTemplate template, TokenParser parser, ProvisioningTemplateApplyingInformation applyingInformation) { using (var scope = new PnPMonitoredScope(this.Name)) { if (template.SitePolicy != null) { if (web.GetSitePolicyByName(template.SitePolicy) != null) // Site Policy Available? { web.ApplySitePolicy(template.SitePolicy); scope.LogInfo(CoreResources.Provisioning_ObjectHandlers_SitePolicy_PolicyAdded, template.SitePolicy); } else { scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_SitePolicy_PolicyNotFound, template.SitePolicy); } } } return parser; }
public override ProvisioningTemplate ExtractObjects(Web web, ProvisioningTemplate template, ProvisioningTemplateCreationInformation creationInfo) { using (var scope = new PnPMonitoredScope(this.Name)) { scope.LogInfo(CoreResources.Provisioning_ObjectHandlers_ComposedLooks_ExtractObjects_Retrieving_current_composed_look); // Ensure that we have URL property loaded for web and site web.EnsureProperty(w => w.Url); Site site = (web.Context as ClientContext).Site; site.EnsureProperty(s => s.Url); SharePointConnector spConnector = new SharePointConnector(web.Context, web.Url, "dummy"); // to get files from theme catalog we need a connector linked to the root site SharePointConnector spConnectorRoot; if (!site.Url.Equals(web.Url, StringComparison.InvariantCultureIgnoreCase)) { spConnectorRoot = new SharePointConnector(web.Context.Clone(site.Url), site.Url, "dummy"); } else { spConnectorRoot = spConnector; } // Check if we have composed look info in the property bag, if so, use that, otherwise try to detect the current composed look if (web.PropertyBagContainsKey("_PnP_ProvisioningTemplateComposedLookInfo")) { scope.LogInfo(CoreResources.Provisioning_ObjectHandlers_ComposedLooks_ExtractObjects_Using_ComposedLookInfoFromPropertyBag); try { var composedLook = JsonConvert.DeserializeObject <ComposedLook>(web.GetPropertyBagValueString("_PnP_ProvisioningTemplateComposedLookInfo", "")); if (composedLook.Name == null) { scope.LogError(CoreResources.Provisioning_ObjectHandlers_ComposedLooks_ExtractObjects_ComposedLookInfoFailedToDeserialize); throw new JsonSerializationException(); } composedLook.BackgroundFile = Tokenize(composedLook.BackgroundFile, web.Url); composedLook.FontFile = Tokenize(composedLook.FontFile, web.Url); composedLook.ColorFile = Tokenize(composedLook.ColorFile, web.Url); template.ComposedLook = composedLook; if (!web.IsSubSite() && creationInfo != null && creationInfo.PersistBrandingFiles && creationInfo.FileConnector != null) { scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_ComposedLooks_ExtractObjects_Creating_SharePointConnector); // Let's create a SharePoint connector since our files anyhow are in SharePoint at this moment TokenParser parser = new TokenParser(web, template); DownLoadFile(spConnector, spConnectorRoot, creationInfo.FileConnector, web.Url, parser.ParseString(composedLook.BackgroundFile), scope); DownLoadFile(spConnector, spConnectorRoot, creationInfo.FileConnector, web.Url, parser.ParseString(composedLook.ColorFile), scope); DownLoadFile(spConnector, spConnectorRoot, creationInfo.FileConnector, web.Url, parser.ParseString(composedLook.FontFile), scope); } // Create file entries for the custom theme files if (!string.IsNullOrEmpty(template.ComposedLook.BackgroundFile)) { var f = GetComposedLookFile(template.ComposedLook.BackgroundFile); f.Folder = Tokenize(f.Folder, web.Url); template.Files.Add(f); } if (!string.IsNullOrEmpty(template.ComposedLook.ColorFile)) { var f = GetComposedLookFile(template.ComposedLook.ColorFile); f.Folder = Tokenize(f.Folder, web.Url); template.Files.Add(f); } if (!string.IsNullOrEmpty(template.ComposedLook.FontFile)) { var f = GetComposedLookFile(template.ComposedLook.FontFile); f.Folder = Tokenize(f.Folder, web.Url); template.Files.Add(f); } } catch (JsonSerializationException) { // cannot deserialize the object, fall back to composed look detection template = DetectComposedLook(web, template, creationInfo, scope, spConnector, spConnectorRoot); } } else { template = DetectComposedLook(web, template, creationInfo, scope, spConnector, spConnectorRoot); } if (creationInfo != null && creationInfo.BaseTemplate != null) { template = CleanupEntities(template, creationInfo.BaseTemplate); } } return(template); }
/// <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); } }
internal void ApplyProvisioningHierarchy(Tenant tenant, OfficeDevPnP.Core.Framework.Provisioning.Model.ProvisioningHierarchy hierarchy, string sequenceId, ProvisioningTemplateApplyingInformation provisioningInfo) { using (var scope = new PnPMonitoredScope(CoreResources.Provisioning_ObjectHandlers_Provisioning)) { ProvisioningProgressDelegate progressDelegate = null; ProvisioningMessagesDelegate messagesDelegate = null; if (provisioningInfo == null) { // When no provisioning info was passed then we want to execute all handlers provisioningInfo = new ProvisioningTemplateApplyingInformation(); provisioningInfo.HandlersToProcess = Handlers.All; } else { 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); } if (provisioningInfo.HandlersToProcess == default(Handlers)) { provisioningInfo.HandlersToProcess = Handlers.All; } } List <ObjectHierarchyHandlerBase> objectHandlers = new List <ObjectHierarchyHandlerBase> { new ObjectHierarchyTenant(), new ObjectHierarchySequenceTermGroups(), new ObjectHierarchySequenceSites(), new ObjectTeams(), new ObjectAzureActiveDirectory(), }; var count = objectHandlers.Count(o => o.ReportProgress && o.WillProvision(tenant, hierarchy, sequenceId, provisioningInfo)) + 1; progressDelegate?.Invoke("Initializing engine", 1, count); // handlers + initializing message) int step = 2; TokenParser sequenceTokenParser = new TokenParser(tenant, hierarchy); foreach (var handler in objectHandlers) { if (handler.WillProvision(tenant, hierarchy, sequenceId, provisioningInfo)) { if (messagesDelegate != null) { handler.MessagesDelegate = messagesDelegate; } if (handler.ReportProgress && progressDelegate != null) { progressDelegate(handler.Name, step, count); step++; } sequenceTokenParser = handler.ProvisionObjects(tenant, hierarchy, sequenceId, sequenceTokenParser, provisioningInfo); } } } }
public override ProvisioningTemplate ExtractObjects(Web web, ProvisioningTemplate template, ProvisioningTemplateCreationInformation creationInfo) { using (var scope = new PnPMonitoredScope(this.Name)) { // Load object if not there #if !CLIENTSDKV15 web.EnsureProperties(w => w.Url, w => w.MasterUrl, w => w.AlternateCssUrl, w => w.SiteLogoUrl); #else web.EnsureProperties(w => w.Url, w => w.MasterUrl); #endif // Information coming from the site template.ComposedLook.MasterPage = Tokenize(web.MasterUrl, web.Url); #if !CLIENTSDKV15 template.ComposedLook.AlternateCSS = Tokenize(web.AlternateCssUrl, web.Url); template.ComposedLook.SiteLogo = Tokenize(web.SiteLogoUrl, web.Url); #else template.ComposedLook.AlternateCSS = null; template.ComposedLook.SiteLogo = null; #endif scope.LogInfo(CoreResources.Provisioning_ObjectHandlers_ComposedLooks_ExtractObjects_Retrieving_current_composed_look); Site site = (web.Context as ClientContext).Site; if (!site.IsObjectPropertyInstantiated("Url")) { web.Context.Load(site); web.Context.ExecuteQueryRetry(); } SharePointConnector spConnector = new SharePointConnector(web.Context, web.Url, "dummy"); // to get files from theme catalog we need a connector linked to the root site SharePointConnector spConnectorRoot; if (!site.Url.Equals(web.Url, StringComparison.InvariantCultureIgnoreCase)) { spConnectorRoot = new SharePointConnector(web.Context.Clone(site.Url), site.Url, "dummy"); } else { spConnectorRoot = spConnector; } // Check if we have composed look info in the property bag, if so, use that, otherwise try to detect the current composed look if (web.PropertyBagContainsKey("_PnP_ProvisioningTemplateComposedLookInfo")) { scope.LogInfo(CoreResources.Provisioning_ObjectHandlers_ComposedLooks_ExtractObjects_Using_ComposedLookInfoFromPropertyBag); try { var composedLook = JsonConvert.DeserializeObject <ComposedLook>(web.GetPropertyBagValueString("_PnP_ProvisioningTemplateComposedLookInfo", "")); if (composedLook.Name == null || composedLook.BackgroundFile == null || composedLook.FontFile == null || composedLook.MasterPage == null || composedLook.SiteLogo == null) { scope.LogError(CoreResources.Provisioning_ObjectHandlers_ComposedLooks_ExtractObjects_ComposedLookInfoFailedToDeserialize); throw new JsonSerializationException(); } composedLook.AlternateCSS = Tokenize(composedLook.AlternateCSS, web.Url); composedLook.BackgroundFile = Tokenize(composedLook.BackgroundFile, web.Url); composedLook.MasterPage = Tokenize(composedLook.MasterPage, web.Url); composedLook.SiteLogo = Tokenize(composedLook.SiteLogo, web.Url); composedLook.FontFile = Tokenize(composedLook.FontFile, web.Url); composedLook.ColorFile = Tokenize(composedLook.ColorFile, web.Url); template.ComposedLook = composedLook; if (creationInfo != null && creationInfo.PersistComposedLookFiles && creationInfo.FileConnector != null) { scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_ComposedLooks_ExtractObjects_Creating_SharePointConnector); // Let's create a SharePoint connector since our files anyhow are in SharePoint at this moment // Download the theme/branding specific files #if !CLIENTSDKV15 DownLoadFile(spConnector, spConnectorRoot, creationInfo.FileConnector, web.Url, web.AlternateCssUrl, scope); DownLoadFile(spConnector, spConnectorRoot, creationInfo.FileConnector, web.Url, web.SiteLogoUrl, scope); #endif TokenParser parser = new TokenParser(web, template); DownLoadFile(spConnector, spConnectorRoot, creationInfo.FileConnector, web.Url, parser.ParseString(composedLook.BackgroundFile), scope); DownLoadFile(spConnector, spConnectorRoot, creationInfo.FileConnector, web.Url, parser.ParseString(composedLook.ColorFile), scope); DownLoadFile(spConnector, spConnectorRoot, creationInfo.FileConnector, web.Url, parser.ParseString(composedLook.FontFile), scope); } // Create file entries for the custom theme files if (!string.IsNullOrEmpty(template.ComposedLook.BackgroundFile)) { var f = GetComposedLookFile(template.ComposedLook.BackgroundFile); f.Folder = Tokenize(f.Folder, web.Url); template.Files.Add(f); } if (!string.IsNullOrEmpty(template.ComposedLook.ColorFile)) { var f = GetComposedLookFile(template.ComposedLook.ColorFile); f.Folder = Tokenize(f.Folder, web.Url); template.Files.Add(f); } if (!string.IsNullOrEmpty(template.ComposedLook.FontFile)) { var f = GetComposedLookFile(template.ComposedLook.FontFile); f.Folder = Tokenize(f.Folder, web.Url); template.Files.Add(f); } if (!string.IsNullOrEmpty(template.ComposedLook.SiteLogo)) { var f = GetComposedLookFile(template.ComposedLook.SiteLogo); f.Folder = Tokenize(f.Folder, web.Url); template.Files.Add(f); } } catch (JsonSerializationException) { // cannot deserialize the object, fall back to composed look detection template = DetectComposedLook(web, template, creationInfo, scope, spConnector, spConnectorRoot); } } else { template = DetectComposedLook(web, template, creationInfo, scope, spConnector, spConnectorRoot); } if (creationInfo != null && creationInfo.BaseTemplate != null) { template = CleanupEntities(template, creationInfo.BaseTemplate); } } return(template); }
public override ProvisioningTemplate ExtractObjects(Web web, ProvisioningTemplate template, ProvisioningTemplateCreationInformation creationInfo) { using (var scope = new PnPMonitoredScope(this.Name)) { // Extract the Home Page web.EnsureProperties(w => w.RootFolder.WelcomePage, w => w.ServerRelativeUrl, w => w.Url); var homePageUrl = web.RootFolder.WelcomePage; var homepageName = System.IO.Path.GetFileName(web.RootFolder.WelcomePage); if (string.IsNullOrEmpty(homepageName)) { homepageName = "Home.aspx"; } try { var homePage = web.LoadClientSidePage(homepageName); if (homePage.Sections.Count == 0 && homePage.Controls.Count == 0) { // This is default home page which was not customized...and as such there's no page definition stored in the list item. We don't need to extact this page. scope.LogInfo(CoreResources.Provisioning_ObjectHandlers_ClientSidePageContents_DefaultHomePage); } else { // Create the page var homePageInstance = new ClientSidePage() { PageName = homepageName, PromoteAsNewsArticle = false, Overwrite = false, }; // Add the sections foreach (var section in homePage.Sections) { // Set order var sectionInstance = new CanvasSection() { Order = section.Order, }; // Set section type switch (section.Type) { case Pages.CanvasSectionTemplate.OneColumn: sectionInstance.Type = CanvasSectionType.OneColumn; break; case Pages.CanvasSectionTemplate.TwoColumn: sectionInstance.Type = CanvasSectionType.TwoColumn; break; case Pages.CanvasSectionTemplate.TwoColumnLeft: sectionInstance.Type = CanvasSectionType.TwoColumnLeft; break; case Pages.CanvasSectionTemplate.TwoColumnRight: sectionInstance.Type = CanvasSectionType.TwoColumnRight; break; case Pages.CanvasSectionTemplate.ThreeColumn: sectionInstance.Type = CanvasSectionType.ThreeColumn; break; case Pages.CanvasSectionTemplate.OneColumnFullWidth: sectionInstance.Type = CanvasSectionType.OneColumnFullWidth; break; default: sectionInstance.Type = CanvasSectionType.OneColumn; break; } // Add controls to section foreach (var column in section.Columns) { foreach (var control in column.Controls) { // Create control CanvasControl controlInstance = new CanvasControl() { Column = column.Order, ControlId = control.InstanceId, Order = control.Order, }; // Set control type if (control.Type == typeof(Pages.ClientSideText)) { controlInstance.Type = WebPartType.Text; // Set text content controlInstance.ControlProperties = new System.Collections.Generic.Dictionary <string, string>(1) { { "Text", (control as Pages.ClientSideText).Text } }; } else { // set ControlId to webpart id controlInstance.ControlId = Guid.Parse((control as Pages.ClientSideWebPart).WebPartId); var webPartType = Pages.ClientSidePage.NameToClientSideWebPartEnum((control as Pages.ClientSideWebPart).WebPartId); switch (webPartType) { case Pages.DefaultClientSideWebParts.ContentRollup: controlInstance.Type = WebPartType.ContentRollup; break; case Pages.DefaultClientSideWebParts.BingMap: controlInstance.Type = WebPartType.BingMap; break; case Pages.DefaultClientSideWebParts.ContentEmbed: controlInstance.Type = WebPartType.ContentEmbed; break; case Pages.DefaultClientSideWebParts.DocumentEmbed: controlInstance.Type = WebPartType.DocumentEmbed; break; case Pages.DefaultClientSideWebParts.Image: controlInstance.Type = WebPartType.Image; break; case Pages.DefaultClientSideWebParts.ImageGallery: controlInstance.Type = WebPartType.ImageGallery; break; case Pages.DefaultClientSideWebParts.LinkPreview: controlInstance.Type = WebPartType.LinkPreview; break; case Pages.DefaultClientSideWebParts.NewsFeed: controlInstance.Type = WebPartType.NewsFeed; break; case Pages.DefaultClientSideWebParts.NewsReel: controlInstance.Type = WebPartType.NewsReel; break; case Pages.DefaultClientSideWebParts.PowerBIReportEmbed: controlInstance.Type = WebPartType.PowerBIReportEmbed; break; case Pages.DefaultClientSideWebParts.QuickChart: controlInstance.Type = WebPartType.QuickChart; break; case Pages.DefaultClientSideWebParts.SiteActivity: controlInstance.Type = WebPartType.SiteActivity; break; case Pages.DefaultClientSideWebParts.VideoEmbed: controlInstance.Type = WebPartType.VideoEmbed; break; case Pages.DefaultClientSideWebParts.YammerEmbed: controlInstance.Type = WebPartType.YammerEmbed; break; case Pages.DefaultClientSideWebParts.Events: controlInstance.Type = WebPartType.Events; break; case Pages.DefaultClientSideWebParts.GroupCalendar: controlInstance.Type = WebPartType.GroupCalendar; break; case Pages.DefaultClientSideWebParts.Hero: controlInstance.Type = WebPartType.Hero; break; case Pages.DefaultClientSideWebParts.List: controlInstance.Type = WebPartType.List; break; case Pages.DefaultClientSideWebParts.PageTitle: controlInstance.Type = WebPartType.PageTitle; break; case Pages.DefaultClientSideWebParts.People: controlInstance.Type = WebPartType.People; break; case Pages.DefaultClientSideWebParts.QuickLinks: controlInstance.Type = WebPartType.QuickLinks; break; case Pages.DefaultClientSideWebParts.ThirdParty: controlInstance.Type = WebPartType.Custom; break; default: controlInstance.Type = WebPartType.Custom; break; } // set the control properties controlInstance.JsonControlData = (control as Pages.ClientSideWebPart).PropertiesJson; } // add control to section sectionInstance.Controls.Add(controlInstance); } } homePageInstance.Sections.Add(sectionInstance); } // Renumber the sections...when editing modern homepages you can end up with section with order 0.5 or 0.75...let's ensure we render section as of 1 int sectionOrder = 1; foreach (var sectionInstance in homePageInstance.Sections) { sectionInstance.Order = sectionOrder; sectionOrder++; } // Add the page to the template template.ClientSidePages.Add(homePageInstance); // Set the homepage if (template.WebSettings == null) { template.WebSettings = new WebSettings(); } template.WebSettings.WelcomePage = homePageUrl; } } catch (ArgumentException ex) { scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ClientSidePageContents_NoValidPage, ex.Message); } // If a base template is specified then use that one to "cleanup" the generated template model if (creationInfo.BaseTemplate != null) { template = CleanupEntities(template, creationInfo.BaseTemplate); } } return(template); }
/// <summary> /// Actual implementation of the apply templates /// </summary> /// <param name="web"></param> /// <param name="template"></param> /// <param name="provisioningInfo"></param> internal void ApplyRemoteTemplate(Web web, ProvisioningTemplate template, ProvisioningTemplateApplyingInformation provisioningInfo) { using (var scope = new PnPMonitoredScope(CoreResources.Provisioning_ObjectHandlers_Provisioning)) { ProvisioningProgressDelegate progressDelegate = null; ProvisioningMessagesDelegate messagesDelegate = 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); } } else { // When no provisioning info was passed then we want to execute all handlers provisioningInfo = new ProvisioningTemplateApplyingInformation(); provisioningInfo.HandlersToProcess = Handlers.All; } 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()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Fields) || provisioningInfo.HandlersToProcess.HasFlag(Handlers.Lists)) { objectHandlers.Add(new ObjectField()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.ContentTypes)) { objectHandlers.Add(new ObjectContentType()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Lists)) { objectHandlers.Add(new ObjectListInstance()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Fields) || provisioningInfo.HandlersToProcess.HasFlag(Handlers.Lists)) { objectHandlers.Add(new ObjectLookupFields()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Fields) || provisioningInfo.HandlersToProcess.HasFlag(Handlers.Lists)) { objectHandlers.Add(new ObjectListInstanceDataRows()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Files)) { objectHandlers.Add(new ObjectFiles()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Pages)) { objectHandlers.Add(new ObjectPages()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.PageContents)) { objectHandlers.Add(new ObjectPageContents()); } 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.Workflows)) { objectHandlers.Add(new ObjectWorkflows()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.PropertyBagEntries)) { objectHandlers.Add(new ObjectPropertyBagEntry()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.WebSettings)) { objectHandlers.Add(new ObjectWebSettings()); } 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 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 = 1; var count = objectHandlers.Count(o => o.ReportProgress && o.WillProvision(web, template)); foreach (var handler in objectHandlers) { if (handler.WillProvision(web, template)) { if (messagesDelegate != null) { handler.MessagesDelegate = messagesDelegate; } if (handler.ReportProgress && progressDelegate != null) { progressDelegate(handler.Name, step, count); step++; } tokenParser = handler.ProvisionObjects(web, template, tokenParser, provisioningInfo); } } } }
public override ProvisioningTemplate ExtractObjects(Web web, ProvisioningTemplate template, ProvisioningTemplateCreationInformation creationInfo) { using (var scope = new PnPMonitoredScope(this.Name)) { // Load object if not there #if !CLIENTSDKV15 web.EnsureProperties(w => w.Url, w => w.MasterUrl, w => w.AlternateCssUrl, w => w.SiteLogoUrl); #else web.EnsureProperties(w => w.Url, w => w.MasterUrl); #endif // Information coming from the site template.ComposedLook.MasterPage = Tokenize(web.MasterUrl, web.Url); #if !CLIENTSDKV15 template.ComposedLook.AlternateCSS = Tokenize(web.AlternateCssUrl, web.Url); template.ComposedLook.SiteLogo = Tokenize(web.SiteLogoUrl, web.Url); #else template.ComposedLook.AlternateCSS = null; template.ComposedLook.SiteLogo = null; #endif scope.LogInfo(CoreResources.Provisioning_ObjectHandlers_ComposedLooks_ExtractObjects_Retrieving_current_composed_look); var theme = web.GetCurrentComposedLook(); if (theme != null) { if (creationInfo != null) { // Don't exclude the DesignPreviewThemedCssFolderUrl property bag, if any creationInfo.PropertyBagPropertiesToPreserve.Add("DesignPreviewThemedCssFolderUrl"); } template.ComposedLook.Name = theme.Name; if (theme.IsCustomComposedLook) { if (creationInfo != null && creationInfo.PersistComposedLookFiles && creationInfo.FileConnector != null) { Site site = (web.Context as ClientContext).Site; if (!site.IsObjectPropertyInstantiated("Url")) { web.Context.Load(site); web.Context.ExecuteQueryRetry(); } scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_ComposedLooks_ExtractObjects_Creating_SharePointConnector); // Let's create a SharePoint connector since our files anyhow are in SharePoint at this moment SharePointConnector spConnector = new SharePointConnector(web.Context, web.Url, "dummy"); // to get files from theme catalog we need a connector linked to the root site SharePointConnector spConnectorRoot; if (!site.Url.Equals(web.Url, StringComparison.InvariantCultureIgnoreCase)) { spConnectorRoot = new SharePointConnector(web.Context.Clone(site.Url), site.Url, "dummy"); } else { spConnectorRoot = spConnector; } // Download the theme/branding specific files #if !CLIENTSDKV15 DownLoadFile(spConnector, spConnectorRoot, creationInfo.FileConnector, web.Url, web.AlternateCssUrl, scope); DownLoadFile(spConnector, spConnectorRoot, creationInfo.FileConnector, web.Url, web.SiteLogoUrl, scope); #endif DownLoadFile(spConnector, spConnectorRoot, creationInfo.FileConnector, web.Url, theme.BackgroundImage, scope); DownLoadFile(spConnector, spConnectorRoot, creationInfo.FileConnector, web.Url, theme.Theme, scope); DownLoadFile(spConnector, spConnectorRoot, creationInfo.FileConnector, web.Url, theme.Font, scope); } template.ComposedLook.BackgroundFile = FixFileUrl(Tokenize(theme.BackgroundImage, web.Url)); template.ComposedLook.ColorFile = FixFileUrl(Tokenize(theme.Theme, web.Url)); template.ComposedLook.FontFile = FixFileUrl(Tokenize(theme.Font, web.Url)); // Create file entries for the custom theme files if (!string.IsNullOrEmpty(template.ComposedLook.BackgroundFile)) { template.Files.Add(GetComposedLookFile(template.ComposedLook.BackgroundFile)); } if (!string.IsNullOrEmpty(template.ComposedLook.ColorFile)) { template.Files.Add(GetComposedLookFile(template.ComposedLook.ColorFile)); } if (!string.IsNullOrEmpty(template.ComposedLook.FontFile)) { template.Files.Add(GetComposedLookFile(template.ComposedLook.FontFile)); } if (!string.IsNullOrEmpty(template.ComposedLook.SiteLogo)) { template.Files.Add(GetComposedLookFile(template.ComposedLook.SiteLogo)); } // If a base template is specified then use that one to "cleanup" the generated template model if (creationInfo != null && creationInfo.BaseTemplate != null) { template = CleanupEntities(template, creationInfo.BaseTemplate); } } else { template.ComposedLook.BackgroundFile = ""; template.ComposedLook.ColorFile = ""; template.ComposedLook.FontFile = ""; } } else { template.ComposedLook = null; } if (creationInfo != null && creationInfo.BaseTemplate != null) { template = CleanupEntities(template, creationInfo.BaseTemplate); } } return(template); }
public override ProvisioningTemplate ExtractObjects(Web web, ProvisioningTemplate template, ProvisioningTemplateCreationInformation creationInfo) { using (var scope = new PnPMonitoredScope(this.Name)) { scope.LogInfo(CoreResources.Provisioning_ObjectHandlers_ComposedLooks_ExtractObjects_Retrieving_current_composed_look); // Ensure that we have URL property loaded for web and site web.EnsureProperty(w => w.Url); Site site = (web.Context as ClientContext).Site; site.EnsureProperty(s => s.Url); SharePointConnector spConnector = new SharePointConnector(web.Context, web.Url, "dummy"); // to get files from theme catalog we need a connector linked to the root site SharePointConnector spConnectorRoot; if (!site.Url.Equals(web.Url, StringComparison.InvariantCultureIgnoreCase)) { spConnectorRoot = new SharePointConnector(web.Context.Clone(site.Url), site.Url, "dummy"); } else { spConnectorRoot = spConnector; } // Check if we have composed look info in the property bag, if so, use that, otherwise try to detect the current composed look if (web.PropertyBagContainsKey("_PnP_ProvisioningTemplateComposedLookInfo")) { scope.LogInfo(CoreResources.Provisioning_ObjectHandlers_ComposedLooks_ExtractObjects_Using_ComposedLookInfoFromPropertyBag); try { var composedLook = JsonConvert.DeserializeObject<ComposedLook>(web.GetPropertyBagValueString("_PnP_ProvisioningTemplateComposedLookInfo", "")); if (composedLook.Name == null || composedLook.BackgroundFile == null || composedLook.FontFile == null) { scope.LogError(CoreResources.Provisioning_ObjectHandlers_ComposedLooks_ExtractObjects_ComposedLookInfoFailedToDeserialize); throw new JsonSerializationException(); } composedLook.BackgroundFile = Tokenize(composedLook.BackgroundFile, web.Url); composedLook.FontFile = Tokenize(composedLook.FontFile, web.Url); composedLook.ColorFile = Tokenize(composedLook.ColorFile, web.Url); template.ComposedLook = composedLook; if (!web.IsSubSite() && creationInfo != null && creationInfo.PersistBrandingFiles && creationInfo.FileConnector != null) { scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_ComposedLooks_ExtractObjects_Creating_SharePointConnector); // Let's create a SharePoint connector since our files anyhow are in SharePoint at this moment TokenParser parser = new TokenParser(web, template); DownLoadFile(spConnector, spConnectorRoot, creationInfo.FileConnector, web.Url, parser.ParseString(composedLook.BackgroundFile), scope); DownLoadFile(spConnector, spConnectorRoot, creationInfo.FileConnector, web.Url, parser.ParseString(composedLook.ColorFile), scope); DownLoadFile(spConnector, spConnectorRoot, creationInfo.FileConnector, web.Url, parser.ParseString(composedLook.FontFile), scope); } // Create file entries for the custom theme files if (!string.IsNullOrEmpty(template.ComposedLook.BackgroundFile)) { var f = GetComposedLookFile(template.ComposedLook.BackgroundFile); f.Folder = Tokenize(f.Folder, web.Url); template.Files.Add(f); } if (!string.IsNullOrEmpty(template.ComposedLook.ColorFile)) { var f = GetComposedLookFile(template.ComposedLook.ColorFile); f.Folder = Tokenize(f.Folder, web.Url); template.Files.Add(f); } if (!string.IsNullOrEmpty(template.ComposedLook.FontFile)) { var f = GetComposedLookFile(template.ComposedLook.FontFile); f.Folder = Tokenize(f.Folder, web.Url); template.Files.Add(f); } } catch (JsonSerializationException) { // cannot deserialize the object, fall back to composed look detection template = DetectComposedLook(web, template, creationInfo, scope, spConnector, spConnectorRoot); } } else { template = DetectComposedLook(web, template, creationInfo, scope, spConnector, spConnectorRoot); } if (creationInfo != null && creationInfo.BaseTemplate != null) { template = CleanupEntities(template, creationInfo.BaseTemplate); } } return template; }
/// <summary> /// Extracts a client side page /// </summary> /// <param name="web">Web to extract the page from</param> /// <param name="template">Current provisioning template that will hold the extracted page</param> /// <param name="creationInfo">ProvisioningTemplateCreationInformation passed into the provisioning engine</param> /// <param name="scope">Scope used for logging</param> /// <param name="page">page to be exported</param> public void ExtractClientSidePage(Web web, ProvisioningTemplate template, ProvisioningTemplateCreationInformation creationInfo, PnPMonitoredScope scope, PageToExport page) { bool excludeAuthorInformation = false; if (creationInfo.ExtractConfiguration != null && creationInfo.ExtractConfiguration.Pages != null) { excludeAuthorInformation = creationInfo.ExtractConfiguration.Pages.ExcludeAuthorInformation; } try { List <string> errorneousOrNonImageFileGuids = new List <string>(); var pageToExtract = web.LoadClientSidePage(page.PageName); if (pageToExtract.Sections.Count == 0 && pageToExtract.Controls.Count == 0 && page.IsHomePage) { // This is default home page which was not customized...and as such there's no page definition stored in the list item. We don't need to extact this page. scope.LogInfo(CoreResources.Provisioning_ObjectHandlers_ClientSidePageContents_DefaultHomePage); } else { // Get the page content type string pageContentTypeId = pageToExtract.PageListItem[ContentTypeIdField].ToString(); if (!string.IsNullOrEmpty(pageContentTypeId)) { pageContentTypeId = GetParentIdValue(pageContentTypeId); } var isNews = pageToExtract.LayoutType != Pages.ClientSidePageLayoutType.Home && int.Parse(pageToExtract.PageListItem[OfficeDevPnP.Core.Pages.ClientSidePage.PromotedStateField].ToString()) == (int)Pages.PromotedState.Promoted; // Create the page; BaseClientSidePage extractedPageInstance; if (page.IsTranslation) { extractedPageInstance = new TranslatedClientSidePage(); (extractedPageInstance as TranslatedClientSidePage).PageName = page.PageName; } else { extractedPageInstance = new ClientSidePage(); (extractedPageInstance as ClientSidePage).PageName = page.PageName; } extractedPageInstance.PromoteAsNewsArticle = isNews; extractedPageInstance.PromoteAsTemplate = page.IsTemplate; extractedPageInstance.Overwrite = true; extractedPageInstance.Publish = true; extractedPageInstance.Layout = pageToExtract.LayoutType.ToString(); extractedPageInstance.EnableComments = !pageToExtract.CommentsDisabled; extractedPageInstance.Title = pageToExtract.PageTitle; extractedPageInstance.ContentTypeID = !pageContentTypeId.Equals(BuiltInContentTypeId.ModernArticlePage, StringComparison.InvariantCultureIgnoreCase) ? pageContentTypeId : null; extractedPageInstance.ThumbnailUrl = pageToExtract.ThumbnailUrl != null?TokenizeJsonControlData(web, pageToExtract.ThumbnailUrl) : ""; if (pageToExtract.PageHeader != null) { var extractedHeader = new ClientSidePageHeader() { Type = (ClientSidePageHeaderType)Enum.Parse(typeof(Pages.ClientSidePageHeaderType), pageToExtract.PageHeader.Type.ToString()), ServerRelativeImageUrl = TokenizeJsonControlData(web, pageToExtract.PageHeader.ImageServerRelativeUrl), TranslateX = pageToExtract.PageHeader.TranslateX, TranslateY = pageToExtract.PageHeader.TranslateY, LayoutType = (ClientSidePageHeaderLayoutType)Enum.Parse(typeof(Pages.ClientSidePageHeaderLayoutType), pageToExtract.PageHeader.LayoutType.ToString()), #if !SP2019 TextAlignment = (ClientSidePageHeaderTextAlignment)Enum.Parse(typeof(Pages.ClientSidePageHeaderTitleAlignment), pageToExtract.PageHeader.TextAlignment.ToString()), ShowTopicHeader = pageToExtract.PageHeader.ShowTopicHeader, ShowPublishDate = pageToExtract.PageHeader.ShowPublishDate, TopicHeader = pageToExtract.PageHeader.TopicHeader, AlternativeText = pageToExtract.PageHeader.AlternativeText, Authors = !excludeAuthorInformation ? pageToExtract.PageHeader.Authors : "", AuthorByLine = !excludeAuthorInformation ? pageToExtract.PageHeader.AuthorByLine : "", AuthorByLineId = !excludeAuthorInformation ? pageToExtract.PageHeader.AuthorByLineId : -1 #endif }; extractedPageInstance.Header = extractedHeader; // Add the page header image to template if that was requested if (creationInfo.PersistBrandingFiles && !string.IsNullOrEmpty(pageToExtract.PageHeader.ImageServerRelativeUrl)) { IncludePageHeaderImageInExport(web, pageToExtract.PageHeader.ImageServerRelativeUrl, template, creationInfo, scope); } } // define reusable RegEx pre-compiled objects string guidPattern = "\"{?[a-fA-F0-9]{8}-([a-fA-F0-9]{4}-){3}[a-fA-F0-9]{12}}?\""; Regex regexGuidPattern = new Regex(guidPattern, RegexOptions.Compiled); string guidPatternEncoded = "=[a-fA-F0-9]{8}(?:%2D|-)([a-fA-F0-9]{4}(?:%2D|-)){3}[a-fA-F0-9]{12}"; Regex regexGuidPatternEncoded = new Regex(guidPatternEncoded, RegexOptions.Compiled); string guidPatternNoDashes = "[a-fA-F0-9]{32}"; Regex regexGuidPatternNoDashes = new Regex(guidPatternNoDashes, RegexOptions.Compiled); string siteAssetUrlsPattern = "(?:\")(?<AssetUrl>[\\w|\\.|\\/|:|-]*\\/SiteAssets\\/SitePages\\/[\\w|\\.|\\/|:|-]*)(?:\")"; // OLD RegEx with Catastrophic Backtracking: @".*""(.*?/SiteAssets/SitePages/.+?)"".*"; Regex regexSiteAssetUrls = new Regex(siteAssetUrlsPattern, RegexOptions.Compiled); if (creationInfo.PersistBrandingFiles && !string.IsNullOrEmpty(extractedPageInstance.ThumbnailUrl)) { var thumbnailFileIds = new List <Guid>(); CollectImageFilesFromGenericGuids(regexGuidPatternNoDashes, null, extractedPageInstance.ThumbnailUrl, thumbnailFileIds); if (thumbnailFileIds.Count == 1) { var file = web.GetFileById(thumbnailFileIds[0]); web.Context.Load(file, f => f.Level, f => f.ServerRelativeUrl, f => f.UniqueId); web.Context.ExecuteQueryRetry(); // Item1 = was file added to the template // Item2 = file name (if file found) var imageAddedTuple = LoadAndAddPageImage(web, file, template, creationInfo, scope); if (imageAddedTuple.Item1) { extractedPageInstance.ThumbnailUrl = Regex.Replace(extractedPageInstance.ThumbnailUrl, file.UniqueId.ToString("N"), $"{{fileuniqueid:{file.ServerRelativeUrl.Substring(web.ServerRelativeUrl.Length).TrimStart("/".ToCharArray())}}}"); } } } // Add the sections foreach (var section in pageToExtract.Sections) { // Set order var sectionInstance = new CanvasSection() { Order = section.Order, BackgroundEmphasis = (Emphasis)section.ZoneEmphasis, }; if (section.VerticalSectionColumn != null) { sectionInstance.VerticalSectionEmphasis = (Emphasis)section.VerticalSectionColumn.VerticalSectionEmphasis; } // Set section type switch (section.Type) { case Pages.CanvasSectionTemplate.OneColumn: sectionInstance.Type = CanvasSectionType.OneColumn; break; case Pages.CanvasSectionTemplate.TwoColumn: sectionInstance.Type = CanvasSectionType.TwoColumn; break; case Pages.CanvasSectionTemplate.TwoColumnLeft: sectionInstance.Type = CanvasSectionType.TwoColumnLeft; break; case Pages.CanvasSectionTemplate.TwoColumnRight: sectionInstance.Type = CanvasSectionType.TwoColumnRight; break; case Pages.CanvasSectionTemplate.ThreeColumn: sectionInstance.Type = CanvasSectionType.ThreeColumn; break; case Pages.CanvasSectionTemplate.OneColumnFullWidth: sectionInstance.Type = CanvasSectionType.OneColumnFullWidth; break; #if !SP2019 case Pages.CanvasSectionTemplate.OneColumnVerticalSection: sectionInstance.Type = CanvasSectionType.OneColumnVerticalSection; break; case Pages.CanvasSectionTemplate.TwoColumnVerticalSection: sectionInstance.Type = CanvasSectionType.TwoColumnVerticalSection; break; case Pages.CanvasSectionTemplate.TwoColumnLeftVerticalSection: sectionInstance.Type = CanvasSectionType.TwoColumnLeftVerticalSection; break; case Pages.CanvasSectionTemplate.TwoColumnRightVerticalSection: sectionInstance.Type = CanvasSectionType.TwoColumnRightVerticalSection; break; case Pages.CanvasSectionTemplate.ThreeColumnVerticalSection: sectionInstance.Type = CanvasSectionType.ThreeColumnVerticalSection; break; #endif default: sectionInstance.Type = CanvasSectionType.OneColumn; break; } // Add controls to section foreach (var column in section.Columns) { foreach (var control in column.Controls) { // Create control CanvasControl controlInstance = new CanvasControl() { Column = column.IsVerticalSectionColumn ? section.Columns.IndexOf(column) + 1 : column.Order, ControlId = control.InstanceId, Order = control.Order, }; // Set control type if (control.Type == typeof(Pages.ClientSideText)) { controlInstance.Type = WebPartType.Text; // Set text content controlInstance.ControlProperties = new System.Collections.Generic.Dictionary <string, string>(1) { { "Text", TokenizeJsonTextData(web, (control as Pages.ClientSideText).Text) } }; } else { // set ControlId to webpart id controlInstance.ControlId = Guid.Parse((control as Pages.ClientSideWebPart).WebPartId); var webPartType = Pages.ClientSidePage.NameToClientSideWebPartEnum((control as Pages.ClientSideWebPart).WebPartId); switch (webPartType) { case Pages.DefaultClientSideWebParts.ContentRollup: controlInstance.Type = WebPartType.ContentRollup; break; #if !SP2019 case Pages.DefaultClientSideWebParts.BingMap: controlInstance.Type = WebPartType.BingMap; break; case Pages.DefaultClientSideWebParts.Button: controlInstance.Type = WebPartType.Button; break; case Pages.DefaultClientSideWebParts.CallToAction: controlInstance.Type = WebPartType.CallToAction; break; case Pages.DefaultClientSideWebParts.News: controlInstance.Type = WebPartType.News; break; case Pages.DefaultClientSideWebParts.PowerBIReportEmbed: controlInstance.Type = WebPartType.PowerBIReportEmbed; break; case Pages.DefaultClientSideWebParts.Sites: controlInstance.Type = WebPartType.Sites; break; case Pages.DefaultClientSideWebParts.GroupCalendar: controlInstance.Type = WebPartType.GroupCalendar; break; case Pages.DefaultClientSideWebParts.MicrosoftForms: controlInstance.Type = WebPartType.MicrosoftForms; break; case Pages.DefaultClientSideWebParts.ClientWebPart: controlInstance.Type = WebPartType.ClientWebPart; break; #endif case Pages.DefaultClientSideWebParts.ContentEmbed: controlInstance.Type = WebPartType.ContentEmbed; break; case Pages.DefaultClientSideWebParts.DocumentEmbed: controlInstance.Type = WebPartType.DocumentEmbed; break; case Pages.DefaultClientSideWebParts.Image: controlInstance.Type = WebPartType.Image; break; case Pages.DefaultClientSideWebParts.ImageGallery: controlInstance.Type = WebPartType.ImageGallery; break; case Pages.DefaultClientSideWebParts.LinkPreview: controlInstance.Type = WebPartType.LinkPreview; break; case Pages.DefaultClientSideWebParts.NewsFeed: controlInstance.Type = WebPartType.NewsFeed; break; case Pages.DefaultClientSideWebParts.NewsReel: controlInstance.Type = WebPartType.NewsReel; break; case Pages.DefaultClientSideWebParts.QuickChart: controlInstance.Type = WebPartType.QuickChart; break; case Pages.DefaultClientSideWebParts.SiteActivity: controlInstance.Type = WebPartType.SiteActivity; break; case Pages.DefaultClientSideWebParts.VideoEmbed: controlInstance.Type = WebPartType.VideoEmbed; break; case Pages.DefaultClientSideWebParts.YammerEmbed: controlInstance.Type = WebPartType.YammerEmbed; break; case Pages.DefaultClientSideWebParts.Events: controlInstance.Type = WebPartType.Events; break; case Pages.DefaultClientSideWebParts.Hero: controlInstance.Type = WebPartType.Hero; break; case Pages.DefaultClientSideWebParts.List: controlInstance.Type = WebPartType.List; break; case Pages.DefaultClientSideWebParts.PageTitle: controlInstance.Type = WebPartType.PageTitle; break; case Pages.DefaultClientSideWebParts.People: controlInstance.Type = WebPartType.People; break; case Pages.DefaultClientSideWebParts.QuickLinks: controlInstance.Type = WebPartType.QuickLinks; break; case Pages.DefaultClientSideWebParts.CustomMessageRegion: controlInstance.Type = WebPartType.CustomMessageRegion; break; case Pages.DefaultClientSideWebParts.Divider: controlInstance.Type = WebPartType.Divider; break; case Pages.DefaultClientSideWebParts.Spacer: controlInstance.Type = WebPartType.Spacer; break; case Pages.DefaultClientSideWebParts.ThirdParty: controlInstance.Type = WebPartType.Custom; break; default: controlInstance.Type = WebPartType.Custom; break; } if (excludeAuthorInformation) { #if !SP2019 if (webPartType == Pages.DefaultClientSideWebParts.News) { var properties = (control as Pages.ClientSideWebPart).Properties; var authorTokens = properties.SelectTokens("$..author").ToList(); foreach (var authorToken in authorTokens) { authorToken.Parent.Remove(); } var authorAccountNameTokens = properties.SelectTokens("$..authorAccountName").ToList(); foreach (var authorAccountNameToken in authorAccountNameTokens) { authorAccountNameToken.Parent.Remove(); } (control as Pages.ClientSideWebPart).PropertiesJson = properties.ToString(); } #endif } string jsonControlData = "\"id\": \"" + (control as Pages.ClientSideWebPart).WebPartId + "\", \"instanceId\": \"" + (control as Pages.ClientSideWebPart).InstanceId + "\", \"title\": " + JsonConvert.ToString((control as Pages.ClientSideWebPart).Title) + ", \"description\": " + JsonConvert.ToString((control as Pages.ClientSideWebPart).Description) + ", \"dataVersion\": \"" + (control as Pages.ClientSideWebPart).DataVersion + "\", \"properties\": " + (control as Pages.ClientSideWebPart).PropertiesJson + ""; // set the control properties if ((control as Pages.ClientSideWebPart).ServerProcessedContent != null) { // If we have serverProcessedContent then also export that one, it's important as some controls depend on this information to be present string serverProcessedContent = (control as Pages.ClientSideWebPart).ServerProcessedContent.ToString(Formatting.None); jsonControlData = jsonControlData + ", \"serverProcessedContent\": " + serverProcessedContent + ""; } if ((control as Pages.ClientSideWebPart).DynamicDataPaths != null) { // If we have serverProcessedContent then also export that one, it's important as some controls depend on this information to be present string dynamicDataPaths = (control as Pages.ClientSideWebPart).DynamicDataPaths.ToString(Formatting.None); jsonControlData = jsonControlData + ", \"dynamicDataPaths\": " + dynamicDataPaths + ""; } if ((control as Pages.ClientSideWebPart).DynamicDataValues != null) { // If we have serverProcessedContent then also export that one, it's important as some controls depend on this information to be present string dynamicDataValues = (control as Pages.ClientSideWebPart).DynamicDataValues.ToString(Formatting.None); jsonControlData = jsonControlData + ", \"dynamicDataValues\": " + dynamicDataValues + ""; } controlInstance.JsonControlData = "{" + jsonControlData + "}"; var untokenizedJsonControlData = controlInstance.JsonControlData; // Tokenize the JsonControlData controlInstance.JsonControlData = TokenizeJsonControlData(web, controlInstance.JsonControlData); // Export relevant files if this flag is set if (creationInfo.PersistBrandingFiles) { List <Guid> fileGuids = new List <Guid>(); Dictionary <string, string> exportedFiles = new Dictionary <string, string>(); Dictionary <string, string> exportedPages = new Dictionary <string, string>(); CollectSiteAssetImageFiles(regexSiteAssetUrls, web, untokenizedJsonControlData, fileGuids); CollectImageFilesFromGenericGuids(regexGuidPattern, regexGuidPatternEncoded, untokenizedJsonControlData, fileGuids); // Iterate over the found guids to see if they're exportable files foreach (var uniqueId in fileGuids) { try { if (!exportedFiles.ContainsKey(uniqueId.ToString()) && !errorneousOrNonImageFileGuids.Contains(uniqueId.ToString())) { // Try to see if this is a file var file = web.GetFileById(uniqueId); web.Context.Load(file, f => f.Level, f => f.ServerRelativeUrl); web.Context.ExecuteQueryRetry(); // Item1 = was file added to the template // Item2 = file name (if file found) var imageAddedTuple = LoadAndAddPageImage(web, file, template, creationInfo, scope); if (!string.IsNullOrEmpty(imageAddedTuple.Item2)) { if (!imageAddedTuple.Item2.EndsWith(".aspx", StringComparison.InvariantCultureIgnoreCase)) { if (imageAddedTuple.Item1) { // Keep track of the exported file path and it's UniqueId exportedFiles.Add(uniqueId.ToString(), file.ServerRelativeUrl.Substring(web.ServerRelativeUrl.Length).TrimStart("/".ToCharArray())); } } else { if (!exportedPages.ContainsKey(uniqueId.ToString())) { exportedPages.Add(uniqueId.ToString(), file.ServerRelativeUrl.Substring(web.ServerRelativeUrl.Length).TrimStart("/".ToCharArray())); } } } } } catch (Exception ex) { scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ClientSidePageContents_ErrorDuringFileExport, ex.Message); errorneousOrNonImageFileGuids.Add(uniqueId.ToString()); } } // Tokenize based on the found files, use a different token for encoded guids do we can later on replace by a new encoded guid foreach (var exportedFile in exportedFiles) { controlInstance.JsonControlData = Regex.Replace(controlInstance.JsonControlData, exportedFile.Key.Replace("-", "%2D"), $"{{fileuniqueidencoded:{exportedFile.Value}}}", RegexOptions.IgnoreCase); controlInstance.JsonControlData = Regex.Replace(controlInstance.JsonControlData, exportedFile.Key, $"{{fileuniqueid:{exportedFile.Value}}}", RegexOptions.IgnoreCase); } foreach (var exportedPage in exportedPages) { controlInstance.JsonControlData = Regex.Replace(controlInstance.JsonControlData, exportedPage.Key.Replace("-", "%2D"), $"{{pageuniqueidencoded:{exportedPage.Value}}}", RegexOptions.IgnoreCase); controlInstance.JsonControlData = Regex.Replace(controlInstance.JsonControlData, exportedPage.Key, $"{{pageuniqueid:{exportedPage.Value}}}", RegexOptions.IgnoreCase); } } } // add control to section sectionInstance.Controls.Add(controlInstance); } } extractedPageInstance.Sections.Add(sectionInstance); } // Renumber the sections...when editing modern homepages you can end up with section with order 0.5 or 0.75...let's ensure we render section as of 1 int sectionOrder = 1; foreach (var sectionInstance in extractedPageInstance.Sections) { sectionInstance.Order = sectionOrder; sectionOrder++; } #if !SP2019 // Spaces support if (pageToExtract.LayoutType == Pages.ClientSidePageLayoutType.Spaces && !string.IsNullOrEmpty(pageToExtract.SpaceContent)) { extractedPageInstance.FieldValues.Add(Pages.ClientSidePage.SpaceContentField, pageToExtract.SpaceContent); } #endif // Add the page to the template if (page.IsTranslation) { var parentPage = template.ClientSidePages.Where(p => p.PageName == page.SourcePageName).FirstOrDefault(); if (parentPage != null) { var translatedPageInstance = (TranslatedClientSidePage)extractedPageInstance; translatedPageInstance.LCID = new CultureInfo(page.Language).LCID; parentPage.Translations.Add(translatedPageInstance); } } else { var clientSidePageInstance = (ClientSidePage)extractedPageInstance; if (page.TranslatedLanguages != null && page.TranslatedLanguages.Count > 0) { clientSidePageInstance.CreateTranslations = true; clientSidePageInstance.LCID = Convert.ToInt32(web.EnsureProperty(p => p.Language)); } template.ClientSidePages.Add(clientSidePageInstance); } // Set the homepage if (page.IsHomePage) { if (template.WebSettings == null) { template.WebSettings = new WebSettings(); } if (page.PageUrl.StartsWith(web.ServerRelativeUrl, StringComparison.InvariantCultureIgnoreCase)) { template.WebSettings.WelcomePage = page.PageUrl.Replace(web.ServerRelativeUrl + "/", ""); } else { template.WebSettings.WelcomePage = page.PageUrl; } } } } catch (ArgumentException ex) { scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ClientSidePageContents_NoValidPage, ex.Message); } }
/// <summary> /// Actual implementation of the apply templates /// </summary> /// <param name="web"></param> /// <param name="template"></param> /// <param name="provisioningInfo"></param> internal void ApplyRemoteTemplate(Web web, ProvisioningTemplate template, ProvisioningTemplateApplyingInformation provisioningInfo) { using (var scope = new PnPMonitoredScope(CoreResources.Provisioning_ObjectHandlers_Provisioning)) { ProvisioningProgressDelegate progressDelegate = null; ProvisioningMessagesDelegate messagesDelegate = 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); } } else { // When no provisioning info was passed then we want to execute all handlers provisioningInfo = new ProvisioningTemplateApplyingInformation(); provisioningInfo.HandlersToProcess = Handlers.All; } 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()); if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Fields) || provisioningInfo.HandlersToProcess.HasFlag(Handlers.Lists)) objectHandlers.Add(new ObjectField()); if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.ContentTypes)) objectHandlers.Add(new ObjectContentType()); if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Lists)) objectHandlers.Add(new ObjectListInstance()); if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Fields) || provisioningInfo.HandlersToProcess.HasFlag(Handlers.Lists)) objectHandlers.Add(new ObjectLookupFields()); if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Fields) || provisioningInfo.HandlersToProcess.HasFlag(Handlers.Lists)) objectHandlers.Add(new ObjectListInstanceDataRows()); if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Files)) objectHandlers.Add(new ObjectFiles()); if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Pages)) objectHandlers.Add(new ObjectPages()); if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.PageContents)) objectHandlers.Add(new ObjectPageContents()); 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.Workflows)) objectHandlers.Add(new ObjectWorkflows()); if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.PropertyBagEntries)) objectHandlers.Add(new ObjectPropertyBagEntry()); if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.WebSettings)) objectHandlers.Add(new ObjectWebSettings()); 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 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 = 1; var count = objectHandlers.Count(o => o.ReportProgress && o.WillProvision(web, template)); foreach (var handler in objectHandlers) { if (handler.WillProvision(web, template)) { if (messagesDelegate != null) { handler.MessagesDelegate = messagesDelegate; } if (handler.ReportProgress && progressDelegate != null) { progressDelegate(handler.Name, step, count); step++; } tokenParser = handler.ProvisionObjects(web, template, tokenParser, provisioningInfo); } } } }
private void CreateSiteCollection(SiteCollectionProvisioningJob job) { using (PnPMonitoredScope Log = new PnPMonitoredScope("CreateSiteCollection")) { // build site collection url String siteUrl = String.Format("{0}{1}", ConfigurationHelper.GetConfiguration.HostSiteUrl.Substring(0, ConfigurationHelper.GetConfiguration.HostSiteUrl.LastIndexOf("/") + 1), job.RelativeUrl.TrimStart('/')); // get provisioning template var provisioningTemplate = GetProvisioningTemplate(job.PnPTemplate); if (provisioningTemplate != null) { job.BaseTemplate = string.IsNullOrEmpty(provisioningTemplate.BaseSiteTemplate) ? (string.IsNullOrEmpty(job.BaseTemplate) ? ConfigurationHelper.GetConfiguration.BaseSiteTemplate : job.BaseTemplate) : provisioningTemplate.BaseSiteTemplate; } else { job.BaseTemplate = string.IsNullOrEmpty(job.BaseTemplate) ? ConfigurationHelper.GetConfiguration.BaseSiteTemplate : job.BaseTemplate; } using (var adminContext = AppOnlyContextProvider.GetAppOnlyContext(ConfigurationHelper.GetConfiguration.TenantAdminUrl)) { adminContext.RequestTimeout = Timeout.Infinite; // Create the Site Collection and wait for its creation (we're asynchronous) var tenant = new Tenant(adminContext); // check if site collection already exists and in active state. if (tenant.CheckIfSiteExists(siteUrl, Constants.Site_Status_Active)) { Log.LogError($"Site collection with url \"{siteUrl}\" already exists."); } else { Log.LogInfo($"Creating site collection \"{job.RelativeUrl}\" with template {job.BaseTemplate})"); try { tenant.CreateSiteCollection(new SiteEntity() { Description = job.Description, Title = job.Title, Url = siteUrl, SiteOwnerLogin = ConfigurationHelper.GetConfiguration.PrimarySiteCollectionAdministrator, StorageMaximumLevel = job.StorageMaximumLevel, StorageWarningLevel = job.StorageWarningLevel, Template = job.BaseTemplate, Lcid = ((provisioningTemplate != null && provisioningTemplate.RegionalSettings != null) && provisioningTemplate.RegionalSettings.LocaleId > 0) ? uint.Parse(provisioningTemplate.RegionalSettings.LocaleId.ToString()) : job.Language, TimeZoneId = ((provisioningTemplate != null && provisioningTemplate.RegionalSettings != null) && provisioningTemplate.RegionalSettings.TimeZone > 0) ? provisioningTemplate.RegionalSettings.TimeZone : job.TimeZone, }, removeFromRecycleBin: true, wait: true); } catch (Exception exception) { Log.LogError($"Error occured while creating site collection {job.RelativeUrl}"); if (tenant.SiteExists(siteUrl)) { tenant.DeleteSiteCollection(siteUrl, useRecycleBin: false); } throw exception; } } if (provisioningTemplate != null) { Log.LogInfo($"Applying provisioning template {provisioningTemplate.DisplayName}"); ApplyProvisioningTemplate(provisioningTemplate, siteUrl); } Log.LogInfo($"Site collection {siteUrl} provisioned successfully."); } } }
private IEnumerable <ContentType> GetEntities(Web web, PnPMonitoredScope scope, ProvisioningTemplateCreationInformation creationInfo, ProvisioningTemplate template) { var cts = web.ContentTypes; web.Context.Load(cts, ctCollection => ctCollection.IncludeWithDefaultProperties(ct => ct.FieldLinks, ct => ct.SchemaXmlWithResourceTokens)); web.Context.ExecuteQueryRetry(); if (cts.Count > 0 && web.IsSubSite()) { WriteMessage("We discovered content types in this subweb. While technically possible, we recommend moving these content types to the root site collection. Consider excluding them from this template.", ProvisioningMessageType.Warning); } List <ContentType> ctsToReturn = new List <ContentType>(); var currentCtIndex = 0; foreach (var ct in cts) { currentCtIndex++; WriteMessage($"Content Type|{ct.Name}|{currentCtIndex}|{cts.Count()}", ProvisioningMessageType.Progress); if (!BuiltInContentTypeId.Contains(ct.StringId) && (creationInfo.ContentTypeGroupsToInclude.Count == 0 || creationInfo.ContentTypeGroupsToInclude.Contains(ct.Group))) { // Exclude the content type if it's from syndication, and if the flag is not set if (!creationInfo.IncludeContentTypesFromSyndication && IsContentTypeFromSyndication(ct)) { scope.LogInfo($"Content type {ct.Name} excluded from export because it's a syndicated content type."); continue; } string ctDocumentTemplate = null; if (!string.IsNullOrEmpty(ct.DocumentTemplate)) { if (!ct.DocumentTemplate.StartsWith("_cts/")) { ctDocumentTemplate = ct.DocumentTemplate; } } var newCT = new ContentType( ct.StringId, ct.Name, ct.Description, ct.Group, ct.Sealed, ct.Hidden, ct.ReadOnly, ctDocumentTemplate, false, (from fieldLink in ct.FieldLinks.AsEnumerable <FieldLink>() select new FieldRef(fieldLink.Name) { Id = fieldLink.Id, Hidden = fieldLink.Hidden, Required = fieldLink.Required, }) ) { DisplayFormUrl = ct.DisplayFormUrl, EditFormUrl = ct.EditFormUrl, NewFormUrl = ct.NewFormUrl, }; if (creationInfo.PersistMultiLanguageResources) { #if !SP2013 // only persist language values for content types we actually will keep...no point in spending time on this is we clean the field afterwards var persistLanguages = true; if (creationInfo.BaseTemplate != null) { int index = creationInfo.BaseTemplate.ContentTypes.FindIndex(c => c.Id.Equals(ct.StringId)); if (index > -1) { persistLanguages = false; } } if (persistLanguages) { var escapedCTName = ct.Name.Replace(" ", "_"); if (UserResourceExtensions.PersistResourceValue(ct.NameResource, $"ContentType_{escapedCTName}_Title", template, creationInfo)) { newCT.Name = $"{{res:ContentType_{escapedCTName}_Title}}"; } if (UserResourceExtensions.PersistResourceValue(ct.DescriptionResource, $"ContentType_{escapedCTName}_Description", template, creationInfo)) { newCT.Description = $"{{res:ContentType_{escapedCTName}_Description}}"; } } #endif } // If the Content Type is a DocumentSet if (Microsoft.SharePoint.Client.DocumentSet.DocumentSetTemplate.IsChildOfDocumentSetContentType(web.Context, ct).Value || ct.StringId.StartsWith(BuiltInContentTypeId.DocumentSet)) // TODO: This is kind of an hack... we should find a better solution ... { Microsoft.SharePoint.Client.DocumentSet.DocumentSetTemplate documentSetTemplate = Microsoft.SharePoint.Client.DocumentSet.DocumentSetTemplate.GetDocumentSetTemplate(web.Context, ct); // Retrieve the Document Set web.Context.Load(documentSetTemplate, t => t.AllowedContentTypes, t => t.DefaultDocuments, t => t.SharedFields, t => t.WelcomePageFields); web.Context.ExecuteQueryRetry(); newCT.DocumentSetTemplate = new DocumentSetTemplate( null, // TODO: WelcomePage not yet supported (from allowedCT in documentSetTemplate.AllowedContentTypes.AsEnumerable() select allowedCT.StringValue).ToArray(), (from defaultDocument in documentSetTemplate.DefaultDocuments.AsEnumerable() select new DefaultDocument { ContentTypeId = defaultDocument.ContentTypeId.StringValue, Name = defaultDocument.Name, FileSourcePath = String.Empty, // TODO: How can we extract the proper file?! }).ToArray(), (from sharedField in documentSetTemplate.SharedFields.AsEnumerable() select sharedField.Id).ToArray(), (from welcomePageField in documentSetTemplate.WelcomePageFields.AsEnumerable() select welcomePageField.Id).ToArray() ); } ctsToReturn.Add(newCT); } } WriteMessage("Done processing Content Types", ProvisioningMessageType.Completed); return(ctsToReturn); }
/// <summary> /// Actual implementation of the apply templates /// </summary> /// <param name="web"></param> /// <param name="template"></param> /// <param name="provisioningInfo"></param> internal void ApplyRemoteTemplate(Web web, ProvisioningTemplate template, ProvisioningTemplateApplyingInformation provisioningInfo) { using (var scope = new PnPMonitoredScope(CoreResources.Provisioning_ObjectHandlers_Provisioning)) { ProvisioningProgressDelegate progressDelegate = null; ProvisioningMessagesDelegate messagesDelegate = 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); } } else { // When no provisioning info was passed then we want to execute all handlers provisioningInfo = new ProvisioningTemplateApplyingInformation(); provisioningInfo.HandlersToProcess = Handlers.All; } // 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); if (provisioningInfo.MessagesDelegate != null) { provisioningInfo.MessagesDelegate(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()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Fields) || provisioningInfo.HandlersToProcess.HasFlag(Handlers.Lists)) { objectHandlers.Add(new ObjectField()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.ContentTypes)) { objectHandlers.Add(new ObjectContentType()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Lists)) { objectHandlers.Add(new ObjectListInstance()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Fields) || provisioningInfo.HandlersToProcess.HasFlag(Handlers.Lists)) { objectHandlers.Add(new ObjectLookupFields()); } 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.Publishing)) { objectHandlers.Add(new ObjectPublishing()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Files)) { objectHandlers.Add(new ObjectFiles()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Pages)) { objectHandlers.Add(new ObjectPages()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.PageContents)) { objectHandlers.Add(new ObjectPageContents()); } if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.CustomActions)) { objectHandlers.Add(new ObjectCustomActions()); } 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.Navigation)) { objectHandlers.Add(new ObjectNavigation()); } 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 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 = 1; var count = objectHandlers.Count(o => o.ReportProgress && o.WillProvision(web, template)); foreach (var handler in objectHandlers) { if (handler.WillProvision(web, template)) { if (messagesDelegate != null) { handler.MessagesDelegate = messagesDelegate; } if (handler.ReportProgress && progressDelegate != null) { progressDelegate(handler.Name, step, count); step++; } tokenParser = handler.ProvisionObjects(web, template, tokenParser, provisioningInfo); } } } }
/// <summary> /// Extracts a client side page /// </summary> /// <param name="web">Web to extract the page from</param> /// <param name="template">Current provisioning template that will hold the extracted page</param> /// <param name="creationInfo">ProvisioningTemplateCreationInformation passed into the provisioning engine</param> /// <param name="scope">Scope used for logging</param> /// <param name="pageUrl">Url of the page to extract</param> /// <param name="pageName">Name of the page to extract</param> /// <param name="isHomePage">Is this a home page?</param> public void ExtractClientSidePage(Web web, ProvisioningTemplate template, ProvisioningTemplateCreationInformation creationInfo, PnPMonitoredScope scope, string pageUrl, string pageName, bool isHomePage) { try { var pageToExtract = web.LoadClientSidePage(pageName); if (pageToExtract.Sections.Count == 0 && pageToExtract.Controls.Count == 0 && isHomePage) { // This is default home page which was not customized...and as such there's no page definition stored in the list item. We don't need to extact this page. scope.LogInfo(CoreResources.Provisioning_ObjectHandlers_ClientSidePageContents_DefaultHomePage); } else { // Create the page var extractedPageInstance = new ClientSidePage() { PageName = pageName, PromoteAsNewsArticle = false, Overwrite = true, Publish = true, Layout = pageToExtract.LayoutType.ToString(), EnableComments = !pageToExtract.CommentsDisabled, Title = pageToExtract.PageTitle }; if (pageToExtract.PageHeader != null) { var extractedHeader = new ClientSidePageHeader() { Type = (ClientSidePageHeaderType)Enum.Parse(typeof(Pages.ClientSidePageHeaderType), pageToExtract.PageHeader.Type.ToString()), ServerRelativeImageUrl = pageToExtract.PageHeader.ImageServerRelativeUrl, TranslateX = pageToExtract.PageHeader.TranslateX, TranslateY = pageToExtract.PageHeader.TranslateY }; extractedPageInstance.Header = extractedHeader; } // Add the sections foreach (var section in pageToExtract.Sections) { // Set order var sectionInstance = new CanvasSection() { Order = section.Order, }; // Set section type switch (section.Type) { case Pages.CanvasSectionTemplate.OneColumn: sectionInstance.Type = CanvasSectionType.OneColumn; break; case Pages.CanvasSectionTemplate.TwoColumn: sectionInstance.Type = CanvasSectionType.TwoColumn; break; case Pages.CanvasSectionTemplate.TwoColumnLeft: sectionInstance.Type = CanvasSectionType.TwoColumnLeft; break; case Pages.CanvasSectionTemplate.TwoColumnRight: sectionInstance.Type = CanvasSectionType.TwoColumnRight; break; case Pages.CanvasSectionTemplate.ThreeColumn: sectionInstance.Type = CanvasSectionType.ThreeColumn; break; case Pages.CanvasSectionTemplate.OneColumnFullWidth: sectionInstance.Type = CanvasSectionType.OneColumnFullWidth; break; default: sectionInstance.Type = CanvasSectionType.OneColumn; break; } // Add controls to section foreach (var column in section.Columns) { foreach (var control in column.Controls) { // Create control CanvasControl controlInstance = new CanvasControl() { Column = column.Order, ControlId = control.InstanceId, Order = control.Order, }; // Set control type if (control.Type == typeof(Pages.ClientSideText)) { controlInstance.Type = WebPartType.Text; // Set text content controlInstance.ControlProperties = new System.Collections.Generic.Dictionary <string, string>(1) { { "Text", TokenizeJsonTextData(web, (control as Pages.ClientSideText).Text) } }; } else { // set ControlId to webpart id controlInstance.ControlId = Guid.Parse((control as Pages.ClientSideWebPart).WebPartId); var webPartType = Pages.ClientSidePage.NameToClientSideWebPartEnum((control as Pages.ClientSideWebPart).WebPartId); switch (webPartType) { case Pages.DefaultClientSideWebParts.ContentRollup: controlInstance.Type = WebPartType.ContentRollup; break; case Pages.DefaultClientSideWebParts.BingMap: controlInstance.Type = WebPartType.BingMap; break; case Pages.DefaultClientSideWebParts.ContentEmbed: controlInstance.Type = WebPartType.ContentEmbed; break; case Pages.DefaultClientSideWebParts.DocumentEmbed: controlInstance.Type = WebPartType.DocumentEmbed; break; case Pages.DefaultClientSideWebParts.Image: controlInstance.Type = WebPartType.Image; break; case Pages.DefaultClientSideWebParts.ImageGallery: controlInstance.Type = WebPartType.ImageGallery; break; case Pages.DefaultClientSideWebParts.LinkPreview: controlInstance.Type = WebPartType.LinkPreview; break; case Pages.DefaultClientSideWebParts.NewsFeed: controlInstance.Type = WebPartType.NewsFeed; break; case Pages.DefaultClientSideWebParts.NewsReel: controlInstance.Type = WebPartType.NewsReel; break; case Pages.DefaultClientSideWebParts.PowerBIReportEmbed: controlInstance.Type = WebPartType.PowerBIReportEmbed; break; case Pages.DefaultClientSideWebParts.QuickChart: controlInstance.Type = WebPartType.QuickChart; break; case Pages.DefaultClientSideWebParts.SiteActivity: controlInstance.Type = WebPartType.SiteActivity; break; case Pages.DefaultClientSideWebParts.VideoEmbed: controlInstance.Type = WebPartType.VideoEmbed; break; case Pages.DefaultClientSideWebParts.YammerEmbed: controlInstance.Type = WebPartType.YammerEmbed; break; case Pages.DefaultClientSideWebParts.Events: controlInstance.Type = WebPartType.Events; break; case Pages.DefaultClientSideWebParts.GroupCalendar: controlInstance.Type = WebPartType.GroupCalendar; break; case Pages.DefaultClientSideWebParts.Hero: controlInstance.Type = WebPartType.Hero; break; case Pages.DefaultClientSideWebParts.List: controlInstance.Type = WebPartType.List; break; case Pages.DefaultClientSideWebParts.PageTitle: controlInstance.Type = WebPartType.PageTitle; break; case Pages.DefaultClientSideWebParts.People: controlInstance.Type = WebPartType.People; break; case Pages.DefaultClientSideWebParts.QuickLinks: controlInstance.Type = WebPartType.QuickLinks; break; case Pages.DefaultClientSideWebParts.CustomMessageRegion: controlInstance.Type = WebPartType.CustomMessageRegion; break; case Pages.DefaultClientSideWebParts.Divider: controlInstance.Type = WebPartType.Divider; break; case Pages.DefaultClientSideWebParts.MicrosoftForms: controlInstance.Type = WebPartType.MicrosoftForms; break; case Pages.DefaultClientSideWebParts.Spacer: controlInstance.Type = WebPartType.Spacer; break; case Pages.DefaultClientSideWebParts.ClientWebPart: controlInstance.Type = WebPartType.ClientWebPart; break; case Pages.DefaultClientSideWebParts.ThirdParty: controlInstance.Type = WebPartType.Custom; break; default: controlInstance.Type = WebPartType.Custom; break; } // set the control properties if ((control as Pages.ClientSideWebPart).ServerProcessedContent != null) { // If we have serverProcessedContent then also export that one, it's important as some controls depend on this information to be present string serverProcessedContent = (control as Pages.ClientSideWebPart).ServerProcessedContent.ToString(Formatting.None); controlInstance.JsonControlData = "{ \"serverProcessedContent\": " + serverProcessedContent + ", \"properties\": " + (control as Pages.ClientSideWebPart).PropertiesJson + "}"; } else { controlInstance.JsonControlData = (control as Pages.ClientSideWebPart).PropertiesJson; } // Tokenize the JsonControlData controlInstance.JsonControlData = TokenizeJsonControlData(web, controlInstance.JsonControlData); // Export relevant files if this flag is set if (creationInfo.PersistBrandingFiles) { List <Guid> fileGuids = new List <Guid>(); Dictionary <string, string> exportedFiles = new Dictionary <string, string>(); Dictionary <string, string> exportedPages = new Dictionary <string, string>(); // grab all the guids in the already tokenized json and check try to get them as a file string guidPattern = "\"[a-fA-F0-9]{8}-([a-fA-F0-9]{4}-){3}[a-fA-F0-9]{12}\""; Regex regexClientIds = new Regex(guidPattern); if (regexClientIds.IsMatch(controlInstance.JsonControlData)) { foreach (Match guidMatch in regexClientIds.Matches(controlInstance.JsonControlData)) { Guid uniqueId; if (Guid.TryParse(guidMatch.Value.Trim("\"".ToCharArray()), out uniqueId)) { fileGuids.Add(uniqueId); } } } // grab all the encoded guids in the already tokenized json and check try to get them as a file guidPattern = "=[a-fA-F0-9]{8}%2D([a-fA-F0-9]{4}%2D){3}[a-fA-F0-9]{12}"; regexClientIds = new Regex(guidPattern); if (regexClientIds.IsMatch(controlInstance.JsonControlData)) { foreach (Match guidMatch in regexClientIds.Matches(controlInstance.JsonControlData)) { Guid uniqueId; if (Guid.TryParse(guidMatch.Value.TrimStart("=".ToCharArray()), out uniqueId)) { fileGuids.Add(uniqueId); } } } // Iterate over the found guids to see if they're exportable files foreach (var uniqueId in fileGuids) { try { if (!exportedFiles.ContainsKey(uniqueId.ToString())) { // Try to see if this is a file var file = web.GetFileById(uniqueId); web.Context.Load(file, f => f.Level, f => f.ServerRelativeUrl); web.Context.ExecuteQueryRetry(); // If we got here it's a file, let's grab the file's path and name var baseUri = new Uri(web.Url); var fullUri = new Uri(baseUri, file.ServerRelativeUrl); var folderPath = HttpUtility.UrlDecode(fullUri.Segments.Take(fullUri.Segments.Count() - 1).ToArray().Aggregate((i, x) => i + x).TrimEnd('/')); var fileName = HttpUtility.UrlDecode(fullUri.Segments[fullUri.Segments.Count() - 1]); // Don't export aspx files as some web parts refer to other client side pages --> pages have to be either exported as well or already exist in the target site if (!fileName.EndsWith(".aspx", StringComparison.InvariantCultureIgnoreCase)) { var templateFolderPath = folderPath.Substring(web.ServerRelativeUrl.Length).TrimStart("/".ToCharArray()); // Avoid duplicate file entries var fileAlreadyExported = template.Files.Where(p => p.Folder.Equals(templateFolderPath, StringComparison.CurrentCultureIgnoreCase) && p.Src.Equals(fileName, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault(); if (fileAlreadyExported == null) { // Add a File to the template template.Files.Add(new Model.File() { Folder = templateFolderPath, Src = $"{templateFolderPath}/{fileName}", Overwrite = true, Level = (Model.FileLevel)Enum.Parse(typeof(Model.FileLevel), file.Level.ToString()) }); // Export the file PersistFile(web, creationInfo, scope, folderPath, fileName); // Keep track of the exported file path and it's UniqueId exportedFiles.Add(uniqueId.ToString(), file.ServerRelativeUrl.Substring(web.ServerRelativeUrl.Length).TrimStart("/".ToCharArray())); } } else { if (!exportedPages.ContainsKey(uniqueId.ToString())) { exportedPages.Add(uniqueId.ToString(), file.ServerRelativeUrl.Substring(web.ServerRelativeUrl.Length).TrimStart("/".ToCharArray())); } } } } catch (Exception ex) { scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ClientSidePageContents_ErrorDuringFileExport, ex.Message); } } // Tokenize based on the found files, use a different token for encoded guids do we can later on replace by a new encoded guid foreach (var exportedFile in exportedFiles) { controlInstance.JsonControlData = Regex.Replace(controlInstance.JsonControlData, exportedFile.Key.Replace("-", "%2D"), $"{{fileuniqueidencoded:{exportedFile.Value}}}", RegexOptions.IgnoreCase); controlInstance.JsonControlData = Regex.Replace(controlInstance.JsonControlData, exportedFile.Key, $"{{fileuniqueid:{exportedFile.Value}}}", RegexOptions.IgnoreCase); } foreach (var exportedPage in exportedPages) { controlInstance.JsonControlData = Regex.Replace(controlInstance.JsonControlData, exportedPage.Key.Replace("-", "%2D"), $"{{pageuniqueidencoded:{exportedPage.Value}}}", RegexOptions.IgnoreCase); controlInstance.JsonControlData = Regex.Replace(controlInstance.JsonControlData, exportedPage.Key, $"{{pageuniqueid:{exportedPage.Value}}}", RegexOptions.IgnoreCase); } } } // add control to section sectionInstance.Controls.Add(controlInstance); } } extractedPageInstance.Sections.Add(sectionInstance); } // Renumber the sections...when editing modern homepages you can end up with section with order 0.5 or 0.75...let's ensure we render section as of 1 int sectionOrder = 1; foreach (var sectionInstance in extractedPageInstance.Sections) { sectionInstance.Order = sectionOrder; sectionOrder++; } // Add the page to the template template.ClientSidePages.Add(extractedPageInstance); // Set the homepage if (isHomePage) { if (template.WebSettings == null) { template.WebSettings = new WebSettings(); } if (pageUrl.StartsWith(web.ServerRelativeUrl, StringComparison.InvariantCultureIgnoreCase)) { template.WebSettings.WelcomePage = pageUrl.Replace(web.ServerRelativeUrl + "/", ""); } else { template.WebSettings.WelcomePage = pageUrl; } } } } catch (ArgumentException ex) { scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ClientSidePageContents_NoValidPage, ex.Message); } }
private void CreateSubSite(SubSiteProvisioningJob job) { using (PnPMonitoredScope Log = new PnPMonitoredScope("CreateSubSite")) { // get provisioning template var provisioningTemplate = GetProvisioningTemplate(job.PnPTemplate); if (provisioningTemplate != null) { job.BaseTemplate = string.IsNullOrEmpty(provisioningTemplate.BaseSiteTemplate) ? (string.IsNullOrEmpty(job.BaseTemplate) ? ConfigurationHelper.GetConfiguration.BaseSiteTemplate : job.BaseTemplate) : provisioningTemplate.BaseSiteTemplate; } else { job.BaseTemplate = string.IsNullOrEmpty(job.BaseTemplate) ? ConfigurationHelper.GetConfiguration.BaseSiteTemplate : job.BaseTemplate; } Web newWeb = null; using (var siteContext = AppOnlyContextProvider.GetAppOnlyContext(job.ParentWebUrl)) { Web parentWeb = siteContext.Web; siteContext.Load(parentWeb, w => w.Language, w => w.RegionalSettings.TimeZone); siteContext.ExecuteQueryRetry(); if (parentWeb.WebExists(job.RelativeUrl)) { Log.LogError($"Web with url \"{job.RelativeUrl}\" already exists."); newWeb = parentWeb.GetWeb(job.RelativeUrl); siteContext.Load(newWeb); siteContext.ExecuteQueryRetry(); } else { Log.LogInfo($"Creating web \"{job.RelativeUrl}\" with template {job.BaseTemplate}"); try { // Create the new sub site as a new child Web newWeb = parentWeb.CreateWeb(new SiteEntity() { Title = job.Title, Description = job.Description, Lcid = job.Language, Url = job.RelativeUrl, Template = job.BaseTemplate }, job.InheritPermissions, job.InheritNavigation); } catch (Exception exception) { if (parentWeb.WebExists(job.RelativeUrl)) { parentWeb.DeleteWeb(job.RelativeUrl); } throw exception; } } if (provisioningTemplate != null) { Log.LogInfo($"Applying provisioning template {provisioningTemplate.DisplayName}"); ApplyProvisioningTemplate(provisioningTemplate, newWeb); } Log.LogInfo($"Web {job.RelativeUrl} provisioned successfully."); } } }
internal void ApplyTenantTemplate(Tenant tenant, PnP.Framework.Provisioning.Model.ProvisioningHierarchy hierarchy, string sequenceId, ApplyConfiguration configuration) { using (var scope = new PnPMonitoredScope(CoreResources.Provisioning_ObjectHandlers_Provisioning)) { ProvisioningProgressDelegate progressDelegate = null; ProvisioningMessagesDelegate messagesDelegate = null; if (configuration == null) { // When no provisioning info was passed then we want to execute all handlers configuration = new ApplyConfiguration(); } else { progressDelegate = configuration.ProgressDelegate; if (configuration.ProgressDelegate != null) { scope.LogInfo(CoreResources.SiteToTemplateConversion_ProgressDelegate_registered); } messagesDelegate = configuration.MessagesDelegate; if (configuration.MessagesDelegate != null) { scope.LogInfo(CoreResources.SiteToTemplateConversion_MessagesDelegate_registered); } } List <ObjectHierarchyHandlerBase> objectHandlers = new List <ObjectHierarchyHandlerBase> { new ObjectHierarchyTenant(), new ObjectHierarchySequenceTermGroups(), new ObjectHierarchySequenceSites(), new ObjectTeams(), new ObjectAzureActiveDirectory(), }; var count = objectHandlers.Count(o => o.ReportProgress && o.WillProvision(tenant, hierarchy, sequenceId, configuration)) + 1; progressDelegate?.Invoke("Initializing engine", 1, count); // handlers + initializing message) int step = 2; TokenParser sequenceTokenParser = new TokenParser(tenant, hierarchy); CallWebHooks(hierarchy.Templates.FirstOrDefault(), sequenceTokenParser, ProvisioningTemplateWebhookKind.ProvisioningStarted); foreach (var handler in objectHandlers) { if (handler.WillProvision(tenant, hierarchy, sequenceId, configuration)) { if (messagesDelegate != null) { handler.MessagesDelegate = messagesDelegate; } if (handler.ReportProgress && progressDelegate != null) { progressDelegate(handler.Name, step, count); step++; } try { sequenceTokenParser = handler.ProvisionObjects(tenant, hierarchy, sequenceId, sequenceTokenParser, configuration); } catch (Exception ex) { CallWebHooks(hierarchy.Templates.FirstOrDefault(), sequenceTokenParser, ProvisioningTemplateWebhookKind.ProvisioningExceptionOccurred, handler.Name, ex); throw; } } } CallWebHooks(hierarchy.Templates.FirstOrDefault(), sequenceTokenParser, ProvisioningTemplateWebhookKind.ProvisioningCompleted); } }
/// <summary> /// Extracts a client side page /// </summary> /// <param name="web">Web to extract the page from</param> /// <param name="template">Current provisioning template that will hold the extracted page</param> /// <param name="creationInfo">ProvisioningTemplateCreationInformation passed into the provisioning engine</param> /// <param name="scope">Scope used for logging</param> /// <param name="pageUrl">Url of the page to extract</param> /// <param name="pageName">Name of the page to extract</param> /// <param name="isHomePage">Is this a home page?</param> /// <param name="isTemplate">Is this a template?</param> public void ExtractClientSidePage(Web web, ProvisioningTemplate template, ProvisioningTemplateCreationInformation creationInfo, PnPMonitoredScope scope, string pageUrl, string pageName, bool isHomePage, bool isTemplate = false) { try { List <string> errorneousOrNonImageFileGuids = new List <string>(); var pageToExtract = web.LoadClientSidePage(pageName); if (pageToExtract.Sections.Count == 0 && pageToExtract.Controls.Count == 0 && isHomePage) { // This is default home page which was not customized...and as such there's no page definition stored in the list item. We don't need to extact this page. scope.LogInfo(CoreResources.Provisioning_ObjectHandlers_ClientSidePageContents_DefaultHomePage); } else { // Get the page content type string pageContentTypeId = pageToExtract.PageListItem[ContentTypeIdField].ToString(); if (!string.IsNullOrEmpty(pageContentTypeId)) { pageContentTypeId = GetParentIdValue(pageContentTypeId); } // Create the page var extractedPageInstance = new ClientSidePage() { PageName = pageName, PromoteAsNewsArticle = false, PromoteAsTemplate = isTemplate, Overwrite = true, Publish = true, Layout = pageToExtract.LayoutType.ToString(), EnableComments = !pageToExtract.CommentsDisabled, Title = pageToExtract.PageTitle, ContentTypeID = !pageContentTypeId.Equals(BuiltInContentTypeId.ModernArticlePage, StringComparison.InvariantCultureIgnoreCase) ? pageContentTypeId : null, }; if (pageToExtract.PageHeader != null) { var extractedHeader = new ClientSidePageHeader() { Type = (ClientSidePageHeaderType)Enum.Parse(typeof(Pages.ClientSidePageHeaderType), pageToExtract.PageHeader.Type.ToString()), ServerRelativeImageUrl = TokenizeJsonControlData(web, pageToExtract.PageHeader.ImageServerRelativeUrl), TranslateX = pageToExtract.PageHeader.TranslateX, TranslateY = pageToExtract.PageHeader.TranslateY, LayoutType = (ClientSidePageHeaderLayoutType)Enum.Parse(typeof(Pages.ClientSidePageHeaderLayoutType), pageToExtract.PageHeader.LayoutType.ToString()), TextAlignment = (ClientSidePageHeaderTextAlignment)Enum.Parse(typeof(Pages.ClientSidePageHeaderTitleAlignment), pageToExtract.PageHeader.TextAlignment.ToString()), ShowTopicHeader = pageToExtract.PageHeader.ShowTopicHeader, ShowPublishDate = pageToExtract.PageHeader.ShowPublishDate, TopicHeader = pageToExtract.PageHeader.TopicHeader, AlternativeText = pageToExtract.PageHeader.AlternativeText, Authors = pageToExtract.PageHeader.Authors, AuthorByLine = pageToExtract.PageHeader.AuthorByLine, AuthorByLineId = pageToExtract.PageHeader.AuthorByLineId, }; extractedPageInstance.Header = extractedHeader; // Add the page header image to template if that was requested if (creationInfo.PersistBrandingFiles && !string.IsNullOrEmpty(pageToExtract.PageHeader.ImageServerRelativeUrl)) { IncludePageHeaderImageInExport(web, pageToExtract.PageHeader.ImageServerRelativeUrl, template, creationInfo, scope); } } // Add the sections foreach (var section in pageToExtract.Sections) { // Set order var sectionInstance = new CanvasSection() { Order = section.Order, BackgroundEmphasis = (BackgroundEmphasis)section.ZoneEmphasis, }; // Set section type switch (section.Type) { case Pages.CanvasSectionTemplate.OneColumn: sectionInstance.Type = CanvasSectionType.OneColumn; break; case Pages.CanvasSectionTemplate.TwoColumn: sectionInstance.Type = CanvasSectionType.TwoColumn; break; case Pages.CanvasSectionTemplate.TwoColumnLeft: sectionInstance.Type = CanvasSectionType.TwoColumnLeft; break; case Pages.CanvasSectionTemplate.TwoColumnRight: sectionInstance.Type = CanvasSectionType.TwoColumnRight; break; case Pages.CanvasSectionTemplate.ThreeColumn: sectionInstance.Type = CanvasSectionType.ThreeColumn; break; case Pages.CanvasSectionTemplate.OneColumnFullWidth: sectionInstance.Type = CanvasSectionType.OneColumnFullWidth; break; default: sectionInstance.Type = CanvasSectionType.OneColumn; break; } // Add controls to section foreach (var column in section.Columns) { foreach (var control in column.Controls) { // Create control CanvasControl controlInstance = new CanvasControl() { Column = column.Order, ControlId = control.InstanceId, Order = control.Order, }; // Set control type if (control.Type == typeof(Pages.ClientSideText)) { controlInstance.Type = WebPartType.Text; // Set text content controlInstance.ControlProperties = new System.Collections.Generic.Dictionary <string, string>(1) { { "Text", TokenizeJsonTextData(web, (control as Pages.ClientSideText).Text) } }; } else { // set ControlId to webpart id controlInstance.ControlId = Guid.Parse((control as Pages.ClientSideWebPart).WebPartId); var webPartType = Pages.ClientSidePage.NameToClientSideWebPartEnum((control as Pages.ClientSideWebPart).WebPartId); switch (webPartType) { case Pages.DefaultClientSideWebParts.ContentRollup: controlInstance.Type = WebPartType.ContentRollup; break; case Pages.DefaultClientSideWebParts.BingMap: controlInstance.Type = WebPartType.BingMap; break; case Pages.DefaultClientSideWebParts.ContentEmbed: controlInstance.Type = WebPartType.ContentEmbed; break; case Pages.DefaultClientSideWebParts.DocumentEmbed: controlInstance.Type = WebPartType.DocumentEmbed; break; case Pages.DefaultClientSideWebParts.Image: controlInstance.Type = WebPartType.Image; break; case Pages.DefaultClientSideWebParts.ImageGallery: controlInstance.Type = WebPartType.ImageGallery; break; case Pages.DefaultClientSideWebParts.LinkPreview: controlInstance.Type = WebPartType.LinkPreview; break; case Pages.DefaultClientSideWebParts.News: controlInstance.Type = WebPartType.News; break; case Pages.DefaultClientSideWebParts.NewsFeed: controlInstance.Type = WebPartType.NewsFeed; break; case Pages.DefaultClientSideWebParts.NewsReel: controlInstance.Type = WebPartType.NewsReel; break; case Pages.DefaultClientSideWebParts.PowerBIReportEmbed: controlInstance.Type = WebPartType.PowerBIReportEmbed; break; case Pages.DefaultClientSideWebParts.QuickChart: controlInstance.Type = WebPartType.QuickChart; break; case Pages.DefaultClientSideWebParts.SiteActivity: controlInstance.Type = WebPartType.SiteActivity; break; case Pages.DefaultClientSideWebParts.VideoEmbed: controlInstance.Type = WebPartType.VideoEmbed; break; case Pages.DefaultClientSideWebParts.YammerEmbed: controlInstance.Type = WebPartType.YammerEmbed; break; case Pages.DefaultClientSideWebParts.Events: controlInstance.Type = WebPartType.Events; break; case Pages.DefaultClientSideWebParts.GroupCalendar: controlInstance.Type = WebPartType.GroupCalendar; break; case Pages.DefaultClientSideWebParts.Hero: controlInstance.Type = WebPartType.Hero; break; case Pages.DefaultClientSideWebParts.List: controlInstance.Type = WebPartType.List; break; case Pages.DefaultClientSideWebParts.PageTitle: controlInstance.Type = WebPartType.PageTitle; break; case Pages.DefaultClientSideWebParts.People: controlInstance.Type = WebPartType.People; break; case Pages.DefaultClientSideWebParts.QuickLinks: controlInstance.Type = WebPartType.QuickLinks; break; case Pages.DefaultClientSideWebParts.CustomMessageRegion: controlInstance.Type = WebPartType.CustomMessageRegion; break; case Pages.DefaultClientSideWebParts.Divider: controlInstance.Type = WebPartType.Divider; break; case Pages.DefaultClientSideWebParts.MicrosoftForms: controlInstance.Type = WebPartType.MicrosoftForms; break; case Pages.DefaultClientSideWebParts.Spacer: controlInstance.Type = WebPartType.Spacer; break; case Pages.DefaultClientSideWebParts.ClientWebPart: controlInstance.Type = WebPartType.ClientWebPart; break; case Pages.DefaultClientSideWebParts.ThirdParty: controlInstance.Type = WebPartType.Custom; break; default: controlInstance.Type = WebPartType.Custom; break; } string jsonControlData = "\"id\": \"" + (control as Pages.ClientSideWebPart).WebPartId + "\", \"instanceId\": \"" + (control as Pages.ClientSideWebPart).InstanceId + "\", \"title\": " + JsonConvert.ToString((control as Pages.ClientSideWebPart).Title) + ", \"description\": " + JsonConvert.ToString((control as Pages.ClientSideWebPart).Description) + ", \"dataVersion\": \"" + (control as Pages.ClientSideWebPart).DataVersion + "\", \"properties\": " + (control as Pages.ClientSideWebPart).PropertiesJson + ""; // set the control properties if ((control as Pages.ClientSideWebPart).ServerProcessedContent != null) { // If we have serverProcessedContent then also export that one, it's important as some controls depend on this information to be present string serverProcessedContent = (control as Pages.ClientSideWebPart).ServerProcessedContent.ToString(Formatting.None); jsonControlData = jsonControlData + ", \"serverProcessedContent\": " + serverProcessedContent + ""; } if ((control as Pages.ClientSideWebPart).DynamicDataPaths != null) { // If we have serverProcessedContent then also export that one, it's important as some controls depend on this information to be present string dynamicDataPaths = (control as Pages.ClientSideWebPart).DynamicDataPaths.ToString(Formatting.None); jsonControlData = jsonControlData + ", \"dynamicDataPaths\": " + dynamicDataPaths + ""; } if ((control as Pages.ClientSideWebPart).DynamicDataValues != null) { // If we have serverProcessedContent then also export that one, it's important as some controls depend on this information to be present string dynamicDataValues = (control as Pages.ClientSideWebPart).DynamicDataValues.ToString(Formatting.None); jsonControlData = jsonControlData + ", \"dynamicDataValues\": " + dynamicDataValues + ""; } controlInstance.JsonControlData = "{" + jsonControlData + "}"; var untokenizedJsonControlData = controlInstance.JsonControlData; // Tokenize the JsonControlData controlInstance.JsonControlData = TokenizeJsonControlData(web, controlInstance.JsonControlData); // Export relevant files if this flag is set if (creationInfo.PersistBrandingFiles) { List <Guid> fileGuids = new List <Guid>(); Dictionary <string, string> exportedFiles = new Dictionary <string, string>(); Dictionary <string, string> exportedPages = new Dictionary <string, string>(); CollectSiteAssetImageFiles(web, untokenizedJsonControlData, fileGuids); CollectImageFilesFromGenericGuids(controlInstance.JsonControlData, fileGuids); // Iterate over the found guids to see if they're exportable files foreach (var uniqueId in fileGuids) { try { if (!exportedFiles.ContainsKey(uniqueId.ToString()) && !errorneousOrNonImageFileGuids.Contains(uniqueId.ToString())) { // Try to see if this is a file var file = web.GetFileById(uniqueId); web.Context.Load(file, f => f.Level, f => f.ServerRelativeUrl); web.Context.ExecuteQueryRetry(); // Item1 = was file added to the template // Item2 = file name (if file found) var imageAddedTuple = LoadAndAddPageImage(web, file, template, creationInfo, scope); if (!string.IsNullOrEmpty(imageAddedTuple.Item2)) { if (!imageAddedTuple.Item2.EndsWith(".aspx", StringComparison.InvariantCultureIgnoreCase)) { if (imageAddedTuple.Item1) { // Keep track of the exported file path and it's UniqueId exportedFiles.Add(uniqueId.ToString(), file.ServerRelativeUrl.Substring(web.ServerRelativeUrl.Length).TrimStart("/".ToCharArray())); } } else { if (!exportedPages.ContainsKey(uniqueId.ToString())) { exportedPages.Add(uniqueId.ToString(), file.ServerRelativeUrl.Substring(web.ServerRelativeUrl.Length).TrimStart("/".ToCharArray())); } } } } } catch (Exception ex) { scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ClientSidePageContents_ErrorDuringFileExport, ex.Message); errorneousOrNonImageFileGuids.Add(uniqueId.ToString()); } } // Tokenize based on the found files, use a different token for encoded guids do we can later on replace by a new encoded guid foreach (var exportedFile in exportedFiles) { controlInstance.JsonControlData = Regex.Replace(controlInstance.JsonControlData, exportedFile.Key.Replace("-", "%2D"), $"{{fileuniqueidencoded:{exportedFile.Value}}}", RegexOptions.IgnoreCase); controlInstance.JsonControlData = Regex.Replace(controlInstance.JsonControlData, exportedFile.Key, $"{{fileuniqueid:{exportedFile.Value}}}", RegexOptions.IgnoreCase); } foreach (var exportedPage in exportedPages) { controlInstance.JsonControlData = Regex.Replace(controlInstance.JsonControlData, exportedPage.Key.Replace("-", "%2D"), $"{{pageuniqueidencoded:{exportedPage.Value}}}", RegexOptions.IgnoreCase); controlInstance.JsonControlData = Regex.Replace(controlInstance.JsonControlData, exportedPage.Key, $"{{pageuniqueid:{exportedPage.Value}}}", RegexOptions.IgnoreCase); } } } // add control to section sectionInstance.Controls.Add(controlInstance); } } extractedPageInstance.Sections.Add(sectionInstance); } // Renumber the sections...when editing modern homepages you can end up with section with order 0.5 or 0.75...let's ensure we render section as of 1 int sectionOrder = 1; foreach (var sectionInstance in extractedPageInstance.Sections) { sectionInstance.Order = sectionOrder; sectionOrder++; } // Add the page to the template template.ClientSidePages.Add(extractedPageInstance); // Set the homepage if (isHomePage) { if (template.WebSettings == null) { template.WebSettings = new WebSettings(); } if (pageUrl.StartsWith(web.ServerRelativeUrl, StringComparison.InvariantCultureIgnoreCase)) { template.WebSettings.WelcomePage = pageUrl.Replace(web.ServerRelativeUrl + "/", ""); } else { template.WebSettings.WelcomePage = pageUrl; } } } } catch (ArgumentException ex) { scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ClientSidePageContents_NoValidPage, ex.Message); } }
/// <summary> /// Actual implementation of the apply templates /// </summary> /// <param name="web"></param> /// <param name="template"></param> /// <param name="provisioningInfo"></param> internal void ApplyRemoteTemplate(Web web, ProvisioningTemplate template, ProvisioningTemplateApplyingInformation provisioningInfo) { using (var scope = new PnPMonitoredScope(CoreResources.Provisioning_ObjectHandlers_Provisioning)) { ProvisioningProgressDelegate progressDelegate = null; ProvisioningMessagesDelegate messagesDelegate = 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); } } List <ObjectHandlerBase> objectHandlers = new List <ObjectHandlerBase>(); objectHandlers.Add(new ObjectRegionalSettings()); objectHandlers.Add(new ObjectSupportedUILanguages()); objectHandlers.Add(new ObjectAuditSettings()); objectHandlers.Add(new ObjectSitePolicy()); objectHandlers.Add(new ObjectSiteSecurity()); objectHandlers.Add(new ObjectFeatures()); objectHandlers.Add(new ObjectTermGroups()); objectHandlers.Add(new ObjectField()); objectHandlers.Add(new ObjectContentType()); objectHandlers.Add(new ObjectListInstance()); objectHandlers.Add(new ObjectLookupFields()); objectHandlers.Add(new ObjectListInstanceDataRows()); objectHandlers.Add(new ObjectFiles()); objectHandlers.Add(new ObjectPages()); objectHandlers.Add(new ObjectCustomActions()); objectHandlers.Add(new ObjectPublishing()); objectHandlers.Add(new ObjectComposedLook()); objectHandlers.Add(new ObjectSearchSettings()); objectHandlers.Add(new ObjectWorkflows()); objectHandlers.Add(new ObjectPropertyBagEntry()); objectHandlers.Add(new ObjectExtensibilityProviders()); objectHandlers.Add(new ObjectPersistTemplateInfo()); var tokenParser = new TokenParser(web, template); int step = 1; var count = objectHandlers.Count(o => o.ReportProgress && o.WillProvision(web, template)); foreach (var handler in objectHandlers) { if (handler.WillProvision(web, template)) { if (messagesDelegate != null) { handler.MessagesDelegate = messagesDelegate; } if (handler.ReportProgress && progressDelegate != null) { progressDelegate(handler.Name, step, count); step++; } tokenParser = handler.ProvisionObjects(web, template, tokenParser, provisioningInfo); } } } }
/// <summary> /// Actual implementation of the apply templates /// </summary> /// <param name="web"></param> /// <param name="template"></param> /// <param name="provisioningInfo"></param> internal void ApplyRemoteTemplate(Web web, ProvisioningTemplate template, ProvisioningTemplateApplyingInformation provisioningInfo) { using (var scope = new PnPMonitoredScope(CoreResources.Provisioning_ObjectHandlers_Provisioning)) { ProvisioningProgressDelegate progressDelegate = null; ProvisioningMessagesDelegate messagesDelegate = 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); } } else { // When no provisioning info was passed then we want to execute all handlers provisioningInfo = new ProvisioningTemplateApplyingInformation(); provisioningInfo.HandlersToProcess = Handlers.All; } // 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); if (provisioningInfo.MessagesDelegate!= null) { provisioningInfo.MessagesDelegate(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()); if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Fields) || provisioningInfo.HandlersToProcess.HasFlag(Handlers.Lists)) objectHandlers.Add(new ObjectField()); if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.ContentTypes)) objectHandlers.Add(new ObjectContentType()); if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Lists)) objectHandlers.Add(new ObjectListInstance()); if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Fields) || provisioningInfo.HandlersToProcess.HasFlag(Handlers.Lists)) objectHandlers.Add(new ObjectLookupFields()); 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.Files)) objectHandlers.Add(new ObjectFiles()); if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Pages)) objectHandlers.Add(new ObjectPages()); if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.PageContents)) objectHandlers.Add(new ObjectPageContents()); 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.PublishingPages)) objectHandlers.Add(new ObjectPublishingPages()); 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.Navigation)) objectHandlers.Add(new ObjectNavigation()); 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 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 = 1; var count = objectHandlers.Count(o => o.ReportProgress && o.WillProvision(web, template)); foreach (var handler in objectHandlers) { if (handler.WillProvision(web, template)) { if (messagesDelegate != null) { handler.MessagesDelegate = messagesDelegate; } if (handler.ReportProgress && progressDelegate != null) { progressDelegate(handler.Name, step, count); step++; } tokenParser = handler.ProvisionObjects(web, template, tokenParser, provisioningInfo); } } } }
public override ProvisioningTemplate ExtractObjects(Web web, ProvisioningTemplate template, ProvisioningTemplateCreationInformation creationInfo) { using (var scope = new PnPMonitoredScope(this.Name)) { // Load object if not there #if !CLIENTSDKV15 web.EnsureProperties(w => w.Url, w => w.MasterUrl, w => w.AlternateCssUrl, w => w.SiteLogoUrl); #else web.EnsureProperties(w => w.Url, w => w.MasterUrl); #endif // Information coming from the site template.ComposedLook.MasterPage = Tokenize(web.MasterUrl, web.Url); #if !CLIENTSDKV15 template.ComposedLook.AlternateCSS = Tokenize(web.AlternateCssUrl, web.Url); template.ComposedLook.SiteLogo = Tokenize(web.SiteLogoUrl, web.Url); #else template.ComposedLook.AlternateCSS = null; template.ComposedLook.SiteLogo = null; #endif scope.LogInfo(CoreResources.Provisioning_ObjectHandlers_ComposedLooks_ExtractObjects_Retrieving_current_composed_look); var theme = web.GetCurrentComposedLook(); if (theme != null) { if (creationInfo != null) { // Don't exclude the DesignPreviewThemedCssFolderUrl property bag, if any creationInfo.PropertyBagPropertiesToPreserve.Add("DesignPreviewThemedCssFolderUrl"); } template.ComposedLook.Name = theme.Name; if (theme.IsCustomComposedLook) { if (creationInfo != null && creationInfo.PersistComposedLookFiles && creationInfo.FileConnector != null) { Site site = (web.Context as ClientContext).Site; if (!site.IsObjectPropertyInstantiated("Url")) { web.Context.Load(site); web.Context.ExecuteQueryRetry(); } scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_ComposedLooks_ExtractObjects_Creating_SharePointConnector); // Let's create a SharePoint connector since our files anyhow are in SharePoint at this moment SharePointConnector spConnector = new SharePointConnector(web.Context, web.Url, "dummy"); // to get files from theme catalog we need a connector linked to the root site SharePointConnector spConnectorRoot; if (!site.Url.Equals(web.Url, StringComparison.InvariantCultureIgnoreCase)) { spConnectorRoot = new SharePointConnector(web.Context.Clone(site.Url), site.Url, "dummy"); } else { spConnectorRoot = spConnector; } // Download the theme/branding specific files DownLoadFile(spConnector, spConnectorRoot, creationInfo.FileConnector, web.Url, web.AlternateCssUrl, scope); DownLoadFile(spConnector, spConnectorRoot, creationInfo.FileConnector, web.Url, web.SiteLogoUrl, scope); DownLoadFile(spConnector, spConnectorRoot, creationInfo.FileConnector, web.Url, theme.BackgroundImage, scope); DownLoadFile(spConnector, spConnectorRoot, creationInfo.FileConnector, web.Url, theme.Theme, scope); DownLoadFile(spConnector, spConnectorRoot, creationInfo.FileConnector, web.Url, theme.Font, scope); } template.ComposedLook.BackgroundFile = FixFileUrl(Tokenize(theme.BackgroundImage, web.Url)); template.ComposedLook.ColorFile = FixFileUrl(Tokenize(theme.Theme, web.Url)); template.ComposedLook.FontFile = FixFileUrl(Tokenize(theme.Font, web.Url)); // Create file entries for the custom theme files if (!string.IsNullOrEmpty(template.ComposedLook.BackgroundFile)) { template.Files.Add(GetComposedLookFile(template.ComposedLook.BackgroundFile)); } if (!string.IsNullOrEmpty(template.ComposedLook.ColorFile)) { template.Files.Add(GetComposedLookFile(template.ComposedLook.ColorFile)); } if (!string.IsNullOrEmpty(template.ComposedLook.FontFile)) { template.Files.Add(GetComposedLookFile(template.ComposedLook.FontFile)); } if (!string.IsNullOrEmpty(template.ComposedLook.SiteLogo)) { template.Files.Add(GetComposedLookFile(template.ComposedLook.SiteLogo)); } // If a base template is specified then use that one to "cleanup" the generated template model if (creationInfo != null && creationInfo.BaseTemplate != null) { template = CleanupEntities(template, creationInfo.BaseTemplate); } } else { template.ComposedLook.BackgroundFile = ""; template.ComposedLook.ColorFile = ""; template.ComposedLook.FontFile = ""; } } else { template.ComposedLook = null; } if (creationInfo != null && creationInfo.BaseTemplate != null) { template = CleanupEntities(template, creationInfo.BaseTemplate); } } return template; }
public override ProvisioningTemplate ExtractObjects(Web web, ProvisioningTemplate template, ProvisioningTemplateCreationInformation creationInfo) { using (var scope = new PnPMonitoredScope(this.Name)) { // Load object if not there #if !CLIENTSDKV15 web.EnsureProperties(w => w.Url, w => w.MasterUrl, w => w.AlternateCssUrl, w => w.SiteLogoUrl); #else web.EnsureProperties(w => w.Url, w => w.MasterUrl); #endif // Information coming from the site template.ComposedLook.MasterPage = Tokenize(web.MasterUrl, web.Url); #if !CLIENTSDKV15 template.ComposedLook.AlternateCSS = Tokenize(web.AlternateCssUrl, web.Url); template.ComposedLook.SiteLogo = Tokenize(web.SiteLogoUrl, web.Url); #else template.ComposedLook.AlternateCSS = null; template.ComposedLook.SiteLogo = null; #endif scope.LogInfo(CoreResources.Provisioning_ObjectHandlers_ComposedLooks_ExtractObjects_Retrieving_current_composed_look); Site site = (web.Context as ClientContext).Site; if (!site.IsObjectPropertyInstantiated("Url")) { web.Context.Load(site); web.Context.ExecuteQueryRetry(); } SharePointConnector spConnector = new SharePointConnector(web.Context, web.Url, "dummy"); // to get files from theme catalog we need a connector linked to the root site SharePointConnector spConnectorRoot; if (!site.Url.Equals(web.Url, StringComparison.InvariantCultureIgnoreCase)) { spConnectorRoot = new SharePointConnector(web.Context.Clone(site.Url), site.Url, "dummy"); } else { spConnectorRoot = spConnector; } // Check if we have composed look info in the property bag, if so, use that, otherwise try to detect the current composed look if (web.PropertyBagContainsKey("_PnP_ProvisioningTemplateComposedLookInfo")) { scope.LogInfo(CoreResources.Provisioning_ObjectHandlers_ComposedLooks_ExtractObjects_Using_ComposedLookInfoFromPropertyBag); try { var composedLook = JsonConvert.DeserializeObject<ComposedLook>(web.GetPropertyBagValueString("_PnP_ProvisioningTemplateComposedLookInfo", "")); if (composedLook.Name == null || composedLook.BackgroundFile == null || composedLook.FontFile == null || composedLook.MasterPage == null || composedLook.SiteLogo == null) { scope.LogError(CoreResources.Provisioning_ObjectHandlers_ComposedLooks_ExtractObjects_ComposedLookInfoFailedToDeserialize); throw new JsonSerializationException(); } template.ComposedLook = composedLook; if (creationInfo != null && creationInfo.PersistComposedLookFiles && creationInfo.FileConnector != null) { scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_ComposedLooks_ExtractObjects_Creating_SharePointConnector); // Let's create a SharePoint connector since our files anyhow are in SharePoint at this moment // Download the theme/branding specific files #if !CLIENTSDKV15 DownLoadFile(spConnector, spConnectorRoot, creationInfo.FileConnector, web.Url, web.AlternateCssUrl, scope); DownLoadFile(spConnector, spConnectorRoot, creationInfo.FileConnector, web.Url, web.SiteLogoUrl, scope); #endif TokenParser parser = new TokenParser(web, template); DownLoadFile(spConnector, spConnectorRoot, creationInfo.FileConnector, web.Url, parser.ParseString(composedLook.BackgroundFile), scope); DownLoadFile(spConnector, spConnectorRoot, creationInfo.FileConnector, web.Url, parser.ParseString(composedLook.ColorFile), scope); DownLoadFile(spConnector, spConnectorRoot, creationInfo.FileConnector, web.Url, parser.ParseString(composedLook.FontFile), scope); } // Create file entries for the custom theme files if (!string.IsNullOrEmpty(template.ComposedLook.BackgroundFile)) { template.Files.Add(GetComposedLookFile(template.ComposedLook.BackgroundFile)); } if (!string.IsNullOrEmpty(template.ComposedLook.ColorFile)) { template.Files.Add(GetComposedLookFile(template.ComposedLook.ColorFile)); } if (!string.IsNullOrEmpty(template.ComposedLook.FontFile)) { template.Files.Add(GetComposedLookFile(template.ComposedLook.FontFile)); } if (!string.IsNullOrEmpty(template.ComposedLook.SiteLogo)) { template.Files.Add(GetComposedLookFile(template.ComposedLook.SiteLogo)); } } catch (JsonSerializationException) { // cannot deserialize the object, fall back to composed look detection template = DetectComposedLook(web, template, creationInfo, scope, spConnector, spConnectorRoot); } } else { template = DetectComposedLook(web, template, creationInfo, scope, spConnector, spConnectorRoot); } if (creationInfo != null && creationInfo.BaseTemplate != null) { template = CleanupEntities(template, creationInfo.BaseTemplate); } } return template; }
/// <summary> /// Actual implementation of the apply templates /// </summary> /// <param name="web"></param> /// <param name="template"></param> /// <param name="provisioningInfo"></param> internal void ApplyRemoteTemplate(Web web, ProvisioningTemplate template, ProvisioningTemplateApplyingInformation provisioningInfo) { using (var scope = new PnPMonitoredScope(CoreResources.Provisioning_ObjectHandlers_Provisioning)) { ProvisioningProgressDelegate progressDelegate = null; ProvisioningMessagesDelegate messagesDelegate = 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); } } List<ObjectHandlerBase> objectHandlers = new List<ObjectHandlerBase>(); objectHandlers.Add(new ObjectRegionalSettings()); objectHandlers.Add(new ObjectSupportedUILanguages()); objectHandlers.Add(new ObjectAuditSettings()); objectHandlers.Add(new ObjectSitePolicy()); objectHandlers.Add(new ObjectSiteSecurity()); objectHandlers.Add(new ObjectFeatures()); objectHandlers.Add(new ObjectTermGroups()); objectHandlers.Add(new ObjectField()); objectHandlers.Add(new ObjectContentType()); objectHandlers.Add(new ObjectListInstance()); objectHandlers.Add(new ObjectLookupFields()); objectHandlers.Add(new ObjectListInstanceDataRows()); objectHandlers.Add(new ObjectFiles()); objectHandlers.Add(new ObjectPages()); objectHandlers.Add(new ObjectCustomActions()); objectHandlers.Add(new ObjectPublishing()); objectHandlers.Add(new ObjectComposedLook()); objectHandlers.Add(new ObjectSearchSettings()); objectHandlers.Add(new ObjectWorkflows()); objectHandlers.Add(new ObjectPropertyBagEntry()); objectHandlers.Add(new ObjectExtensibilityProviders()); objectHandlers.Add(new ObjectPersistTemplateInfo()); var tokenParser = new TokenParser(web, template); int step = 1; var count = objectHandlers.Count(o => o.ReportProgress && o.WillProvision(web, template)); foreach (var handler in objectHandlers) { if (handler.WillProvision(web, template)) { if (messagesDelegate != null) { handler.MessagesDelegate = messagesDelegate; } if (handler.ReportProgress && progressDelegate != null) { progressDelegate(handler.Name, step, count); step++; } tokenParser = handler.ProvisionObjects(web, template, tokenParser, provisioningInfo); } } } }