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);
                    }
                }
            }
        }
Example #2
0
        // 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);
                }
            }
        }
Example #3
0
        // 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);
                }
            }
        }
Example #4
0
        /// <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);
                        }
                    }
                }
            }
        }
Example #5
0
        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);
                        }
                    }
                }
            }
        }
Example #6
0
        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");
                }
            }
        }
Example #7
0
        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);
        }
Example #10
0
        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}");
                }
            }
        }
Example #11
0
        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();
            }
        }
Example #12
0
 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);
                 }
             }
         }
     }
 }
Example #13
0
        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 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");
                }
            }
        }
Example #17
0
 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);
 }
Example #18
0
 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);
        }
Example #20
0
        /// <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);
            }
        }
Example #21
0
        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.");
                }
            }
        }
Example #30
0
        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);
        }
Example #31
0
        /// <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);
                    }
                }
            }
        }
Example #32
0
        /// <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);
            }
        }
Example #33
0
        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.");
                }
            }
        }
Example #34
0
        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);
            }
        }
Example #36
0
        /// <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);
                    }
                }
            }
        }