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;
 }
 internal static void LocalizeView(this Microsoft.SharePoint.Client.View view, Web web, string token, TokenParser parser, PnPMonitoredScope scope)
 {
     if (CanUseAcceptLanguageHeaderForLocalization(web))
     {
         var context = web.Context;
         var resourceValues = parser.GetResourceTokenResourceValues(token);
         foreach (var resourceValue in resourceValues)
         {
             // Save property with correct locale on the request to make it stick
             // http://sadomovalex.blogspot.no/2015/09/localize-web-part-titles-via-client.html
             context.PendingRequest.RequestExecutor.WebRequest.Headers["Accept-Language"] = resourceValue.Item1;
             view.Title = resourceValue.Item2;
             view.Update();
             context.ExecuteQueryRetry();
         }
     }
     else
     {
         // warning
         scope.LogWarning(CoreResources.Provisioning_Extensions_ViewLocalization_Skip);
     }
 }
Beispiel #3
0
        public override TokenParser ProvisionObjects(Web web, ProvisioningTemplate template, TokenParser parser, ProvisioningTemplateApplyingInformation applyingInformation)
        {
            using (var scope = new PnPMonitoredScope(this.Name))
            {
                if (!template.Lists.Any())
                {
                    return(parser);
                }

                web.EnsureProperties(w => w.ServerRelativeUrl);

                web.Context.Load(web.Lists, lc => lc.IncludeWithDefaultProperties(l => l.RootFolder.ServerRelativeUrl));
                web.Context.ExecuteQueryRetry();

                #region DataRows

                foreach (var listInstance in template.Lists)
                {
                    if (listInstance.DataRows != null && listInstance.DataRows.Any())
                    {
                        scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_ListInstancesDataRows_Processing_data_rows_for__0_, listInstance.Title);
                        // Retrieve the target list
                        var list = web.Lists.GetByTitle(parser.ParseString(listInstance.Title));
                        web.Context.Load(list);

                        // Retrieve the fields' types from the list
                        Microsoft.SharePoint.Client.FieldCollection fields = list.Fields;
                        web.Context.Load(fields, fs => fs.Include(f => f.InternalName, f => f.FieldTypeKind, f => f.TypeAsString, f => f.ReadOnlyField, f => f.Title));
                        web.Context.ExecuteQueryRetry();

                        var keyColumnType   = "Text";
                        var parsedKeyColumn = parser.ParseString(listInstance.DataRows.KeyColumn);
                        if (!string.IsNullOrEmpty(parsedKeyColumn))
                        {
                            var keyColumn = fields.FirstOrDefault(f => f.InternalName.Equals(parsedKeyColumn, StringComparison.InvariantCultureIgnoreCase));
                            if (keyColumn != null)
                            {
                                switch (keyColumn.FieldTypeKind)
                                {
                                case FieldType.User:
                                case FieldType.Lookup:
                                    keyColumnType = "Lookup";
                                    break;

                                case FieldType.URL:
                                    keyColumnType = "Url";
                                    break;

                                case FieldType.DateTime:
                                    keyColumnType = "DateTime";
                                    break;

                                case FieldType.Number:
                                case FieldType.Counter:
                                    keyColumnType = "Number";
                                    break;
                                }
                            }
                        }

                        foreach (var dataRow in listInstance.DataRows)
                        {
                            try
                            {
                                scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_ListInstancesDataRows_Creating_list_item__0_, listInstance.DataRows.IndexOf(dataRow) + 1);

                                bool     processItem = true;
                                ListItem listitem    = null;

                                if (!string.IsNullOrEmpty(listInstance.DataRows.KeyColumn))
                                {
                                    // Get value from key column
                                    var dataRowValues = dataRow.Values.Where(v => v.Key == listInstance.DataRows.KeyColumn).ToList();

                                    // if it is empty, skip the check
                                    if (dataRowValues.Any())
                                    {
                                        var query     = $@"<View><Query><Where><Eq><FieldRef Name=""{parsedKeyColumn}""/><Value Type=""{keyColumnType}"">{parser.ParseString(dataRowValues.FirstOrDefault().Value)}</Value></Eq></Where></Query><RowLimit>1</RowLimit></View>";
                                        var camlQuery = new CamlQuery()
                                        {
                                            ViewXml = query
                                        };
                                        var existingItems = list.GetItems(camlQuery);
                                        list.Context.Load(existingItems);
                                        list.Context.ExecuteQueryRetry();
                                        if (existingItems.Count > 0)
                                        {
                                            if (listInstance.DataRows.UpdateBehavior == UpdateBehavior.Skip)
                                            {
                                                processItem = false;
                                            }
                                            else
                                            {
                                                listitem    = existingItems[0];
                                                processItem = true;
                                            }
                                        }
                                    }
                                }

                                if (processItem)
                                {
                                    if (listitem == null)
                                    {
                                        var listitemCI = new ListItemCreationInformation();
                                        listitem = list.AddItem(listitemCI);
                                    }

                                    ListItemUtilities.UpdateListItem(web, listitem, parser, fields, dataRow.Values);

                                    if (dataRow.Security != null && (dataRow.Security.ClearSubscopes || dataRow.Security.CopyRoleAssignments || dataRow.Security.RoleAssignments.Count > 0))
                                    {
                                        listitem.SetSecurity(parser, dataRow.Security);
                                    }
                                }
                            }
                            catch (ServerException ex)
                            {
                                if (ex.ServerErrorTypeName.Equals("Microsoft.SharePoint.SPDuplicateValuesFoundException", StringComparison.InvariantCultureIgnoreCase) &&
                                    applyingInformation.IgnoreDuplicateDataRowErrors)
                                {
                                    scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ListInstancesDataRows_Creating_listitem_duplicate);
                                    continue;
                                }
                            }
                            catch (Exception ex)
                            {
                                scope.LogError(CoreResources.Provisioning_ObjectHandlers_ListInstancesDataRows_Creating_listitem_failed___0_____1_, ex.Message, ex.StackTrace);
                                throw;
                            }
                        }
                    }
                }

                #endregion DataRows
            }

            return(parser);
        }
        public override ProvisioningTemplate ExtractObjects(Web web, ProvisioningTemplate template, ProvisioningTemplateCreationInformation creationInfo)
        {
            using (var scope = new PnPMonitoredScope(this.Name))
            {
                web.EnsureProperties(w => w.ServerRelativeUrl, w => w.Url);

                var serverRelativeUrl = web.ServerRelativeUrl;

                // For each list in the site
                var lists = web.Lists;

                web.Context.Load(lists,
                    lc => lc.IncludeWithDefaultProperties(
                        l => l.ContentTypes,
                        l => l.Views,
                        l => l.EnableModeration,
                        l => l.ForceCheckout,
                        l => l.BaseTemplate,
                        l => l.OnQuickLaunch,
                        l => l.RootFolder.ServerRelativeUrl,
                        l => l.UserCustomActions,
                        l => l.MajorVersionLimit,
                        l => l.MajorWithMinorVersionsLimit,
                        l => l.DraftVersionVisibility,
                        l => l.DocumentTemplateUrl,
                        l => l.Fields.IncludeWithDefaultProperties(
                            f => f.Id,
                            f => f.Title,
                            f => f.Hidden,
                            f => f.InternalName,
                            f => f.DefaultValue,
                            f => f.Required)));

                web.Context.ExecuteQueryRetry();

                var allLists = new List<List>();

                if (web.IsSubSite())
                {
                    // If current web is subweb then include the lists in the rootweb for lookup column support
                    var rootWeb = (web.Context as ClientContext).Site.RootWeb;
                    rootWeb.Context.Load(rootWeb.Lists, lsts => lsts.Include(l => l.Id, l => l.Title));
                    rootWeb.Context.ExecuteQueryRetry();
                    foreach (var rootList in rootWeb.Lists)
                    {
                        allLists.Add(rootList);
                    }
                }

                foreach (var list in lists)
                {
                    allLists.Add(list);
                }
                // Let's see if there are workflow subscriptions
                Microsoft.SharePoint.Client.WorkflowServices.WorkflowSubscription[] workflowSubscriptions = null;
                try
                {
                    workflowSubscriptions = web.GetWorkflowSubscriptions();
                }
                catch (ServerException)
                {
                    // If there is no workflow service present in the farm this method will throw an error.
                    // Swallow the exception
                }

                // Retrieve all not hidden lists and the Workflow History Lists, just in case there are active workflow subscriptions
                foreach (var siteList in lists.AsEnumerable().Where(l => (l.Hidden == false || ((workflowSubscriptions != null && workflowSubscriptions.Length > 0) && l.BaseTemplate == 140))))
                {
                    ListInstance baseTemplateList = null;
                    if (creationInfo.BaseTemplate != null)
                    {
                        // Check if we need to skip this list...if so let's do it before we gather all the other information for this list...improves performance
                        var index = creationInfo.BaseTemplate.Lists.FindIndex(f => f.Url.Equals(siteList.RootFolder.ServerRelativeUrl.Substring(serverRelativeUrl.Length + 1)) &&
                                                                                   f.TemplateType.Equals(siteList.BaseTemplate));
                        if (index != -1)
                        {
                            baseTemplateList = creationInfo.BaseTemplate.Lists[index];
                        }
                    }

                    var contentTypeFields = new List<FieldRef>();
                    var list = new ListInstance
                    {
                        Description = siteList.Description,
                        EnableVersioning = siteList.EnableVersioning,
                        TemplateType = siteList.BaseTemplate,
                        Title = siteList.Title,
                        Hidden = siteList.Hidden,
                        EnableFolderCreation = siteList.EnableFolderCreation,
                        DocumentTemplate = Tokenize(siteList.DocumentTemplateUrl, web.Url),
                        ContentTypesEnabled = siteList.ContentTypesEnabled,
                        Url = siteList.RootFolder.ServerRelativeUrl.Substring(serverRelativeUrl.Length).TrimStart('/'),
                        TemplateFeatureID = siteList.TemplateFeatureId,
                        EnableAttachments = siteList.EnableAttachments,
                        OnQuickLaunch = siteList.OnQuickLaunch,
                        EnableModeration = siteList.EnableModeration,
                        MaxVersionLimit =
                            siteList.IsPropertyAvailable("MajorVersionLimit") ? siteList.MajorVersionLimit : 0,
                        EnableMinorVersions = siteList.EnableMinorVersions,
                        MinorVersionLimit =
                            siteList.IsPropertyAvailable("MajorWithMinorVersionsLimit")
                                ? siteList.MajorWithMinorVersionsLimit
                                : 0,
                        ForceCheckout = siteList.IsPropertyAvailable("ForceCheckout") ?
                            siteList.ForceCheckout : false,
                        DraftVersionVisibility = siteList.IsPropertyAvailable("DraftVersionVisibility") ? (int)siteList.DraftVersionVisibility : 0,
                    };

                    if (creationInfo.PersistMultiLanguageResources)
                    {
            #if !SP2013
                        if (UserResourceExtensions.PersistResourceValue(siteList.TitleResource, string.Format("List_{0}_Title", siteList.Title.Replace(" ", "_")), template, creationInfo))
                        {
                            list.Title = string.Format("{{res:List_{0}_Title}}", siteList.Title.Replace(" ", "_"));
                        }
                        if (UserResourceExtensions.PersistResourceValue(siteList.DescriptionResource, string.Format("List_{0}_Description", siteList.Title.Replace(" ", "_")), template, creationInfo))
                        {
                            list.Description = string.Format("{{res:List_{0}_Description}}", siteList.Title.Replace(" ", "_"));
                        }
            #endif
                    }

                    list = ExtractContentTypes(web, siteList, contentTypeFields, list);

                    list = ExtractViews(siteList, list);

                    list = ExtractFields(web, siteList, contentTypeFields, list, allLists, creationInfo, template);

                    list = ExtractUserCustomActions(web, siteList, list, creationInfo, template);

                    list.Security = siteList.GetSecurity();

                    var logCTWarning = false;
                    if (baseTemplateList != null)
                    {
                        if (!baseTemplateList.Equals(list))
                        {
                            scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_ListInstances_Adding_list___0_____1_, list.Title, list.Url);
                            template.Lists.Add(list);
                            if (list.ContentTypesEnabled && list.ContentTypeBindings.Any() && web.IsSubSite())
                            {
                                logCTWarning = true;
                            }
                        }
                    }
                    else
                    {
                        scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_ListInstances_Adding_list___0_____1_, list.Title, list.Url);
                        template.Lists.Add(list);
                        if (list.ContentTypesEnabled && list.ContentTypeBindings.Any() && web.IsSubSite())
                        {
                            logCTWarning = true;
                        }

                    }
                    if (logCTWarning)
                    {
                        scope.LogWarning("You are extracting a template from a subweb. List '{0}' refers to content types. Content types are not exported when extracting a template from a subweb", siteList.Title);
                        WriteWarning(string.Format("You are extracting a template from a subweb. List '{0}' refers to content types. Content types are not exported when extracting a template from a subweb", siteList.Title), ProvisioningMessageType.Warning);
                    }
                }

            }
            return template;
        }
        private Tuple<List, TokenParser> CreateList(Web web, ListInstance list, TokenParser parser, PnPMonitoredScope scope)
        {
            var listCreate = new ListCreationInformation();
            listCreate.Description = list.Description;
            listCreate.TemplateType = list.TemplateType;
            listCreate.Title = parser.ParseString(list.Title);

            // the line of code below doesn't add the list to QuickLaunch
            // the OnQuickLaunch property is re-set on the Created List object
            listCreate.QuickLaunchOption = list.OnQuickLaunch ? QuickLaunchOptions.On : QuickLaunchOptions.Off;

            listCreate.Url = parser.ParseString(list.Url);
            listCreate.TemplateFeatureId = list.TemplateFeatureID;

            var createdList = web.Lists.Add(listCreate);
            createdList.Update();
            web.Context.Load(createdList, l => l.BaseTemplate);
            web.Context.ExecuteQueryRetry();

            if (!String.IsNullOrEmpty(list.DocumentTemplate))
            {
                createdList.DocumentTemplateUrl = parser.ParseString(list.DocumentTemplate);
            }

            // EnableAttachments are not supported for DocumentLibraries and Surveys
            // TODO: the user should be warned
            if (createdList.BaseTemplate != (int)ListTemplateType.DocumentLibrary && createdList.BaseTemplate != (int)ListTemplateType.Survey)
            {
                createdList.EnableAttachments = list.EnableAttachments;
            }

            createdList.EnableModeration = list.EnableModeration;

            // Done for all other lists than for Survey - With Surveys versioning configuration will cause an exception
            if (createdList.BaseTemplate != (int)ListTemplateType.Survey)
            {
                createdList.EnableVersioning = list.EnableVersioning;
                if (list.EnableVersioning)
                {
            #if !CLIENTSDKV15
                    createdList.MajorVersionLimit = list.MaxVersionLimit;
            #endif

                    if (createdList.BaseTemplate == (int)ListTemplateType.DocumentLibrary)
                    {
                        // Only supported on Document Libraries
                        createdList.EnableMinorVersions = list.EnableMinorVersions;
                        createdList.DraftVersionVisibility = (DraftVisibilityType)list.DraftVersionVisibility;

                        if (list.EnableMinorVersions)
                        {
                            createdList.MajorWithMinorVersionsLimit = list.MinorVersionLimit; // Set only if enabled, otherwise you'll get exception due setting value to zero.

                            // DraftVisibilityType.Approver is available only when the EnableModeration option of the list is true
                            if (DraftVisibilityType.Approver ==
                                (DraftVisibilityType)list.DraftVersionVisibility)
                            {
                                if (list.EnableModeration)
                                {
                                    createdList.DraftVersionVisibility =
                                        (DraftVisibilityType)list.DraftVersionVisibility;
                                }
                                else
                                {
                                    scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ListInstances_DraftVersionVisibility_not_applied_because_EnableModeration_is_not_set_to_true);
                                    WriteWarning(CoreResources.Provisioning_ObjectHandlers_ListInstances_DraftVersionVisibility_not_applied_because_EnableModeration_is_not_set_to_true, ProvisioningMessageType.Warning);
                                }
                            }
                            else
                            {
                                createdList.DraftVersionVisibility = (DraftVisibilityType)list.DraftVersionVisibility;
                            }
                        }
                    }
                }
            }

            createdList.OnQuickLaunch = list.OnQuickLaunch;
            if (createdList.BaseTemplate != (int)ListTemplateType.DiscussionBoard)
            {
                createdList.EnableFolderCreation = list.EnableFolderCreation;
            }
            createdList.Hidden = list.Hidden;
            createdList.ContentTypesEnabled = list.ContentTypesEnabled;

            createdList.Update();

            web.Context.Load(createdList.Views);
            web.Context.Load(createdList, l => l.Id);
            web.Context.Load(createdList, l => l.RootFolder.ServerRelativeUrl);
            web.Context.Load(createdList.ContentTypes);
            web.Context.ExecuteQueryRetry();

            // Remove existing content types only if there are custom content type bindings
            var contentTypesToRemove = new List<ContentType>();
            if (list.RemoveExistingContentTypes && list.ContentTypeBindings.Count > 0)
            {
                contentTypesToRemove.AddRange(createdList.ContentTypes);
            }

            ContentTypeBinding defaultCtBinding = null;
            foreach (var ctBinding in list.ContentTypeBindings)
            {
                var tempCT = web.GetContentTypeById(ctBinding.ContentTypeId, searchInSiteHierarchy: true);
                if (tempCT != null)
                {
                    // Check if CT is already available
                    var name = tempCT.EnsureProperty(ct => ct.Name);
                    if (!createdList.ContentTypeExistsByName(name))
                    {
                        createdList.AddContentTypeToListById(ctBinding.ContentTypeId, searchContentTypeInSiteHierarchy: true);
                    }
                    if (ctBinding.Default)
                    {
                        defaultCtBinding = ctBinding;
                    }
                }
            }

            // default ContentTypeBinding should be set last because
            // list extension .SetDefaultContentTypeToList() re-sets
            // the list.RootFolder UniqueContentTypeOrder property
            // which may cause missing CTs from the "New Button"
            if (defaultCtBinding != null)
            {
                createdList.SetDefaultContentTypeToList(defaultCtBinding.ContentTypeId);
            }

            // Effectively remove existing content types, if any
            foreach (var ct in contentTypesToRemove)
            {
                ct.DeleteObject();
                web.Context.ExecuteQueryRetry();
            }

            if (list.Security != null)
            {
                createdList.SetSecurity(parser, list.Security);
            }
            return Tuple.Create(createdList, parser);
        }
        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;
                if (string.IsNullOrEmpty(homepageUrl))
                {
                    homepageUrl = "Default.aspx";
                }
                var welcomePageUrl = UrlUtility.Combine(web.ServerRelativeUrl, homepageUrl);

                var file = web.GetFileByServerRelativeUrl(welcomePageUrl);
                try
                {
                    var listItem = file.EnsureProperty(f => f.ListItemAllFields);
                    if (listItem != null)
                    {
                        if (listItem.FieldValues.ContainsKey("WikiField"))
                        {
                            // Wiki page
                            var fullUri = new Uri(UrlUtility.Combine(web.Url, web.RootFolder.WelcomePage));

                            var folderPath = fullUri.Segments.Take(fullUri.Segments.Count() - 1).ToArray().Aggregate((i, x) => i + x).TrimEnd('/');
                            var fileName = fullUri.Segments[fullUri.Segments.Count() - 1];

                            var homeFile = web.GetFileByServerRelativeUrl(welcomePageUrl);

                            LimitedWebPartManager limitedWPManager =
                                homeFile.GetLimitedWebPartManager(PersonalizationScope.Shared);

                            web.Context.Load(limitedWPManager);

                            var webParts = web.GetWebParts(welcomePageUrl);

                            var page = new Page()
                            {
                                Layout = WikiPageLayout.Custom,
                                Overwrite = true,
                                Url = Tokenize(fullUri.PathAndQuery, web.Url),
                            };
                            var pageContents = listItem.FieldValues["WikiField"].ToString();

                            Regex regexClientIds = new Regex(@"id=\""div_(?<ControlId>(\w|\-)+)");
                            if (regexClientIds.IsMatch(pageContents))
                            {
                                foreach (Match webPartMatch in regexClientIds.Matches(pageContents))
                                {
                                    String serverSideControlId = webPartMatch.Groups["ControlId"].Value;

                                    try
                                    {
                                        String serverSideControlIdToSearchFor = String.Format("g_{0}",
                                            serverSideControlId.Replace("-", "_"));

                                        WebPartDefinition webPart = limitedWPManager.WebParts.GetByControlId(serverSideControlIdToSearchFor);
                                        web.Context.Load(webPart,
                                            wp => wp.Id,
                                            wp => wp.WebPart.Title,
                                            wp => wp.WebPart.ZoneIndex
                                            );
                                        web.Context.ExecuteQueryRetry();

                                        var webPartxml = TokenizeWebPartXml(web, web.GetWebPartXml(webPart.Id, welcomePageUrl));

                                        page.WebParts.Add(new Model.WebPart()
                                        {
                                            Title = webPart.WebPart.Title,
                                            Contents = webPartxml,
                                            Order = (uint)webPart.WebPart.ZoneIndex,
                                            Row = 1, // By default we will create a onecolumn layout, add the webpart to it, and later replace the wikifield on the page to position the webparts correctly.
                                            Column = 1 // By default we will create a onecolumn layout, add the webpart to it, and later replace the wikifield on the page to position the webparts correctly.
                                        });

                                        pageContents = Regex.Replace(pageContents, serverSideControlId, string.Format("{{webpartid:{0}}}", webPart.WebPart.Title), RegexOptions.IgnoreCase);
                                    }
                                    catch (ServerException)
                                    {
                                        scope.LogWarning("Found a WebPart ID which is not available on the server-side. ID: {0}", serverSideControlId);
                                    }
                                }
                            }

                            page.Fields.Add("WikiField", pageContents);
                            template.Pages.Add(page);

                            // Set the homepage
                            if (template.WebSettings == null)
                            {
                                template.WebSettings = new WebSettings();
                            }
                            template.WebSettings.WelcomePage = homepageUrl;

                        }
                        else
                        {
                            if (web.Context.HasMinimalServerLibraryVersion(Constants.MINIMUMZONEIDREQUIREDSERVERVERSION))
                            {
                                // Not a wikipage
                                template = GetFileContents(web, template, welcomePageUrl, creationInfo, scope);
                                if (template.WebSettings == null)
                                {
                                    template.WebSettings = new WebSettings();
                                }
                                template.WebSettings.WelcomePage = homepageUrl;
                            }
                            else
                            {
                                WriteWarning(string.Format("Page content export requires a server version that is newer than the current server. Server version is {0}, minimal required is {1}", web.Context.ServerLibraryVersion, Constants.MINIMUMZONEIDREQUIREDSERVERVERSION), ProvisioningMessageType.Warning);
                                scope.LogWarning("Page content export requires a server version that is newer than the current server. Server version is {0}, minimal required is {1}", web.Context.ServerLibraryVersion, Constants.MINIMUMZONEIDREQUIREDSERVERVERSION);
                            }
                        }
                    }
                }
                catch (ServerException ex)
                {
                    if (ex.ServerErrorCode != -2146232832)
                    {
                        throw;
                    }
                    else
                    {
                        if (web.Context.HasMinimalServerLibraryVersion(Constants.MINIMUMZONEIDREQUIREDSERVERVERSION))
                        {
                            // Page does not belong to a list, extract the file as is
                            template = GetFileContents(web, template, welcomePageUrl, creationInfo, scope);
                            if (template.WebSettings == null)
                            {
                                template.WebSettings = new WebSettings();
                            }
                            template.WebSettings.WelcomePage = homepageUrl;
                        }
                        else
                        {
                            WriteWarning(string.Format("Page content export requires a server version that is newer than the current server. Server version is {0}, minimal required is {1}", web.Context.ServerLibraryVersion, Constants.MINIMUMZONEIDREQUIREDSERVERVERSION), ProvisioningMessageType.Warning);
                            scope.LogWarning("Page content export requires a server version that is newer than the current server. Server version is {0}, minimal required is {1}", web.Context.ServerLibraryVersion, Constants.MINIMUMZONEIDREQUIREDSERVERVERSION);
                        }
                    }
                }

                // 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;
        }
        private static void UpdateContentType(Web web, Microsoft.SharePoint.Client.ContentType existingContentType, ContentType templateContentType, TokenParser parser, PnPMonitoredScope scope, bool isNoScriptSite = false)
        {
            var isDirty = false;
            if (existingContentType.Hidden != templateContentType.Hidden)
            {
                scope.LogPropertyUpdate("Hidden");
                existingContentType.Hidden = templateContentType.Hidden;
                isDirty = true;
            }
            if (existingContentType.ReadOnly != templateContentType.ReadOnly)
            {
                scope.LogPropertyUpdate("ReadOnly");
                existingContentType.ReadOnly = templateContentType.ReadOnly;
                isDirty = true;
            }
            if (existingContentType.Sealed != templateContentType.Sealed)
            {
                scope.LogPropertyUpdate("Sealed");
                existingContentType.Sealed = templateContentType.Sealed;
                isDirty = true;
            }
            if (templateContentType.Description != null && existingContentType.Description != parser.ParseString(templateContentType.Description))
            {
                scope.LogPropertyUpdate("Description");
                existingContentType.Description = parser.ParseString(templateContentType.Description);
                isDirty = true;
            }
            if (templateContentType.DocumentTemplate != null && existingContentType.DocumentTemplate != parser.ParseString(templateContentType.DocumentTemplate))
            {
                scope.LogPropertyUpdate("DocumentTemplate");
                existingContentType.DocumentTemplate = parser.ParseString(templateContentType.DocumentTemplate);
                isDirty = true;
            }
            if (existingContentType.Name != parser.ParseString(templateContentType.Name))
            {
                scope.LogPropertyUpdate("Name");
                existingContentType.Name = parser.ParseString(templateContentType.Name);
                isDirty = true;
                // CT is being renamed, add an extra token to the tokenparser
                parser.AddToken(new ContentTypeIdToken(web, existingContentType.Name, existingContentType.StringId));
            }
            if (templateContentType.Group != null && existingContentType.Group != parser.ParseString(templateContentType.Group))
            {
                scope.LogPropertyUpdate("Group");
                existingContentType.Group = parser.ParseString(templateContentType.Group);
                isDirty = true;
            }
            if (!isNoScriptSite)
            {
                if (templateContentType.DisplayFormUrl != null && existingContentType.DisplayFormUrl != parser.ParseString(templateContentType.DisplayFormUrl))
                {
                    scope.LogPropertyUpdate("DisplayFormUrl");
                    existingContentType.DisplayFormUrl = parser.ParseString(templateContentType.DisplayFormUrl);
                    isDirty = true;
                }
                if (templateContentType.EditFormUrl != null && existingContentType.EditFormUrl != parser.ParseString(templateContentType.EditFormUrl))
                {
                    scope.LogPropertyUpdate("EditFormUrl");
                    existingContentType.EditFormUrl = parser.ParseString(templateContentType.EditFormUrl);
                    isDirty = true;
                }
                if (templateContentType.NewFormUrl != null && existingContentType.NewFormUrl != parser.ParseString(templateContentType.NewFormUrl))
                {
                    scope.LogPropertyUpdate("NewFormUrl");
                    existingContentType.NewFormUrl = parser.ParseString(templateContentType.NewFormUrl);
                    isDirty = true;
                }
            }
            else
            {
                if (!String.IsNullOrEmpty(parser.ParseString(templateContentType.DisplayFormUrl)) ||
                    !String.IsNullOrEmpty(parser.ParseString(templateContentType.EditFormUrl)) ||
                    !String.IsNullOrEmpty(parser.ParseString(templateContentType.NewFormUrl)))
                {
                    // log message
                    scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ContentTypes_SkipCustomFormUrls, existingContentType.Name);
                }
            }

            #if !SP2013
            if (templateContentType.Name.ContainsResourceToken())
            {
                existingContentType.NameResource.SetUserResourceValue(templateContentType.Name, parser);
                isDirty = true;
            }
            if (templateContentType.Description.ContainsResourceToken())
            {
                existingContentType.DescriptionResource.SetUserResourceValue(templateContentType.Description, parser);
                isDirty = true;
            }
            #endif
            if (isDirty)
            {
                existingContentType.Update(true);
                web.Context.ExecuteQueryRetry();
            }
            // Delta handling
            existingContentType.EnsureProperty(c => c.FieldLinks);
            List<Guid> targetIds = existingContentType.FieldLinks.AsEnumerable().Select(c1 => c1.Id).ToList();
            List<Guid> sourceIds = templateContentType.FieldRefs.Select(c1 => c1.Id).ToList();

            var fieldsNotPresentInTarget = sourceIds.Except(targetIds).ToArray();

            if (fieldsNotPresentInTarget.Any())
            {
                foreach (var fieldId in fieldsNotPresentInTarget)
                {
                    var fieldRef = templateContentType.FieldRefs.Find(fr => fr.Id == fieldId);
                    var field = web.Fields.GetById(fieldId);
                    scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_ContentTypes_Adding_field__0__to_content_type, fieldId);
                    web.AddFieldToContentType(existingContentType, field, fieldRef.Required, fieldRef.Hidden);
                }
            }

            isDirty = false;
            foreach (var fieldId in targetIds.Intersect(sourceIds))
            {
                var fieldLink = existingContentType.FieldLinks.FirstOrDefault(fl => fl.Id == fieldId);
                var fieldRef = templateContentType.FieldRefs.Find(fr => fr.Id == fieldId);
                if (fieldRef != null)
                {
                    scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_ContentTypes_Field__0__exists_in_content_type, fieldId);
                    if (fieldLink.Required != fieldRef.Required)
                    {
                        scope.LogPropertyUpdate("Required");
                        fieldLink.Required = fieldRef.Required;
                        isDirty = true;
                    }
                    if (fieldLink.Hidden != fieldRef.Hidden)
                    {
                        scope.LogPropertyUpdate("Hidden");
                        fieldLink.Hidden = fieldRef.Hidden;
                        isDirty = true;
                    }
                }
            }

            // The new CT is a DocumentSet, and the target should be, as well
            if (templateContentType.DocumentSetTemplate != null)
            {
                if (!Microsoft.SharePoint.Client.DocumentSet.DocumentSetTemplate.IsChildOfDocumentSetContentType(web.Context, existingContentType).Value)
                {
                    scope.LogError(CoreResources.Provisioning_ObjectHandlers_ContentTypes_InvalidDocumentSet_Update_Request, existingContentType.Id, existingContentType.Name);
                }
                else
                {
                    Microsoft.SharePoint.Client.DocumentSet.DocumentSetTemplate templateToUpdate =
                        Microsoft.SharePoint.Client.DocumentSet.DocumentSetTemplate.GetDocumentSetTemplate(web.Context, existingContentType);

                    // TODO: Implement Delta Handling
                    scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ContentTypes_DocumentSet_DeltaHandling_OnHold, existingContentType.Id, existingContentType.Name);
                }
            }

            if (isDirty)
            {
                existingContentType.Update(true);
                web.Context.ExecuteQueryRetry();
            }
        }
        public override TokenParser ProvisionObjects(Web web, ProvisioningTemplate template, TokenParser parser, ProvisioningTemplateApplyingInformation applyingInformation)
        {
            using (var scope = new PnPMonitoredScope(this.Name))
            {
                var context = web.Context as ClientContext;

                web.EnsureProperties(w => w.ServerRelativeUrl);

                // Check if this is not a noscript site as we're not allowed to update some properties
                bool isNoScriptSite = web.IsNoScriptSite();

                foreach (var clientSidePage in template.ClientSidePages)
                {
                    // determine pages library
                    string pagesLibrary = "SitePages";
                    string pageName     = $"{System.IO.Path.GetFileNameWithoutExtension(clientSidePage.PageName)}.aspx";

                    string url = $"{pagesLibrary}/{pageName}";

                    url = UrlUtility.Combine(web.ServerRelativeUrl, url);

                    var exists = true;
                    Microsoft.SharePoint.Client.File file = null;
                    try
                    {
                        file = web.GetFileByServerRelativeUrl(url);
                        web.Context.Load(file);
                        web.Context.ExecuteQueryRetry();
                    }
                    catch (ServerException ex)
                    {
                        if (ex.ServerErrorTypeName == "System.IO.FileNotFoundException")
                        {
                            exists = false;
                        }
                    }

                    Pages.ClientSidePage page = null;
                    if (exists)
                    {
                        if (clientSidePage.Overwrite)
                        {
                            // Get the existing page
                            page = web.LoadClientSidePage(pageName);
                            // Clear the page
                            page.ClearPage();
                        }
                        else
                        {
                            scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ClientSidePages_NoOverWrite, pageName);
                        }
                    }
                    else
                    {
                        // Create new client side page
                        page = web.AddClientSidePage(pageName);
                    }

                    // Load existing available controls
                    var componentsToAdd = page.AvailableClientSideComponents();

                    // if no section specified then add a default single column section
                    if (!clientSidePage.Sections.Any())
                    {
                        clientSidePage.Sections.Add(new CanvasSection()
                        {
                            Type = CanvasSectionType.OneColumn, Order = 10
                        });
                    }

                    int sectionCount = -1;
                    // Apply the "layout" and content
                    foreach (var section in clientSidePage.Sections)
                    {
                        sectionCount++;
                        switch (section.Type)
                        {
                        case CanvasSectionType.OneColumn:
                            page.AddSection(Pages.CanvasSectionTemplate.OneColumn, section.Order);
                            break;

                        case CanvasSectionType.OneColumnFullWidth:
                            page.AddSection(Pages.CanvasSectionTemplate.OneColumnFullWidth, section.Order);
                            break;

                        case CanvasSectionType.TwoColumn:
                            page.AddSection(Pages.CanvasSectionTemplate.TwoColumn, section.Order);
                            break;

                        case CanvasSectionType.ThreeColumn:
                            page.AddSection(Pages.CanvasSectionTemplate.ThreeColumn, section.Order);
                            break;

                        case CanvasSectionType.TwoColumnLeft:
                            page.AddSection(Pages.CanvasSectionTemplate.TwoColumnLeft, section.Order);
                            break;

                        case CanvasSectionType.TwoColumnRight:
                            page.AddSection(Pages.CanvasSectionTemplate.TwoColumnRight, section.Order);
                            break;

                        default:
                            page.AddSection(Pages.CanvasSectionTemplate.OneColumn, section.Order);
                            break;
                        }

                        // Add controls to the section
                        if (section.Controls.Any())
                        {
                            // Safety measure: reset column order to 1 for columns marked with 0 or lower
                            foreach (var control in section.Controls.Where(p => p.Column <= 0).ToList())
                            {
                                control.Column = 1;
                            }

                            foreach (var control in section.Controls)
                            {
                                Pages.ClientSideComponent baseControl = null;

                                // Is it a text control?
                                if (control.Type == WebPartType.Text)
                                {
                                    Pages.ClientSideText textControl = new Pages.ClientSideText();
                                    if (control.ControlProperties.Any())
                                    {
                                        var textProperty = control.ControlProperties.First();
                                        textControl.Text = textProperty.Value;
                                        // Reduce column number by 1 due 0 start indexing
                                        page.AddControl(textControl, page.Sections[sectionCount].Columns[control.Column - 1], control.Order);
                                    }
                                }
                                // It is a web part
                                else
                                {
                                    // Is a custom developed client side web part (3rd party)
                                    if (control.Type == WebPartType.Custom)
                                    {
                                        if (!string.IsNullOrEmpty(control.CustomWebPartName))
                                        {
                                            baseControl = componentsToAdd.Where(p => p.Name.Equals(control.CustomWebPartName, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
                                        }
                                        else if (control.ControlId != Guid.Empty)
                                        {
                                            baseControl = componentsToAdd.Where(p => p.Id.Equals($"{{{control.ControlId.ToString()}}}", StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();
                                        }
                                    }
                                    // Is an OOB client side web part (1st party)
                                    else
                                    {
                                        string webPartName = "";
                                        switch (control.Type)
                                        {
                                        case WebPartType.Image:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.Image);
                                            break;

                                        case WebPartType.BingMap:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.BingMap);
                                            break;

                                        case WebPartType.ContentEmbed:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.ContentEmbed);
                                            break;

                                        case WebPartType.ContentRollup:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.ContentRollup);
                                            break;

                                        case WebPartType.DocumentEmbed:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.DocumentEmbed);
                                            break;

                                        case WebPartType.Events:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.Events);
                                            break;

                                        case WebPartType.GroupCalendar:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.GroupCalendar);
                                            break;

                                        case WebPartType.Hero:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.Hero);
                                            break;

                                        case WebPartType.ImageGallery:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.ImageGallery);
                                            break;

                                        case WebPartType.LinkPreview:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.LinkPreview);
                                            break;

                                        case WebPartType.List:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.List);
                                            break;

                                        case WebPartType.NewsFeed:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.NewsFeed);
                                            break;

                                        case WebPartType.NewsReel:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.NewsReel);
                                            break;

                                        case WebPartType.PageTitle:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.PageTitle);
                                            break;

                                        case WebPartType.People:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.People);
                                            break;

                                        case WebPartType.PowerBIReportEmbed:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.PowerBIReportEmbed);
                                            break;

                                        case WebPartType.QuickChart:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.QuickChart);
                                            break;

                                        case WebPartType.QuickLinks:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.QuickLinks);
                                            break;

                                        case WebPartType.SiteActivity:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.SiteActivity);
                                            break;

                                        case WebPartType.VideoEmbed:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.VideoEmbed);
                                            break;

                                        case WebPartType.YammerEmbed:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.YammerEmbed);
                                            break;

                                        default:
                                            break;
                                        }

                                        baseControl = componentsToAdd.Where(p => p.Name.Equals(webPartName, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
                                    }

                                    if (baseControl != null)
                                    {
                                        Pages.ClientSideWebPart myWebPart = new Pages.ClientSideWebPart(baseControl)
                                        {
                                            Order = control.Order
                                        };

                                        // Reduce column number by 1 due 0 start indexing
                                        page.AddControl(myWebPart, page.Sections[sectionCount].Columns[control.Column - 1], control.Order);

                                        // set properties using json string
                                        if (!String.IsNullOrEmpty(control.JsonControlData))
                                        {
                                            myWebPart.PropertiesJson = control.JsonControlData;
                                        }

                                        // set using property collection
                                        if (control.ControlProperties.Any())
                                        {
                                            // grab the "default" properties so we can deduct their types, needed to correctly apply the set properties
                                            var    controlManifest   = JObject.Parse(baseControl.Manifest);
                                            JToken controlProperties = null;
                                            if (controlManifest != null)
                                            {
                                                controlProperties = controlManifest.SelectToken("preconfiguredEntries[0].properties");
                                            }

                                            foreach (var property in control.ControlProperties)
                                            {
                                                Type propertyType = typeof(string);

                                                if (controlProperties != null)
                                                {
                                                    var defaultProperty = controlProperties.SelectToken(property.Key, false);
                                                    if (defaultProperty != null)
                                                    {
                                                        propertyType = Type.GetType($"System.{defaultProperty.Type.ToString()}");

                                                        if (propertyType == null)
                                                        {
                                                            if (defaultProperty.Type.ToString().Equals("integer", StringComparison.InvariantCultureIgnoreCase))
                                                            {
                                                                propertyType = typeof(int);
                                                            }
                                                        }
                                                    }
                                                }

                                                myWebPart.Properties[property.Key] = JToken.FromObject(Convert.ChangeType(parser.ParseString(property.Value), propertyType));
                                            }
                                        }
                                    }
                                    else
                                    {
                                        scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ClientSidePages_BaseControlNotFound, control.ControlId, control.CustomWebPartName);
                                    }
                                }
                            }
                        }
                    }

                    // Persist the page
                    page.Save(pageName);

                    // Make it a news page if requested
                    if (clientSidePage.PromoteAsNewsArticle)
                    {
                        page.PromoteAsNewsArticle();
                    }
                }
            }
            return(parser);
        }
        public override ProvisioningTemplate ExtractObjects(Web web, ProvisioningTemplate template, ProvisioningTemplateCreationInformation creationInfo)
        {
            using (var scope = new PnPMonitoredScope(this.Name))
            {
                web.EnsureProperties(w => w.ServerRelativeUrl, w => w.Url);

                var serverRelativeUrl = web.ServerRelativeUrl;

                // For each list in the site
                var lists = web.Lists;

                web.Context.Load(lists,
                    lc => lc.IncludeWithDefaultProperties(
                        l => l.ContentTypes,
                        l => l.Views,
                        l => l.BaseTemplate,
                        l => l.OnQuickLaunch,
                        l => l.RootFolder.ServerRelativeUrl,
                        l => l.Fields.IncludeWithDefaultProperties(
                            f => f.Id,
                            f => f.Title,
                            f => f.Hidden,
                            f => f.InternalName,
                            f => f.Required)));

                web.Context.ExecuteQueryRetry();

                // Let's see if there are workflow subscriptions
                Microsoft.SharePoint.Client.WorkflowServices.WorkflowSubscription[] workflowSubscriptions = null;
                try
                {
                    workflowSubscriptions = web.GetWorkflowSubscriptions();
                }
                catch (ServerException)
                {
                    // If there is no workflow service present in the farm this method will throw an error.
                    // Swallow the exception
                }

                // Retrieve all not hidden lists and the Workflow History Lists, just in case there are active workflow subscriptions
                foreach (var siteList in lists.AsEnumerable().Where(l => (l.Hidden == false || ((workflowSubscriptions != null && workflowSubscriptions.Length > 0) && l.BaseTemplate == 140))))
                {
                    ListInstance baseTemplateList = null;
                    if (creationInfo.BaseTemplate != null)
                    {
                        // Check if we need to skip this list...if so let's do it before we gather all the other information for this list...improves performance
                        var index = creationInfo.BaseTemplate.Lists.FindIndex(f => f.Url.Equals(siteList.RootFolder.ServerRelativeUrl.Substring(serverRelativeUrl.Length + 1)) &&
                                                                                   f.TemplateType.Equals(siteList.BaseTemplate));
                        if (index != -1)
                        {
                            baseTemplateList = creationInfo.BaseTemplate.Lists[index];
                        }
                    }

                    var contentTypeFields = new List<FieldRef>();
                    var list = new ListInstance
                    {
                        Description = siteList.Description,
                        EnableVersioning = siteList.EnableVersioning,
                        TemplateType = siteList.BaseTemplate,
                        Title = siteList.Title,
                        Hidden = siteList.Hidden,
                        EnableFolderCreation = siteList.EnableFolderCreation,
                        DocumentTemplate = Tokenize(siteList.DocumentTemplateUrl, web.Url),
                        ContentTypesEnabled = siteList.ContentTypesEnabled,
                        Url = siteList.RootFolder.ServerRelativeUrl.Substring(serverRelativeUrl.Length).TrimStart('/'),
                        TemplateFeatureID = siteList.TemplateFeatureId,
                        EnableAttachments = siteList.EnableAttachments,
                        OnQuickLaunch = siteList.OnQuickLaunch,
                        MaxVersionLimit =
                            siteList.IsObjectPropertyInstantiated("MajorVersionLimit") ? siteList.MajorVersionLimit : 0,
                        EnableMinorVersions = siteList.EnableMinorVersions,
                        MinorVersionLimit =
                            siteList.IsObjectPropertyInstantiated("MajorWithMinorVersionsLimit")
                                ? siteList.MajorWithMinorVersionsLimit
                                : 0
                    };
                    var count = 0;

                    foreach (var ct in siteList.ContentTypes)
                    {
                        web.Context.Load(ct, c => c.Parent);
                        web.Context.ExecuteQueryRetry();

                        list.ContentTypeBindings.Add(new ContentTypeBinding
                        {
                            ContentTypeId = ct.Parent != null ? ct.Parent.StringId : ct.StringId,
                            Default = count == 0
                        });

                        //if (ct.Parent != null)
                        //{
                        //    //Add the parent to the list of content types
                        //    if (!BuiltInContentTypeId.Contains(ct.Parent.StringId))
                        //    {
                        //    list.ContentTypeBindings.Add(new ContentTypeBinding { ContentTypeId = ct.Parent.StringId, Default = count == 0 });
                        //    }
                        //}
                        //else
                        //{
                        //    list.ContentTypeBindings.Add(new ContentTypeBinding { ContentTypeId = ct.StringId, Default = count == 0 });
                        //}

                        web.Context.Load(ct.FieldLinks);
                        web.Context.ExecuteQueryRetry();
                        foreach (var fieldLink in ct.FieldLinks)
                        {
                            if (!fieldLink.Hidden)
                            {
                                contentTypeFields.Add(new FieldRef() { Id = fieldLink.Id });
                            }
                        }
                        count++;
                    }

                    foreach (var view in siteList.Views.AsEnumerable().Where(view => !view.Hidden))
                    {
                        var schemaElement = XElement.Parse(view.ListViewXml);

                        // Toolbar is not supported

                        var toolbarElement = schemaElement.Descendants("Toolbar").FirstOrDefault();
                        if (toolbarElement != null)
                        {
                            toolbarElement.Remove();
                        }

                        // XslLink is not supported
                        var xslLinkElement = schemaElement.Descendants("XslLink").FirstOrDefault();
                        if (xslLinkElement != null)
                        {
                            xslLinkElement.Remove();
                        }

                        list.Views.Add(new View { SchemaXml = schemaElement.ToString() });
                    }

                    var siteColumns = web.Fields;
                    web.Context.Load(siteColumns, scs => scs.Include(sc => sc.Id));
                    web.Context.ExecuteQueryRetry();

                    foreach (var field in siteList.Fields.AsEnumerable().Where(field => !field.Hidden))
                    {
                        if (siteColumns.FirstOrDefault(sc => sc.Id == field.Id) != null)
                        {
                            var addField = true;
                            if (siteList.ContentTypesEnabled && contentTypeFields.FirstOrDefault(c => c.Id == field.Id) == null)
                            {
                                if (contentTypeFields.FirstOrDefault(c => c.Id == field.Id) == null)
                                {
                                    addField = false;
                                }
                            }

                            var fieldElement = XElement.Parse(field.SchemaXml);
                            var sourceId = fieldElement.Attribute("SourceID") != null ? fieldElement.Attribute("SourceID").Value : null;

                            if (sourceId != null && sourceId == "http://schemas.microsoft.com/sharepoint/v3")
                            {
                                if (field.InternalName == "Editor" ||
                                    field.InternalName == "Author" ||
                                    field.InternalName == "Title" ||
                                    field.InternalName == "ID" ||
                                    field.InternalName == "Created" ||
                                    field.InternalName == "Modified" ||
                                    field.InternalName == "Attachments" ||
                                    field.InternalName == "_UIVersionString" ||
                                    field.InternalName == "DocIcon" ||
                                    field.InternalName == "LinkTitleNoMenu" ||
                                    field.InternalName == "LinkTitle" ||
                                    field.InternalName == "Edit" ||
                                    field.InternalName == "AppAuthor" ||
                                    field.InternalName == "AppEditor" ||
                                    field.InternalName == "ContentType" ||
                                    field.InternalName == "ItemChildCount" ||
                                    field.InternalName == "FolderChildCount" ||
                                    field.InternalName == "LinkFilenameNoMenu" ||
                                    field.InternalName == "LinkFilename" ||
                                    field.InternalName == "_CopySource" ||
                                    field.InternalName == "ParentVersionString" ||
                                    field.InternalName == "ParentLeafName" ||
                                    field.InternalName == "_CheckinComment" ||
                                    field.InternalName == "FileLeafRef" ||
                                    field.InternalName == "FileSizeDisplay" ||
                                    field.InternalName == "Preview" ||
                                    field.InternalName == "ThumbnailOnForm")
                                {
                                    addField = false;
                                }
                            }
                            if (addField)
                            {
                                list.FieldRefs.Add(new FieldRef(field.InternalName)
                                {
                                    Id = field.Id,
                                    DisplayName = field.Title,
                                    Required = field.Required,
                                    Hidden = field.Hidden,
                                });
                            }
                        }
                        else
                        {
                            list.Fields.Add((new Model.Field { SchemaXml = field.SchemaXml }));
                        }

                        list.Security = siteList.GetSecurity();
                    }
                    var logCTWarning = false;
                    if (baseTemplateList != null)
                    {
                        if (!baseTemplateList.Equals(list))
                        {
                            scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_ListInstances_Adding_list___0_____1_, list.Title, list.Url);
                            template.Lists.Add(list);
                            if (list.ContentTypesEnabled && list.ContentTypeBindings.Any() && web.IsSubSite())
                            {
                                logCTWarning = true;
                            }
                        }
                    }
                    else
                    {
                        scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_ListInstances_Adding_list___0_____1_, list.Title, list.Url);
                        template.Lists.Add(list);
                        if (list.ContentTypesEnabled && list.ContentTypeBindings.Any() && web.IsSubSite())
                        {
                            logCTWarning = true;
                        }

                    }
                    if (logCTWarning)
                    {
                        scope.LogWarning("You are extracting a template from a subweb. List '{0}' refers to content types. Content types are not exported when extracting a template from a subweb", list.Title);
                        WriteWarning(string.Format("You are extracting a template from a subweb. List '{0}' refers to content types. Content types are not exported when extracting a template from a subweb", list.Title), ProvisioningMessageType.Warning);
                    }
                }

            }
            return template;
        }
Beispiel #10
0
        public override TokenParser ProvisionObjects(Web web, ProvisioningTemplate template, TokenParser parser, ProvisioningTemplateApplyingInformation applyingInformation)
        {
            using (var scope = new PnPMonitoredScope(this.Name))
            {
                // Check if this is not a noscript site as we're not allowed to write to the web property bag is that one
                bool isNoScriptSite = web.IsNoScriptSite();

                var context = web.Context as ClientContext;

                web.EnsureProperties(w => w.ServerRelativeUrl, w => w.Url);

                // Build on the fly the list of additional files coming from the Directories
                var directoryFiles = new List <Model.File>();
                foreach (var directory in template.Directories)
                {
                    var metadataProperties = directory.GetMetadataProperties();
                    directoryFiles.AddRange(directory.GetDirectoryFiles(metadataProperties));
                }

                foreach (var file in template.Files.Union(directoryFiles))
                {
                    var folderName = parser.ParseString(file.Folder);

                    if (folderName.ToLower().StartsWith((web.ServerRelativeUrl.ToLower())))
                    {
                        folderName = folderName.Substring(web.ServerRelativeUrl.Length);
                    }

                    if (SkipFile(isNoScriptSite, file.Src, folderName))
                    {
                        // add log message
                        scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_Files_SkipFileUpload, file.Src, folderName);
                        continue;
                    }

                    var folder = web.EnsureFolderPath(folderName);

                    var checkedOut = false;

                    var targetFile = folder.GetFile(template.Connector.GetFilenamePart(file.Src));

                    if (targetFile != null)
                    {
                        if (file.Overwrite)
                        {
                            scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_Files_Uploading_and_overwriting_existing_file__0_, file.Src);
                            checkedOut = CheckOutIfNeeded(web, targetFile);

                            using (var stream = GetFileStream(template, file))
                            {
                                targetFile = UploadFile(template, file, folder, stream);
                            }
                        }
                        else
                        {
                            checkedOut = CheckOutIfNeeded(web, targetFile);
                        }
                    }
                    else
                    {
                        using (var stream = GetFileStream(template, file))
                        {
                            scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_Files_Uploading_file__0_, file.Src);
                            targetFile = UploadFile(template, file, folder, stream);
                        }

                        checkedOut = CheckOutIfNeeded(web, targetFile);
                    }

                    if (targetFile != null)
                    {
                        if (file.Properties != null && file.Properties.Any())
                        {
                            Dictionary <string, string> transformedProperties = file.Properties.ToDictionary(property => property.Key, property => parser.ParseString(property.Value));
                            SetFileProperties(targetFile, transformedProperties, false);
                        }

#if !SP2013
                        bool webPartsNeedLocalization = false;
#endif
                        if (file.WebParts != null && file.WebParts.Any())
                        {
                            targetFile.EnsureProperties(f => f.ServerRelativeUrl);

                            var existingWebParts = web.GetWebParts(targetFile.ServerRelativeUrl).ToList();
                            foreach (var webPart in file.WebParts)
                            {
                                // check if the webpart is already set on the page
                                if (existingWebParts.FirstOrDefault(w => w.WebPart.Title == parser.ParseString(webPart.Title)) == null)
                                {
                                    scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_Files_Adding_webpart___0___to_page, webPart.Title);
                                    var wpEntity = new WebPartEntity();
                                    wpEntity.WebPartTitle = parser.ParseString(webPart.Title);
                                    wpEntity.WebPartXml   = parser.ParseString(webPart.Contents).Trim(new[] { '\n', ' ' });
                                    wpEntity.WebPartZone  = webPart.Zone;
                                    wpEntity.WebPartIndex = (int)webPart.Order;
                                    var wpd = web.AddWebPartToWebPartPage(targetFile.ServerRelativeUrl, wpEntity);
#if !SP2013
                                    if (webPart.Title.ContainsResourceToken())
                                    {
                                        // update data based on where it was added - needed in order to localize wp title
#if !SP2016
                                        wpd.EnsureProperties(w => w.ZoneId, w => w.WebPart, w => w.WebPart.Properties);
                                        webPart.Zone = wpd.ZoneId;
#else
                                        wpd.EnsureProperties(w => w.WebPart, w => w.WebPart.Properties);
#endif
                                        webPart.Order            = (uint)wpd.WebPart.ZoneIndex;
                                        webPartsNeedLocalization = true;
                                    }
#endif
                                }
                            }
                        }

#if !SP2013
                        if (webPartsNeedLocalization)
                        {
                            file.LocalizeWebParts(web, parser, targetFile, scope);
                        }
#endif

                        switch (file.Level)
                        {
                        case Model.FileLevel.Published:
                        {
                            targetFile.PublishFileToLevel(Microsoft.SharePoint.Client.FileLevel.Published);
                            break;
                        }

                        case Model.FileLevel.Draft:
                        {
                            targetFile.PublishFileToLevel(Microsoft.SharePoint.Client.FileLevel.Draft);
                            break;
                        }

                        default:
                        {
                            if (checkedOut)
                            {
                                targetFile.CheckIn("", CheckinType.MajorCheckIn);
                                web.Context.ExecuteQueryRetry();
                            }
                            break;
                        }
                        }

                        // Don't set security when nothing is defined. This otherwise breaks on files set outside of a list
                        if (file.Security != null &&
                            (file.Security.ClearSubscopes == true || file.Security.CopyRoleAssignments == true || file.Security.RoleAssignments.Count > 0))
                        {
                            targetFile.ListItemAllFields.SetSecurity(parser, file.Security);
                        }
                    }
                }
            }
            return(parser);
        }
        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;
                if (string.IsNullOrEmpty(homepageUrl))
                {
                    homepageUrl = "Default.aspx";
                }
                var welcomePageUrl = UrlUtility.Combine(web.ServerRelativeUrl, homepageUrl);

                List pagesLibrary = web.GetPagesLibrary();
                var  items        = pagesLibrary.GetItems(CamlQuery.CreateAllItemsQuery());
                web.Context.Load(items, i => i.Include(it => it.DisplayName, it => it.File));
                web.Context.ExecuteQuery();

                foreach (ListItem listItem in items)
                {
                    try
                    {
                        //var listItem = file.EnsureProperty(f => f.ListItemAllFields);
                        if (listItem != null)
                        {
                            File file = listItem.File;
                            if (listItem.FieldValues.ContainsKey("WikiField") && listItem.FieldValues["WikiField"] != null)
                            {
                                // Wiki page
                                var fullUri    = new Uri(UrlUtility.Combine(new Uri(web.Url).GetLeftPart(UriPartial.Authority), file.ServerRelativeUrl));
                                var folderPath = fullUri.Segments.Take(fullUri.Segments.Count() - 1).ToArray().Aggregate((i, x) => i + x).TrimEnd('/');

                                LimitedWebPartManager limitedWPManager = file.GetLimitedWebPartManager(PersonalizationScope.Shared);

                                web.Context.Load(limitedWPManager);

                                var webParts = web.GetWebParts(file.ServerRelativeUrl);

                                var page = new Page()
                                {
                                    Layout    = WikiPageLayout.Custom,
                                    Overwrite = true,
                                    Url       = Tokenize(fullUri.PathAndQuery, web.Url),
                                };
                                var pageContents = listItem.FieldValues["WikiField"].ToString();

                                Regex regexClientIds = new Regex(@"id=\""div_(?<ControlId>(\w|\-)+)");
                                if (regexClientIds.IsMatch(pageContents))
                                {
                                    foreach (Match webPartMatch in regexClientIds.Matches(pageContents))
                                    {
                                        String serverSideControlId = webPartMatch.Groups["ControlId"].Value;

                                        try
                                        {
                                            String serverSideControlIdToSearchFor = String.Format("g_{0}",
                                                                                                  serverSideControlId.Replace("-", "_"));

                                            WebPartDefinition webPart = limitedWPManager.WebParts.GetByControlId(serverSideControlIdToSearchFor);
                                            web.Context.Load(webPart,
                                                             wp => wp.Id,
                                                             wp => wp.WebPart.Title,
                                                             wp => wp.WebPart.ZoneIndex
                                                             );
                                            web.Context.ExecuteQueryRetry();

                                            var webPartxml = TokenizeWebPartXml(web, web.GetWebPartXml(webPart.Id, file.ServerRelativeUrl));

                                            page.WebParts.Add(new Model.WebPart()
                                            {
                                                Title    = webPart.WebPart.Title,
                                                Contents = webPartxml,
                                                Order    = (uint)webPart.WebPart.ZoneIndex,
                                                Row      = 1, // By default we will create a onecolumn layout, add the webpart to it, and later replace the wikifield on the page to position the webparts correctly.
                                                Column   = 1  // By default we will create a onecolumn layout, add the webpart to it, and later replace the wikifield on the page to position the webparts correctly.
                                            });

                                            pageContents = Regex.Replace(pageContents, serverSideControlId, string.Format("{{webpartid:{0}}}", webPart.WebPart.Title), RegexOptions.IgnoreCase);
                                        }
                                        catch (ServerException)
                                        {
                                            scope.LogWarning("Found a WebPart ID which is not available on the server-side. ID: {0}", serverSideControlId);
                                        }
                                    }
                                }

                                page.Fields.Add("WikiField", pageContents);
                                template.Pages.Add(page);

                                // Set the homepage
                                if (template.WebSettings == null)
                                {
                                    template.WebSettings = new WebSettings();
                                }
                                template.WebSettings.WelcomePage = homepageUrl;
                            }
                            else
                            {
                                if (web.Context.HasMinimalServerLibraryVersion(Constants.MINIMUMZONEIDREQUIREDSERVERVERSION))
                                {
                                    // Not a wikipage
                                    template = GetFileContents(web, template, file.ServerRelativeUrl, creationInfo, scope);
                                    if (template.WebSettings == null)
                                    {
                                        template.WebSettings = new WebSettings();
                                    }
                                    template.WebSettings.WelcomePage = homepageUrl;
                                }
                                else
                                {
                                    WriteMessage(string.Format("Page content export requires a server version that is newer than the current server. Server version is {0}, minimal required is {1}", web.Context.ServerLibraryVersion, Constants.MINIMUMZONEIDREQUIREDSERVERVERSION), ProvisioningMessageType.Warning);
                                    scope.LogWarning("Page content export requires a server version that is newer than the current server. Server version is {0}, minimal required is {1}", web.Context.ServerLibraryVersion, Constants.MINIMUMZONEIDREQUIREDSERVERVERSION);
                                }
                            }
                        }
                    }
                    catch (ServerException ex)
                    {
                        if (ex.ServerErrorCode != -2146232832)
                        {
                            throw;
                        }
                        else
                        {
                            if (web.Context.HasMinimalServerLibraryVersion(Constants.MINIMUMZONEIDREQUIREDSERVERVERSION))
                            {
                                // Page does not belong to a list, extract the file as is
                                template = GetFileContents(web, template, welcomePageUrl, creationInfo, scope);
                                if (template.WebSettings == null)
                                {
                                    template.WebSettings = new WebSettings();
                                }
                                template.WebSettings.WelcomePage = homepageUrl;
                            }
                            else
                            {
                                WriteMessage(string.Format("Page content export requires a server version that is newer than the current server. Server version is {0}, minimal required is {1}", web.Context.ServerLibraryVersion, Constants.MINIMUMZONEIDREQUIREDSERVERVERSION), ProvisioningMessageType.Warning);
                                scope.LogWarning("Page content export requires a server version that is newer than the current server. Server version is {0}, minimal required is {1}", web.Context.ServerLibraryVersion, Constants.MINIMUMZONEIDREQUIREDSERVERVERSION);
                            }
                        }
                    }
                }

                // 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);
        }
        public override TokenParser ProvisionObjects(Web web, ProvisioningTemplate template, TokenParser parser, ProvisioningTemplateApplyingInformation applyingInformation)
        {
            using (var scope = new PnPMonitoredScope(this.Name))
            {
                var site = (web.Context as ClientContext).Site;

                // Check if this is not a noscript site as publishing features are not supported
                if (web.IsNoScriptSite())
                {
                    scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_Publishing_SkipProvisioning);
                    return parser;
                }

                var webFeatureActive = web.IsFeatureActive(PUBLISHING_FEATURE_WEB);
                var siteFeatureActive = site.IsFeatureActive(PUBLISHING_FEATURE_SITE);
                if (template.Publishing.AutoCheckRequirements == AutoCheckRequirementsOptions.SkipIfNotCompliant && !webFeatureActive)
                {
                    scope.LogDebug("Publishing Feature (Web Scoped) not active. Skipping provisioning of Publishing settings");
                    return parser;
                }
                else if (template.Publishing.AutoCheckRequirements == AutoCheckRequirementsOptions.MakeCompliant)
                {
                    if (!siteFeatureActive)
                    {
                        scope.LogDebug("Making site compliant for publishing");
                        site.ActivateFeature(PUBLISHING_FEATURE_SITE);
                        web.ActivateFeature(PUBLISHING_FEATURE_WEB);
                    }
                    else
                    {
                        if (!web.IsFeatureActive(PUBLISHING_FEATURE_WEB))
                        {
                            scope.LogDebug("Making site compliant for publishing");
                            web.ActivateFeature(PUBLISHING_FEATURE_WEB);
                        }
                    }
                }
                else
                {
                    throw new Exception("Publishing Feature not active. Provisioning failed");
                }

                // Set allowed web templates
                var availableWebTemplates = template.Publishing.AvailableWebTemplates.Select(t => new WebTemplateEntity() { LanguageCode = t.LanguageCode.ToString(), TemplateName = t.TemplateName }).ToList();
                if (availableWebTemplates.Any())
                {
                    web.SetAvailableWebTemplates(availableWebTemplates);
                }

                if (template.Publishing.DesignPackage != null)
                {
                    var package = template.Publishing.DesignPackage;

                    var tempFileName = Path.Combine(Path.GetTempPath(), template.Connector.GetFilenamePart(package.DesignPackagePath));
                    scope.LogDebug("Saving {0} to temporary file: {1}", package.DesignPackagePath, tempFileName);
                    using (var stream = template.Connector.GetFileStream(package.DesignPackagePath))
                    {
                        using (var outstream = System.IO.File.Create(tempFileName))
                        {
                            stream.CopyTo(outstream);
                        }
                    }
                    scope.LogDebug("Installing design package");
                    site.InstallSolution(package.PackageGuid, tempFileName, package.MajorVersion, package.MinorVersion);
                    System.IO.File.Delete(tempFileName);
                }
                // Set allowed page layouts
                var availablePageLayouts = template.Publishing.PageLayouts.Select(p => p.Path);
                if (availablePageLayouts.Any())
                {
                    web.SetAvailablePageLayouts(site.RootWeb, availablePageLayouts);
                }

                // Set default page layout, if any
                var defaultPageLayout = template.Publishing.PageLayouts.FirstOrDefault(p => p.IsDefault);
                if (defaultPageLayout != null)
                {
                    web.SetDefaultPageLayoutForSite(site.RootWeb, defaultPageLayout.Path);
                }

                return parser;
            }
        }
Beispiel #13
0
        private void CreatePage(Web web, ProvisioningTemplate template, TokenParser parser, PnPMonitoredScope scope, BaseClientSidePage clientSidePage, string pagesLibrary, List pagesLibraryList, ref int currentPageIndex, List <string> preCreatedPages)
        {
            string pageName = DeterminePageName(parser, clientSidePage);
            string url      = $"{pagesLibrary}/{pageName}";

            if (clientSidePage.Layout == "Article" && clientSidePage.PromoteAsTemplate)
            {
                if (clientSidePage is TranslatedClientSidePage)
                {
                    url = $"{pagesLibrary}/{pageName}";
                }
                else
                {
                    url = $"{pagesLibrary}/{Pages.ClientSidePage.GetTemplatesFolder(pagesLibraryList)}/{pageName}";
                }
            }

            // Write page level status messages, needed in case many pages are provisioned
            currentPageIndex++;
            int totalPages = 0;

            foreach (var p in template.ClientSidePages)
            {
                totalPages++;
                if (p.Translations.Any())
                {
                    totalPages += p.Translations.Count();
                }
            }
            WriteSubProgress("Provision ClientSidePage", pageName, currentPageIndex, totalPages);

            url = UrlUtility.Combine(web.ServerRelativeUrl, url);

            var exists = true;

            try
            {
                var file = web.GetFileByServerRelativePath(ResourcePath.FromDecodedUrl(url));
                web.Context.Load(file);
                web.Context.ExecuteQueryRetry();
            }
            catch (ServerException ex)
            {
                if (ex.ServerErrorTypeName == "System.IO.FileNotFoundException")
                {
                    exists = false;
                }
            }

            Pages.ClientSidePage page = null;
            if (exists)
            {
                if (clientSidePage.Overwrite || preCreatedPages.Contains(url))
                {
                    if (clientSidePage.Layout == "Article" && clientSidePage.PromoteAsTemplate)
                    {
                        // Get the existing template page
                        if (clientSidePage is TranslatedClientSidePage)
                        {
                            page = web.LoadClientSidePage($"{pageName}");
                        }
                        else
                        {
                            page = web.LoadClientSidePage($"{Pages.ClientSidePage.GetTemplatesFolder(pagesLibraryList)}/{pageName}");
                        }
                    }
                    else
                    {
                        // Get the existing page
                        page = web.LoadClientSidePage(pageName);
                    }

                    // Clear the page
                    page.ClearPage();
                }
                else
                {
                    scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ClientSidePages_NoOverWrite, pageName);
                    return;
                }
            }
            else
            {
                // Create new client side page
                page = web.AddClientSidePage(pageName);
            }

            // Set page title
            string newTitle = parser.ParseString(clientSidePage.Title);

            if (page.PageTitle != newTitle)
            {
                page.PageTitle = newTitle;
            }

            // Page Header
            if (clientSidePage.Header != null)
            {
                switch (clientSidePage.Header.Type)
                {
                case ClientSidePageHeaderType.None:
                {
                    page.RemovePageHeader();
                    break;
                }

                case ClientSidePageHeaderType.Default:
                {
                    page.SetDefaultPageHeader();
                    break;
                }

                case ClientSidePageHeaderType.Custom:
                {
                    var serverRelativeImageUrl = parser.ParseString(clientSidePage.Header.ServerRelativeImageUrl);
                    if (clientSidePage.Header.TranslateX.HasValue && clientSidePage.Header.TranslateY.HasValue)
                    {
                        page.SetCustomPageHeader(serverRelativeImageUrl, clientSidePage.Header.TranslateX.Value, clientSidePage.Header.TranslateY.Value);
                    }
                    else
                    {
                        page.SetCustomPageHeader(serverRelativeImageUrl);
                    }

                    page.PageHeader.LayoutType = (Pages.ClientSidePageHeaderLayoutType)Enum.Parse(typeof(ClientSidePageHeaderLayoutType), clientSidePage.Header.LayoutType.ToString());
#if !SP2019
                    page.PageHeader.TextAlignment   = (Pages.ClientSidePageHeaderTitleAlignment)Enum.Parse(typeof(ClientSidePageHeaderTextAlignment), clientSidePage.Header.TextAlignment.ToString());
                    page.PageHeader.ShowTopicHeader = clientSidePage.Header.ShowTopicHeader;
                    page.PageHeader.ShowPublishDate = clientSidePage.Header.ShowPublishDate;
                    page.PageHeader.TopicHeader     = clientSidePage.Header.TopicHeader;
                    page.PageHeader.AlternativeText = clientSidePage.Header.AlternativeText;
                    page.PageHeader.Authors         = clientSidePage.Header.Authors;
                    page.PageHeader.AuthorByLine    = clientSidePage.Header.AuthorByLine;
                    page.PageHeader.AuthorByLineId  = clientSidePage.Header.AuthorByLineId;
#endif
                    break;
                }
                }
            }

            // Set page layout
            if (!string.IsNullOrEmpty(clientSidePage.Layout))
            {
                page.LayoutType = (Pages.ClientSidePageLayoutType)Enum.Parse(typeof(Pages.ClientSidePageLayoutType), clientSidePage.Layout);
            }

            if (!string.IsNullOrEmpty(clientSidePage.ThumbnailUrl))
            {
                page.ThumbnailUrl = parser.ParseString(clientSidePage.ThumbnailUrl);
            }

            // Add content on the page, not needed for repost pages
            if (page.LayoutType != Pages.ClientSidePageLayoutType.RepostPage)
            {
                // Load existing available controls
                var componentsToAdd = page.AvailableClientSideComponents().ToList();

                // if no section specified then add a default single column section
                if (!clientSidePage.Sections.Any())
                {
                    clientSidePage.Sections.Add(new CanvasSection()
                    {
                        Type = CanvasSectionType.OneColumn, Order = 10
                    });
                }

                int sectionCount = -1;
                // Apply the "layout" and content
                foreach (var section in clientSidePage.Sections)
                {
                    sectionCount++;
                    switch (section.Type)
                    {
                    case CanvasSectionType.OneColumn:
                        page.AddSection(Pages.CanvasSectionTemplate.OneColumn, section.Order, (Int32)section.BackgroundEmphasis);
                        break;

                    case CanvasSectionType.OneColumnFullWidth:
                        page.AddSection(Pages.CanvasSectionTemplate.OneColumnFullWidth, section.Order, (Int32)section.BackgroundEmphasis);
                        break;

                    case CanvasSectionType.TwoColumn:
                        page.AddSection(Pages.CanvasSectionTemplate.TwoColumn, section.Order, (Int32)section.BackgroundEmphasis);
                        break;

                    case CanvasSectionType.ThreeColumn:
                        page.AddSection(Pages.CanvasSectionTemplate.ThreeColumn, section.Order, (Int32)section.BackgroundEmphasis);
                        break;

                    case CanvasSectionType.TwoColumnLeft:
                        page.AddSection(Pages.CanvasSectionTemplate.TwoColumnLeft, section.Order, (Int32)section.BackgroundEmphasis);
                        break;

                    case CanvasSectionType.TwoColumnRight:
                        page.AddSection(Pages.CanvasSectionTemplate.TwoColumnRight, section.Order, (Int32)section.BackgroundEmphasis);
                        break;

#if !SP2019
                    case CanvasSectionType.OneColumnVerticalSection:
                        page.AddSection(Pages.CanvasSectionTemplate.OneColumnVerticalSection, section.Order, (Int32)section.BackgroundEmphasis, (Int32)section.VerticalSectionEmphasis);
                        break;

                    case CanvasSectionType.TwoColumnVerticalSection:
                        page.AddSection(Pages.CanvasSectionTemplate.TwoColumnVerticalSection, section.Order, (Int32)section.BackgroundEmphasis, (Int32)section.VerticalSectionEmphasis);
                        break;

                    case CanvasSectionType.TwoColumnLeftVerticalSection:
                        page.AddSection(Pages.CanvasSectionTemplate.TwoColumnLeftVerticalSection, section.Order, (Int32)section.BackgroundEmphasis, (Int32)section.VerticalSectionEmphasis);
                        break;

                    case CanvasSectionType.TwoColumnRightVerticalSection:
                        page.AddSection(Pages.CanvasSectionTemplate.TwoColumnRightVerticalSection, section.Order, (Int32)section.BackgroundEmphasis, (Int32)section.VerticalSectionEmphasis);
                        break;

                    case CanvasSectionType.ThreeColumnVerticalSection:
                        page.AddSection(Pages.CanvasSectionTemplate.ThreeColumnVerticalSection, section.Order, (Int32)section.BackgroundEmphasis, (Int32)section.VerticalSectionEmphasis);
                        break;
#endif
                    default:
                        page.AddSection(Pages.CanvasSectionTemplate.OneColumn, section.Order, (Int32)section.BackgroundEmphasis);
                        break;
                    }

                    // Add controls to the section
                    if (section.Controls.Any())
                    {
                        // Safety measure: reset column order to 1 for columns marked with 0 or lower
                        foreach (var control in section.Controls.Where(p => p.Column <= 0).ToList())
                        {
                            control.Column = 1;
                        }

                        foreach (CanvasControl control in section.Controls)
                        {
                            Pages.ClientSideComponent baseControl = null;

                            // Is it a text control?
                            if (control.Type == WebPartType.Text)
                            {
                                Pages.ClientSideText textControl = new Pages.ClientSideText();
                                if (control.ControlProperties.Any())
                                {
                                    var textProperty = control.ControlProperties.First();
                                    textControl.Text = parser.ParseString(textProperty.Value);
                                }
                                else
                                {
                                    if (!string.IsNullOrEmpty(control.JsonControlData))
                                    {
                                        var json = JsonConvert.DeserializeObject <Dictionary <string, string> >(control.JsonControlData);

                                        if (json.Count > 0)
                                        {
                                            textControl.Text = parser.ParseString(json.First().Value);
                                        }
                                    }
                                }
                                // Reduce column number by 1 due 0 start indexing
                                page.AddControl(textControl, page.Sections[sectionCount].Columns[control.Column - 1], control.Order);
                            }
                            // It is a web part
                            else
                            {
                                // apply token parsing on the web part properties
                                control.JsonControlData = parser.ParseString(control.JsonControlData);

                                // perform processing of web part properties (e.g. include listid property based list title property)
                                var webPartPostProcessor = CanvasControlPostProcessorFactory.Resolve(control);
                                webPartPostProcessor.Process(control, page);

                                // Is a custom developed client side web part (3rd party)
                                if (control.Type == WebPartType.Custom)
                                {
                                    if (!string.IsNullOrEmpty(control.CustomWebPartName))
                                    {
                                        baseControl = componentsToAdd.FirstOrDefault(p => p.Name.Equals(control.CustomWebPartName, StringComparison.InvariantCultureIgnoreCase));
                                    }
                                    else if (control.ControlId != Guid.Empty)
                                    {
                                        baseControl = componentsToAdd.FirstOrDefault(p => p.Id.Equals($"{{{control.ControlId.ToString()}}}", StringComparison.CurrentCultureIgnoreCase));

                                        if (baseControl == null)
                                        {
                                            baseControl = componentsToAdd.FirstOrDefault(p => p.Id.Equals(control.ControlId.ToString(), StringComparison.InvariantCultureIgnoreCase));
                                        }
                                    }
                                }
                                // Is an OOB client side web part (1st party)
                                else
                                {
                                    string webPartName = "";
                                    switch (control.Type)
                                    {
                                    case WebPartType.Image:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.Image);
                                        break;

#if !SP2019
                                    case WebPartType.BingMap:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.BingMap);
                                        break;

                                    case WebPartType.Button:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.Button);
                                        break;

                                    case WebPartType.CallToAction:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.CallToAction);
                                        break;

                                    case WebPartType.GroupCalendar:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.GroupCalendar);
                                        break;

                                    case WebPartType.News:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.News);
                                        break;

                                    case WebPartType.PowerBIReportEmbed:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.PowerBIReportEmbed);
                                        break;

                                    case WebPartType.Sites:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.Sites);
                                        break;

                                    case WebPartType.MicrosoftForms:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.MicrosoftForms);
                                        break;

                                    case WebPartType.ClientWebPart:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.ClientWebPart);
                                        break;
#endif
                                    case WebPartType.ContentEmbed:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.ContentEmbed);
                                        break;

                                    case WebPartType.ContentRollup:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.ContentRollup);
                                        break;

                                    case WebPartType.DocumentEmbed:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.DocumentEmbed);
                                        break;

                                    case WebPartType.Events:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.Events);
                                        break;

                                    case WebPartType.Hero:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.Hero);
                                        break;

                                    case WebPartType.ImageGallery:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.ImageGallery);
                                        break;

                                    case WebPartType.LinkPreview:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.LinkPreview);
                                        break;

                                    case WebPartType.List:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.List);
                                        break;

                                    case WebPartType.NewsFeed:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.NewsFeed);
                                        break;

                                    case WebPartType.NewsReel:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.NewsReel);
                                        break;

                                    case WebPartType.PageTitle:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.PageTitle);
                                        break;

                                    case WebPartType.People:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.People);
                                        break;

                                    case WebPartType.QuickChart:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.QuickChart);
                                        break;

                                    case WebPartType.QuickLinks:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.QuickLinks);
                                        break;

                                    case WebPartType.SiteActivity:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.SiteActivity);
                                        break;

                                    case WebPartType.VideoEmbed:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.VideoEmbed);
                                        break;

                                    case WebPartType.YammerEmbed:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.YammerEmbed);
                                        break;

                                    case WebPartType.CustomMessageRegion:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.CustomMessageRegion);
                                        break;

                                    case WebPartType.Divider:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.Divider);
                                        break;

                                    case WebPartType.Spacer:
                                        webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.Spacer);
                                        break;
                                    }

                                    baseControl = componentsToAdd.FirstOrDefault(p => p.Name.Equals(webPartName, StringComparison.InvariantCultureIgnoreCase));
                                }

                                if (baseControl != null)
                                {
                                    Pages.ClientSideWebPart myWebPart = new Pages.ClientSideWebPart(baseControl)
                                    {
                                        Order = control.Order
                                    };

                                    if (!String.IsNullOrEmpty(control.JsonControlData))
                                    {
                                        var json = JsonConvert.DeserializeObject <JObject>(control.JsonControlData);
                                        if (json["instanceId"] != null && json["instanceId"].Type != JTokenType.Null)
                                        {
                                            if (Guid.TryParse(json["instanceId"].Value <string>(), out Guid instanceId))
                                            {
                                                myWebPart.instanceId = instanceId;
                                            }
                                        }
                                    }

                                    // Reduce column number by 1 due 0 start indexing
                                    page.AddControl(myWebPart, page.Sections[sectionCount].Columns[control.Column - 1], control.Order);

                                    // set properties using json string
                                    if (!String.IsNullOrEmpty(control.JsonControlData))
                                    {
                                        myWebPart.PropertiesJson = control.JsonControlData;
                                    }

                                    // set using property collection
                                    if (control.ControlProperties.Any())
                                    {
                                        // grab the "default" properties so we can deduct their types, needed to correctly apply the set properties
                                        var    controlManifest   = JObject.Parse(baseControl.Manifest);
                                        JToken controlProperties = null;
                                        if (controlManifest != null)
                                        {
                                            controlProperties = controlManifest.SelectToken("preconfiguredEntries[0].properties");
                                        }

                                        foreach (var property in control.ControlProperties)
                                        {
                                            Type propertyType = typeof(string);

                                            if (controlProperties != null)
                                            {
                                                var defaultProperty = controlProperties.SelectToken(property.Key, false);
                                                if (defaultProperty != null)
                                                {
                                                    propertyType = Type.GetType($"System.{defaultProperty.Type.ToString()}");

                                                    if (propertyType == null)
                                                    {
                                                        if (defaultProperty.Type.ToString().Equals("integer", StringComparison.InvariantCultureIgnoreCase))
                                                        {
                                                            propertyType = typeof(int);
                                                        }
                                                    }
                                                }
                                            }

                                            myWebPart.Properties[property.Key] = JToken.FromObject(Convert.ChangeType(parser.ParseString(property.Value), propertyType));
                                        }
                                    }
                                }
                                else
                                {
                                    scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ClientSidePages_BaseControlNotFound, control.ControlId, control.CustomWebPartName);
                                }
                            }
                        }
                    }
                }
            }

            // Persist the page
            if (clientSidePage.Layout == "Article" && clientSidePage.PromoteAsTemplate)
            {
                page.SaveAsTemplate(pageName.Replace($"{Pages.ClientSidePage.GetTemplatesFolder(pagesLibraryList)}/", ""));
            }
            else
            {
                page.Save(pageName);
            }

            // Update page content type
            bool isDirty = false;
            if (!string.IsNullOrEmpty(clientSidePage.ContentTypeID))
            {
                page.PageListItem[ContentTypeIdField] = clientSidePage.ContentTypeID;
                page.PageListItem.UpdateOverwriteVersion();
                //page.PageListItem.Update();
                web.Context.Load(page.PageListItem);
                isDirty = true;
            }

            if (clientSidePage.PromoteAsTemplate && page.LayoutType == Pages.ClientSidePageLayoutType.Article)
            {
                // Choice field, currently there's only one value possible and that's Template
                page.PageListItem[SPSitePageFlagsField] = ";#Template;#";
                page.PageListItem.UpdateOverwriteVersion();
                //page.PageListItem.Update();
                web.Context.Load(page.PageListItem);
                isDirty = true;
            }

            if (isDirty)
            {
                web.Context.ExecuteQueryRetry();
            }

            if (clientSidePage.FieldValues != null && clientSidePage.FieldValues.Any())
            {
                ListItemUtilities.UpdateListItem(page.PageListItem, parser, clientSidePage.FieldValues, ListItemUtilities.ListItemUpdateType.UpdateOverwriteVersion);
            }

            // Set page property bag values
            if (clientSidePage.Properties != null && clientSidePage.Properties.Any())
            {
                string pageFilePath = page.PageListItem[FileRefField].ToString();
                var    pageFile     = web.GetFileByServerRelativeUrl(pageFilePath);
                web.Context.Load(pageFile, p => p.Properties);

                foreach (var pageProperty in clientSidePage.Properties)
                {
                    if (!string.IsNullOrEmpty(pageProperty.Key))
                    {
                        pageFile.Properties[pageProperty.Key] = pageProperty.Value;
                    }
                }

                pageFile.Update();
                web.Context.Load(page.PageListItem);
                web.Context.ExecuteQueryRetry();
            }

#if !SP2019
            if (page.LayoutType != Pages.ClientSidePageLayoutType.SingleWebPartAppPage)
            {
                // Set commenting, ignore on pages of the type Home or page templates
                if (page.LayoutType != Pages.ClientSidePageLayoutType.Home && !clientSidePage.PromoteAsTemplate)
                {
                    // Make it a news page if requested
                    if (clientSidePage.PromoteAsNewsArticle)
                    {
                        page.PromoteAsNewsArticle();
                    }
                }

                if (page.LayoutType != Pages.ClientSidePageLayoutType.RepostPage)
                {
                    if (clientSidePage.EnableComments)
                    {
                        page.EnableComments();
                    }
                    else
                    {
                        page.DisableComments();
                    }
                }
            }
#endif

            // Publish page, page templates cannot be published
            if (clientSidePage.Publish && !clientSidePage.PromoteAsTemplate)
            {
                page.Publish();
            }

            // Set any security on the page
            if (clientSidePage.Security != null && clientSidePage.Security.RoleAssignments.Count != 0)
            {
                web.Context.Load(page.PageListItem);
                web.Context.ExecuteQueryRetry();
                page.PageListItem.SetSecurity(parser, clientSidePage.Security);
            }
        }
        public override TokenParser ProvisionObjects(Web web, ProvisioningTemplate template, TokenParser parser, ProvisioningTemplateApplyingInformation applyingInformation)
        {
            using (var scope = new PnPMonitoredScope(this.Name))
            {
                if (template.WebSettings != null)
                {
                    // Check if this is not a noscript site as we're not allowed to update some properties
                    bool isNoScriptSite = web.IsNoScriptSite();

                    web.EnsureProperties(
#if !ONPREMISES
                        w => w.CommentsOnSitePagesDisabled,
#endif
                        w => w.WebTemplate,
                        w => w.HasUniqueRoleAssignments);

                    var webSettings = template.WebSettings;
#if !ONPREMISES
                    if (!isNoScriptSite)
                    {
                        web.NoCrawl = webSettings.NoCrawl;
                    }
                    else
                    {
                        scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_WebSettings_SkipNoCrawlUpdate);
                    }
#endif

                    if (!web.IsSubSite() || (web.IsSubSite() && web.HasUniqueRoleAssignments))
                    {
                        String requestAccessEmailValue = parser.ParseString(webSettings.RequestAccessEmail);
                        if (!String.IsNullOrEmpty(requestAccessEmailValue) && requestAccessEmailValue.Length >= 255)
                        {
                            requestAccessEmailValue = requestAccessEmailValue.Substring(0, 255);
                        }
                        if (!String.IsNullOrEmpty(requestAccessEmailValue))
                        {
                            web.RequestAccessEmail = requestAccessEmailValue;

                            web.Update();
                            web.Context.ExecuteQueryRetry();
                        }
                    }

#if !ONPREMISES
                    if (web.CommentsOnSitePagesDisabled != webSettings.CommentsOnSitePagesDisabled)
                    {
                        web.CommentsOnSitePagesDisabled = webSettings.CommentsOnSitePagesDisabled;
                    }
#endif
                    var masterUrl = parser.ParseString(webSettings.MasterPageUrl);
                    if (!string.IsNullOrEmpty(masterUrl))
                    {
                        if (!isNoScriptSite)
                        {
                            web.MasterUrl = masterUrl;
                        }
                        else
                        {
                            scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_WebSettings_SkipMasterPageUpdate);
                        }
                    }
                    var customMasterUrl = parser.ParseString(webSettings.CustomMasterPageUrl);
                    if (!string.IsNullOrEmpty(customMasterUrl))
                    {
                        if (!isNoScriptSite)
                        {
                            web.CustomMasterUrl = customMasterUrl;
                        }
                        else
                        {
                            scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_WebSettings_SkipCustomMasterPageUpdate);
                        }
                    }
                    if (webSettings.Title != null)
                    {
                        web.Title = parser.ParseString(webSettings.Title);
                    }
                    if (webSettings.Description != null)
                    {
                        web.Description = parser.ParseString(webSettings.Description);
                    }
                    if (webSettings.SiteLogo != null)
                    {
                        var logoUrl = parser.ParseString(webSettings.SiteLogo);
                        // Modern site? Then we assume the SiteLogo is actually a filepath
                        if (web.WebTemplate == "GROUP")
                        {
#if !ONPREMISES
                            if (!string.IsNullOrEmpty(logoUrl) && !logoUrl.ToLower().Contains("_api/groupservice/getgroupimage"))
                            {
                                var fileBytes = ConnectorFileHelper.GetFileBytes(template.Connector, logoUrl);
                                if (fileBytes != null && fileBytes.Length > 0)
                                {
#if !NETSTANDARD2_0
                                    var mimeType = MimeMapping.GetMimeMapping(logoUrl);
#else
                                    var mimeType = "";
                                    var imgUrl   = logoUrl;
                                    if (imgUrl.Contains("?"))
                                    {
                                        imgUrl = imgUrl.Split(new[] { '?' })[0];
                                    }
                                    if (imgUrl.EndsWith(".gif", StringComparison.InvariantCultureIgnoreCase))
                                    {
                                        mimeType = "image/gif";
                                    }
                                    if (imgUrl.EndsWith(".png", StringComparison.InvariantCultureIgnoreCase))
                                    {
                                        mimeType = "image/png";
                                    }
                                    if (imgUrl.EndsWith(".jpg", StringComparison.InvariantCultureIgnoreCase))
                                    {
                                        mimeType = "image/jpeg";
                                    }
#endif
                                    Sites.SiteCollection.SetGroupImage((ClientContext)web.Context, fileBytes, mimeType).GetAwaiter().GetResult();
                                }
                            }
#endif
                        }
                        else
                        {
                            web.SiteLogoUrl = logoUrl;
                        }
                    }
                    var welcomePage = parser.ParseString(webSettings.WelcomePage);
                    if (!string.IsNullOrEmpty(welcomePage))
                    {
                        web.RootFolder.WelcomePage = welcomePage;
                        web.RootFolder.Update();
                    }
                    if (webSettings.AlternateCSS != null)
                    {
                        web.AlternateCssUrl = parser.ParseString(webSettings.AlternateCSS);
                    }
                    web.Update();
                    web.Context.ExecuteQueryRetry();

#if !ONPREMISES
                    if (webSettings.HubSiteUrl != null)
                    {
                        var hubsiteUrl = parser.ParseString(webSettings.HubSiteUrl);
                        try
                        {
                            using (var tenantContext = web.Context.Clone(web.GetTenantAdministrationUrl(), applyingInformation.AccessTokens))
                            {
                                var tenant = new Tenant(tenantContext);
                                tenant.ConnectSiteToHubSite(web.Url, hubsiteUrl);
                                tenantContext.ExecuteQueryRetry();
                            }
                        }
                        catch (Exception ex)
                        {
                            WriteMessage($"Hub site association failed: {ex.Message}", ProvisioningMessageType.Warning);
                        }
                    }
#endif
                }
            }

            return(parser);
        }
        private void UpdateField(Web web, string fieldId, XElement templateFieldElement, PnPMonitoredScope scope, TokenParser parser)
        {
            var existingField = web.Fields.GetById(Guid.Parse(fieldId));

            web.Context.Load(existingField, f => f.SchemaXml);
            web.Context.ExecuteQueryRetry();

            XElement existingFieldElement = XElement.Parse(existingField.SchemaXml);

            XNodeEqualityComparer equalityComparer = new XNodeEqualityComparer();

            if (equalityComparer.GetHashCode(existingFieldElement) != equalityComparer.GetHashCode(templateFieldElement)) // Is field different in template?
            {
                if (existingFieldElement.Attribute("Type").Value == templateFieldElement.Attribute("Type").Value)         // Is existing field of the same type?
                {
                    var listIdentifier = templateFieldElement.Attribute("List") != null?templateFieldElement.Attribute("List").Value : null;

                    if (listIdentifier != null)
                    {
                        // Temporary remove list attribute from list
                        templateFieldElement.Attribute("List").Remove();
                    }

                    foreach (var attribute in templateFieldElement.Attributes())
                    {
                        if (existingFieldElement.Attribute(attribute.Name) != null)
                        {
                            existingFieldElement.Attribute(attribute.Name).Value = attribute.Value;
                        }
                        else
                        {
                            existingFieldElement.Add(attribute);
                        }
                    }
                    foreach (var element in templateFieldElement.Elements())
                    {
                        if (existingFieldElement.Element(element.Name) != null)
                        {
                            existingFieldElement.Element(element.Name).Remove();
                        }
                        existingFieldElement.Add(element);
                    }

                    if (existingFieldElement.Attribute("Version") != null)
                    {
                        existingFieldElement.Attributes("Version").Remove();
                    }
                    existingField.SchemaXml = parser.ParseString(existingFieldElement.ToString(), "~sitecollection", "~site");
                    existingField.UpdateAndPushChanges(true);
                    web.Context.Load(existingField, f => f.TypeAsString, f => f.DefaultValue);
                    web.Context.ExecuteQueryRetry();

                    if ((existingField.TypeAsString == "TaxonomyFieldType" || existingField.TypeAsString == "TaxonomyFieldTypeMulti") && !string.IsNullOrEmpty(existingField.DefaultValue))
                    {
                        var taxField = web.Context.CastTo <TaxonomyField>(existingField);
                        ValidateTaxonomyFieldDefaultValue(taxField);
                    }
                }
                else
                {
                    var fieldName = existingFieldElement.Attribute("Name") != null?existingFieldElement.Attribute("Name").Value : existingFieldElement.Attribute("StaticName").Value;

                    WriteWarning(string.Format(CoreResources.Provisioning_ObjectHandlers_Fields_Field__0____1___exists_but_is_of_different_type__Skipping_field_, fieldName, fieldId), ProvisioningMessageType.Warning);
                    scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_Fields_Field__0____1___exists_but_is_of_different_type__Skipping_field_, fieldName, fieldId);
                }
            }
        }
        public override TokenParser ProvisionObjects(Web web, ProvisioningTemplate template, TokenParser parser, ProvisioningTemplateApplyingInformation applyingInformation)
        {
            using (var scope = new PnPMonitoredScope(this.Name))
            {
                // Check if this is not a noscript site as we're not allowed to write to the web property bag is that one
                bool isNoScriptSite = web.IsNoScriptSite();
                web.EnsureProperties(w => w.ServerRelativeUrl, w => w.Url);

                // Build on the fly the list of additional files coming from the Directories
                var directoryFiles = new List <Model.File>();
                foreach (var directory in template.Directories)
                {
                    var metadataProperties = directory.GetMetadataProperties();
                    directoryFiles.AddRange(directory.GetDirectoryFiles(metadataProperties));
                }

                var filesToProcess = template.Files.Union(directoryFiles).ToArray();

                var siteAssetsFiles = filesToProcess.Where(f => f.Folder.ToLower().Contains("siteassets")).FirstOrDefault();
                if (siteAssetsFiles != null)
                {
                    // Need this so that we dont have access denied error during the first time upload, especially for modern sites
                    web.Lists.EnsureSiteAssetsLibrary();
                    web.Context.ExecuteQueryRetry();
                }

                var currentFileIndex = 0;
                var originalWeb      = web; // Used to store and re-store context in case files are deployed to masterpage gallery
                foreach (var file in filesToProcess)
                {
                    file.Src = parser.ParseString(file.Src);
                    var targetFileName = parser.ParseString(
                        !String.IsNullOrEmpty(file.TargetFileName) ? file.TargetFileName : template.Connector.GetFilenamePart(file.Src)
                        );

                    currentFileIndex++;
                    WriteSubProgress("File", targetFileName, currentFileIndex, filesToProcess.Length);
                    var folderName = parser.ParseString(file.Folder);

                    if (folderName.ToLower().Contains("/_catalogs/"))
                    {
                        // Edge case where you have files in the template which should be provisioned to the site collection
                        // master page gallery and not to a connected subsite
                        web = web.Context.GetSiteCollectionContext().Web;
                        web.EnsureProperties(w => w.ServerRelativeUrl, w => w.Url);
                    }

                    if (folderName.ToLower().StartsWith((web.ServerRelativeUrl.ToLower())))
                    {
                        folderName = folderName.Substring(web.ServerRelativeUrl.Length);
                    }

                    if (SkipFile(isNoScriptSite, targetFileName, folderName))
                    {
                        // add log message
                        scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_Files_SkipFileUpload, targetFileName, folderName);
                        continue;
                    }

                    var folder = web.EnsureFolderPath(folderName);

                    folder.EnsureProperties(p => p.UniqueId, p => p.ServerRelativeUrl);
                    parser.AddToken(new FileUniqueIdToken(web, folder.ServerRelativeUrl.Substring(web.ServerRelativeUrl.Length).TrimStart("/".ToCharArray()), folder.UniqueId));
                    parser.AddToken(new FileUniqueIdEncodedToken(web, folder.ServerRelativeUrl.Substring(web.ServerRelativeUrl.Length).TrimStart("/".ToCharArray()), folder.UniqueId));

                    var checkedOut = false;

                    var targetFile = folder.GetFile(template.Connector.GetFilenamePart(targetFileName));

                    if (targetFile != null)
                    {
                        if (file.Overwrite)
                        {
                            scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_Files_Uploading_and_overwriting_existing_file__0_, targetFileName);
                            checkedOut = CheckOutIfNeeded(web, targetFile);

                            using (var stream = FileUtilities.GetFileStream(template, file))
                            {
                                targetFile = UploadFile(folder, stream, targetFileName, file.Overwrite);
                            }
                        }
                        else
                        {
                            checkedOut = CheckOutIfNeeded(web, targetFile);
                        }
                    }
                    else
                    {
                        using (var stream = FileUtilities.GetFileStream(template, file))
                        {
                            if (stream == null)
                            {
                                throw new FileNotFoundException($"File {file.Src} does not exist");
                            }
                            else
                            {
                                scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_Files_Uploading_file__0_, targetFileName);
                                targetFile = UploadFile(folder, stream, targetFileName, file.Overwrite);
                            }
                        }

                        checkedOut = CheckOutIfNeeded(web, targetFile);
                    }

                    if (targetFile != null)
                    {
                        // Add the fileuniqueid tokens
                        targetFile.EnsureProperties(p => p.UniqueId, p => p.ServerRelativePath);

                        // Add ListItemId token, given that a file can live outside of a library ensure this does not break provisioning
                        try
                        {
                            web.Context.Load(targetFile, p => p.ListItemAllFields.Id);
                            web.Context.ExecuteQueryRetry();
                            if (targetFile.ListItemAllFields.ServerObjectIsNull.HasValue &&
                                !targetFile.ListItemAllFields.ServerObjectIsNull.Value)
                            {
                                parser.AddToken(new FileListItemIdToken(web, targetFile.ServerRelativePath.DecodedUrl.Substring(web.ServerRelativeUrl.Length).TrimStart("/".ToCharArray()), targetFile.ListItemAllFields.Id));
                            }
                        }
                        catch (ServerException ex)
                        {
                            // If this throws ServerException (does not belong to list), then shouldn't be trying to set properties)
                            // Handling the exception stating the "The object specified does not belong to a list."
                            if (ex.ServerErrorCode != -2113929210)
                            {
                                throw;
                            }
                        }

                        parser.AddToken(new FileUniqueIdToken(web, targetFile.ServerRelativePath.DecodedUrl.Substring(web.ServerRelativeUrl.Length).TrimStart("/".ToCharArray()), targetFile.UniqueId));
                        parser.AddToken(new FileUniqueIdEncodedToken(web, targetFile.ServerRelativePath.DecodedUrl.Substring(web.ServerRelativeUrl.Length).TrimStart("/".ToCharArray()), targetFile.UniqueId));

                        bool webPartsNeedLocalization = false;
                        if (file.WebParts != null && file.WebParts.Any())
                        {
                            targetFile.EnsureProperties(f => f.ServerRelativePath);

                            var existingWebParts = web.GetWebParts(targetFile.ServerRelativePath.DecodedUrl).ToList();
                            foreach (var webPart in file.WebParts)
                            {
                                // check if the webpart is already set on the page
                                if (existingWebParts.FirstOrDefault(w => w.WebPart.Title == parser.ParseString(webPart.Title)) == null)
                                {
                                    scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_Files_Adding_webpart___0___to_page, webPart.Title);
                                    var wpEntity = new WebPartEntity
                                    {
                                        WebPartTitle = parser.ParseString(webPart.Title),
                                        WebPartXml   = parser.ParseXmlString(webPart.Contents).Trim(new[] { '\n', ' ' }),
                                        WebPartZone  = webPart.Zone,
                                        WebPartIndex = (int)webPart.Order
                                    };
                                    var wpd = web.AddWebPartToWebPartPage(targetFile.ServerRelativePath.DecodedUrl, wpEntity);
                                    if (webPart.Title.ContainsResourceToken())
                                    {
                                        // update data based on where it was added - needed in order to localize wp title
                                        wpd.EnsureProperties(w => w.ZoneId, w => w.WebPart, w => w.WebPart.Properties);
                                        webPart.Zone             = wpd.ZoneId;
                                        webPart.Order            = (uint)wpd.WebPart.ZoneIndex;
                                        webPartsNeedLocalization = true;
                                    }
                                }
                            }
                        }

                        if (webPartsNeedLocalization)
                        {
                            file.LocalizeWebParts(web, parser, targetFile, scope);
                        }

                        //Set Properties before Checkin
                        if (file.Properties != null && file.Properties.Any())
                        {
                            Dictionary <string, string> transformedProperties = file.Properties.ToDictionary(property => property.Key, property => parser.ParseString(property.Value));
                            SetFileProperties(targetFile, transformedProperties, parser, false);
                        }

                        switch (file.Level)
                        {
                        case Model.FileLevel.Published:
                        {
                            targetFile.PublishFileToLevel(Microsoft.SharePoint.Client.FileLevel.Published);
                            break;
                        }

                        case Model.FileLevel.Draft:
                        {
                            targetFile.PublishFileToLevel(Microsoft.SharePoint.Client.FileLevel.Draft);
                            break;
                        }

                        default:
                        {
                            if (checkedOut)
                            {
                                targetFile.CheckIn("", CheckinType.MajorCheckIn);
                                web.Context.ExecuteQueryRetry();
                            }
                            break;
                        }
                        }

                        // Don't set security when nothing is defined. This otherwise breaks on files set outside of a list
                        if (file.Security != null &&
                            (file.Security.ClearSubscopes == true || file.Security.CopyRoleAssignments == true || file.Security.RoleAssignments.Count > 0))
                        {
                            targetFile.ListItemAllFields.SetSecurity(parser, file.Security, WriteMessage);
                        }
                    }

                    web = originalWeb; // restore context in case files are provisioned to the master page gallery #1059
                }
            }
            WriteMessage("Done processing files", ProvisioningMessageType.Completed);
            return(parser);
        }
        public override TokenParser ProvisionObjects(Web web, ProvisioningTemplate template, TokenParser parser, ProvisioningTemplateApplyingInformation applyingInformation)
        {
            using (var scope = new PnPMonitoredScope(this.Name))
            {
                if (template.WebSettings != null)
                {
                    // Check if this is not a noscript site as we're not allowed to update some properties
                    bool isNoScriptSite = web.IsNoScriptSite();

                    web.EnsureProperties(
#if !ONPREMISES
                        w => w.CommentsOnSitePagesDisabled,
#endif
                        w => w.HasUniqueRoleAssignments);

                    var webSettings = template.WebSettings;
#if !ONPREMISES
                    if (!isNoScriptSite)
                    {
                        web.NoCrawl = webSettings.NoCrawl;
                    }
                    else
                    {
                        scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_WebSettings_SkipNoCrawlUpdate);
                    }

                    if (!web.IsSubSite() || (web.IsSubSite() && web.HasUniqueRoleAssignments))
                    {
                        String requestAccessEmailValue = parser.ParseString(webSettings.RequestAccessEmail);
                        if (!String.IsNullOrEmpty(requestAccessEmailValue) && requestAccessEmailValue.Length >= 255)
                        {
                            requestAccessEmailValue = requestAccessEmailValue.Substring(0, 255);
                        }
                        if (!String.IsNullOrEmpty(requestAccessEmailValue))
                        {
                            web.RequestAccessEmail = requestAccessEmailValue;

                            web.Update();
                            web.Context.ExecuteQueryRetry();
                        }
                    }

                    if (web.CommentsOnSitePagesDisabled != webSettings.CommentsOnSitePagesDisabled)
                    {
                        web.CommentsOnSitePagesDisabled = webSettings.CommentsOnSitePagesDisabled;
                    }
#endif
                    var masterUrl = parser.ParseString(webSettings.MasterPageUrl);
                    if (!string.IsNullOrEmpty(masterUrl))
                    {
                        if (!isNoScriptSite)
                        {
                            web.MasterUrl = masterUrl;
                        }
                        else
                        {
                            scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_WebSettings_SkipMasterPageUpdate);
                        }
                    }
                    var customMasterUrl = parser.ParseString(webSettings.CustomMasterPageUrl);
                    if (!string.IsNullOrEmpty(customMasterUrl))
                    {
                        if (!isNoScriptSite)
                        {
                            web.CustomMasterUrl = customMasterUrl;
                        }
                        else
                        {
                            scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_WebSettings_SkipCustomMasterPageUpdate);
                        }
                    }
                    if (webSettings.Title != null)
                    {
                        web.Title = parser.ParseString(webSettings.Title);
                    }
                    if (webSettings.Description != null)
                    {
                        web.Description = parser.ParseString(webSettings.Description);
                    }
                    if (webSettings.SiteLogo != null)
                    {
                        web.SiteLogoUrl = parser.ParseString(webSettings.SiteLogo);
                    }
                    var welcomePage = parser.ParseString(webSettings.WelcomePage);
                    if (!string.IsNullOrEmpty(welcomePage))
                    {
                        web.RootFolder.WelcomePage = welcomePage;
                        web.RootFolder.Update();
                    }
                    if (webSettings.AlternateCSS != null)
                    {
                        web.AlternateCssUrl = parser.ParseString(webSettings.AlternateCSS);
                    }
                    web.Update();
                    web.Context.ExecuteQueryRetry();

#if !ONPREMISES
                    if (webSettings.HubSiteUrl != null)
                    {
                        var hubsiteUrl = parser.ParseString(webSettings.HubSiteUrl);
                        try
                        {
                            using (var tenantContext = web.Context.Clone(web.GetTenantAdministrationUrl()))
                            {
                                var tenant = new Tenant(tenantContext);
                                tenant.ConnectSiteToHubSite(web.Url, hubsiteUrl);
                                tenantContext.ExecuteQueryRetry();
                            }
                        }
                        catch (Exception ex)
                        {
                            WriteMessage($"Hub site association failed: {ex.Message}", ProvisioningMessageType.Warning);
                        }
                    }
#endif
                }
            }

            return(parser);
        }
Beispiel #18
0
        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;
                if (string.IsNullOrEmpty(homepageUrl))
                {
                    homepageUrl = "Default.aspx";
                }
                var welcomePageUrl = UrlUtility.Combine(web.ServerRelativeUrl, homepageUrl);

                var file = web.GetFileByServerRelativeUrl(welcomePageUrl);
                try
                {
                    var listItem = file.EnsureProperty(f => f.ListItemAllFields);
                    if (listItem != null)
                    {
                        if (listItem.FieldValues.ContainsKey("WikiField") && listItem.FieldValues["WikiField"] != null)
                        {
                            // Wiki page
                            var fullUri = new Uri(UrlUtility.Combine(web.Url, web.RootFolder.WelcomePage));

                            //var folderPath = fullUri.Segments.Take(fullUri.Segments.Count() - 1).ToArray().Aggregate((i, x) => i + x).TrimEnd('/');
                            //var fileName = fullUri.Segments[fullUri.Segments.Count() - 1];

                            var homeFile = web.GetFileByServerRelativeUrl(welcomePageUrl);

                            var limitedWPManager = homeFile.GetLimitedWebPartManager(PersonalizationScope.Shared);

                            web.Context.Load(limitedWPManager);

                            //var webParts = web.GetWebParts(welcomePageUrl);

                            var page = new Page()
                            {
                                Layout    = WikiPageLayout.Custom,
                                Overwrite = true,
                                Url       = Tokenize(fullUri.PathAndQuery, web.Url),
                            };
                            var pageContents = listItem.FieldValues["WikiField"].ToString();

                            Regex regexClientIds = new Regex(@"id=\""div_(?<ControlId>(\w|\-)+)");
                            if (regexClientIds.IsMatch(pageContents))
                            {
                                foreach (Match webPartMatch in regexClientIds.Matches(pageContents))
                                {
                                    String serverSideControlId = webPartMatch.Groups["ControlId"].Value;

                                    try
                                    {
                                        var serverSideControlIdToSearchFor =
                                            $"g_{serverSideControlId.Replace("-", "_")}";

                                        var webPart = limitedWPManager.WebParts.GetByControlId(serverSideControlIdToSearchFor);
                                        web.Context.Load(webPart,
                                                         wp => wp.Id,
                                                         wp => wp.WebPart.Title,
                                                         wp => wp.WebPart.ZoneIndex
                                                         );
                                        web.Context.ExecuteQueryRetry();

                                        var webPartxml = TokenizeWebPartXml(web, web.GetWebPartXml(webPart.Id, welcomePageUrl));

                                        page.WebParts.Add(new Model.WebPart()
                                        {
                                            Title    = webPart.WebPart.Title,
                                            Contents = webPartxml,
                                            Order    = (uint)webPart.WebPart.ZoneIndex,
                                            Row      = 1, // By default we will create a onecolumn layout, add the webpart to it, and later replace the wikifield on the page to position the webparts correctly.
                                            Column   = 1  // By default we will create a onecolumn layout, add the webpart to it, and later replace the wikifield on the page to position the webparts correctly.
                                        });

                                        pageContents = Regex.Replace(pageContents, serverSideControlId, $"{{webpartid:{webPart.WebPart.Title}}}", RegexOptions.IgnoreCase);
                                    }
                                    catch (ServerException)
                                    {
                                        scope.LogWarning("Found a WebPart ID which is not available on the server-side. ID: {0}", serverSideControlId);
                                    }
                                }
                            }

                            page.Fields.Add("WikiField", pageContents);
                            template.Pages.Add(page);

                            // Set the homepage
                            if (template.WebSettings == null)
                            {
                                template.WebSettings = new WebSettings();
                            }
                            template.WebSettings.WelcomePage = homepageUrl;
                        }
                        else if (listItem.FieldValues.ContainsKey("ClientSideApplicationId") && listItem.FieldValues["ClientSideApplicationId"] != null && listItem.FieldValues["ClientSideApplicationId"].ToString().ToLower() == "b6917cb1-93a0-4b97-a84d-7cf49975d4ec")
                        {
                            // this is a client side page, so let's skip it since it's handled by the Client Side Page contents handler
                        }
                        else
                        {
                            if (web.Context.HasMinimalServerLibraryVersion(Constants.MINIMUMZONEIDREQUIREDSERVERVERSION) || creationInfo.SkipVersionCheck)
                            {
                                // Not a wikipage
                                template = GetFileContents(web, template, welcomePageUrl, creationInfo, scope);
                                if (template.WebSettings == null)
                                {
                                    template.WebSettings = new WebSettings();
                                }
                                template.WebSettings.WelcomePage = homepageUrl;
                            }
                            else
                            {
                                WriteMessage(
                                    $"Page content export requires a server version that is newer than the current server. Server version is {web.Context.ServerLibraryVersion}, minimal required is {Constants.MINIMUMZONEIDREQUIREDSERVERVERSION}. Set SkipVersionCheck to true to override this check.", ProvisioningMessageType.Warning);
                                scope.LogWarning("Page content export requires a server version that is newer than the current server. Server version is {0}, minimal required is {1}", web.Context.ServerLibraryVersion, Constants.MINIMUMZONEIDREQUIREDSERVERVERSION);
                            }
                        }
                    }
                }
                catch (ServerException ex)
                {
                    //ignore this error. The default page is not a page but a list view.
                    if (ex.ServerErrorCode != -2146232832 && ex.HResult != -2146233088)
                    {
                        throw;
                    }
                    else
                    {
                        if (ex.HResult != -2146233088)
                        {
                            if (web.Context.HasMinimalServerLibraryVersion(Constants.MINIMUMZONEIDREQUIREDSERVERVERSION) || creationInfo.SkipVersionCheck)
                            {
                                // Page does not belong to a list, extract the file as is
                                template = GetFileContents(web, template, welcomePageUrl, creationInfo, scope);
                                if (template.WebSettings == null)
                                {
                                    template.WebSettings = new WebSettings();
                                }
                                template.WebSettings.WelcomePage = homepageUrl;
                            }
                            else
                            {
                                WriteMessage(
                                    $"Page content export requires a server version that is newer than the current server. Server version is {web.Context.ServerLibraryVersion}, minimal required is {Constants.MINIMUMZONEIDREQUIREDSERVERVERSION}. Set SkipVersionCheck to true to override this check.", ProvisioningMessageType.Warning);
                                scope.LogWarning("Page content export requires a server version that is newer than the current server. Server version is {0}, minimal required is {1}", web.Context.ServerLibraryVersion, Constants.MINIMUMZONEIDREQUIREDSERVERVERSION);
                            }
                        }
                    }
                }

                // 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);
        }
        public override TokenParser ProvisionObjects(Web web, ProvisioningTemplate template, TokenParser parser, ProvisioningTemplateApplyingInformation applyingInformation)
        {
            using (var scope = new PnPMonitoredScope(this.Name))
            {
                if (template.AuditSettings != null)
                {
                    // Check if this is not a noscript site as we're not allowed to update some properties
                    bool isNoScriptSite = web.IsNoScriptSite();

                    var site = (web.Context as ClientContext).Site;

                    site.EnsureProperties(s => s.Audit, s => s.AuditLogTrimmingRetention, s => s.TrimAuditLog);

                    var siteAuditSettings = site.Audit;

                    var isDirty = false;
                    if (template.AuditSettings.AuditFlags != siteAuditSettings.AuditFlags)
                    {
                        site.Audit.AuditFlags = template.AuditSettings.AuditFlags;
                        site.Audit.Update();
                        isDirty = true;
                    }

                    if (!isNoScriptSite)
                    {
                        if (template.AuditSettings.AuditLogTrimmingRetention != site.AuditLogTrimmingRetention)
                        {
                            site.AuditLogTrimmingRetention = template.AuditSettings.AuditLogTrimmingRetention;
                            isDirty = true;
                        }
                    }
                    else
                    {
                        scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_Audit_SkipAuditLogTrimmingRetention);
                    }

                    if (template.AuditSettings.TrimAuditLog != site.TrimAuditLog)
                    {
                        site.TrimAuditLog = template.AuditSettings.TrimAuditLog;
                        isDirty = true;
                    }
                    if (isDirty)
                    {
                        web.Context.ExecuteQueryRetry();
                    }
                }
            }

            return parser;
        }
        /// <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)
        {
            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(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);
                    }

                    var isNews = pageToExtract.LayoutType != Pages.ClientSidePageLayoutType.Home && int.Parse(pageToExtract.PageListItem[OfficeDevPnP.Core.Pages.ClientSidePage.PromotedStateField].ToString()) == (int)Pages.PromotedState.Promoted;
                    // Create the page
                    var extractedPageInstance = new ClientSidePage()
                    {
                        PageName             = pageName,
                        PromoteAsNewsArticle = isNews,
                        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,
                        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++;
                    }

                    // Add the page to the template
                    template.ClientSidePages.Add(extractedPageInstance);

                    // Set the homepage
                    if (isHomePage)
                    {
                        if (template.WebSettings == null)
                        {
                            template.WebSettings = new WebSettings();
                        }

                        if (pageUrl.StartsWith(web.ServerRelativeUrl, StringComparison.InvariantCultureIgnoreCase))
                        {
                            template.WebSettings.WelcomePage = pageUrl.Replace(web.ServerRelativeUrl + "/", "");
                        }
                        else
                        {
                            template.WebSettings.WelcomePage = pageUrl;
                        }
                    }
                }
            }
            catch (ArgumentException ex)
            {
                scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ClientSidePageContents_NoValidPage, ex.Message);
            }
        }
        private bool PersistFile(Web web, ProvisioningTemplateCreationInformation creationInfo, PnPMonitoredScope scope, string serverRelativeUrl)
        {
            var success = false;
            if (creationInfo.PersistBrandingFiles)
            {
                if (creationInfo.FileConnector != null)
                {

                    try
                    {
                        var file = web.GetFileByServerRelativeUrl(serverRelativeUrl);
                        string fileName = string.Empty;
                        if (serverRelativeUrl.IndexOf("/") > -1)
                        {
                            fileName = serverRelativeUrl.Substring(serverRelativeUrl.LastIndexOf("/") + 1);
                        }
                        else
                        {
                            fileName = serverRelativeUrl;
                        }
                        web.Context.Load(file);
                        web.Context.ExecuteQueryRetry();
                        ClientResult<Stream> stream = file.OpenBinaryStream();
                        web.Context.ExecuteQueryRetry();

                        using (Stream memStream = new MemoryStream())
                        {
                            CopyStream(stream.Value, memStream);
                            memStream.Position = 0;
                            creationInfo.FileConnector.SaveFileStream(fileName, memStream);
                        }
                        success = true;
                    }
                    catch (ServerException ex1)
                    {
                        // If we are referring a file from a location outside of the current web or at a location where we cannot retrieve the file an exception is thrown. We swallow this exception.
                        if (ex1.ServerErrorCode != -2147024809)
                        {
                            throw;
                        }
                        else
                        {
                            scope.LogWarning("File is not necessarily located in the current web. Not retrieving {0}", serverRelativeUrl);
                        }
                    }
                }
                else
                {
                    WriteWarning("No connector present to persist homepage.", ProvisioningMessageType.Error);
                    scope.LogError("No connector present to persist homepage");
                }
            }
            else
            {
                success = true;
            }
            return success;
        }
        public override ProvisioningTemplate ExtractObjects(Web web, ProvisioningTemplate template, ProvisioningTemplateCreationInformation creationInfo)
        {
            if (template.Workflows == null)
            {
                template.Workflows = new Workflows();
            }

            using (var scope = new PnPMonitoredScope(this.Name))
            {
                if (creationInfo.FileConnector == null)
                {
                    scope.LogWarning("Cannot export Workflow definitions without a FileConnector.");
                }
                else
                {
                    // Pre-load useful properties
                    web.EnsureProperties(w => w.Id, w => w.ServerRelativeUrl, w => w.Url);

                    // Retrieve all the lists and libraries
                    var lists = web.Lists;
                    web.Context.Load(lists);
                    web.Context.ExecuteQueryRetry();

                    // Retrieve the workflow definitions (including unpublished ones)
                    Microsoft.SharePoint.Client.WorkflowServices.WorkflowDefinition[] definitions = null;

                    try
                    {
                        definitions = web.GetWorkflowDefinitions(false);
                    }
                    catch (ServerException)
                    {
                        // If there is no workflow service present in the farm this method will throw an error.
                        // Swallow the exception
                    }

                    if (definitions != null)
                    {
                        template.Workflows.WorkflowDefinitions.AddRange(
                            from d in definitions.AsEnumerable()
                            select new Model.WorkflowDefinition(d.Properties.TokenizeWorkflowDefinitionProperties(lists))
                        {
                            AssociationUrl          = d.AssociationUrl,
                            Description             = d.Description,
                            DisplayName             = d.DisplayName,
                            DraftVersion            = d.DraftVersion,
                            FormField               = d.FormField,
                            Id                      = d.Id,
                            InitiationUrl           = d.InitiationUrl,
                            Published               = d.Published,
                            RequiresAssociationForm = d.RequiresAssociationForm,
                            RequiresInitiationForm  = d.RequiresInitiationForm,
                            RestrictToScope         = (!String.IsNullOrEmpty(d.RestrictToScope) && Guid.Parse(d.RestrictToScope) != web.Id) ? WorkflowExtension.TokenizeListIdProperty(d.RestrictToScope, lists) : null,
                            RestrictToType          = !String.IsNullOrEmpty(d.RestrictToType) ? d.RestrictToType : "Universal",
                            XamlPath                = d.Xaml.SaveXamlToFile(d.Id, creationInfo.FileConnector, lists),
                        }
                            );

                        foreach (var d in definitions.AsEnumerable())
                        {
                            if (d.RequiresInitiationForm)
                            {
                                PersistWorkflowForm(web, template, creationInfo, scope, d.InitiationUrl);
                            }
                            if (d.RequiresAssociationForm)
                            {
                                PersistWorkflowForm(web, template, creationInfo, scope, d.AssociationUrl);
                            }
                        }
                    }

                    // Retrieve the workflow subscriptions
                    Microsoft.SharePoint.Client.WorkflowServices.WorkflowSubscription[] subscriptions = null;

                    try
                    {
                        subscriptions = web.GetWorkflowSubscriptions();
                    }
                    catch (ServerException)
                    {
                        // If there is no workflow service present in the farm this method will throw an error.
                        // Swallow the exception
                    }

                    if (subscriptions != null)
                    {
#if ONPREMISES
                        template.Workflows.WorkflowSubscriptions.AddRange(
                            from s in subscriptions.AsEnumerable()
                            select new Model.WorkflowSubscription(s.PropertyDefinitions.TokenizeWorkflowSubscriptionProperties(lists))
                        {
                            DefinitionId  = s.DefinitionId,
                            Enabled       = s.Enabled,
                            EventSourceId = s.EventSourceId != web.Id ? String.Format("{{listid:{0}}}", lists.First(l => l.Id == s.EventSourceId).Title) : null,
                            EventTypes    = s.EventTypes.ToList(),
                            ManualStartBypassesActivationLimit = s.ManualStartBypassesActivationLimit,
                            Name            = s.Name,
                            ListId          = s.EventSourceId != web.Id ? String.Format("{{listid:{0}}}", lists.First(l => l.Id == s.EventSourceId).Title) : null,
                            StatusFieldName = s.StatusFieldName,
                        }
                            );
#else
                        template.Workflows.WorkflowSubscriptions.AddRange(
                            from s in subscriptions.AsEnumerable()
                            select new Model.WorkflowSubscription(s.PropertyDefinitions.TokenizeWorkflowSubscriptionProperties(lists))
                        {
                            DefinitionId  = s.DefinitionId,
                            Enabled       = s.Enabled,
                            EventSourceId = s.EventSourceId != web.Id ? WorkflowExtension.TokenizeListIdProperty(s.EventSourceId.ToString(), lists) : null,
                            EventTypes    = s.EventTypes.ToList(),
                            ManualStartBypassesActivationLimit = s.ManualStartBypassesActivationLimit,
                            Name   = s.Name,
                            ListId = s.EventSourceId != web.Id ? WorkflowExtension.TokenizeListIdProperty(s.EventSourceId.ToString(), lists) : null,
                            ParentContentTypeId = s.ParentContentTypeId,
                            StatusFieldName     = s.StatusFieldName,
                        }
                            );
#endif
                    }
                }
            }
            return(template);
        }
        private Tuple<List, TokenParser> UpdateList(Web web, List existingList, ListInstance templateList, TokenParser parser, PnPMonitoredScope scope)
        {
            web.Context.Load(existingList,
                l => l.Title,
                l => l.Description,
                l => l.OnQuickLaunch,
                l => l.Hidden,
                l => l.ContentTypesEnabled,
                l => l.EnableAttachments,
                l => l.EnableFolderCreation,
                l => l.EnableMinorVersions,
                l => l.DraftVersionVisibility,
                l => l.Views
            #if !CLIENTSDKV15
            , l => l.MajorWithMinorVersionsLimit
            #endif
            );
            web.Context.ExecuteQueryRetry();

            if (existingList.BaseTemplate == templateList.TemplateType)
            {
                var isDirty = false;
                if (parser.ParseString(templateList.Title) != existingList.Title)
                {
                    existingList.Title = parser.ParseString(templateList.Title);
                    isDirty = true;
                }
                if (!string.IsNullOrEmpty(templateList.DocumentTemplate))
                {
                    if (existingList.DocumentTemplateUrl != parser.ParseString(templateList.DocumentTemplate))
                    {
                        existingList.DocumentTemplateUrl = parser.ParseString(templateList.DocumentTemplate);
                        isDirty = true;
                    }
                }
                if (!string.IsNullOrEmpty(templateList.Description) && templateList.Description != existingList.Description)
                {
                    existingList.Description = templateList.Description;
                    isDirty = true;
                }
                if (templateList.Hidden != existingList.Hidden)
                {
                    existingList.Hidden = templateList.Hidden;
                    isDirty = true;
                }
                if (templateList.OnQuickLaunch != existingList.OnQuickLaunch)
                {
                    existingList.OnQuickLaunch = templateList.OnQuickLaunch;
                    isDirty = true;
                }
                if (templateList.ContentTypesEnabled != existingList.ContentTypesEnabled)
                {
                    existingList.ContentTypesEnabled = templateList.ContentTypesEnabled;
                    isDirty = true;
                }
                if (existingList.BaseTemplate != (int)ListTemplateType.Survey && existingList.BaseTemplate != (int)ListTemplateType.DocumentLibrary)
                {
                    // https://msdn.microsoft.com/EN-US/library/microsoft.sharepoint.splist.enableattachments.aspx
                    // The EnableAttachments property does not apply to any list that has a base type of Survey or DocumentLibrary.
                    // If you set this property to true for either type of list, it throws an SPException.
                    if (templateList.EnableAttachments != existingList.EnableAttachments)
                    {
                        existingList.EnableAttachments = templateList.EnableAttachments;
                        isDirty = true;
                    }
                }
                if (existingList.BaseTemplate != (int)ListTemplateType.DiscussionBoard)
                {
                    if (templateList.EnableFolderCreation != existingList.EnableFolderCreation)
                    {
                        existingList.EnableFolderCreation = templateList.EnableFolderCreation;
                        isDirty = true;
                    }
                }
                if (templateList.EnableVersioning)
                {
                    if (existingList.EnableVersioning != templateList.EnableVersioning)
                    {
                        existingList.EnableVersioning = templateList.EnableVersioning;
                        isDirty = true;
                    }
            #if !CLIENTSDKV15
                    if (existingList.IsObjectPropertyInstantiated("MajorVersionLimit") && existingList.MajorVersionLimit != templateList.MaxVersionLimit)
                    {
                        existingList.MajorVersionLimit = templateList.MaxVersionLimit;
                        isDirty = true;
                    }
            #endif
                    if (existingList.BaseTemplate == (int)ListTemplateType.DocumentLibrary)
                    {
                        // Only supported on Document Libraries
                        if (templateList.EnableMinorVersions != existingList.EnableMinorVersions)
                        {
                            existingList.EnableMinorVersions = templateList.EnableMinorVersions;
                            isDirty = true;
                        }
                        if ((DraftVisibilityType)templateList.DraftVersionVisibility != existingList.DraftVersionVisibility)
                        {
                            existingList.DraftVersionVisibility = (DraftVisibilityType)templateList.DraftVersionVisibility;
                            isDirty = true;
                        }

                        if (templateList.EnableMinorVersions)
                        {
                            if (templateList.MinorVersionLimit != existingList.MajorWithMinorVersionsLimit)
                            {
                                existingList.MajorWithMinorVersionsLimit = templateList.MinorVersionLimit;
                            }

                            if (DraftVisibilityType.Approver ==
                                (DraftVisibilityType)templateList.DraftVersionVisibility)
                            {
                                if (templateList.EnableModeration)
                                {
                                    if ((DraftVisibilityType)templateList.DraftVersionVisibility != existingList.DraftVersionVisibility)
                                    {
                                        existingList.DraftVersionVisibility = (DraftVisibilityType)templateList.DraftVersionVisibility;
                                        isDirty = true;
                                    }
                                }
                            }
                            else
                            {
                                if ((DraftVisibilityType)templateList.DraftVersionVisibility != existingList.DraftVersionVisibility)
                                {
                                    existingList.DraftVersionVisibility = (DraftVisibilityType)templateList.DraftVersionVisibility;
                                    isDirty = true;
                                }
                            }
                        }
                    }
                }
                if (isDirty)
                {
                    existingList.Update();
                    web.Context.ExecuteQueryRetry();
                }

                if (existingList.ContentTypesEnabled)
                {
                    // Check if we need to add a content type

                    var existingContentTypes = existingList.ContentTypes;
                    web.Context.Load(existingContentTypes, cts => cts.Include(ct => ct.StringId));
                    web.Context.ExecuteQueryRetry();

                    var bindingsToAdd = templateList.ContentTypeBindings.Where(ctb => existingContentTypes.All(ct => !ctb.ContentTypeId.Equals(ct.StringId, StringComparison.InvariantCultureIgnoreCase))).ToList();
                    var defaultCtBinding = templateList.ContentTypeBindings.FirstOrDefault(ctb => ctb.Default == true);
                    foreach (var ctb in bindingsToAdd)
                    {
                        existingList.AddContentTypeToListById(ctb.ContentTypeId, searchContentTypeInSiteHierarchy: true);
                    }

                    // default ContentTypeBinding should be set last because
                    // list extension .SetDefaultContentTypeToList() re-sets
                    // the list.RootFolder UniqueContentTypeOrder property
                    // which may cause missing CTs from the "New Button"
                    if (defaultCtBinding != null)
                    {
                        existingList.SetDefaultContentTypeToList(defaultCtBinding.ContentTypeId);
                    }
                }
                if (templateList.Security != null)
                {
                    existingList.SetSecurity(parser, templateList.Security);
                }
                return Tuple.Create(existingList, parser);
            }
            else
            {
                scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ListInstances_List__0____1____2___exists_but_is_of_a_different_type__Skipping_list_, templateList.Title, templateList.Url, existingList.Id);
                WriteWarning(string.Format(CoreResources.Provisioning_ObjectHandlers_ListInstances_List__0____1____2___exists_but_is_of_a_different_type__Skipping_list_, templateList.Title, templateList.Url, existingList.Id), ProvisioningMessageType.Warning);
                return null;
            }
        }
Beispiel #24
0
        public override TokenParser ProvisionObjects(Web web, ProvisioningTemplate template, TokenParser parser, ProvisioningTemplateApplyingInformation applyingInformation)
        {
            using (var scope = new PnPMonitoredScope(this.Name))
            {
                if (template.Navigation != null)
                {
                    if (!WebSupportsProvisionNavigation(web, template))
                    {
                        scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_Navigation_Context_web_is_not_publishing);
                        return(parser);
                    }

                    // Check if this is not a noscript site as navigation features are not supported
                    if (web.IsNoScriptSite())
                    {
                        scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_Navigation_SkipProvisioning);
                        return(parser);
                    }

                    // Retrieve the current web navigation settings
                    var navigationSettings = new WebNavigationSettings(web.Context, web);
                    web.Context.Load(navigationSettings, ns => ns.CurrentNavigation, ns => ns.GlobalNavigation);
                    web.Context.ExecuteQueryRetry();

                    if (template.Navigation.GlobalNavigation != null)
                    {
                        switch (template.Navigation.GlobalNavigation.NavigationType)
                        {
                        case GlobalNavigationType.Inherit:
                            navigationSettings.GlobalNavigation.Source = StandardNavigationSource.InheritFromParentWeb;
                            web.Navigation.UseShared = true;
                            break;

                        case GlobalNavigationType.Managed:
                            if (template.Navigation.GlobalNavigation.ManagedNavigation == null)
                            {
                                throw new ApplicationException(CoreResources.Provisioning_ObjectHandlers_Navigation_missing_global_managed_navigation);
                            }
                            navigationSettings.GlobalNavigation.Source      = StandardNavigationSource.TaxonomyProvider;
                            navigationSettings.GlobalNavigation.TermStoreId = Guid.Parse(parser.ParseString(template.Navigation.GlobalNavigation.ManagedNavigation.TermStoreId));
                            navigationSettings.GlobalNavigation.TermSetId   = Guid.Parse(parser.ParseString(template.Navigation.GlobalNavigation.ManagedNavigation.TermSetId));
                            break;

                        case GlobalNavigationType.Structural:
                        default:
                            if (template.Navigation.GlobalNavigation.StructuralNavigation == null)
                            {
                                throw new ApplicationException(CoreResources.Provisioning_ObjectHandlers_Navigation_missing_global_structural_navigation);
                            }
                            ProvisionGlobalStructuralNavigation(web,
                                                                template.Navigation.GlobalNavigation.StructuralNavigation, parser, applyingInformation.ClearNavigation, scope);
                            break;
                        }
                        web.Context.ExecuteQueryRetry();
                    }

                    if (template.Navigation.CurrentNavigation != null)
                    {
                        switch (template.Navigation.CurrentNavigation.NavigationType)
                        {
                        case CurrentNavigationType.Inherit:
                            navigationSettings.CurrentNavigation.Source = StandardNavigationSource.InheritFromParentWeb;
                            break;

                        case CurrentNavigationType.Managed:
                            if (template.Navigation.CurrentNavigation.ManagedNavigation == null)
                            {
                                throw new ApplicationException(CoreResources.Provisioning_ObjectHandlers_Navigation_missing_current_managed_navigation);
                            }
                            navigationSettings.CurrentNavigation.Source      = StandardNavigationSource.TaxonomyProvider;
                            navigationSettings.CurrentNavigation.TermStoreId = Guid.Parse(parser.ParseString(template.Navigation.CurrentNavigation.ManagedNavigation.TermStoreId));
                            navigationSettings.CurrentNavigation.TermSetId   = Guid.Parse(parser.ParseString(template.Navigation.CurrentNavigation.ManagedNavigation.TermSetId));
                            break;

                        case CurrentNavigationType.StructuralLocal:
                            web.SetPropertyBagValue(NavigationShowSiblings, "false");
                            if (template.Navigation.CurrentNavigation.StructuralNavigation == null)
                            {
                                throw new ApplicationException(CoreResources.Provisioning_ObjectHandlers_Navigation_missing_current_structural_navigation);
                            }
                            ProvisionCurrentStructuralNavigation(web,
                                                                 template.Navigation.CurrentNavigation.StructuralNavigation, parser, applyingInformation.ClearNavigation, scope);
                            break;

                        case CurrentNavigationType.Structural:
                        default:
                            if (template.Navigation.CurrentNavigation.StructuralNavigation == null)
                            {
                                throw new ApplicationException(CoreResources.Provisioning_ObjectHandlers_Navigation_missing_current_structural_navigation);
                            }
                            ProvisionCurrentStructuralNavigation(web,
                                                                 template.Navigation.CurrentNavigation.StructuralNavigation, parser, applyingInformation.ClearNavigation, scope);
                            break;
                        }
                        web.Context.ExecuteQueryRetry();
                    }
                }
            }

            return(parser);
        }
        public override ProvisioningTemplate ExtractObjects(Web web, ProvisioningTemplate template, ProvisioningTemplateCreationInformation creationInfo)
        {
            if (template.Workflows == null)
            {
                template.Workflows = new Workflows();
            }

            using (var scope = new PnPMonitoredScope(this.Name))
            {
                if (creationInfo.FileConnector == null)
                {
                    scope.LogWarning("Cannot export Workflow definitions without a FileConnector.");
                }
                else
                {
                    // Pre-load useful properties
                    web.EnsureProperty(w => w.Id);

                    // Retrieve all the lists and libraries
                    var lists = web.Lists;
                    web.Context.Load(lists);
                    web.Context.ExecuteQuery();

                    // Retrieve the workflow definitions (including unpublished ones)
                    Microsoft.SharePoint.Client.WorkflowServices.WorkflowDefinition[] definitions = null;

                    try
                    {
                        definitions = web.GetWorkflowDefinitions(false);
                    }
                    catch (ServerException)
                    {
                        // If there is no workflow service present in the farm this method will throw an error.
                        // Swallow the exception
                    }

                    if (definitions != null)
                    {
                        template.Workflows.WorkflowDefinitions.AddRange(
                            from d in definitions
                            select new Model.WorkflowDefinition(d.Properties.TokenizeWorkflowDefinitionProperties(lists))
                            {
                                AssociationUrl = d.AssociationUrl,
                                Description = d.Description,
                                DisplayName = d.DisplayName,
                                DraftVersion = d.DraftVersion,
                                FormField = d.FormField,
                                Id = d.Id,
                                InitiationUrl = d.InitiationUrl,
                                Published = d.Published,
                                RequiresAssociationForm = d.RequiresAssociationForm,
                                RequiresInitiationForm = d.RequiresInitiationForm,
                                RestrictToScope = (!String.IsNullOrEmpty(d.RestrictToScope) && Guid.Parse(d.RestrictToScope) != web.Id) ? WorkflowExtension.TokenizeListIdProperty(d.RestrictToScope, lists) : null,
                                RestrictToType = !String.IsNullOrEmpty(d.RestrictToType) ? d.RestrictToType : "Universal",
                                XamlPath = d.Xaml.SaveXamlToFile(d.Id, creationInfo.FileConnector),
                            }
                            );
                    }

                    // Retrieve the workflow subscriptions
                    Microsoft.SharePoint.Client.WorkflowServices.WorkflowSubscription[] subscriptions = null;

                    try
                    {
                        subscriptions = web.GetWorkflowSubscriptions();
                    }
                    catch (ServerException)
                    {
                        // If there is no workflow service present in the farm this method will throw an error.
                        // Swallow the exception
                    }

                    if (subscriptions != null)
                    {
            #if CLIENTSDKV15
                        template.Workflows.WorkflowSubscriptions.AddRange(
                            from s in subscriptions
                            select new Model.WorkflowSubscription(s.PropertyDefinitions.TokenizeWorkflowSubscriptionProperties(lists))
                            {
                                DefinitionId = s.DefinitionId,
                                Enabled = s.Enabled,
                                EventSourceId = s.EventSourceId != web.Id ? String.Format("{{listid:{0}}}", lists.First(l => l.Id == s.EventSourceId).Title) : null,
                                EventTypes = s.EventTypes.ToList(),
                                ManualStartBypassesActivationLimit = s.ManualStartBypassesActivationLimit,
                                Name = s.Name,
                                ListId = s.EventSourceId != web.Id ? String.Format("{{listid:{0}}}", lists.First(l => l.Id == s.EventSourceId).Title) : null,
                                StatusFieldName = s.StatusFieldName,
                            }
                            );
            #else
                        template.Workflows.WorkflowSubscriptions.AddRange(
                            from s in subscriptions
                            select new Model.WorkflowSubscription(s.PropertyDefinitions.TokenizeWorkflowSubscriptionProperties(lists))
                            {
                                DefinitionId = s.DefinitionId,
                                Enabled = s.Enabled,
                                EventSourceId = s.EventSourceId != web.Id ? WorkflowExtension.TokenizeListIdProperty(s.EventSourceId.ToString(), lists) : null,
                                EventTypes = s.EventTypes.ToList(),
                                ManualStartBypassesActivationLimit = s.ManualStartBypassesActivationLimit,
                                Name = s.Name,
                                ListId = s.EventSourceId != web.Id ? WorkflowExtension.TokenizeListIdProperty(s.EventSourceId.ToString(), lists) : null,
                                ParentContentTypeId = s.ParentContentTypeId,
                                StatusFieldName = s.StatusFieldName,
                            }
                            );
            #endif
                    }
                }
            }
            return template;
        }
Beispiel #26
0
        public override TokenParser ProvisionObjects(Web web, ProvisioningTemplate template, TokenParser parser, ProvisioningTemplateApplyingInformation applyingInformation)
        {
            using (var scope = new PnPMonitoredScope(this.Name))
            {
                // Check if this is not a noscript site as we're not allowed to update some properties
                bool isNoScriptSite = web.IsNoScriptSite();

                web.Context.Load(web.ContentTypes, ct => ct.IncludeWithDefaultProperties(c => c.StringId, c => c.FieldLinks,
                                                                                         c => c.FieldLinks.Include(fl => fl.Id, fl => fl.Required, fl => fl.Hidden)));
                web.Context.Load(web.Fields, fld => fld.IncludeWithDefaultProperties(f => f.Id));

                web.Context.ExecuteQueryRetry();

                var existingCTs    = web.ContentTypes.ToList();
                var existingFields = web.Fields.ToList();
                var currentCtIndex = 0;

                var doProvision = true;
                if (web.IsSubSite() && !applyingInformation.ProvisionContentTypesToSubWebs)
                {
                    WriteMessage("This template contains content types and you are provisioning to a subweb. If you still want to provision these content types, set the ProvisionContentTypesToSubWebs property to true.", ProvisioningMessageType.Warning);
                    doProvision = false;
                }
                if (doProvision)
                {
                    foreach (var ct in template.ContentTypes.OrderBy(ct => ct.Id)) // ordering to handle references to parent content types that can be in the same template
                    {
                        currentCtIndex++;
                        WriteMessage($"Content Type|{ct.Name}|{currentCtIndex}|{template.ContentTypes.Count}", ProvisioningMessageType.Progress);
                        var existingCT = existingCTs.FirstOrDefault(c => c.StringId.Equals(ct.Id, StringComparison.OrdinalIgnoreCase));
                        if (existingCT == null)
                        {
                            scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_ContentTypes_Creating_new_Content_Type___0_____1_, ct.Id, ct.Name);
                            var newCT = CreateContentType(web, ct, parser, template.Connector ?? null, scope, existingCTs, existingFields, isNoScriptSite);
                            if (newCT != null)
                            {
                                existingCTs.Add(newCT);
                            }
                        }
                        else
                        {
                            if (ct.Overwrite)
                            {
                                scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_ContentTypes_Recreating_existing_Content_Type___0_____1_, ct.Id, ct.Name);

                                existingCT.DeleteObject();
                                web.Context.ExecuteQueryRetry();
                                var newCT = CreateContentType(web, ct, parser, template.Connector ?? null, scope, existingCTs, existingFields, isNoScriptSite);
                                if (newCT != null)
                                {
                                    existingCTs.Add(newCT);
                                }
                            }
                            else
                            {
                                // We can't update a sealed content type unless we change sealed to false
                                if (!existingCT.Sealed || !ct.Sealed)
                                {
                                    scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_ContentTypes_Updating_existing_Content_Type___0_____1_, ct.Id, ct.Name);
                                    UpdateContentType(web, existingCT, ct, parser, scope);
                                }
                                else
                                {
                                    scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ContentTypes_Updating_existing_Content_Type_Sealed, ct.Id, ct.Name);
                                }
                            }
                        }
                    }
                }
            }
            WriteMessage($"Done processing Content Types", ProvisioningMessageType.Completed);
            return(parser);
        }
        private Field UpdateField(ClientObject web, ListInfo listInfo, Guid fieldId, XElement templateFieldElement, Field existingField, PnPMonitoredScope scope, TokenParser parser, string originalFieldXml)
        {
            Field field = null;
            web.Context.Load(existingField, f => f.SchemaXmlWithResourceTokens);
            web.Context.ExecuteQueryRetry();

            var existingFieldElement = XElement.Parse(existingField.SchemaXmlWithResourceTokens);

            var equalityComparer = new XNodeEqualityComparer();

            // Is field different in template?
            if (equalityComparer.GetHashCode(existingFieldElement) != equalityComparer.GetHashCode(templateFieldElement))
            {
                // Is existing field of the same type?
                if (existingFieldElement.Attribute("Type").Value == templateFieldElement.Attribute("Type").Value)
                {
                    templateFieldElement = PrepareField(templateFieldElement);
                    if (IsFieldXmlValid(parser.ParseString(templateFieldElement.ToString()), parser, web.Context))
                    {
                        foreach (var attribute in templateFieldElement.Attributes())
                        {
                            if (existingFieldElement.Attribute(attribute.Name) != null)
                            {
                                existingFieldElement.Attribute(attribute.Name).Value = attribute.Value;
                            }
                            else
                            {
                                existingFieldElement.Add(attribute);
                            }
                        }
                        foreach (var element in templateFieldElement.Elements())
                        {
                            if (existingFieldElement.Element(element.Name) != null)
                            {
                                existingFieldElement.Element(element.Name).Remove();
                            }
                            existingFieldElement.Add(element);
                        }

                        if (existingFieldElement.Attribute("Version") != null)
                        {
                            existingFieldElement.Attributes("Version").Remove();
                        }
                        existingField.SchemaXml = parser.ParseString(existingFieldElement.ToString(), "~sitecollection", "~site");
                        existingField.UpdateAndPushChanges(true);
                        web.Context.ExecuteQueryRetry();
                        bool isDirty = false;
            #if !SP2013
                        if (originalFieldXml.ContainsResourceToken())
                        {
                            var originalFieldElement = XElement.Parse(originalFieldXml);
                            var nameAttributeValue = originalFieldElement.Attribute("DisplayName") != null ? originalFieldElement.Attribute("DisplayName").Value : "";
                            if (nameAttributeValue.ContainsResourceToken())
                            {
                                if (existingField.TitleResource.SetUserResourceValue(nameAttributeValue, parser))
                                {
                                    isDirty = true;
                                }
                            }
                            var descriptionAttributeValue = originalFieldElement.Attribute("Description") != null ? originalFieldElement.Attribute("Description").Value : "";
                            if (descriptionAttributeValue.ContainsResourceToken())
                            {
                                if (existingField.DescriptionResource.SetUserResourceValue(descriptionAttributeValue, parser))
                                {
                                    isDirty = true;
                                }
                            }
                        }
            #endif
                        if (isDirty)
                        {
                            existingField.Update();
                            web.Context.ExecuteQueryRetry();
                            field = existingField;
                        }
                    }
                    else
                    {
                        // The field Xml was found invalid
                        var tokenString = parser.GetLeftOverTokens(originalFieldXml).Aggregate(String.Empty, (acc, i) => acc + " " + i);
                        scope.LogError("The field was found invalid: {0}", tokenString);
                        throw new Exception(string.Format("The field was found invalid: {0}", tokenString));
                    }
                }
                else
                {
                    var fieldName = existingFieldElement.Attribute("Name") != null ? existingFieldElement.Attribute("Name").Value : existingFieldElement.Attribute("StaticName").Value;
                    scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ListInstances_Field__0____1___exists_in_list__2____3___but_is_of_different_type__Skipping_field_, fieldName, fieldId, listInfo.TemplateList.Title, listInfo.SiteList.Id);
                    WriteWarning(string.Format(CoreResources.Provisioning_ObjectHandlers_ListInstances_Field__0____1___exists_in_list__2____3___but_is_of_different_type__Skipping_field_, fieldName, fieldId, listInfo.TemplateList.Title, listInfo.SiteList.Id), ProvisioningMessageType.Warning);
                }
            }
            return field;
        }
        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;

                                        // Tokenize the JsonControlData
                                        controlInstance.JsonControlData = TokenizeJsonControlData(web, controlInstance.JsonControlData);
                                    }

                                    // 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);
        }
        private Tuple<List, TokenParser> CreateList(Web web, ListInstance list, TokenParser parser, PnPMonitoredScope scope, bool isNoScriptSite = false)
        {
            List createdList;
            if (list.Url.Equals("SiteAssets") && list.TemplateType == (int)ListTemplateType.DocumentLibrary)
            {
                //Ensure that the Site Assets library is created using the out of the box creation mechanism
                //Site Assets that are created using the EnsureSiteAssetsLibrary method slightly differ from
                //default Document Libraries. See issue 512 (https://github.com/OfficeDev/PnP-Sites-Core/issues/512)
                //for details about the issue fixed by this approach.
                createdList = web.Lists.EnsureSiteAssetsLibrary();
                //Check that Title and Description have the correct values
                web.Context.Load(createdList, l => l.Title,
                                              l => l.Description);
                web.Context.ExecuteQueryRetry();
                var isDirty = false;
                if (!string.Equals(createdList.Description, list.Description))
                {
                    createdList.Description = list.Description;
                    isDirty = true;
                }
                if (!string.Equals(createdList.Title, list.Title))
                {
                    createdList.Title = list.Title;
                    isDirty = true;
                }
                if (isDirty)
                {
                    createdList.Update();
                    web.Context.ExecuteQueryRetry();
                }

            }
            else
            {
                var listCreate = new ListCreationInformation();
                listCreate.Description = list.Description;
                listCreate.TemplateType = list.TemplateType;
                listCreate.Title = parser.ParseString(list.Title);

                // the line of code below doesn't add the list to QuickLaunch
                // the OnQuickLaunch property is re-set on the Created List object
                listCreate.QuickLaunchOption = list.OnQuickLaunch ? QuickLaunchOptions.On : QuickLaunchOptions.Off;

                listCreate.Url = parser.ParseString(list.Url);
                listCreate.TemplateFeatureId = list.TemplateFeatureID;

                createdList = web.Lists.Add(listCreate);
                createdList.Update();
            }
            web.Context.Load(createdList, l => l.BaseTemplate);
            web.Context.ExecuteQueryRetry();

            #if !SP2013
            if (list.Title.ContainsResourceToken())
            {
                createdList.TitleResource.SetUserResourceValue(list.Title, parser);
            }
            if (list.Description.ContainsResourceToken())
            {
                createdList.DescriptionResource.SetUserResourceValue(list.Description, parser);
            }
            #endif
            if (!String.IsNullOrEmpty(list.DocumentTemplate))
            {
                createdList.DocumentTemplateUrl = parser.ParseString(list.DocumentTemplate);
            }

            // EnableAttachments are not supported for DocumentLibraries, Survey and PictureLibraries
            // TODO: the user should be warned
            if (createdList.BaseTemplate != (int)ListTemplateType.DocumentLibrary &&
                createdList.BaseTemplate != (int)ListTemplateType.Survey &&
                createdList.BaseTemplate != (int)ListTemplateType.PictureLibrary)
            {
                createdList.EnableAttachments = list.EnableAttachments;
            }

            createdList.EnableModeration = list.EnableModeration;
            createdList.ForceCheckout = list.ForceCheckout;

            // Done for all other lists than for Survey - With Surveys versioning configuration will cause an exception
            if (createdList.BaseTemplate != (int)ListTemplateType.Survey)
            {
                createdList.EnableVersioning = list.EnableVersioning;
                if (list.EnableVersioning)
                {
            #if !SP2013
                    createdList.MajorVersionLimit = list.MaxVersionLimit;
            #endif
                    // DraftVisibilityType.Approver is available only when the EnableModeration option of the list is true
                    if (DraftVisibilityType.Approver ==
                        (DraftVisibilityType)list.DraftVersionVisibility)
                    {
                        if (list.EnableModeration)
                        {
                            createdList.DraftVersionVisibility =
                                (DraftVisibilityType)list.DraftVersionVisibility;
                        }
                        else
                        {
                            scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ListInstances_DraftVersionVisibility_not_applied_because_EnableModeration_is_not_set_to_true);
                            WriteWarning(CoreResources.Provisioning_ObjectHandlers_ListInstances_DraftVersionVisibility_not_applied_because_EnableModeration_is_not_set_to_true, ProvisioningMessageType.Warning);
                        }
                    }
                    else
                    {
                        createdList.DraftVersionVisibility = (DraftVisibilityType)list.DraftVersionVisibility;
                    }

                    if (createdList.BaseTemplate == (int)ListTemplateType.DocumentLibrary)
                    {
                        // Only supported on Document Libraries
                        createdList.EnableMinorVersions = list.EnableMinorVersions;
                        createdList.DraftVersionVisibility = (DraftVisibilityType)list.DraftVersionVisibility;

                        if (list.EnableMinorVersions)
                        {
                            createdList.MajorWithMinorVersionsLimit = list.MinorVersionLimit; // Set only if enabled, otherwise you'll get exception due setting value to zero.
                        }
                    }
                }
            }

            createdList.OnQuickLaunch = list.OnQuickLaunch;
            if (createdList.BaseTemplate != (int)ListTemplateType.DiscussionBoard &&
                createdList.BaseTemplate != (int)ListTemplateType.Events)
            {
                createdList.EnableFolderCreation = list.EnableFolderCreation;
            }
            createdList.Hidden = list.Hidden;

            if (createdList.BaseTemplate != (int)ListTemplateType.Survey)
            {
                createdList.ContentTypesEnabled = list.ContentTypesEnabled;
            }

            createdList.Update();

            web.Context.Load(createdList.Views);
            web.Context.Load(createdList, l => l.Id);
            web.Context.Load(createdList, l => l.RootFolder.ServerRelativeUrl);
            web.Context.Load(createdList.ContentTypes);
            web.Context.ExecuteQueryRetry();

            if (createdList.BaseTemplate != (int)ListTemplateType.Survey)
            {
                // Remove existing content types only if there are custom content type bindings
                var contentTypesToRemove = new List<ContentType>();
                if (list.RemoveExistingContentTypes && list.ContentTypeBindings.Count > 0)
                {
                    contentTypesToRemove.AddRange(createdList.ContentTypes);
                }

                ContentTypeBinding defaultCtBinding = null;
                foreach (var ctBinding in list.ContentTypeBindings)
                {
                    var tempCT = web.GetContentTypeById(ctBinding.ContentTypeId, searchInSiteHierarchy: true);
                    if (tempCT != null)
                    {
                        // Get the name of the existing CT
                        var name = tempCT.EnsureProperty(ct => ct.Name);

                        // If the CT does not exist in the target list, and we don't have to remove it
                        if (!createdList.ContentTypeExistsByName(name) && !ctBinding.Remove)
                        {
                            // Then add it to the target list
                            createdList.AddContentTypeToListById(ctBinding.ContentTypeId, searchContentTypeInSiteHierarchy: true);
                        }
                        // Else if the CT exists in the target list, and we have to remove it
                        else if (createdList.ContentTypeExistsByName(name) && ctBinding.Remove)
                        {
                            // Then remove it from the target list
                            createdList.RemoveContentTypeByName(name);
                        }

                        if (ctBinding.Default)
                        {
                            defaultCtBinding = ctBinding;
                        }
                    }
                }

                // default ContentTypeBinding should be set last because
                // list extension .SetDefaultContentTypeToList() re-sets
                // the list.RootFolder UniqueContentTypeOrder property
                // which may cause missing CTs from the "New Button"
                if (defaultCtBinding != null)
                {
                    createdList.SetDefaultContentTypeToList(defaultCtBinding.ContentTypeId);
                }

                // Effectively remove existing content types, if any
                foreach (var ct in contentTypesToRemove)
                {
                    var shouldDelete = true;
                    shouldDelete &= (createdList.BaseTemplate != (int)ListTemplateType.DocumentLibrary || !ct.StringId.StartsWith(BuiltInContentTypeId.Folder + "00"));

                    if (shouldDelete)
                    {
                        ct.DeleteObject();
                        web.Context.ExecuteQueryRetry();
                    }
                }
            }

            // Add any custom action
            if (list.UserCustomActions.Any())
            {
                if (!isNoScriptSite)
                {
                    foreach (var userCustomAction in list.UserCustomActions)
                    {
                        CreateListCustomAction(createdList, parser, userCustomAction);
                    }

                    web.Context.ExecuteQueryRetry();
                }
                else
                {
                    scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ListInstances_SkipAddingOrUpdatingCustomActions);
                }
            }

            if (list.Security != null)
            {
                createdList.SetSecurity(parser, list.Security);
            }
            return Tuple.Create(createdList, parser);
        }
Beispiel #30
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 !SP2013
                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());
                }
#endif
#if !SP2013 && !SP2016
                if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.ApplicationLifecycleManagement))
                {
                    objectHandlers.Add(new ObjectApplicationLifecycleManagement());
                }
                if (provisioningInfo.HandlersToProcess.HasFlag(Handlers.Pages))
                {
                    objectHandlers.Add(new ObjectClientSidePages());
                }
#endif
#if !ONPREMISES
                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.SiteSettings))
                {
                    objectHandlers.Add(new ObjectSiteSettings());
                }
                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.ProvisioningTemplateStarted);

                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.InternalName);

                        try
                        {
                            tokenParser = handler.ProvisionObjects(web, template, tokenParser, provisioningInfo);
                        }
                        catch (Exception ex)
                        {
                            CallWebHooks(template, tokenParser, ProvisioningTemplateWebhookKind.ExceptionOccurred,
                                         handler.InternalName, ex);
                            throw ex;
                        }
                        CallWebHooks(template, tokenParser,
                                     ProvisioningTemplateWebhookKind.ObjectHandlerProvisioningCompleted,
                                     handler.InternalName);
                    }
                }

                // 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.ProvisioningTemplateCompleted);

                System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(currentCultureInfoValue);
            }
        }
        /// <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)
                {
                    // 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,
                    };

                    // 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.ThirdParty:
                                        controlInstance.Type = WebPartType.Custom;
                                        break;

                                    default:
                                        controlInstance.Type = WebPartType.Custom;
                                        break;
                                    }

                                    // set the control properties
                                    if ((control as Pages.ClientSideWebPart).ServerProcessedContent != null)
                                    {
                                        // If we have serverProcessedContent then also export that one, it's important as some controls depend on this information to be present
                                        string serverProcessedContent = (control as Pages.ClientSideWebPart).ServerProcessedContent.ToString(Formatting.None);
                                        controlInstance.JsonControlData = "{ \"serverProcessedContent\": " + serverProcessedContent + ", \"properties\": " + (control as Pages.ClientSideWebPart).PropertiesJson + "}";
                                    }
                                    else
                                    {
                                        controlInstance.JsonControlData = (control as Pages.ClientSideWebPart).PropertiesJson;
                                    }

                                    // Tokenize the JsonControlData
                                    controlInstance.JsonControlData = TokenizeJsonControlData(web, controlInstance.JsonControlData);

                                    // Export relevant files if this flag is set
                                    if (creationInfo.PersistBrandingFiles)
                                    {
                                        List <Guid> fileGuids = new List <Guid>();
                                        Dictionary <string, string> exportedFiles = new Dictionary <string, string>();
                                        Dictionary <string, string> exportedPages = new Dictionary <string, string>();

                                        // grab all the guids in the already tokenized json and check try to get them as a file
                                        string guidPattern    = "\"[a-fA-F0-9]{8}-([a-fA-F0-9]{4}-){3}[a-fA-F0-9]{12}\"";
                                        Regex  regexClientIds = new Regex(guidPattern);
                                        if (regexClientIds.IsMatch(controlInstance.JsonControlData))
                                        {
                                            foreach (Match guidMatch in regexClientIds.Matches(controlInstance.JsonControlData))
                                            {
                                                Guid uniqueId;
                                                if (Guid.TryParse(guidMatch.Value.Trim("\"".ToCharArray()), out uniqueId))
                                                {
                                                    fileGuids.Add(uniqueId);
                                                }
                                            }
                                        }
                                        // grab all the encoded guids in the already tokenized json and check try to get them as a file
                                        guidPattern    = "=[a-fA-F0-9]{8}%2D([a-fA-F0-9]{4}%2D){3}[a-fA-F0-9]{12}";
                                        regexClientIds = new Regex(guidPattern);
                                        if (regexClientIds.IsMatch(controlInstance.JsonControlData))
                                        {
                                            foreach (Match guidMatch in regexClientIds.Matches(controlInstance.JsonControlData))
                                            {
                                                Guid uniqueId;
                                                if (Guid.TryParse(guidMatch.Value.TrimStart("=".ToCharArray()), out uniqueId))
                                                {
                                                    fileGuids.Add(uniqueId);
                                                }
                                            }
                                        }

                                        // Iterate over the found guids to see if they're exportable files
                                        foreach (var uniqueId in fileGuids)
                                        {
                                            try
                                            {
                                                if (!exportedFiles.ContainsKey(uniqueId.ToString()))
                                                {
                                                    // Try to see if this is a file
                                                    var file = web.GetFileById(uniqueId);
                                                    web.Context.Load(file, f => f.Level, f => f.ServerRelativeUrl);
                                                    web.Context.ExecuteQueryRetry();

                                                    // If we got here it's a file, let's grab the file's path and name
                                                    var baseUri    = new Uri(web.Url);
                                                    var fullUri    = new Uri(baseUri, file.ServerRelativeUrl);
                                                    var folderPath = HttpUtility.UrlDecode(fullUri.Segments.Take(fullUri.Segments.Count() - 1).ToArray().Aggregate((i, x) => i + x).TrimEnd('/'));
                                                    var fileName   = HttpUtility.UrlDecode(fullUri.Segments[fullUri.Segments.Count() - 1]);

                                                    // Don't export aspx files as some web parts refer to other client side pages --> pages have to be either exported as well or already exist in the target site
                                                    if (!fileName.EndsWith(".aspx", StringComparison.InvariantCultureIgnoreCase))
                                                    {
                                                        var templateFolderPath = folderPath.Substring(web.ServerRelativeUrl.Length).TrimStart("/".ToCharArray());

                                                        // Avoid duplicate file entries
                                                        var fileAlreadyExported = template.Files.Where(p => p.Folder.Equals(templateFolderPath, StringComparison.CurrentCultureIgnoreCase) &&
                                                                                                       p.Src.Equals(fileName, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();
                                                        if (fileAlreadyExported == null)
                                                        {
                                                            // Add a File to the template
                                                            template.Files.Add(new Model.File()
                                                            {
                                                                Folder    = templateFolderPath,
                                                                Src       = $"{templateFolderPath}/{fileName}",
                                                                Overwrite = true,
                                                                Level     = (Model.FileLevel)Enum.Parse(typeof(Model.FileLevel), file.Level.ToString())
                                                            });

                                                            // Export the file
                                                            PersistFile(web, creationInfo, scope, folderPath, fileName);

                                                            // Keep track of the exported file path and it's UniqueId
                                                            exportedFiles.Add(uniqueId.ToString(), file.ServerRelativeUrl.Substring(web.ServerRelativeUrl.Length).TrimStart("/".ToCharArray()));
                                                        }
                                                    }
                                                    else
                                                    {
                                                        if (!exportedPages.ContainsKey(uniqueId.ToString()))
                                                        {
                                                            exportedPages.Add(uniqueId.ToString(), file.ServerRelativeUrl.Substring(web.ServerRelativeUrl.Length).TrimStart("/".ToCharArray()));
                                                        }
                                                    }
                                                }
                                            }
                                            catch (Exception ex)
                                            {
                                                scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ClientSidePageContents_ErrorDuringFileExport, ex.Message);
                                            }
                                        }

                                        // Tokenize based on the found files, use a different token for encoded guids do we can later on replace by a new encoded guid
                                        foreach (var exportedFile in exportedFiles)
                                        {
                                            controlInstance.JsonControlData = Regex.Replace(controlInstance.JsonControlData, exportedFile.Key.Replace("-", "%2D"), $"{{fileuniqueidencoded:{exportedFile.Value}}}", RegexOptions.IgnoreCase);
                                            controlInstance.JsonControlData = Regex.Replace(controlInstance.JsonControlData, exportedFile.Key, $"{{fileuniqueid:{exportedFile.Value}}}", RegexOptions.IgnoreCase);
                                        }
                                        foreach (var exportedPage in exportedPages)
                                        {
                                            controlInstance.JsonControlData = Regex.Replace(controlInstance.JsonControlData, exportedPage.Key.Replace("-", "%2D"), $"{{pageuniqueidencoded:{exportedPage.Value}}}", RegexOptions.IgnoreCase);
                                            controlInstance.JsonControlData = Regex.Replace(controlInstance.JsonControlData, exportedPage.Key, $"{{pageuniqueid:{exportedPage.Value}}}", RegexOptions.IgnoreCase);
                                        }
                                    }
                                }

                                // add control to section
                                sectionInstance.Controls.Add(controlInstance);
                            }
                        }

                        extractedPageInstance.Sections.Add(sectionInstance);
                    }

                    // Renumber the sections...when editing modern homepages you can end up with section with order 0.5 or 0.75...let's ensure we render section as of 1
                    int sectionOrder = 1;
                    foreach (var sectionInstance in extractedPageInstance.Sections)
                    {
                        sectionInstance.Order = sectionOrder;
                        sectionOrder++;
                    }

                    // Add the page to the template
                    template.ClientSidePages.Add(extractedPageInstance);

                    // Set the homepage
                    if (isHomePage)
                    {
                        if (template.WebSettings == null)
                        {
                            template.WebSettings = new WebSettings();
                        }

                        if (pageUrl.StartsWith(web.ServerRelativeUrl, StringComparison.InvariantCultureIgnoreCase))
                        {
                            template.WebSettings.WelcomePage = pageUrl.Replace(web.ServerRelativeUrl + "/", "");
                        }
                        else
                        {
                            template.WebSettings.WelcomePage = pageUrl;
                        }
                    }
                }
            }
            catch (ArgumentException ex)
            {
                scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ClientSidePageContents_NoValidPage, ex.Message);
            }
        }
        private bool PersistFile(Web web, ProvisioningTemplateCreationInformation creationInfo, PnPMonitoredScope scope, string serverRelativeUrl)
        {
            var success = false;

            if (creationInfo.PersistBrandingFiles)
            {
                if (creationInfo.FileConnector != null)
                {
                    if (UrlUtility.IsIisVirtualDirectory(serverRelativeUrl))
                    {
                        scope.LogWarning("File is not located in the content database. Not retrieving {0}", serverRelativeUrl);
                        return(success);
                    }

                    try
                    {
                        var    file     = web.GetFileByServerRelativeUrl(serverRelativeUrl);
                        string fileName = string.Empty;
                        if (serverRelativeUrl.IndexOf("/") > -1)
                        {
                            fileName = serverRelativeUrl.Substring(serverRelativeUrl.LastIndexOf("/") + 1);
                        }
                        else
                        {
                            fileName = serverRelativeUrl;
                        }
                        web.Context.Load(file);
                        web.Context.ExecuteQueryRetry();
                        ClientResult <Stream> stream = file.OpenBinaryStream();
                        web.Context.ExecuteQueryRetry();

                        using (Stream memStream = new MemoryStream())
                        {
                            CopyStream(stream.Value, memStream);
                            memStream.Position = 0;
                            creationInfo.FileConnector.SaveFileStream(fileName, memStream);
                        }
                        success = true;
                    }
                    catch (ServerException ex1)
                    {
                        // If we are referring a file from a location outside of the current web or at a location where we cannot retrieve the file an exception is thrown. We swallow this exception.
                        if (ex1.ServerErrorCode != -2147024809)
                        {
                            throw;
                        }
                        else
                        {
                            scope.LogWarning("File is not necessarily located in the current web. Not retrieving {0}", serverRelativeUrl);
                        }
                    }
                }
                else
                {
                    WriteWarning("No connector present to persist homepage.", ProvisioningMessageType.Error);
                    scope.LogError("No connector present to persist homepage");
                }
            }
            else
            {
                success = true;
            }
            return(success);
        }
        private bool PersistFile(Web web, ProvisioningTemplateCreationInformation creationInfo, PnPMonitoredScope scope, string serverRelativeUrl)
        {
            var success = false;

            if (creationInfo.PersistBrandingFiles)
            {
                if (creationInfo.FileConnector != null)
                {
                    if (UrlUtility.IsIisVirtualDirectory(serverRelativeUrl))
                    {
                        scope.LogWarning("File is not located in the content database. Not retrieving {0}", serverRelativeUrl);
                        return(success);
                    }

                    try
                    {
                        var    file     = web.GetFileByServerRelativeUrl(serverRelativeUrl);
                        string fileName = string.Empty;
                        if (serverRelativeUrl.IndexOf("/") > -1)
                        {
                            fileName = serverRelativeUrl.Substring(serverRelativeUrl.LastIndexOf("/") + 1);
                        }
                        else
                        {
                            fileName = serverRelativeUrl;
                        }
                        web.Context.Load(file);
                        web.Context.ExecuteQueryRetry();
                        ClientResult <Stream> stream = file.OpenBinaryStream();
                        web.Context.ExecuteQueryRetry();

                        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('/'));

                        // Configure the filename to use
                        fileName = HttpUtility.UrlDecode(fullUri.Segments[fullUri.Segments.Count() - 1]);

                        // Build up a site relative container URL...might end up empty as well
                        String container = HttpUtility.UrlDecode(folderPath.Replace(web.ServerRelativeUrl, "")).Trim('/').Replace("/", "\\");

                        using (Stream memStream = new MemoryStream())
                        {
                            CopyStream(stream.Value, memStream);
                            memStream.Position = 0;
                            if (!string.IsNullOrEmpty(container))
                            {
                                creationInfo.FileConnector.SaveFileStream(fileName, container, memStream);
                            }
                            else
                            {
                                creationInfo.FileConnector.SaveFileStream(fileName, memStream);
                            }
                        }
                        success = true;
                    }
                    catch (ServerException ex1)
                    {
                        // If we are referring a file from a location outside of the current web or at a location where we cannot retrieve the file an exception is thrown. We swallow this exception.
                        if (ex1.ServerErrorCode != -2147024809)
                        {
                            throw;
                        }
                        else
                        {
                            scope.LogWarning("File is not necessarily located in the current web. Not retrieving {0}", serverRelativeUrl);
                        }
                    }
                }
                else
                {
                    WriteMessage("No connector present to persist homepage.", ProvisioningMessageType.Error);
                    scope.LogError("No connector present to persist homepage");
                }
            }
            else
            {
                success = true;
            }
            return(success);
        }
        public override TokenParser ProvisionObjects(Web web, ProvisioningTemplate template, TokenParser parser, ProvisioningTemplateApplyingInformation applyingInformation)
        {
            using (var scope = new PnPMonitoredScope(this.Name))
            {
                if (template.WebSettings != null)
                {
                    // Check if this is not a noscript site as we're not allowed to update some properties
                    bool isNoScriptSite = web.IsNoScriptSite();

                    web.EnsureProperty(w => w.HasUniqueRoleAssignments);

                    var webSettings = template.WebSettings;
#if !ONPREMISES
                    if (!isNoScriptSite)
                    {
                        web.NoCrawl = webSettings.NoCrawl;
                    }
                    else
                    {
                        scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_WebSettings_SkipNoCrawlUpdate);
                    }

                    if (!web.IsSubSite() || (web.IsSubSite() && web.HasUniqueRoleAssignments))
                    {
                        String requestAccessEmailValue = parser.ParseString(webSettings.RequestAccessEmail);
                        if (!String.IsNullOrEmpty(requestAccessEmailValue) && requestAccessEmailValue.Length >= 255)
                        {
                            requestAccessEmailValue = requestAccessEmailValue.Substring(0, 255);
                        }
                        if (!String.IsNullOrEmpty(requestAccessEmailValue))
                        {
                            web.RequestAccessEmail = requestAccessEmailValue;

                            web.Update();
                            web.Context.ExecuteQueryRetry();
                        }
                    }
#endif
                    var masterUrl = parser.ParseString(webSettings.MasterPageUrl);
                    if (!string.IsNullOrEmpty(masterUrl))
                    {
                        if (!isNoScriptSite)
                        {
                            web.MasterUrl = masterUrl;
                        }
                        else
                        {
                            scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_WebSettings_SkipMasterPageUpdate);
                        }
                    }
                    var customMasterUrl = parser.ParseString(webSettings.CustomMasterPageUrl);
                    if (!string.IsNullOrEmpty(customMasterUrl))
                    {
                        if (!isNoScriptSite)
                        {
                            web.CustomMasterUrl = customMasterUrl;
                        }
                        else
                        {
                            scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_WebSettings_SkipCustomMasterPageUpdate);
                        }
                    }
                    if (webSettings.Title != null)
                    {
                        web.Title = parser.ParseString(webSettings.Title);
                    }
                    if (webSettings.Description != null)
                    {
                        web.Description = parser.ParseString(webSettings.Description);
                    }
                    if (webSettings.SiteLogo != null)
                    {
                        web.SiteLogoUrl = parser.ParseString(webSettings.SiteLogo);
                    }
                    var welcomePage = parser.ParseString(webSettings.WelcomePage);
                    if (!string.IsNullOrEmpty(welcomePage))
                    {
                        web.RootFolder.WelcomePage = welcomePage;
                        web.RootFolder.Update();
                    }
                    if (webSettings.AlternateCSS != null)
                    {
                        web.AlternateCssUrl = parser.ParseString(webSettings.AlternateCSS);
                    }

                    web.Update();
                    web.Context.ExecuteQueryRetry();
                }
            }

            return(parser);
        }
        private static Microsoft.SharePoint.Client.ContentType CreateContentType(Web web, ContentType templateContentType, TokenParser parser, FileConnectorBase connector, PnPMonitoredScope scope,
            List<Microsoft.SharePoint.Client.ContentType> existingCTs = null, List<Microsoft.SharePoint.Client.Field> existingFields = null, bool isNoScriptSite = false)
        {
            var name = parser.ParseString(templateContentType.Name);
            var description = parser.ParseString(templateContentType.Description);
            var id = parser.ParseString(templateContentType.Id);
            var group = parser.ParseString(templateContentType.Group);

            var createdCT = web.CreateContentType(name, description, id, group);
            foreach (var fieldRef in templateContentType.FieldRefs)
            {
                Microsoft.SharePoint.Client.Field field = null;
                try
                {
                    // Try to get the field by ID
                    field = web.Fields.GetById(fieldRef.Id);
                }
                catch (ArgumentException)
                {
                    // In case of failure, if we have the name
                    if (!String.IsNullOrEmpty(fieldRef.Name))
                    {
                        // Let's try with that one
                        field = web.Fields.GetByInternalNameOrTitle(fieldRef.Name);
                    }
                }

                // Add it to the target content type
                // Notice that this code will fail if the field does not exist
                web.AddFieldToContentType(createdCT, field, fieldRef.Required, fieldRef.Hidden);
            }

            // Add new CTs
            parser.AddToken(new ContentTypeIdToken(web, name, id));

            #if !ONPREMISES
            // Set resources
            if (templateContentType.Name.ContainsResourceToken())
            {
                createdCT.NameResource.SetUserResourceValue(templateContentType.Name, parser);
            }
            if (templateContentType.Description.ContainsResourceToken())
            {
                createdCT.DescriptionResource.SetUserResourceValue(templateContentType.Description, parser);
            }
            #endif
            //Reorder the elements so that the new created Content Type has the same order as defined in the
            //template. The order can be different if the new Content Type inherits from another Content Type.
            //In this case the new Content Type has all field of the original Content Type and missing fields
            //will be added at the end. To fix this issue we ordering the fields once more.

            createdCT.FieldLinks.Reorder(templateContentType.FieldRefs.Select(fld => parser.ParseString(fld.Name)).ToArray());

            createdCT.ReadOnly = templateContentType.ReadOnly;
            createdCT.Hidden = templateContentType.Hidden;
            createdCT.Sealed = templateContentType.Sealed;

            if (templateContentType.DocumentSetTemplate == null)
            {
                // Only apply a document template when the contenttype is not a document set
                if (!string.IsNullOrEmpty(parser.ParseString(templateContentType.DocumentTemplate)))
                {
                    createdCT.DocumentTemplate = parser.ParseString(templateContentType.DocumentTemplate);
                }
            }

            // Skipping updates of forms as we can't upload forms to noscript sites
            if (!isNoScriptSite)
            {
                if (!String.IsNullOrEmpty(parser.ParseString(templateContentType.NewFormUrl)))
                {
                    createdCT.NewFormUrl = parser.ParseString(templateContentType.NewFormUrl);
                }
                if (!String.IsNullOrEmpty(parser.ParseString(templateContentType.EditFormUrl)))
                {
                    createdCT.EditFormUrl = parser.ParseString(templateContentType.EditFormUrl);
                }
                if (!String.IsNullOrEmpty(parser.ParseString(templateContentType.DisplayFormUrl)))
                {
                    createdCT.DisplayFormUrl = parser.ParseString(templateContentType.DisplayFormUrl);
                }
            }
            else
            {
                if (!String.IsNullOrEmpty(parser.ParseString(templateContentType.DisplayFormUrl)) ||
                    !String.IsNullOrEmpty(parser.ParseString(templateContentType.EditFormUrl)) ||
                    !String.IsNullOrEmpty(parser.ParseString(templateContentType.NewFormUrl)))
                {
                    // log message
                    scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ContentTypes_SkipCustomFormUrls, name);
                }
            }

            createdCT.Update(true);
            web.Context.ExecuteQueryRetry();

            // If the CT is a DocumentSet
            if (templateContentType.DocumentSetTemplate != null)
            {
                // Retrieve a reference to the DocumentSet Content Type
                Microsoft.SharePoint.Client.DocumentSet.DocumentSetTemplate documentSetTemplate =
                    Microsoft.SharePoint.Client.DocumentSet.DocumentSetTemplate.GetDocumentSetTemplate(web.Context, createdCT);

                // Load the collections to allow for deletion scenarions
                web.Context.Load(documentSetTemplate, d => d.AllowedContentTypes, d => d.DefaultDocuments, d => d.SharedFields, d => d.WelcomePageFields);
                web.Context.ExecuteQueryRetry();

                if (!String.IsNullOrEmpty(templateContentType.DocumentSetTemplate.WelcomePage))
                {
                    // TODO: Customize the WelcomePage of the DocumentSet
                }

                // Add additional content types to the set of allowed content types
                bool hasDefaultDocumentContentTypeInTemplate = false;
                foreach (String ctId in templateContentType.DocumentSetTemplate.AllowedContentTypes)
                {
                    Microsoft.SharePoint.Client.ContentType ct = existingCTs.FirstOrDefault(c => c.StringId == ctId);
                    if (ct != null)
                    {
                        if (ct.Id.StringValue.Equals("0x0101", StringComparison.InvariantCultureIgnoreCase))
                        {
                            hasDefaultDocumentContentTypeInTemplate = true;
                        }

                        documentSetTemplate.AllowedContentTypes.Add(ct.Id);
                    }
                }
                // If the default document content type (0x0101) is not in our definition then remove it
                if (!hasDefaultDocumentContentTypeInTemplate)
                {
                    Microsoft.SharePoint.Client.ContentType ct = existingCTs.FirstOrDefault(c => c.StringId == "0x0101");
                    if (ct != null)
                    {
                        documentSetTemplate.AllowedContentTypes.Remove(ct.Id);
                    }
                }

                if (!isNoScriptSite)
                {
                    foreach (var doc in templateContentType.DocumentSetTemplate.DefaultDocuments)
                    {
                        Microsoft.SharePoint.Client.ContentType ct = existingCTs.FirstOrDefault(c => c.StringId == doc.ContentTypeId);
                        if (ct != null)
                        {
                            using (Stream fileStream = connector.GetFileStream(doc.FileSourcePath))
                            {
                                documentSetTemplate.DefaultDocuments.Add(doc.Name, ct.Id, ReadFullStream(fileStream));
                            }
                        }
                    }
                }
                else
                {
                    if (templateContentType.DocumentSetTemplate.DefaultDocuments.Any())
                    {
                        scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ContentTypes_SkipDocumentSetDefaultDocuments, name);
                    }
                }

                foreach (var sharedField in templateContentType.DocumentSetTemplate.SharedFields)
                {
                    Microsoft.SharePoint.Client.Field field = existingFields.FirstOrDefault(f => f.Id == sharedField);
                    if (field != null)
                    {
                        documentSetTemplate.SharedFields.Add(field);
                    }
                }

                foreach (var welcomePageField in templateContentType.DocumentSetTemplate.WelcomePageFields)
                {
                    Microsoft.SharePoint.Client.Field field = existingFields.FirstOrDefault(f => f.Id == welcomePageField);
                    if (field != null)
                    {
                        documentSetTemplate.WelcomePageFields.Add(field);
                    }
                }

                documentSetTemplate.Update(true);
                web.Context.ExecuteQueryRetry();
            }

            web.Context.Load(createdCT);
            web.Context.ExecuteQueryRetry();

            return createdCT;
        }
        private static Microsoft.SharePoint.Client.ContentType CreateContentType(Web web, ContentType templateContentType, TokenParser parser, FileConnectorBase connector, PnPMonitoredScope scope,
                                                                                 List <Microsoft.SharePoint.Client.ContentType> existingCTs = null, List <Microsoft.SharePoint.Client.Field> existingFields = null, bool isNoScriptSite = false)
        {
            var name        = parser.ParseString(templateContentType.Name);
            var description = parser.ParseString(templateContentType.Description);
            var id          = parser.ParseString(templateContentType.Id);
            var group       = parser.ParseString(templateContentType.Group);

            var createdCT = web.CreateContentType(name, description, id, group);

            foreach (var fieldRef in templateContentType.FieldRefs)
            {
                Microsoft.SharePoint.Client.Field field = null;
                try
                {
                    // Try to get the field by ID
                    field = web.Fields.GetById(fieldRef.Id);
                }
                catch (ArgumentException)
                {
                    // In case of failure, if we have the name
                    if (!String.IsNullOrEmpty(fieldRef.Name))
                    {
                        // Let's try with that one
                        field = web.Fields.GetByInternalNameOrTitle(fieldRef.Name);
                    }
                }

                // Add it to the target content type
                // Notice that this code will fail if the field does not exist
                web.AddFieldToContentType(createdCT, field, fieldRef.Required, fieldRef.Hidden);
            }

            // Add new CTs
            parser.AddToken(new ContentTypeIdToken(web, name, id));

#if !ONPREMISES
            // Set resources
            if (templateContentType.Name.ContainsResourceToken())
            {
                createdCT.NameResource.SetUserResourceValue(templateContentType.Name, parser);
            }
            if (templateContentType.Description.ContainsResourceToken())
            {
                createdCT.DescriptionResource.SetUserResourceValue(templateContentType.Description, parser);
            }
#endif
            //Reorder the elements so that the new created Content Type has the same order as defined in the
            //template. The order can be different if the new Content Type inherits from another Content Type.
            //In this case the new Content Type has all field of the original Content Type and missing fields
            //will be added at the end. To fix this issue we ordering the fields once more.

            createdCT.FieldLinks.Reorder(templateContentType.FieldRefs.Select(fld => parser.ParseString(fld.Name)).ToArray());

            createdCT.ReadOnly = templateContentType.ReadOnly;
            createdCT.Hidden   = templateContentType.Hidden;
            createdCT.Sealed   = templateContentType.Sealed;

            if (templateContentType.DocumentSetTemplate == null)
            {
                // Only apply a document template when the contenttype is not a document set
                if (!string.IsNullOrEmpty(parser.ParseString(templateContentType.DocumentTemplate)))
                {
                    createdCT.DocumentTemplate = parser.ParseString(templateContentType.DocumentTemplate);
                }
            }

            // Skipping updates of forms as we can't upload forms to noscript sites
            if (!isNoScriptSite)
            {
                if (!String.IsNullOrEmpty(parser.ParseString(templateContentType.NewFormUrl)))
                {
                    createdCT.NewFormUrl = parser.ParseString(templateContentType.NewFormUrl);
                }
                if (!String.IsNullOrEmpty(parser.ParseString(templateContentType.EditFormUrl)))
                {
                    createdCT.EditFormUrl = parser.ParseString(templateContentType.EditFormUrl);
                }
                if (!String.IsNullOrEmpty(parser.ParseString(templateContentType.DisplayFormUrl)))
                {
                    createdCT.DisplayFormUrl = parser.ParseString(templateContentType.DisplayFormUrl);
                }
            }
            else
            {
                if (!String.IsNullOrEmpty(parser.ParseString(templateContentType.DisplayFormUrl)) ||
                    !String.IsNullOrEmpty(parser.ParseString(templateContentType.EditFormUrl)) ||
                    !String.IsNullOrEmpty(parser.ParseString(templateContentType.NewFormUrl)))
                {
                    // log message
                    scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ContentTypes_SkipCustomFormUrls, name);
                }
            }

            createdCT.Update(true);
            web.Context.ExecuteQueryRetry();

            // If the CT is a DocumentSet
            if (templateContentType.DocumentSetTemplate != null)
            {
                // Retrieve a reference to the DocumentSet Content Type
                Microsoft.SharePoint.Client.DocumentSet.DocumentSetTemplate documentSetTemplate =
                    Microsoft.SharePoint.Client.DocumentSet.DocumentSetTemplate.GetDocumentSetTemplate(web.Context, createdCT);

                // Load the collections to allow for deletion scenarions
                web.Context.Load(documentSetTemplate, d => d.AllowedContentTypes, d => d.DefaultDocuments, d => d.SharedFields, d => d.WelcomePageFields);
                web.Context.ExecuteQueryRetry();

                if (!String.IsNullOrEmpty(templateContentType.DocumentSetTemplate.WelcomePage))
                {
                    // TODO: Customize the WelcomePage of the DocumentSet
                }

                // Add additional content types to the set of allowed content types
                bool hasDefaultDocumentContentTypeInTemplate = false;
                foreach (String ctId in templateContentType.DocumentSetTemplate.AllowedContentTypes)
                {
                    Microsoft.SharePoint.Client.ContentType ct = existingCTs.FirstOrDefault(c => c.StringId == ctId);
                    if (ct != null)
                    {
                        if (ct.Id.StringValue.Equals("0x0101", StringComparison.InvariantCultureIgnoreCase))
                        {
                            hasDefaultDocumentContentTypeInTemplate = true;
                        }

                        documentSetTemplate.AllowedContentTypes.Add(ct.Id);
                    }
                }
                // If the default document content type (0x0101) is not in our definition then remove it
                if (!hasDefaultDocumentContentTypeInTemplate)
                {
                    Microsoft.SharePoint.Client.ContentType ct = existingCTs.FirstOrDefault(c => c.StringId == "0x0101");
                    if (ct != null)
                    {
                        documentSetTemplate.AllowedContentTypes.Remove(ct.Id);
                    }
                }

                if (!isNoScriptSite)
                {
                    foreach (var doc in templateContentType.DocumentSetTemplate.DefaultDocuments)
                    {
                        Microsoft.SharePoint.Client.ContentType ct = existingCTs.FirstOrDefault(c => c.StringId == doc.ContentTypeId);
                        if (ct != null)
                        {
                            using (Stream fileStream = connector.GetFileStream(doc.FileSourcePath))
                            {
                                documentSetTemplate.DefaultDocuments.Add(doc.Name, ct.Id, ReadFullStream(fileStream));
                            }
                        }
                    }
                }
                else
                {
                    if (templateContentType.DocumentSetTemplate.DefaultDocuments.Any())
                    {
                        scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ContentTypes_SkipDocumentSetDefaultDocuments, name);
                    }
                }

                foreach (var sharedField in templateContentType.DocumentSetTemplate.SharedFields)
                {
                    Microsoft.SharePoint.Client.Field field = existingFields.FirstOrDefault(f => f.Id == sharedField);
                    if (field != null)
                    {
                        documentSetTemplate.SharedFields.Add(field);
                    }
                }

                foreach (var welcomePageField in templateContentType.DocumentSetTemplate.WelcomePageFields)
                {
                    Microsoft.SharePoint.Client.Field field = existingFields.FirstOrDefault(f => f.Id == welcomePageField);
                    if (field != null)
                    {
                        documentSetTemplate.WelcomePageFields.Add(field);
                    }
                }

                documentSetTemplate.Update(true);
                web.Context.ExecuteQueryRetry();
            }

            web.Context.Load(createdCT);
            web.Context.ExecuteQueryRetry();

            return(createdCT);
        }
        public override TokenParser ProvisionObjects(Web web, ProvisioningTemplate template, TokenParser parser, ProvisioningTemplateApplyingInformation applyingInformation)
        {
            using (var scope = new PnPMonitoredScope(this.Name))
            {
                if (template.ComposedLook != null &&
                    !template.ComposedLook.Equals(ComposedLook.Empty))
                {
                    // Check if this is not a noscript site as themes and composed looks are not supported
                    if (web.IsNoScriptSite())
                    {
                        scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ComposedLooks_NoSiteCheck);
                        return parser;
                    }

                    bool executeQueryNeeded = false;
                    if (executeQueryNeeded)
                    {
                        web.Context.ExecuteQueryRetry();
                    }

                    if (String.IsNullOrEmpty(template.ComposedLook.ColorFile) &&
                        String.IsNullOrEmpty(template.ComposedLook.FontFile) &&
                        String.IsNullOrEmpty(template.ComposedLook.BackgroundFile))
                    {
                        // Apply OOB theme
                        web.SetComposedLookByUrl(template.ComposedLook.Name, "", "", "");
                    }
                    else
                    {
                        // Apply custom theme
                        string colorFile = null;
                        if (!string.IsNullOrEmpty(template.ComposedLook.ColorFile))
                        {
                            colorFile = parser.ParseString(template.ComposedLook.ColorFile);
                        }
                        string backgroundFile = null;
                        if (!string.IsNullOrEmpty(template.ComposedLook.BackgroundFile))
                        {
                            backgroundFile = parser.ParseString(template.ComposedLook.BackgroundFile);
                        }
                        string fontFile = null;
                        if (!string.IsNullOrEmpty(template.ComposedLook.FontFile))
                        {
                            fontFile = parser.ParseString(template.ComposedLook.FontFile);
                        }

                        string masterUrl = null;
                        if (template.WebSettings != null && !string.IsNullOrEmpty(template.WebSettings.MasterPageUrl))
                        {
                            masterUrl = parser.ParseString(template.WebSettings.MasterPageUrl);
                        }
                        web.CreateComposedLookByUrl(template.ComposedLook.Name, colorFile, fontFile, backgroundFile, masterUrl);
                        web.SetComposedLookByUrl(template.ComposedLook.Name, colorFile, fontFile, backgroundFile, masterUrl);
                    }

                    // Persist composed look info in property bag
                    var composedLookJson = JsonConvert.SerializeObject(template.ComposedLook);
                    web.SetPropertyBagValue("_PnP_ProvisioningTemplateComposedLookInfo", composedLookJson);
                }
            }
            return parser;
        }
        private static void UpdateContentType(Web web, Microsoft.SharePoint.Client.ContentType existingContentType, ContentType templateContentType, TokenParser parser, PnPMonitoredScope scope, bool isNoScriptSite = false)
        {
            var isDirty = false;

            if (existingContentType.Hidden != templateContentType.Hidden)
            {
                scope.LogPropertyUpdate("Hidden");
                existingContentType.Hidden = templateContentType.Hidden;
                isDirty = true;
            }
            if (existingContentType.ReadOnly != templateContentType.ReadOnly)
            {
                scope.LogPropertyUpdate("ReadOnly");
                existingContentType.ReadOnly = templateContentType.ReadOnly;
                isDirty = true;
            }
            if (existingContentType.Sealed != templateContentType.Sealed)
            {
                scope.LogPropertyUpdate("Sealed");
                existingContentType.Sealed = templateContentType.Sealed;
                isDirty = true;
            }
            if (templateContentType.Description != null && existingContentType.Description != parser.ParseString(templateContentType.Description))
            {
                scope.LogPropertyUpdate("Description");
                existingContentType.Description = parser.ParseString(templateContentType.Description);
                isDirty = true;
            }
            if (templateContentType.DocumentTemplate != null && existingContentType.DocumentTemplate != parser.ParseString(templateContentType.DocumentTemplate))
            {
                scope.LogPropertyUpdate("DocumentTemplate");
                existingContentType.DocumentTemplate = parser.ParseString(templateContentType.DocumentTemplate);
                isDirty = true;
            }
            if (existingContentType.Name != parser.ParseString(templateContentType.Name))
            {
                scope.LogPropertyUpdate("Name");
                existingContentType.Name = parser.ParseString(templateContentType.Name);
                isDirty = true;
                // CT is being renamed, add an extra token to the tokenparser
                parser.AddToken(new ContentTypeIdToken(web, existingContentType.Name, existingContentType.StringId));
            }
            if (templateContentType.Group != null && existingContentType.Group != parser.ParseString(templateContentType.Group))
            {
                scope.LogPropertyUpdate("Group");
                existingContentType.Group = parser.ParseString(templateContentType.Group);
                isDirty = true;
            }
            if (!isNoScriptSite)
            {
                if (templateContentType.DisplayFormUrl != null && existingContentType.DisplayFormUrl != parser.ParseString(templateContentType.DisplayFormUrl))
                {
                    scope.LogPropertyUpdate("DisplayFormUrl");
                    existingContentType.DisplayFormUrl = parser.ParseString(templateContentType.DisplayFormUrl);
                    isDirty = true;
                }
                if (templateContentType.EditFormUrl != null && existingContentType.EditFormUrl != parser.ParseString(templateContentType.EditFormUrl))
                {
                    scope.LogPropertyUpdate("EditFormUrl");
                    existingContentType.EditFormUrl = parser.ParseString(templateContentType.EditFormUrl);
                    isDirty = true;
                }
                if (templateContentType.NewFormUrl != null && existingContentType.NewFormUrl != parser.ParseString(templateContentType.NewFormUrl))
                {
                    scope.LogPropertyUpdate("NewFormUrl");
                    existingContentType.NewFormUrl = parser.ParseString(templateContentType.NewFormUrl);
                    isDirty = true;
                }
            }
            else
            {
                if (!String.IsNullOrEmpty(parser.ParseString(templateContentType.DisplayFormUrl)) ||
                    !String.IsNullOrEmpty(parser.ParseString(templateContentType.EditFormUrl)) ||
                    !String.IsNullOrEmpty(parser.ParseString(templateContentType.NewFormUrl)))
                {
                    // log message
                    scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ContentTypes_SkipCustomFormUrls, existingContentType.Name);
                }
            }

#if !SP2013
            if (templateContentType.Name.ContainsResourceToken())
            {
                existingContentType.NameResource.SetUserResourceValue(templateContentType.Name, parser);
                isDirty = true;
            }
            if (templateContentType.Description.ContainsResourceToken())
            {
                existingContentType.DescriptionResource.SetUserResourceValue(templateContentType.Description, parser);
                isDirty = true;
            }
#endif
            if (isDirty)
            {
                existingContentType.Update(true);
                web.Context.ExecuteQueryRetry();
            }
            // Delta handling
            existingContentType.EnsureProperty(c => c.FieldLinks);
            List <Guid> targetIds = existingContentType.FieldLinks.AsEnumerable().Select(c1 => c1.Id).ToList();
            List <Guid> sourceIds = templateContentType.FieldRefs.Select(c1 => c1.Id).ToList();

            var fieldsNotPresentInTarget = sourceIds.Except(targetIds).ToArray();

            if (fieldsNotPresentInTarget.Any())
            {
                foreach (var fieldId in fieldsNotPresentInTarget)
                {
                    var fieldRef = templateContentType.FieldRefs.Find(fr => fr.Id == fieldId);
                    var field    = web.Fields.GetById(fieldId);
                    scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_ContentTypes_Adding_field__0__to_content_type, fieldId);
                    web.AddFieldToContentType(existingContentType, field, fieldRef.Required, fieldRef.Hidden);
                }
            }

            isDirty = false;
            foreach (var fieldId in targetIds.Intersect(sourceIds))
            {
                var fieldLink = existingContentType.FieldLinks.FirstOrDefault(fl => fl.Id == fieldId);
                var fieldRef  = templateContentType.FieldRefs.Find(fr => fr.Id == fieldId);
                if (fieldRef != null)
                {
                    scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_ContentTypes_Field__0__exists_in_content_type, fieldId);
                    if (fieldLink.Required != fieldRef.Required)
                    {
                        scope.LogPropertyUpdate("Required");
                        fieldLink.Required = fieldRef.Required;
                        isDirty            = true;
                    }
                    if (fieldLink.Hidden != fieldRef.Hidden)
                    {
                        scope.LogPropertyUpdate("Hidden");
                        fieldLink.Hidden = fieldRef.Hidden;
                        isDirty          = true;
                    }
                }
            }

            // The new CT is a DocumentSet, and the target should be, as well
            if (templateContentType.DocumentSetTemplate != null)
            {
                if (!Microsoft.SharePoint.Client.DocumentSet.DocumentSetTemplate.IsChildOfDocumentSetContentType(web.Context, existingContentType).Value)
                {
                    scope.LogError(CoreResources.Provisioning_ObjectHandlers_ContentTypes_InvalidDocumentSet_Update_Request, existingContentType.Id, existingContentType.Name);
                }
                else
                {
                    Microsoft.SharePoint.Client.DocumentSet.DocumentSetTemplate templateToUpdate =
                        Microsoft.SharePoint.Client.DocumentSet.DocumentSetTemplate.GetDocumentSetTemplate(web.Context, existingContentType);

                    // TODO: Implement Delta Handling
                    scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ContentTypes_DocumentSet_DeltaHandling_OnHold, existingContentType.Id, existingContentType.Name);
                }
            }

            if (isDirty)
            {
                existingContentType.Update(true);
                web.Context.ExecuteQueryRetry();
            }
        }
        public override TokenParser ProvisionObjects(Web web, ProvisioningTemplate template, TokenParser parser, ProvisioningTemplateApplyingInformation applyingInformation)
        {
            using (var scope = new PnPMonitoredScope(this.Name))
            {

                if (template.Lists.Any())
                {
                    var rootWeb = (web.Context as ClientContext).Site.RootWeb;

                    web.EnsureProperties(w => w.ServerRelativeUrl);

                    web.Context.Load(web.Lists, lc => lc.IncludeWithDefaultProperties(l => l.RootFolder.ServerRelativeUrl));
                    web.Context.ExecuteQueryRetry();
                    var existingLists = web.Lists.AsEnumerable<List>().Select(existingList => existingList.RootFolder.ServerRelativeUrl).ToList();
                    var serverRelativeUrl = web.ServerRelativeUrl;

                    #region DataRows

                    foreach (var listInstance in template.Lists)
                    {
                        if (listInstance.DataRows != null && listInstance.DataRows.Any())
                        {
                            scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_ListInstancesDataRows_Processing_data_rows_for__0_, listInstance.Title);
                            // Retrieve the target list
                            var list = web.Lists.GetByTitle(parser.ParseString(listInstance.Title));
                            web.Context.Load(list);

                            // Retrieve the fields' types from the list
                            Microsoft.SharePoint.Client.FieldCollection fields = list.Fields;
                            web.Context.Load(fields, fs => fs.Include(f => f.InternalName, f => f.FieldTypeKind));
                            web.Context.ExecuteQueryRetry();

                            foreach (var dataRow in listInstance.DataRows)
                            {
                                try
                                {
                                    scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_ListInstancesDataRows_Creating_list_item__0_, listInstance.DataRows.IndexOf(dataRow) + 1);
                                    var listitemCI = new ListItemCreationInformation();
                                    var listitem = list.AddItem(listitemCI);

                                    foreach (var dataValue in dataRow.Values)
                                    {
                                        Field dataField = fields.FirstOrDefault(
                                            f => f.InternalName == parser.ParseString(dataValue.Key));

                                        if (dataField != null)
                                        {
                                            String fieldValue = parser.ParseString(dataValue.Value);

                                            switch (dataField.FieldTypeKind)
                                            {
                                                case FieldType.Geolocation:
                                                    // FieldGeolocationValue - Expected format: Altitude,Latitude,Longitude,Measure
                                                    var geolocationArray = fieldValue.Split(',');
                                                    if (geolocationArray.Length == 4)
                                                    {
                                                        var geolocationValue = new FieldGeolocationValue
                                                        {
                                                            Altitude = Double.Parse(geolocationArray[0]),
                                                            Latitude = Double.Parse(geolocationArray[1]),
                                                            Longitude = Double.Parse(geolocationArray[2]),
                                                            Measure = Double.Parse(geolocationArray[3]),
                                                        };
                                                        listitem[parser.ParseString(dataValue.Key)] = geolocationValue;
                                                    }
                                                    else
                                                    {
                                                        listitem[parser.ParseString(dataValue.Key)] = fieldValue;
                                                    }
                                                    break;
                                                case FieldType.Lookup:
                                                    // FieldLookupValue - Expected format: LookupID or LookupID,LookupID,LookupID...
                                                    if (fieldValue.Contains(","))
                                                    {
                                                        var lookupValues = new List<FieldLookupValue>();
                                                        fieldValue.Split(',').All(value =>
                                                        {
                                                            lookupValues.Add(new FieldLookupValue
                                                            {
                                                                LookupId = int.Parse(value),
                                                            });
                                                            return true;
                                                        });
                                                        listitem[parser.ParseString(dataValue.Key)] = lookupValues.ToArray();
                                                    }
                                                    else
                                                    {
                                                        var lookupValue = new FieldLookupValue
                                                        {
                                                            LookupId = int.Parse(fieldValue),
                                                        };
                                                        listitem[parser.ParseString(dataValue.Key)] = lookupValue;
                                                    }
                                                    break;
                                                case FieldType.URL:
                                                    // FieldUrlValue - Expected format: URL,Description
                                                    var urlArray = fieldValue.Split(',');
                                                    var linkValue = new FieldUrlValue();
                                                    if (urlArray.Length == 2)
                                                    {
                                                        linkValue.Url = urlArray[0];
                                                        linkValue.Description = urlArray[1];
                                                    }
                                                    else
                                                    {
                                                        linkValue.Url = urlArray[0];
                                                        linkValue.Description = urlArray[0];
                                                    }
                                                    listitem[parser.ParseString(dataValue.Key)] = linkValue;
                                                    break;
                                                case FieldType.User:
                                                    // FieldUserValue - Expected format: loginName or loginName,loginName,loginName...
                                                    if (fieldValue.Contains(","))
                                                    {
                                                        var userValues = new List<FieldUserValue>();
                                                        fieldValue.Split(',').All(value =>
                                                        {
                                                            var user = web.EnsureUser(value);
                                                            web.Context.Load(user);
                                                            web.Context.ExecuteQueryRetry();
                                                            if (user != null)
                                                            {
                                                                userValues.Add(new FieldUserValue
                                                                {
                                                                    LookupId = user.Id,
                                                                }); ;
                                                            }
                                                            return true;
                                                        });
                                                        listitem[parser.ParseString(dataValue.Key)] = userValues.ToArray();
                                                    }
                                                    else
                                                    {
                                                        var user = web.EnsureUser(fieldValue);
                                                        web.Context.Load(user);
                                                        web.Context.ExecuteQueryRetry();
                                                        if (user != null)
                                                        {
                                                            var userValue = new FieldUserValue
                                                            {
                                                                LookupId = user.Id,
                                                            };
                                                            listitem[parser.ParseString(dataValue.Key)] = userValue;
                                                        }
                                                        else
                                                        {
                                                            listitem[parser.ParseString(dataValue.Key)] = fieldValue;
                                                        }
                                                    }
                                                    break;
                                                case FieldType.DateTime:
                                                    var dateTime = DateTime.MinValue;
                                                    if (DateTime.TryParse(fieldValue, out dateTime))
                                                    {
                                                        listitem[parser.ParseString(dataValue.Key)] = dateTime;
                                                    }
                                                    break;
                                                default:
                                                    listitem[parser.ParseString(dataValue.Key)] = fieldValue;
                                                    break;
                                            }
                                        }
                                    }
                                    listitem.Update();
                                    web.Context.ExecuteQueryRetry(); // TODO: Run in batches?

                                    if (dataRow.Security != null && dataRow.Security.RoleAssignments.Count != 0)
                                    {
                                        listitem.SetSecurity(parser, dataRow.Security);
                                    }
                                }
                                catch (Exception ex)
                                {

                                    if (ex.GetType().Equals(typeof(ServerException)) &&
                                        (ex as ServerException).ServerErrorTypeName.Equals("Microsoft.SharePoint.SPDuplicateValuesFoundException", StringComparison.InvariantCultureIgnoreCase) &&
                                        applyingInformation.IgnoreDuplicateDataRowErrors)
                                    {
                                        scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ListInstancesDataRows_Creating_listitem_duplicate);
                                        continue;
                                    }
                                    else
                                    {
                                        scope.LogError(CoreResources.Provisioning_ObjectHandlers_ListInstancesDataRows_Creating_listitem_failed___0_____1_, ex.Message, ex.StackTrace);
                                        throw;
                                    }
                                }
                            }
                        }
                    }

                    #endregion
                }
            }

            return parser;
        }
        internal static void UpdateCustomAction(TokenParser parser, PnPMonitoredScope scope, CustomAction customAction, UserCustomAction existingCustomAction, bool isNoScriptSite = false)
        {
            var isDirty = false;

            if (isNoScriptSite)
            {
                scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_CustomActions_SkippingAddUpdateDueToNoScript, customAction.Name);
                return;
            }

            // Otherwise we update it
            if (customAction.CommandUIExtension != null)
            {
                if (existingCustomAction.CommandUIExtension != parser.ParseString(customAction.CommandUIExtension.ToString()))
                {
                    scope.LogPropertyUpdate("CommandUIExtension");
                    existingCustomAction.CommandUIExtension = parser.ParseString(customAction.CommandUIExtension.ToString());
                    isDirty = true;
                }
            }
            else
            {
                // Required to allow for a delta action to blank out the CommandUIExtension attribute
                if (existingCustomAction.CommandUIExtension != null)
                {
                    scope.LogPropertyUpdate("CommandUIExtension");
                    existingCustomAction.CommandUIExtension = null;
                    isDirty = true;
                }
            }

            if (existingCustomAction.Description != customAction.Description)
            {
                scope.LogPropertyUpdate("Description");
                existingCustomAction.Description = customAction.Description;
                isDirty = true;
            }
            #if !ONPREMISES
            if (customAction.Description.ContainsResourceToken())
            {
                if (existingCustomAction.DescriptionResource.SetUserResourceValue(customAction.Description, parser))
                {
                    isDirty = true;
                }
            }
            #endif
            if (existingCustomAction.Group != customAction.Group)
            {
                scope.LogPropertyUpdate("Group");
                existingCustomAction.Group = customAction.Group;
                isDirty = true;
            }
            if (existingCustomAction.ImageUrl != parser.ParseString(customAction.ImageUrl))
            {
                scope.LogPropertyUpdate("ImageUrl");
                existingCustomAction.ImageUrl = parser.ParseString(customAction.ImageUrl);
                isDirty = true;
            }
            if (existingCustomAction.Location != customAction.Location)
            {
                scope.LogPropertyUpdate("Location");
                existingCustomAction.Location = customAction.Location;
                isDirty = true;
            }
            if (existingCustomAction.RegistrationId != customAction.RegistrationId)
            {
                scope.LogPropertyUpdate("RegistrationId");
                existingCustomAction.RegistrationId = customAction.RegistrationId;
                isDirty = true;
            }
            if (existingCustomAction.RegistrationType != customAction.RegistrationType)
            {
                scope.LogPropertyUpdate("RegistrationType");
                existingCustomAction.RegistrationType = customAction.RegistrationType;
                isDirty = true;
            }
            if (existingCustomAction.ScriptBlock != parser.ParseString(customAction.ScriptBlock))
            {
                scope.LogPropertyUpdate("ScriptBlock");
                existingCustomAction.ScriptBlock = parser.ParseString(customAction.ScriptBlock);
                isDirty = true;
            }
            if (existingCustomAction.ScriptSrc != parser.ParseString(customAction.ScriptSrc, "~site", "~sitecollection"))
            {
                scope.LogPropertyUpdate("ScriptSrc");
                existingCustomAction.ScriptSrc = parser.ParseString(customAction.ScriptSrc, "~site", "~sitecollection");
                isDirty = true;
            }
            if (existingCustomAction.Sequence != customAction.Sequence)
            {
                scope.LogPropertyUpdate("Sequence");
                existingCustomAction.Sequence = customAction.Sequence;
                isDirty = true;
            }
            if (existingCustomAction.Title != parser.ParseString(customAction.Title))
            {
                scope.LogPropertyUpdate("Title");
                existingCustomAction.Title = parser.ParseString(customAction.Title);
                isDirty = true;
            }
            #if !ONPREMISES
            if (customAction.Title.ContainsResourceToken())
            {
                if (existingCustomAction.TitleResource.SetUserResourceValue(customAction.Title, parser))
                {
                    isDirty = true;
                }

            }
            #endif
            if (existingCustomAction.Url != parser.ParseString(customAction.Url))
            {
                scope.LogPropertyUpdate("Url");
                existingCustomAction.Url = parser.ParseString(customAction.Url);
                isDirty = true;
            }

            if (isDirty)
            {
                existingCustomAction.Update();
                existingCustomAction.Context.ExecuteQueryRetry();
            }
        }
        private static void UpdateContentType(Web web, Microsoft.SharePoint.Client.ContentType existingContentType, ContentType templateContentType, TokenParser parser, PnPMonitoredScope scope)
        {
            var isDirty = false;
            if (existingContentType.Hidden != templateContentType.Hidden)
            {
                scope.LogPropertyUpdate("Hidden");
                existingContentType.Hidden = templateContentType.Hidden;
                isDirty = true;
            }
            if (existingContentType.ReadOnly != templateContentType.ReadOnly)
            {
                scope.LogPropertyUpdate("ReadOnly");
                existingContentType.ReadOnly = templateContentType.ReadOnly;
                isDirty = true;
            }
            if (existingContentType.Sealed != templateContentType.Sealed)
            {
                scope.LogPropertyUpdate("Sealed");
                existingContentType.Sealed = templateContentType.Sealed;
                isDirty = true;
            }
            if (templateContentType.Description != null && existingContentType.Description != parser.ParseString(templateContentType.Description))
            {
                scope.LogPropertyUpdate("Description");
                existingContentType.Description = parser.ParseString(templateContentType.Description);
                isDirty = true;
            }
            if (templateContentType.DocumentTemplate != null && existingContentType.DocumentTemplate != parser.ParseString(templateContentType.DocumentTemplate))
            {
                scope.LogPropertyUpdate("DocumentTemplate");
                existingContentType.DocumentTemplate = parser.ParseString(templateContentType.DocumentTemplate);
                isDirty = true;
            }
            if (existingContentType.Name != parser.ParseString(templateContentType.Name))
            {
                scope.LogPropertyUpdate("Name");
                existingContentType.Name = parser.ParseString(templateContentType.Name);
                isDirty = true;
            }
            if (templateContentType.Group != null && existingContentType.Group != parser.ParseString(templateContentType.Group))
            {
                scope.LogPropertyUpdate("Group");
                existingContentType.Group = parser.ParseString(templateContentType.Group);
                isDirty = true;
            }
            if (isDirty)
            {
                existingContentType.Update(true);
                web.Context.ExecuteQueryRetry();
            }
            // Delta handling
            List<Guid> targetIds = existingContentType.FieldLinks.AsEnumerable().Select(c1 => c1.Id).ToList();
            List<Guid> sourceIds = templateContentType.FieldRefs.Select(c1 => c1.Id).ToList();

            var fieldsNotPresentInTarget = sourceIds.Except(targetIds).ToArray();

            if (fieldsNotPresentInTarget.Any())
            {
                foreach (var fieldId in fieldsNotPresentInTarget)
                {
                    var fieldRef = templateContentType.FieldRefs.Find(fr => fr.Id == fieldId);
                    var field = web.Fields.GetById(fieldId);
                    scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_ContentTypes_Adding_field__0__to_content_type, fieldId);
                    web.AddFieldToContentType(existingContentType, field, fieldRef.Required, fieldRef.Hidden);
                }
            }

            isDirty = false;
            foreach (var fieldId in targetIds.Intersect(sourceIds))
            {
                var fieldLink = existingContentType.FieldLinks.FirstOrDefault(fl => fl.Id == fieldId);
                var fieldRef = templateContentType.FieldRefs.Find(fr => fr.Id == fieldId);
                if (fieldRef != null)
                {
                    scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_ContentTypes_Field__0__exists_in_content_type, fieldId);
                    if (fieldLink.Required != fieldRef.Required)
                    {
                        scope.LogPropertyUpdate("Required");
                        fieldLink.Required = fieldRef.Required;
                        isDirty = true;
                    }
                    if (fieldLink.Hidden != fieldRef.Hidden)
                    {
                        scope.LogPropertyUpdate("Hidden");
                        fieldLink.Hidden = fieldRef.Hidden;
                        isDirty = true;
                    }
                }
            }

            // The new CT is a DocumentSet, and the target should be, as well
            if (templateContentType.DocumentSetTemplate != null)
            {
                if (!Microsoft.SharePoint.Client.DocumentSet.DocumentSetTemplate.IsChildOfDocumentSetContentType(web.Context, existingContentType).Value)
                {
                    scope.LogError(CoreResources.Provisioning_ObjectHandlers_ContentTypes_InvalidDocumentSet_Update_Request, existingContentType.Id, existingContentType.Name);
                }
                else
                {
                    Microsoft.SharePoint.Client.DocumentSet.DocumentSetTemplate templateToUpdate =
                        Microsoft.SharePoint.Client.DocumentSet.DocumentSetTemplate.GetDocumentSetTemplate(web.Context, existingContentType);

                    // TODO: Implement Delta Handling
                    scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ContentTypes_DocumentSet_DeltaHandling_OnHold, existingContentType.Id, existingContentType.Name);
                }
            }

            if (isDirty)
            {
                existingContentType.Update(true);
                web.Context.ExecuteQueryRetry();
            }
        }
        private void ProvisionCustomActionImplementation(object parent, CustomActionCollection customActions, TokenParser parser, PnPMonitoredScope scope, bool isNoScriptSite= false)
        {
            Web web = null;
            Site site = null;
            if (parent is Site)
            {
                site = parent as Site;

                // Switch parser context;
                parser.Rebase(site.RootWeb);
            }
            else
            {
                web = parent as Web;

                // Switch parser context
                parser.Rebase(web);
            }
            foreach (var customAction in customActions)
            {

                if (isNoScriptSite)
                {
                    scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_CustomActions_SkippingAddUpdateDueToNoScript, customAction.Name);
                    continue;
                }

                var caExists = false;
                if (site != null)
                {
                    caExists = site.CustomActionExists(customAction.Name);
                }
                else
                {
                    caExists = web.CustomActionExists(customAction.Name);
                }

                // If the CustomAction does not exist, we don't have to remove it, and it is enabled
                if (!caExists && !customAction.Remove && customAction.Enabled)
                {
                    // Then we add it to the target
                    var customActionEntity = new CustomActionEntity()
                    {
                        CommandUIExtension = customAction.CommandUIExtension != null ? parser.ParseString(customAction.CommandUIExtension.ToString()) : string.Empty,
                        Description = parser.ParseString(customAction.Description),
                        Group = customAction.Group,
                        ImageUrl = parser.ParseString(customAction.ImageUrl),
                        Location = customAction.Location,
                        Name = customAction.Name,
                        RegistrationId = customAction.RegistrationId,
                        RegistrationType = customAction.RegistrationType,
                        Remove = customAction.Remove,
                        Rights = customAction.Rights,
                        ScriptBlock = parser.ParseString(customAction.ScriptBlock),
                        ScriptSrc = parser.ParseString(customAction.ScriptSrc, "~site", "~sitecollection"),
                        Sequence = customAction.Sequence,
                        Title = parser.ParseString(customAction.Title),
                        Url = parser.ParseString(customAction.Url)
                    };

                    if (site != null)
                    {
                        scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_CustomActions_Adding_custom_action___0___to_scope_Site, customActionEntity.Name);
                        site.AddCustomAction(customActionEntity);
            #if !ONPREMISES
                        if ((!string.IsNullOrEmpty(customAction.Title) && customAction.Title.ContainsResourceToken()) ||
                            (!string.IsNullOrEmpty(customAction.Description) && customAction.Description.ContainsResourceToken()))
                        {
                            var uca = site.GetCustomActions().Where(uc => uc.Name == customAction.Name).FirstOrDefault();
                            SetCustomActionResourceValues(parser, customAction, uca);
                        }
            #endif
                    }
                    else
                    {
                        scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_CustomActions_Adding_custom_action___0___to_scope_Web, customActionEntity.Name);
                        web.AddCustomAction(customActionEntity);
            #if !ONPREMISES
                        if (customAction.Title.ContainsResourceToken() || customAction.Description.ContainsResourceToken())
                        {
                            var uca = web.GetCustomActions().Where(uc => uc.Name == customAction.Name).FirstOrDefault();
                            SetCustomActionResourceValues(parser, customAction, uca);
                        }
            #endif
                    }
                }
                else
                {
                    UserCustomAction existingCustomAction = null;
                    if (site != null)
                    {
                        existingCustomAction = site.GetCustomActions().FirstOrDefault(c => c.Name == customAction.Name);
                    }
                    else
                    {
                        existingCustomAction = web.GetCustomActions().FirstOrDefault(c => c.Name == customAction.Name);
                    }
                    if (existingCustomAction != null)
                    {
                        // If we have to remove the existing CustomAction
                        if (customAction.Remove)
                        {
                            // We simply remove it
                            existingCustomAction.DeleteObject();
                            existingCustomAction.Context.ExecuteQueryRetry();
                        }
                        else
                        {
                            UpdateCustomAction(parser, scope, customAction, existingCustomAction, isNoScriptSite);
                        }
                    }
                }
            }
        }
Beispiel #43
0
        private void UpdateField(Web web, string fieldId, XElement templateFieldElement, PnPMonitoredScope scope, TokenParser parser, string originalFieldXml)
        {
            var existingField = web.Fields.GetById(Guid.Parse(fieldId));
            web.Context.Load(existingField, f => f.SchemaXml);
            web.Context.ExecuteQueryRetry();

            XElement existingFieldElement = XElement.Parse(existingField.SchemaXml);

            XNodeEqualityComparer equalityComparer = new XNodeEqualityComparer();

            if (equalityComparer.GetHashCode(existingFieldElement) != equalityComparer.GetHashCode(templateFieldElement)) // Is field different in template?
            {
                if (existingFieldElement.Attribute("Type").Value == templateFieldElement.Attribute("Type").Value) // Is existing field of the same type?
                {
                    if (IsFieldXmlValid(parser.ParseString(originalFieldXml), parser, web.Context))
                    {
                        var listIdentifier = templateFieldElement.Attribute("List") != null ? templateFieldElement.Attribute("List").Value : null;

                        if (listIdentifier != null)
                        {
                            // Temporary remove list attribute from list
                            templateFieldElement.Attribute("List").Remove();
                        }

                        foreach (var attribute in templateFieldElement.Attributes())
                        {
                            if (existingFieldElement.Attribute(attribute.Name) != null)
                            {
                                existingFieldElement.Attribute(attribute.Name).Value = attribute.Value;
                            }
                            else
                            {
                                existingFieldElement.Add(attribute);
                            }
                        }
                        foreach (var element in templateFieldElement.Elements())
                        {
                            if (existingFieldElement.Element(element.Name) != null)
                            {
                                existingFieldElement.Element(element.Name).Remove();
                            }
                            existingFieldElement.Add(element);
                        }

                        if (existingFieldElement.Attribute("Version") != null)
                        {
                            existingFieldElement.Attributes("Version").Remove();
                        }
                        existingField.SchemaXml = parser.ParseString(existingFieldElement.ToString(), "~sitecollection", "~site");
                        existingField.UpdateAndPushChanges(true);
                        web.Context.Load(existingField, f => f.TypeAsString, f => f.DefaultValue);
                        web.Context.ExecuteQueryRetry();

                        bool isDirty = false;
            #if !CLIENTSDKV15
                        if (originalFieldXml.ContainsResourceToken())
                        {
                            var originalFieldElement = XElement.Parse(originalFieldXml);
                            var nameAttributeValue = originalFieldElement.Attribute("Title") != null ? originalFieldElement.Attribute("Title").Value : "";
                            if (nameAttributeValue.ContainsResourceToken())
                            {
                                existingField.TitleResource.SetUserResourceValue(nameAttributeValue, parser);
                                isDirty = true;
                            }
                            var descriptionAttributeValue = originalFieldElement.Attribute("Description") != null ? originalFieldElement.Attribute("Description").Value : "";
                            if (descriptionAttributeValue.ContainsResourceToken())
                            {
                                existingField.DescriptionResource.SetUserResourceValue(descriptionAttributeValue, parser);
                                isDirty = true;
                            }
                        }
            #endif
                        if (isDirty)
                        {
                            existingField.Update();
                            web.Context.ExecuteQuery();
                        }
                        if ((existingField.TypeAsString == "TaxonomyFieldType" || existingField.TypeAsString == "TaxonomyFieldTypeMulti") && !string.IsNullOrEmpty(existingField.DefaultValue))
                        {
                            var taxField = web.Context.CastTo<TaxonomyField>(existingField);
                            ValidateTaxonomyFieldDefaultValue(taxField);
                        }
                    }
                    else
                    {
                        // The field Xml was found invalid
                        var tokenString = parser.GetLeftOverTokens(originalFieldXml).Aggregate(String.Empty, (acc, i) => acc + " " + i);
                        scope.LogError("The field was found invalid: {0}", tokenString);
                        throw new Exception(string.Format("The field was found invalid: {0}", tokenString));
                    }
                }
                else
                {
                    var fieldName = existingFieldElement.Attribute("Name") != null ? existingFieldElement.Attribute("Name").Value : existingFieldElement.Attribute("StaticName").Value;
                    WriteWarning(string.Format(CoreResources.Provisioning_ObjectHandlers_Fields_Field__0____1___exists_but_is_of_different_type__Skipping_field_, fieldName, fieldId), ProvisioningMessageType.Warning);
                    scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_Fields_Field__0____1___exists_but_is_of_different_type__Skipping_field_, fieldName, fieldId);

                }
            }
        }
        public override TokenParser ProvisionObjects(Web web, ProvisioningTemplate template, TokenParser parser, ProvisioningTemplateApplyingInformation applyingInformation)
        {
            using (var scope = new PnPMonitoredScope(this.Name))
            {
                if (template.Lists.Any())
                {
                    var rootWeb = (web.Context as ClientContext).Site.RootWeb;

                    web.EnsureProperties(w => w.ServerRelativeUrl);

                    web.Context.Load(web.Lists, lc => lc.IncludeWithDefaultProperties(l => l.RootFolder.ServerRelativeUrl));
                    web.Context.ExecuteQueryRetry();
                    var existingLists     = web.Lists.AsEnumerable <List>().Select(existingList => existingList.RootFolder.ServerRelativeUrl).ToList();
                    var serverRelativeUrl = web.ServerRelativeUrl;

                    #region DataRows

                    foreach (var listInstance in template.Lists)
                    {
                        if (listInstance.DataRows != null && listInstance.DataRows.Any())
                        {
                            scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_ListInstancesDataRows_Processing_data_rows_for__0_, listInstance.Title);
                            // Retrieve the target list
                            var list = web.Lists.GetByTitle(parser.ParseString(listInstance.Title));
                            web.Context.Load(list);

                            // Retrieve the fields' types from the list
                            Microsoft.SharePoint.Client.FieldCollection fields = list.Fields;
                            web.Context.Load(fields, fs => fs.Include(f => f.InternalName, f => f.FieldTypeKind));
                            web.Context.ExecuteQueryRetry();

                            foreach (var dataRow in listInstance.DataRows)
                            {
                                try
                                {
                                    scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_ListInstancesDataRows_Creating_list_item__0_, listInstance.DataRows.IndexOf(dataRow) + 1);
                                    var listitemCI = new ListItemCreationInformation();
                                    var listitem   = list.AddItem(listitemCI);

                                    foreach (var dataValue in dataRow.Values)
                                    {
                                        Field dataField = fields.FirstOrDefault(
                                            f => f.InternalName == parser.ParseString(dataValue.Key));

                                        if (dataField != null)
                                        {
                                            String fieldValue = parser.ParseString(dataValue.Value);

                                            switch (dataField.FieldTypeKind)
                                            {
                                            case FieldType.Geolocation:
                                                // FieldGeolocationValue - Expected format: Altitude,Latitude,Longitude,Measure
                                                var geolocationArray = fieldValue.Split(',');
                                                if (geolocationArray.Length == 4)
                                                {
                                                    var geolocationValue = new FieldGeolocationValue
                                                    {
                                                        Altitude  = Double.Parse(geolocationArray[0]),
                                                        Latitude  = Double.Parse(geolocationArray[1]),
                                                        Longitude = Double.Parse(geolocationArray[2]),
                                                        Measure   = Double.Parse(geolocationArray[3]),
                                                    };
                                                    listitem[parser.ParseString(dataValue.Key)] = geolocationValue;
                                                }
                                                else
                                                {
                                                    listitem[parser.ParseString(dataValue.Key)] = fieldValue;
                                                }
                                                break;

                                            case FieldType.Lookup:
                                                // FieldLookupValue - Expected format: LookupID or LookupID,LookupID,LookupID...
                                                if (fieldValue.Contains(","))
                                                {
                                                    var lookupValues = new List <FieldLookupValue>();
                                                    fieldValue.Split(',').All(value =>
                                                    {
                                                        lookupValues.Add(new FieldLookupValue
                                                        {
                                                            LookupId = int.Parse(value),
                                                        });
                                                        return(true);
                                                    });
                                                    listitem[parser.ParseString(dataValue.Key)] = lookupValues.ToArray();
                                                }
                                                else
                                                {
                                                    var lookupValue = new FieldLookupValue
                                                    {
                                                        LookupId = int.Parse(fieldValue),
                                                    };
                                                    listitem[parser.ParseString(dataValue.Key)] = lookupValue;
                                                }
                                                break;

                                            case FieldType.URL:
                                                // FieldUrlValue - Expected format: URL,Description
                                                var urlArray  = fieldValue.Split(',');
                                                var linkValue = new FieldUrlValue();
                                                if (urlArray.Length == 2)
                                                {
                                                    linkValue.Url         = urlArray[0];
                                                    linkValue.Description = urlArray[1];
                                                }
                                                else
                                                {
                                                    linkValue.Url         = urlArray[0];
                                                    linkValue.Description = urlArray[0];
                                                }
                                                listitem[parser.ParseString(dataValue.Key)] = linkValue;
                                                break;

                                            case FieldType.User:
                                                // FieldUserValue - Expected format: loginName or loginName,loginName,loginName...
                                                if (fieldValue.Contains(","))
                                                {
                                                    var userValues = new List <FieldUserValue>();
                                                    fieldValue.Split(',').All(value =>
                                                    {
                                                        var user = web.EnsureUser(value);
                                                        web.Context.Load(user);
                                                        web.Context.ExecuteQueryRetry();
                                                        if (user != null)
                                                        {
                                                            userValues.Add(new FieldUserValue
                                                            {
                                                                LookupId = user.Id,
                                                            });;
                                                        }
                                                        return(true);
                                                    });
                                                    listitem[parser.ParseString(dataValue.Key)] = userValues.ToArray();
                                                }
                                                else
                                                {
                                                    var user = web.EnsureUser(fieldValue);
                                                    web.Context.Load(user);
                                                    web.Context.ExecuteQueryRetry();
                                                    if (user != null)
                                                    {
                                                        var userValue = new FieldUserValue
                                                        {
                                                            LookupId = user.Id,
                                                        };
                                                        listitem[parser.ParseString(dataValue.Key)] = userValue;
                                                    }
                                                    else
                                                    {
                                                        listitem[parser.ParseString(dataValue.Key)] = fieldValue;
                                                    }
                                                }
                                                break;

                                            case FieldType.DateTime:
                                                var dateTime = DateTime.MinValue;
                                                if (DateTime.TryParse(fieldValue, out dateTime))
                                                {
                                                    listitem[parser.ParseString(dataValue.Key)] = dateTime;
                                                }
                                                break;

                                            default:
                                                listitem[parser.ParseString(dataValue.Key)] = fieldValue;
                                                break;
                                            }
                                        }
                                    }
                                    listitem.Update();
                                    web.Context.ExecuteQueryRetry(); // TODO: Run in batches?

                                    if (dataRow.Security != null && (dataRow.Security.ClearSubscopes == true || dataRow.Security.CopyRoleAssignments == true || dataRow.Security.RoleAssignments.Count > 0))
                                    {
                                        listitem.SetSecurity(parser, dataRow.Security);
                                    }
                                }
                                catch (Exception ex)
                                {
                                    if (ex.GetType().Equals(typeof(ServerException)) &&
                                        (ex as ServerException).ServerErrorTypeName.Equals("Microsoft.SharePoint.SPDuplicateValuesFoundException", StringComparison.InvariantCultureIgnoreCase) &&
                                        applyingInformation.IgnoreDuplicateDataRowErrors)
                                    {
                                        scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ListInstancesDataRows_Creating_listitem_duplicate);
                                        continue;
                                    }
                                    else
                                    {
                                        scope.LogError(CoreResources.Provisioning_ObjectHandlers_ListInstancesDataRows_Creating_listitem_failed___0_____1_, ex.Message, ex.StackTrace);
                                        throw;
                                    }
                                }
                            }
                        }
                    }

                    #endregion
                }
            }

            return(parser);
        }
        private void UpdateField(ClientObject web, ListInfo listInfo, Guid fieldId, XElement templateFieldElement, Field existingField, PnPMonitoredScope scope, TokenParser parser)
        {
            web.Context.Load(existingField, f => f.SchemaXml);
            web.Context.ExecuteQueryRetry();

            var existingFieldElement = XElement.Parse(existingField.SchemaXml);

            var equalityComparer = new XNodeEqualityComparer();

            // Is field different in template?
            if (equalityComparer.GetHashCode(existingFieldElement) != equalityComparer.GetHashCode(templateFieldElement))
            {
                // Is existing field of the same type?
                if (existingFieldElement.Attribute("Type").Value == templateFieldElement.Attribute("Type").Value)
                {
                    templateFieldElement = PrepareField(templateFieldElement);

                    foreach (var attribute in templateFieldElement.Attributes())
                    {
                        if (existingFieldElement.Attribute(attribute.Name) != null)
                        {
                            existingFieldElement.Attribute(attribute.Name).Value = attribute.Value;
                        }
                        else
                        {
                            existingFieldElement.Add(attribute);
                        }
                    }
                    foreach (var element in templateFieldElement.Elements())
                    {
                        if (existingFieldElement.Element(element.Name) != null)
                        {
                            existingFieldElement.Element(element.Name).Remove();
                        }
                        existingFieldElement.Add(element);
                    }

                    if (existingFieldElement.Attribute("Version") != null)
                    {
                        existingFieldElement.Attributes("Version").Remove();
                    }
                    existingField.SchemaXml = parser.ParseString(existingFieldElement.ToString(), "~sitecollection", "~site");
                    existingField.UpdateAndPushChanges(true);
                    web.Context.ExecuteQueryRetry();
                }
                else
                {
                    var fieldName = existingFieldElement.Attribute("Name") != null ? existingFieldElement.Attribute("Name").Value : existingFieldElement.Attribute("StaticName").Value;
                    scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ListInstances_Field__0____1___exists_in_list__2____3___but_is_of_different_type__Skipping_field_, fieldName, fieldId, listInfo.TemplateList.Title, listInfo.SiteList.Id);
                    WriteWarning(string.Format(CoreResources.Provisioning_ObjectHandlers_ListInstances_Field__0____1___exists_in_list__2____3___but_is_of_different_type__Skipping_field_, fieldName, fieldId, listInfo.TemplateList.Title, listInfo.SiteList.Id), ProvisioningMessageType.Warning);
                }
            }
        }
Beispiel #46
0
        public override TokenParser ProvisionObjects(Web web, ProvisioningTemplate template, TokenParser parser, ProvisioningTemplateApplyingInformation applyingInformation)
        {
            using (var scope = new PnPMonitoredScope(this.Name))
            {
                web.EnsureProperties(w => w.ServerRelativeUrl);

                // determine pages library
                string pagesLibrary = "SitePages";

                List <string> preCreatedPages = new List <string>();

                // pre create the needed pages so we can fill the needed tokens which might be used later on when we put web parts on those pages
                foreach (var clientSidePage in template.ClientSidePages)
                {
                    string pageName = $"{System.IO.Path.GetFileNameWithoutExtension(clientSidePage.PageName)}.aspx";
                    string url      = $"{pagesLibrary}/{pageName}";

                    url = UrlUtility.Combine(web.ServerRelativeUrl, url);

                    var exists = true;
                    try
                    {
                        var file = web.GetFileByServerRelativeUrl(url);
                        web.Context.Load(file, f => f.UniqueId, f => f.ServerRelativeUrl);
                        web.Context.ExecuteQueryRetry();

                        // Fill token
                        parser.AddToken(new PageUniqueIdToken(web, file.ServerRelativeUrl.Substring(web.ServerRelativeUrl.Length).TrimStart("/".ToCharArray()), file.UniqueId));
                        parser.AddToken(new PageUniqueIdEncodedToken(web, file.ServerRelativeUrl.Substring(web.ServerRelativeUrl.Length).TrimStart("/".ToCharArray()), file.UniqueId));
                    }
                    catch (ServerException ex)
                    {
                        if (ex.ServerErrorTypeName == "System.IO.FileNotFoundException")
                        {
                            exists = false;
                        }
                    }

                    if (!exists)
                    {
                        // Pre-create the page
                        Pages.ClientSidePage page = web.AddClientSidePage(pageName);
                        page.Save(pageName);

                        var file = web.GetFileByServerRelativeUrl(url);
                        web.Context.Load(file, f => f.UniqueId, f => f.ServerRelativeUrl);
                        web.Context.ExecuteQueryRetry();

                        // Fill token
                        parser.AddToken(new PageUniqueIdToken(web, file.ServerRelativeUrl.Substring(web.ServerRelativeUrl.Length).TrimStart("/".ToCharArray()), file.UniqueId));
                        parser.AddToken(new PageUniqueIdEncodedToken(web, file.ServerRelativeUrl.Substring(web.ServerRelativeUrl.Length).TrimStart("/".ToCharArray()), file.UniqueId));

                        // Track that we pre-added this page
                        preCreatedPages.Add(url);
                    }
                }

                // Iterate over the pages and create/update them
                foreach (var clientSidePage in template.ClientSidePages)
                {
                    string pageName = $"{System.IO.Path.GetFileNameWithoutExtension(clientSidePage.PageName)}.aspx";
                    string url      = $"{pagesLibrary}/{pageName}";

                    url = UrlUtility.Combine(web.ServerRelativeUrl, url);

                    var exists = true;
                    try
                    {
                        var file = web.GetFileByServerRelativeUrl(url);
                        web.Context.Load(file);
                        web.Context.ExecuteQueryRetry();
                    }
                    catch (ServerException ex)
                    {
                        if (ex.ServerErrorTypeName == "System.IO.FileNotFoundException")
                        {
                            exists = false;
                        }
                    }

                    Pages.ClientSidePage page = null;
                    if (exists)
                    {
                        if (clientSidePage.Overwrite || preCreatedPages.Contains(url))
                        {
                            // Get the existing page
                            page = web.LoadClientSidePage(pageName);
                            // Clear the page
                            page.ClearPage();
                        }
                        else
                        {
                            scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ClientSidePages_NoOverWrite, pageName);
                            continue;
                        }
                    }
                    else
                    {
                        // Create new client side page
                        page = web.AddClientSidePage(pageName);
                    }

                    // Set page layout
                    if (!string.IsNullOrEmpty(clientSidePage.Layout))
                    {
                        if (clientSidePage.Layout.Equals("Article", StringComparison.InvariantCultureIgnoreCase))
                        {
                            page.LayoutType = Pages.ClientSidePageLayoutType.Article;
                        }
                        else if (clientSidePage.Layout.Equals("Home", StringComparison.InvariantCultureIgnoreCase))
                        {
                            page.LayoutType = Pages.ClientSidePageLayoutType.Home;
                        }
                    }

                    // Load existing available controls
                    var componentsToAdd = page.AvailableClientSideComponents().ToList();

                    // if no section specified then add a default single column section
                    if (!clientSidePage.Sections.Any())
                    {
                        clientSidePage.Sections.Add(new CanvasSection()
                        {
                            Type = CanvasSectionType.OneColumn, Order = 10
                        });
                    }

                    int sectionCount = -1;
                    // Apply the "layout" and content
                    foreach (var section in clientSidePage.Sections)
                    {
                        sectionCount++;
                        switch (section.Type)
                        {
                        case CanvasSectionType.OneColumn:
                            page.AddSection(Pages.CanvasSectionTemplate.OneColumn, section.Order);
                            break;

                        case CanvasSectionType.OneColumnFullWidth:
                            page.AddSection(Pages.CanvasSectionTemplate.OneColumnFullWidth, section.Order);
                            break;

                        case CanvasSectionType.TwoColumn:
                            page.AddSection(Pages.CanvasSectionTemplate.TwoColumn, section.Order);
                            break;

                        case CanvasSectionType.ThreeColumn:
                            page.AddSection(Pages.CanvasSectionTemplate.ThreeColumn, section.Order);
                            break;

                        case CanvasSectionType.TwoColumnLeft:
                            page.AddSection(Pages.CanvasSectionTemplate.TwoColumnLeft, section.Order);
                            break;

                        case CanvasSectionType.TwoColumnRight:
                            page.AddSection(Pages.CanvasSectionTemplate.TwoColumnRight, section.Order);
                            break;

                        default:
                            page.AddSection(Pages.CanvasSectionTemplate.OneColumn, section.Order);
                            break;
                        }

                        // Add controls to the section
                        if (section.Controls.Any())
                        {
                            // Safety measure: reset column order to 1 for columns marked with 0 or lower
                            foreach (var control in section.Controls.Where(p => p.Column <= 0).ToList())
                            {
                                control.Column = 1;
                            }

                            foreach (CanvasControl control in section.Controls)
                            {
                                Pages.ClientSideComponent baseControl = null;

                                // Is it a text control?
                                if (control.Type == WebPartType.Text)
                                {
                                    Pages.ClientSideText textControl = new Pages.ClientSideText();
                                    if (control.ControlProperties.Any())
                                    {
                                        var textProperty = control.ControlProperties.First();
                                        textControl.Text = parser.ParseString(textProperty.Value);
                                        // Reduce column number by 1 due 0 start indexing
                                        page.AddControl(textControl, page.Sections[sectionCount].Columns[control.Column - 1], control.Order);
                                    }
                                }
                                // It is a web part
                                else
                                {
                                    // apply token parsing on the web part properties
                                    control.JsonControlData = parser.ParseString(control.JsonControlData);

                                    // perform processing of web part properties (e.g. include listid property based list title property)
                                    var webPartPostProcessor = CanvasControlPostProcessorFactory.Resolve(control);
                                    webPartPostProcessor.Process(control, page);

                                    // Is a custom developed client side web part (3rd party)
                                    if (control.Type == WebPartType.Custom)
                                    {
                                        if (!string.IsNullOrEmpty(control.CustomWebPartName))
                                        {
                                            baseControl = componentsToAdd.FirstOrDefault(p => p.Name.Equals(control.CustomWebPartName, StringComparison.InvariantCultureIgnoreCase));
                                        }
                                        else if (control.ControlId != Guid.Empty)
                                        {
                                            baseControl = componentsToAdd.FirstOrDefault(p => p.Id.Equals($"{{{control.ControlId.ToString()}}}", StringComparison.CurrentCultureIgnoreCase));
                                        }
                                    }
                                    // Is an OOB client side web part (1st party)
                                    else
                                    {
                                        string webPartName = "";
                                        switch (control.Type)
                                        {
                                        case WebPartType.Image:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.Image);
                                            break;

                                        case WebPartType.BingMap:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.BingMap);
                                            break;

                                        case WebPartType.ContentEmbed:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.ContentEmbed);
                                            break;

                                        case WebPartType.ContentRollup:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.ContentRollup);
                                            break;

                                        case WebPartType.DocumentEmbed:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.DocumentEmbed);
                                            break;

                                        case WebPartType.Events:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.Events);
                                            break;

                                        case WebPartType.GroupCalendar:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.GroupCalendar);
                                            break;

                                        case WebPartType.Hero:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.Hero);
                                            break;

                                        case WebPartType.ImageGallery:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.ImageGallery);
                                            break;

                                        case WebPartType.LinkPreview:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.LinkPreview);
                                            break;

                                        case WebPartType.List:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.List);
                                            break;

                                        case WebPartType.NewsFeed:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.NewsFeed);
                                            break;

                                        case WebPartType.NewsReel:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.NewsReel);
                                            break;

                                        case WebPartType.PageTitle:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.PageTitle);
                                            break;

                                        case WebPartType.People:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.People);
                                            break;

                                        case WebPartType.PowerBIReportEmbed:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.PowerBIReportEmbed);
                                            break;

                                        case WebPartType.QuickChart:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.QuickChart);
                                            break;

                                        case WebPartType.QuickLinks:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.QuickLinks);
                                            break;

                                        case WebPartType.SiteActivity:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.SiteActivity);
                                            break;

                                        case WebPartType.VideoEmbed:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.VideoEmbed);
                                            break;

                                        case WebPartType.YammerEmbed:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.YammerEmbed);
                                            break;

                                        case WebPartType.CustomMessageRegion:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.CustomMessageRegion);
                                            break;

                                        case WebPartType.Divider:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.Divider);
                                            break;

                                        case WebPartType.MicrosoftForms:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.MicrosoftForms);
                                            break;

                                        case WebPartType.Spacer:
                                            webPartName = Pages.ClientSidePage.ClientSideWebPartEnumToName(Pages.DefaultClientSideWebParts.Spacer);
                                            break;
                                        }

                                        baseControl = componentsToAdd.FirstOrDefault(p => p.Name.Equals(webPartName, StringComparison.InvariantCultureIgnoreCase));
                                    }

                                    if (baseControl != null)
                                    {
                                        Pages.ClientSideWebPart myWebPart = new Pages.ClientSideWebPart(baseControl)
                                        {
                                            Order = control.Order
                                        };

                                        // Reduce column number by 1 due 0 start indexing
                                        page.AddControl(myWebPart, page.Sections[sectionCount].Columns[control.Column - 1], control.Order);

                                        // set properties using json string
                                        if (!String.IsNullOrEmpty(control.JsonControlData))
                                        {
                                            myWebPart.PropertiesJson = control.JsonControlData;
                                        }

                                        // set using property collection
                                        if (control.ControlProperties.Any())
                                        {
                                            // grab the "default" properties so we can deduct their types, needed to correctly apply the set properties
                                            var    controlManifest   = JObject.Parse(baseControl.Manifest);
                                            JToken controlProperties = null;
                                            if (controlManifest != null)
                                            {
                                                controlProperties = controlManifest.SelectToken("preconfiguredEntries[0].properties");
                                            }

                                            foreach (var property in control.ControlProperties)
                                            {
                                                Type propertyType = typeof(string);

                                                if (controlProperties != null)
                                                {
                                                    var defaultProperty = controlProperties.SelectToken(property.Key, false);
                                                    if (defaultProperty != null)
                                                    {
                                                        propertyType = Type.GetType($"System.{defaultProperty.Type.ToString()}");

                                                        if (propertyType == null)
                                                        {
                                                            if (defaultProperty.Type.ToString().Equals("integer", StringComparison.InvariantCultureIgnoreCase))
                                                            {
                                                                propertyType = typeof(int);
                                                            }
                                                        }
                                                    }
                                                }

                                                myWebPart.Properties[property.Key] = JToken.FromObject(Convert.ChangeType(parser.ParseString(property.Value), propertyType));
                                            }
                                        }
                                    }
                                    else
                                    {
                                        scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ClientSidePages_BaseControlNotFound, control.ControlId, control.CustomWebPartName);
                                    }
                                }
                            }
                        }
                    }

                    // Persist the page
                    page.Save(pageName);

                    // Set commenting, ignore on pages of the type Home
                    if (page.LayoutType != Pages.ClientSidePageLayoutType.Home)
                    {
                        // Make it a news page if requested
                        if (clientSidePage.PromoteAsNewsArticle)
                        {
                            page.PromoteAsNewsArticle();
                        }
                    }

                    if (clientSidePage.EnableComments)
                    {
                        page.EnableComments();
                    }
                    else
                    {
                        page.DisableComments();
                    }

                    // Publish page
                    if (clientSidePage.Publish)
                    {
                        page.Publish();
                    }
                }
            }
            return(parser);
        }
        public override ProvisioningTemplate ExtractObjects(Web web, ProvisioningTemplate template, ProvisioningTemplateCreationInformation creationInfo)
        {
            using (var scope = new PnPMonitoredScope(this.Name))
            {
                web.EnsureProperties(w => w.ServerRelativeUrl, w => w.Url);

                var serverRelativeUrl = web.ServerRelativeUrl;

                // For each list in the site
                var lists = web.Lists;

                web.Context.Load(lists,
                    lc => lc.IncludeWithDefaultProperties(
                        l => l.ContentTypes,
                        l => l.Views,
                        l => l.BaseTemplate,
                        l => l.OnQuickLaunch,
                        l => l.RootFolder.ServerRelativeUrl,
                        l => l.Fields.IncludeWithDefaultProperties(
                            f => f.Id,
                            f => f.Title,
                            f => f.Hidden,
                            f => f.InternalName,
                            f => f.Required)));

                web.Context.ExecuteQueryRetry();

                // Let's see if there are workflow subscriptions
                Microsoft.SharePoint.Client.WorkflowServices.WorkflowSubscription[] workflowSubscriptions = null;
                try
                {
                    workflowSubscriptions = web.GetWorkflowSubscriptions();
                }
                catch (ServerException)
                {
                    // If there is no workflow service present in the farm this method will throw an error.
                    // Swallow the exception
                }

                // Retrieve all not hidden lists and the Workflow History Lists, just in case there are active workflow subscriptions
                var includeWorkflowSubscriptions = workflowSubscriptions != null && workflowSubscriptions.Length > 0;
               // var allowedLists = lists.Where(l => !l.Hidden || includeWorkflowSubscriptions && l.BaseTemplate == 140);

                foreach (var siteList in lists)
                {
                    ListInstance baseTemplateList = null;
                    if (creationInfo.BaseTemplate != null)
                    {
                        // Check if we need to skip this list...if so let's do it before we gather all the other information for this list...improves performance
                        var index = creationInfo.BaseTemplate.Lists.FindIndex(f => f.Url.Equals(siteList.RootFolder.ServerRelativeUrl.Substring(serverRelativeUrl.Length + 1)) &&
                                                                                   f.TemplateType.Equals(siteList.BaseTemplate));
                        if (index != -1)
                        {
                            baseTemplateList = creationInfo.BaseTemplate.Lists[index];
                            if (siteList.Hidden && !(includeWorkflowSubscriptions && siteList.BaseTemplate == 140))
                            {
                                continue;
                            }
                        }
                    }

                    var contentTypeFields = new List<FieldRef>();
                    var list = new ListInstance
                    {
                        Description = siteList.Description,
                        EnableVersioning = siteList.EnableVersioning,
                        TemplateType = siteList.BaseTemplate,
                        Title = siteList.Title,
                        Hidden = siteList.Hidden,
                        EnableFolderCreation = siteList.EnableFolderCreation,
                        DocumentTemplate = Tokenize(siteList.DocumentTemplateUrl, web.Url),
                        ContentTypesEnabled = siteList.ContentTypesEnabled,
                        Url = siteList.RootFolder.ServerRelativeUrl.Substring(serverRelativeUrl.Length).TrimStart('/'),
                        TemplateFeatureID = siteList.TemplateFeatureId,
                        EnableAttachments = siteList.EnableAttachments,
                        OnQuickLaunch = siteList.OnQuickLaunch,
                        MaxVersionLimit =
                            siteList.IsObjectPropertyInstantiated("MajorVersionLimit") ? siteList.MajorVersionLimit : 0,
                        EnableMinorVersions = siteList.EnableMinorVersions,
                        MinorVersionLimit =
                            siteList.IsObjectPropertyInstantiated("MajorWithMinorVersionsLimit")
                                ? siteList.MajorWithMinorVersionsLimit
                                : 0
                    };

                    list = ExtractContentTypes(web, siteList, contentTypeFields, list);

                    list = ExtractViews(siteList, list);

                    list = ExtractFields(web, siteList, contentTypeFields, list, lists);

                    list.Security = siteList.GetSecurity();

                    var logCTWarning = false;
                    if (baseTemplateList != null)
                    {
                        if (!baseTemplateList.Equals(list))
                        {
                            scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_ListInstances_Adding_list___0_____1_, list.Title, list.Url);
                            template.Lists.Add(list);
                            if (list.ContentTypesEnabled && list.ContentTypeBindings.Any() && web.IsSubSite())
                            {
                                logCTWarning = true;
                            }
                        }
                    }
                    else
                    {
                        scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_ListInstances_Adding_list___0_____1_, list.Title, list.Url);
                        template.Lists.Add(list);
                        if (list.ContentTypesEnabled && list.ContentTypeBindings.Any() && web.IsSubSite())
                        {
                            logCTWarning = true;
                        }

                    }
                    if (logCTWarning)
                    {
                        scope.LogWarning("You are extracting a template from a subweb. List '{0}' refers to content types. Content types are not exported when extracting a template from a subweb", list.Title);
                        WriteWarning(string.Format("You are extracting a template from a subweb. List '{0}' refers to content types. Content types are not exported when extracting a template from a subweb", list.Title), ProvisioningMessageType.Warning);
                    }
                }

            }
            return template;
        }
        public override TokenParser ProvisionObjects(Web web, ProvisioningTemplate template, TokenParser parser, ProvisioningTemplateApplyingInformation applyingInformation)
        {
            using (var scope = new PnPMonitoredScope(this.Name))
            {
                // Changed by Paolo Pialorsi to embrace the new sub-site attributes to break role inheritance and copy role assignments
                // if this is a sub site then we're not provisioning security as by default security is inherited from the root site
                //if (web.IsSubSite() && !template.Security.BreakRoleInheritance)
                //{
                //    scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_SiteSecurity_Context_web_is_subweb__skipping_site_security_provisioning);
                //    return parser;
                //}

                if (web.IsSubSite() && template.Security.BreakRoleInheritance)
                {
                    web.BreakRoleInheritance(template.Security.CopyRoleAssignments, template.Security.ClearSubscopes);
                    web.Update();
                    web.Context.ExecuteQueryRetry();
                }

                var siteSecurity = template.Security;

                var ownerGroup   = web.AssociatedOwnerGroup;
                var memberGroup  = web.AssociatedMemberGroup;
                var visitorGroup = web.AssociatedVisitorGroup;

                web.Context.Load(ownerGroup, o => o.Title, o => o.Users);
                web.Context.Load(memberGroup, o => o.Title, o => o.Users);
                web.Context.Load(visitorGroup, o => o.Title, o => o.Users);
                web.Context.Load(web.SiteUsers);

                web.Context.ExecuteQueryRetry();

                if (!ownerGroup.ServerObjectIsNull())
                {
                    AddUserToGroup(web, ownerGroup, siteSecurity.AdditionalOwners, scope, parser);
                }
                if (!memberGroup.ServerObjectIsNull())
                {
                    AddUserToGroup(web, memberGroup, siteSecurity.AdditionalMembers, scope, parser);
                }
                if (!visitorGroup.ServerObjectIsNull())
                {
                    AddUserToGroup(web, visitorGroup, siteSecurity.AdditionalVisitors, scope, parser);
                }

                //sorting groups with respect to possible dependency through Owner property. Groups that are owners of other groups must be processed prior owned groups.
                for (int i = siteSecurity.SiteGroups.Count - 1; i >= 0; i--)
                {
                    var    currentGroup      = siteSecurity.SiteGroups[i];
                    string currentGroupOwner = parser.ParseString(currentGroup.Owner);
                    string currentGroupTitle = parser.ParseString(currentGroup.Title);

                    if (currentGroupOwner != "SHAREPOINT\\system" && currentGroupOwner != currentGroupTitle && !(currentGroupOwner.StartsWith("{{associated") && currentGroupOwner.EndsWith("group}}")))
                    {
                        for (int j = 0; j < i; j++)
                        {
                            if (parser.ParseString(siteSecurity.SiteGroups[j].Owner) == currentGroupTitle)
                            {
                                siteSecurity.SiteGroups.RemoveAt(i);
                                siteSecurity.SiteGroups.Insert(j, currentGroup);
                                i++;
                                break;
                            }
                        }
                    }
                }

                foreach (var siteGroup in siteSecurity.SiteGroups)
                {
                    Group group;
                    var   allGroups = web.Context.LoadQuery(web.SiteGroups.Include(gr => gr.LoginName));
                    web.Context.ExecuteQueryRetry();

                    string parsedGroupTitle       = parser.ParseString(siteGroup.Title);
                    string parsedGroupOwner       = parser.ParseString(siteGroup.Owner);
                    string parsedGroupDescription = parser.ParseString(siteGroup.Description);
                    bool   descriptionHasHtml     = HttpUtility.HtmlEncode(parsedGroupDescription) != parsedGroupDescription;

                    if (!web.GroupExists(parsedGroupTitle))
                    {
                        scope.LogDebug("Creating group {0}", parsedGroupTitle);
                        group = web.AddGroup(
                            parsedGroupTitle,
                            parsedGroupDescription,
                            parsedGroupTitle == parsedGroupOwner);
                        group.AllowMembersEditMembership   = siteGroup.AllowMembersEditMembership;
                        group.AllowRequestToJoinLeave      = siteGroup.AllowRequestToJoinLeave;
                        group.AutoAcceptRequestToJoinLeave = siteGroup.AutoAcceptRequestToJoinLeave;

                        if (parsedGroupTitle != parsedGroupOwner)
                        {
                            Principal ownerPrincipal = allGroups.FirstOrDefault(gr => gr.LoginName == parsedGroupOwner);
                            if (ownerPrincipal == null)
                            {
                                ownerPrincipal = web.EnsureUser(parsedGroupOwner);
                            }
                            group.Owner = ownerPrincipal;
                        }
                        group.Update();
                        web.Context.Load(group, g => g.Id, g => g.Title);
                        web.Context.ExecuteQueryRetry();
                        parser.AddToken(new GroupIdToken(web, group.Title, group.Id));

                        if (descriptionHasHtml)
                        {
                            var groupItem = web.SiteUserInfoList.GetItemById(group.Id);
                            groupItem["Notes"] = parsedGroupDescription;
                            groupItem.Update();
                            web.Context.ExecuteQueryRetry();
                        }
                    }
                    else
                    {
                        group = web.SiteGroups.GetByName(parsedGroupTitle);
                        web.Context.Load(group,
                                         g => g.Id,
                                         g => g.Title,
                                         g => g.Description,
                                         g => g.AllowMembersEditMembership,
                                         g => g.AllowRequestToJoinLeave,
                                         g => g.AutoAcceptRequestToJoinLeave,
                                         g => g.Owner.LoginName);
                        web.Context.ExecuteQueryRetry();

                        var isDirty = false;
                        if (descriptionHasHtml)
                        {
                            var groupItem = web.SiteUserInfoList.GetItemById(group.Id);
                            web.Context.Load(groupItem, g => g["Notes"]);
                            web.Context.ExecuteQueryRetry();
                            var description = groupItem["Notes"]?.ToString();

                            if (description != parsedGroupDescription)
                            {
                                groupItem["Notes"] = parsedGroupDescription;
                                groupItem.Update();
                                isDirty = true;
                            }
                        }
                        else
                        {
                            if (!String.IsNullOrEmpty(group.Description) && group.Description != parsedGroupDescription)
                            {
                                group.Description = parsedGroupDescription;
                                isDirty           = true;
                            }
                        }
                        if (group.AllowMembersEditMembership != siteGroup.AllowMembersEditMembership)
                        {
                            group.AllowMembersEditMembership = siteGroup.AllowMembersEditMembership;
                            isDirty = true;
                        }
                        if (group.AllowRequestToJoinLeave != siteGroup.AllowRequestToJoinLeave)
                        {
                            group.AllowRequestToJoinLeave = siteGroup.AllowRequestToJoinLeave;
                            isDirty = true;
                        }
                        if (group.AutoAcceptRequestToJoinLeave != siteGroup.AutoAcceptRequestToJoinLeave)
                        {
                            group.AutoAcceptRequestToJoinLeave = siteGroup.AutoAcceptRequestToJoinLeave;
                            isDirty = true;
                        }
                        if (group.Owner.LoginName != parsedGroupOwner)
                        {
                            if (parsedGroupTitle != parsedGroupOwner)
                            {
                                Principal ownerPrincipal = allGroups.FirstOrDefault(gr => gr.LoginName == parsedGroupOwner);
                                if (ownerPrincipal == null)
                                {
                                    ownerPrincipal = web.EnsureUser(parsedGroupOwner);
                                }
                                group.Owner = ownerPrincipal;
                            }
                            else
                            {
                                group.Owner = group;
                            }
                            isDirty = true;
                        }
                        if (isDirty)
                        {
                            scope.LogDebug("Updating existing group {0}", group.Title);
                            group.Update();
                            web.Context.ExecuteQueryRetry();
                        }
                    }
                    if (group != null && siteGroup.Members.Any())
                    {
                        AddUserToGroup(web, group, siteGroup.Members, scope, parser);
                    }
                }

                foreach (var admin in siteSecurity.AdditionalAdministrators)
                {
                    var parsedAdminName = parser.ParseString(admin.Name);
                    try
                    {
                        var user = web.EnsureUser(parsedAdminName);
                        user.IsSiteAdmin = true;
                        user.Update();
                        web.Context.ExecuteQueryRetry();
                    }
                    catch (Exception ex)
                    {
                        scope.LogWarning(ex, "Failed to add AdditionalAdministrator {0}", parsedAdminName);
                    }
                }

                // With the change from october, manage permission levels on subsites as well
                if (siteSecurity.SiteSecurityPermissions != null)
                {
                    var existingRoleDefinitions = web.Context.LoadQuery(web.RoleDefinitions.Include(wr => wr.Name, wr => wr.BasePermissions, wr => wr.Description));
                    web.Context.ExecuteQueryRetry();

                    if (siteSecurity.SiteSecurityPermissions.RoleDefinitions.Any())
                    {
                        foreach (var templateRoleDefinition in siteSecurity.SiteSecurityPermissions.RoleDefinitions)
                        {
                            var roleDefinitions    = existingRoleDefinitions as RoleDefinition[] ?? existingRoleDefinitions.ToArray();
                            var siteRoleDefinition = roleDefinitions.FirstOrDefault(erd => erd.Name == parser.ParseString(templateRoleDefinition.Name));
                            if (siteRoleDefinition == null)
                            {
                                scope.LogDebug("Creating role definition {0}", parser.ParseString(templateRoleDefinition.Name));
                                var roleDefinitionCI = new RoleDefinitionCreationInformation();
                                roleDefinitionCI.Name        = parser.ParseString(templateRoleDefinition.Name);
                                roleDefinitionCI.Description = parser.ParseString(templateRoleDefinition.Description);
                                BasePermissions basePermissions = new BasePermissions();

                                foreach (var permission in templateRoleDefinition.Permissions)
                                {
                                    basePermissions.Set(permission);
                                }

                                roleDefinitionCI.BasePermissions = basePermissions;

                                var newRoleDefinition = web.RoleDefinitions.Add(roleDefinitionCI);
                                web.Context.Load(newRoleDefinition, nrd => nrd.Name, nrd => nrd.Id);
                                web.Context.ExecuteQueryRetry();
                                parser.AddToken(new RoleDefinitionIdToken(web, newRoleDefinition.Name, newRoleDefinition.Id));
                            }
                            else
                            {
                                var isDirty = false;
                                if (siteRoleDefinition.Description != parser.ParseString(templateRoleDefinition.Description))
                                {
                                    siteRoleDefinition.Description = parser.ParseString(templateRoleDefinition.Description);
                                    isDirty = true;
                                }
                                var templateBasePermissions = new BasePermissions();
                                templateRoleDefinition.Permissions.ForEach(p => templateBasePermissions.Set(p));
                                if (siteRoleDefinition.BasePermissions != templateBasePermissions)
                                {
                                    isDirty = true;
                                    foreach (var permission in templateRoleDefinition.Permissions)
                                    {
                                        siteRoleDefinition.BasePermissions.Set(permission);
                                    }
                                }
                                if (isDirty)
                                {
                                    scope.LogDebug("Updating role definition {0}", parser.ParseString(templateRoleDefinition.Name));
                                    siteRoleDefinition.Update();
                                    web.Context.ExecuteQueryRetry();
                                }
                            }
                        }
                    }

                    var webRoleDefinitions = web.Context.LoadQuery(web.RoleDefinitions);
                    var webRoleAssignments = web.Context.LoadQuery(web.RoleAssignments);
                    var groups             = web.Context.LoadQuery(web.SiteGroups.Include(g => g.LoginName));
                    web.Context.ExecuteQueryRetry();

                    if (siteSecurity.SiteSecurityPermissions.RoleAssignments.Any())
                    {
                        foreach (var roleAssignment in siteSecurity.SiteSecurityPermissions.RoleAssignments)
                        {
                            if (!roleAssignment.Remove)
                            {
                                var roleDefinition = webRoleDefinitions.FirstOrDefault(r => r.Name == parser.ParseString(roleAssignment.RoleDefinition));
                                if (roleDefinition != null)
                                {
                                    Principal principal = GetPrincipal(web, parser, scope, groups, roleAssignment);

                                    if (principal != null)
                                    {
                                        var roleDefinitionBindingCollection = new RoleDefinitionBindingCollection(web.Context);
                                        roleDefinitionBindingCollection.Add(roleDefinition);
                                        web.RoleAssignments.Add(principal, roleDefinitionBindingCollection);
                                        web.Context.ExecuteQueryRetry();
                                    }
                                }
                                else
                                {
                                    scope.LogWarning("Role assignment {0} not found in web", roleAssignment.RoleDefinition);
                                }
                            }
                            else
                            {
                                var principal = GetPrincipal(web, parser, scope, groups, roleAssignment);
                                var assignmentsForPrincipal = webRoleAssignments.Where(t => t.PrincipalId == principal.Id);
                                foreach (var assignmentForPrincipal in assignmentsForPrincipal)
                                {
                                    var binding = assignmentForPrincipal.EnsureProperty(r => r.RoleDefinitionBindings).FirstOrDefault(b => b.Name == roleAssignment.RoleDefinition);
                                    if (binding != null)
                                    {
                                        assignmentForPrincipal.DeleteObject();
                                        web.Context.ExecuteQueryRetry();
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            return(parser);
        }
        private void ExtractMasterPagesAndPageLayouts(Web web, ProvisioningTemplate template, PnPMonitoredScope scope, ProvisioningTemplateCreationInformation creationInfo)
        {
            web.EnsureProperty(w => w.Url);
            String webApplicationUrl = GetWebApplicationUrl(web.Url);

            if (!String.IsNullOrEmpty(webApplicationUrl))
            {
                // Get the Publishing Feature reference template
                ProvisioningTemplate publishingFeatureTemplate = GetPublishingFeatureBaseTemplate();

                // Get a reference to the root folder of the master page gallery
                var gallery = web.GetCatalog(116);
                web.Context.Load(gallery, g => g.RootFolder);
                web.Context.ExecuteQueryRetry();

                var masterPageGalleryFolder = gallery.RootFolder;

                // Load the files in the master page gallery
                web.Context.Load(masterPageGalleryFolder.Files);
                web.Context.ExecuteQueryRetry();

                var sourceFiles = masterPageGalleryFolder.Files.AsEnumerable().Where(
                    f => f.Name.EndsWith(".aspx", StringComparison.InvariantCultureIgnoreCase) ||
                    f.Name.EndsWith(".html", StringComparison.InvariantCultureIgnoreCase) ||
                    f.Name.EndsWith(".master", StringComparison.InvariantCultureIgnoreCase));

                foreach (var file in sourceFiles)
                {
                    var listItem = file.EnsureProperty(f => f.ListItemAllFields);
                    listItem.ContentType.EnsureProperties(ct => ct.Id, ct => ct.StringId);

                    // Check if the content type is of type Master Page or Page Layout
                    if (listItem.ContentType.StringId.StartsWith(MASTER_PAGE_CONTENT_TYPE_ID) ||
                        listItem.ContentType.StringId.StartsWith(PAGE_LAYOUT_CONTENT_TYPE_ID) ||
                        listItem.ContentType.StringId.StartsWith(HTML_MASTER_PAGE_CONTENT_TYPE_ID) ||
                        listItem.ContentType.StringId.StartsWith(HTML_PAGE_LAYOUT_CONTENT_TYPE_ID))
                    {
                        // Skip any .ASPX or .MASTER file related to an .HTML designer file
                        if ((file.Name.EndsWith(".aspx", StringComparison.InvariantCultureIgnoreCase)
                            && sourceFiles.Any(f => f.Name.Equals(file.Name.ToLower().Replace(".aspx", ".html"),
                                StringComparison.InvariantCultureIgnoreCase))) ||
                            (file.Name.EndsWith(".master", StringComparison.InvariantCultureIgnoreCase)
                            && sourceFiles.Any(f => f.Name.Equals(file.Name.ToLower().Replace(".master", ".html"),
                                StringComparison.InvariantCultureIgnoreCase))))
                        {
                            continue;
                        }

                        // If the file is a custom one, and not one native
                        // and coming out from the publishing feature
                        if (creationInfo.IncludeNativePublishingFiles ||
                            !IsPublishingFeatureNativeFile(publishingFeatureTemplate, file.Name))
                        {
                            var fullUri = new Uri(UrlUtility.Combine(webApplicationUrl, file.ServerRelativeUrl));

                            var folderPath = fullUri.Segments.Take(fullUri.Segments.Count() - 1).ToArray().Aggregate((i, x) => i + x).TrimEnd('/');
                            var fileName = fullUri.Segments[fullUri.Segments.Count() - 1];

                            var publishingFile = new Model.File()
                            {
                                Folder = Tokenize(folderPath, web.Url),
                                Src = HttpUtility.UrlDecode(fileName),
                                Overwrite = true,
                            };

                            // Add field values to file
                            RetrieveFieldValues(web, file, publishingFile);

                            // Add the file to the template
                            template.Files.Add(publishingFile);

                            // Persist file using connector, if needed
                            if (creationInfo.PersistPublishingFiles)
                            {
                                PersistFile(web, creationInfo, scope, folderPath, fileName, true);
                            }

                            if (listItem.ContentType.StringId.StartsWith(MASTER_PAGE_CONTENT_TYPE_ID))
                            {
                                scope.LogWarning(String.Format("The file \"{0}\" is a custom MasterPage. Accordingly to the PnP Guidance (http://aka.ms/o365pnpguidancemasterpages) you should try to avoid using custom MasterPages.", file.Name));
                            }
                        }
                        else
                        {
                            scope.LogWarning(String.Format("Skipping file \"{0}\" because it is native in the publishing feature.", file.Name));
                        }
                    }
                }
            }
        }
Beispiel #50
0
        public override TokenParser ProvisionObjects(Web web, ProvisioningTemplate template, TokenParser parser, ProvisioningTemplateApplyingInformation applyingInformation)
        {
            using (var scope = new PnPMonitoredScope(this.Name))
            {
                var context = web.Context as ClientContext;

                web.EnsureProperties(w => w.ServerRelativeUrl, w => w.RootFolder.WelcomePage);

                // Check if this is not a noscript site as we're not allowed to update some properties
                bool isNoScriptSite = web.IsNoScriptSite();

                foreach (var page in template.Pages)
                {
                    var url = parser.ParseString(page.Url);

                    if (!url.ToLower().StartsWith(web.ServerRelativeUrl.ToLower()))
                    {
                        url = UrlUtility.Combine(web.ServerRelativeUrl, url);
                    }

                    var exists = true;
                    Microsoft.SharePoint.Client.File file = null;
                    try
                    {
                        file = web.GetFileByServerRelativeUrl(url);
                        web.Context.Load(file);
                        web.Context.ExecuteQueryRetry();
                    }
                    catch (ServerException ex)
                    {
                        if (ex.ServerErrorTypeName == "System.IO.FileNotFoundException")
                        {
                            exists = false;
                        }
                    }
                    if (exists)
                    {
                        if (page.Overwrite)
                        {
                            try
                            {
                                scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_Pages_Overwriting_existing_page__0_, url);

                                if (page.WelcomePage && url.Contains(web.RootFolder.WelcomePage))
                                    web.SetHomePage(string.Empty);

                                file.DeleteObject();
                                web.Context.ExecuteQueryRetry();
                                web.AddWikiPageByUrl(url);
                                if (page.Layout == WikiPageLayout.Custom)
                                {
                                    web.AddLayoutToWikiPage(WikiPageLayout.OneColumn, url);
                                }
                                else {
                                    web.AddLayoutToWikiPage(page.Layout, url);
                                }
                            }
                            catch (Exception ex)
                            {
                                scope.LogError(CoreResources.Provisioning_ObjectHandlers_Pages_Overwriting_existing_page__0__failed___1_____2_, url, ex.Message, ex.StackTrace);
                            }
                        }
                    }
                    else
                    {
                        try
                        {
                            scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_Pages_Creating_new_page__0_, url);

                            web.AddWikiPageByUrl(url);
                            if (page.Layout == WikiPageLayout.Custom)
                            {
                                web.AddLayoutToWikiPage(WikiPageLayout.OneColumn, url);
                            }
                            else {
                                web.AddLayoutToWikiPage(page.Layout, url);
                            }
                        }
                        catch (Exception ex)
                        {
                            scope.LogError(CoreResources.Provisioning_ObjectHandlers_Pages_Creating_new_page__0__failed___1_____2_, url, ex.Message, ex.StackTrace);
                        }
                    }

                    if (page.WelcomePage)
                    {
                        web.RootFolder.EnsureProperty(p => p.ServerRelativeUrl);
                        var rootFolderRelativeUrl = url.Substring(web.RootFolder.ServerRelativeUrl.Length);
                        web.SetHomePage(rootFolderRelativeUrl);
                    }

                    if (page.WebParts != null & page.WebParts.Any())
                    {
                        if (!isNoScriptSite)
                        {
                            var existingWebParts = web.GetWebParts(url);

                            foreach (var webpart in page.WebParts)
                            {
                                if (existingWebParts.FirstOrDefault(w => w.WebPart.Title == webpart.Title) == null)
                                {
                                    WebPartEntity wpEntity = new WebPartEntity();
                                    wpEntity.WebPartTitle = webpart.Title;
                                    wpEntity.WebPartXml = parser.ParseString(webpart.Contents.Trim(new[] { '\n', ' ' }));
                                    web.AddWebPartToWikiPage(url, wpEntity, (int)webpart.Row, (int)webpart.Column, false);
                                }
                            }
                            var allWebParts = web.GetWebParts(url);
                            foreach (var webpart in allWebParts)
                            {
                                parser.AddToken(new WebPartIdToken(web, webpart.WebPart.Title, webpart.Id));
                            }
                        }
                        else
                        {
                            scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_Pages_SkipAddingWebParts, page.Url);
                        }
                    }

                    file = web.GetFileByServerRelativeUrl(url);
                    file.EnsureProperty(f => f.ListItemAllFields);

                    if (page.Fields.Any())
                    {
                        var item = file.ListItemAllFields;
                        foreach (var fieldValue in page.Fields)
                        {
                            item[fieldValue.Key] = parser.ParseString(fieldValue.Value);
                        }
                        item.Update();
                        web.Context.ExecuteQueryRetry();
                    }
                    if (page.Security != null && page.Security.RoleAssignments.Count != 0)
                    {
                        web.Context.Load(file.ListItemAllFields);
                        web.Context.ExecuteQueryRetry();
                        file.ListItemAllFields.SetSecurity(parser, page.Security);
                    }
                }
            }
            return parser;
        }
        private void UpdateField(Web web, string fieldId, XElement templateFieldElement, PnPMonitoredScope scope)
        {
            var existingField = web.Fields.GetById(Guid.Parse(fieldId));
            web.Context.Load(existingField, f => f.SchemaXml);
            web.Context.ExecuteQueryRetry();

            XElement existingFieldElement = XElement.Parse(existingField.SchemaXml);

            XNodeEqualityComparer equalityComparer = new XNodeEqualityComparer();

            if (equalityComparer.GetHashCode(existingFieldElement) != equalityComparer.GetHashCode(templateFieldElement)) // Is field different in template?
            {
                if (existingFieldElement.Attribute("Type").Value == templateFieldElement.Attribute("Type").Value) // Is existing field of the same type?
                {
                    var listIdentifier = templateFieldElement.Attribute("List") != null ? templateFieldElement.Attribute("List").Value : null;

                    if (listIdentifier != null)
                    {
                        // Temporary remove list attribute from list
                        templateFieldElement.Attribute("List").Remove();
                    }

                    foreach (var attribute in templateFieldElement.Attributes())
                    {
                        if (existingFieldElement.Attribute(attribute.Name) != null)
                        {
                            existingFieldElement.Attribute(attribute.Name).Value = attribute.Value;
                        }
                        else
                        {
                            existingFieldElement.Add(attribute);
                        }
                    }
                    foreach (var element in templateFieldElement.Elements())
                    {
                        if (existingFieldElement.Element(element.Name) != null)
                        {
                            existingFieldElement.Element(element.Name).Remove();
                        }
                        existingFieldElement.Add(element);
                    }

                    if (existingFieldElement.Attribute("Version") != null)
                    {
                        existingFieldElement.Attributes("Version").Remove();
                    }
                    existingField.SchemaXml = existingFieldElement.ToString();
                    existingField.UpdateAndPushChanges(true);
                    web.Context.ExecuteQueryRetry();
                }
                else
                {
                    var fieldName = existingFieldElement.Attribute("Name") != null ? existingFieldElement.Attribute("Name").Value : existingFieldElement.Attribute("StaticName").Value;
                    WriteWarning(string.Format(CoreResources.Provisioning_ObjectHandlers_Fields_Field__0____1___exists_but_is_of_different_type__Skipping_field_, fieldName, fieldId), ProvisioningMessageType.Warning);
                    scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_Fields_Field__0____1___exists_but_is_of_different_type__Skipping_field_, fieldName, fieldId);

                }
            }
        }
Beispiel #52
0
        internal static void UpdateCustomAction(TokenParser parser, PnPMonitoredScope scope, CustomAction customAction, UserCustomAction existingCustomAction, bool isNoScriptSite = false)
        {
            var isDirty = false;

            if (isNoScriptSite)
            {
                scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_CustomActions_SkippingAddUpdateDueToNoScript, customAction.Name);
                return;
            }

            // Otherwise we update it
            if (customAction.CommandUIExtension != null)
            {
                if (existingCustomAction.CommandUIExtension != parser.ParseString(customAction.CommandUIExtension.ToString()))
                {
                    scope.LogPropertyUpdate("CommandUIExtension");
                    existingCustomAction.CommandUIExtension = parser.ParseString(customAction.CommandUIExtension.ToString());
                    isDirty = true;
                }
            }
            else
            {
                // Required to allow for a delta action to blank out the CommandUIExtension attribute
                if (existingCustomAction.CommandUIExtension != null)
                {
                    scope.LogPropertyUpdate("CommandUIExtension");
                    existingCustomAction.CommandUIExtension = null;
                    isDirty = true;
                }
            }

            if (customAction.ClientSideComponentId != null && customAction.ClientSideComponentId != Guid.Empty)
            {
                if (existingCustomAction.ClientSideComponentId != customAction.ClientSideComponentId)
                {
                    existingCustomAction.ClientSideComponentId = customAction.ClientSideComponentId;
                    isDirty = true;
                }
            }

            if (!String.IsNullOrEmpty(customAction.ClientSideComponentProperties))
            {
                if (existingCustomAction.ClientSideComponentProperties != parser.ParseString(customAction.ClientSideComponentProperties))
                {
                    existingCustomAction.ClientSideComponentProperties = parser.ParseString(customAction.ClientSideComponentProperties);
                    isDirty = true;
                }
            }

            if (existingCustomAction.Description != customAction.Description)
            {
                scope.LogPropertyUpdate("Description");
                existingCustomAction.Description = customAction.Description;
                isDirty = true;
            }

            if (customAction.Description.ContainsResourceToken())
            {
                if (existingCustomAction.DescriptionResource.SetUserResourceValue(customAction.Description, parser))
                {
                    isDirty = true;
                }
            }

            if (existingCustomAction.Group != customAction.Group)
            {
                scope.LogPropertyUpdate("Group");
                existingCustomAction.Group = customAction.Group;
                isDirty = true;
            }
            if (existingCustomAction.ImageUrl != parser.ParseString(customAction.ImageUrl))
            {
                scope.LogPropertyUpdate("ImageUrl");
                existingCustomAction.ImageUrl = parser.ParseString(customAction.ImageUrl);
                isDirty = true;
            }
            if (existingCustomAction.Location != customAction.Location)
            {
                scope.LogPropertyUpdate("Location");
                existingCustomAction.Location = customAction.Location;
                isDirty = true;
            }
            if (existingCustomAction.RegistrationId != parser.ParseString(customAction.RegistrationId))
            {
                scope.LogPropertyUpdate("RegistrationId");
                existingCustomAction.RegistrationId = parser.ParseString(customAction.RegistrationId);
                isDirty = true;
            }
            if (existingCustomAction.RegistrationType != customAction.RegistrationType)
            {
                scope.LogPropertyUpdate("RegistrationType");
                existingCustomAction.RegistrationType = customAction.RegistrationType;
                isDirty = true;
            }
            if (existingCustomAction.ScriptBlock != parser.ParseString(customAction.ScriptBlock))
            {
                scope.LogPropertyUpdate("ScriptBlock");
                existingCustomAction.ScriptBlock = parser.ParseString(customAction.ScriptBlock);
                isDirty = true;
            }
            if (existingCustomAction.ScriptSrc != parser.ParseString(customAction.ScriptSrc))
            {
                scope.LogPropertyUpdate("ScriptSrc");
                existingCustomAction.ScriptSrc = parser.ParseString(customAction.ScriptSrc);
                isDirty = true;
            }
            if (existingCustomAction.Sequence != customAction.Sequence)
            {
                scope.LogPropertyUpdate("Sequence");
                existingCustomAction.Sequence = customAction.Sequence;
                isDirty = true;
            }
            if (existingCustomAction.Title != parser.ParseString(customAction.Title))
            {
                scope.LogPropertyUpdate("Title");
                existingCustomAction.Title = parser.ParseString(customAction.Title);
                isDirty = true;
            }

            if (customAction.Title.ContainsResourceToken())
            {
                if (existingCustomAction.TitleResource.SetUserResourceValue(customAction.Title, parser))
                {
                    isDirty = true;
                }
            }

            if (existingCustomAction.Url != parser.ParseString(customAction.Url))
            {
                scope.LogPropertyUpdate("Url");
                existingCustomAction.Url = parser.ParseString(customAction.Url);
                isDirty = true;
            }

            if (isDirty)
            {
                existingCustomAction.Update();
                existingCustomAction.Context.ExecuteQueryRetry();
            }
        }
 private static void LocalizeParts(Web web, TokenParser parser, string url, WebPartCollection webParts, PnPMonitoredScope scope)
 {
     if (CanUseAcceptLanguageHeaderForLocalization(web))
     {
         var context = web.Context;
         var allParts = web.GetWebParts(parser.ParseString(url)).ToList();
         foreach (var webPart in webParts)
         {
     #if !SP2016
             var partOnPage = allParts.FirstOrDefault(w => w.ZoneId == webPart.Zone && w.WebPart.ZoneIndex == webPart.Order);
     #else
             var partOnPage = allParts.FirstOrDefault(w => w.WebPart.ZoneIndex == webPart.Order);
     #endif
             if (webPart.Title.ContainsResourceToken() && partOnPage != null)
             {
                 var resourceValues = parser.GetResourceTokenResourceValues(webPart.Title);
                 foreach (var resourceValue in resourceValues)
                 {
                     // Save property with correct locale on the request to make it stick
                     // http://sadomovalex.blogspot.no/2015/09/localize-web-part-titles-via-client.html
                     context.PendingRequest.RequestExecutor.WebRequest.Headers["Accept-Language"] = resourceValue.Item1;
                     partOnPage.WebPart.Properties["Title"] = resourceValue.Item2;
                     partOnPage.SaveWebPartChanges();
                     context.ExecuteQueryRetry();
                 }
             }
         }
         context.PendingRequest.RequestExecutor.WebRequest.Headers.Remove("Accept-Language");
     }
     else
     {
         // warning
         scope.LogWarning(CoreResources.Provisioning_Extensions_WebPartLocalization_Skip);
     }
 }
Beispiel #54
0
        private void ProvisionCustomActionImplementation(object parent, CustomActionCollection customActions, TokenParser parser, PnPMonitoredScope scope, bool isNoScriptSite = false)
        {
            Web  web  = null;
            Site site = null;

            if (parent is Site)
            {
                site = parent as Site;

                // Switch parser context;
                parser.Rebase(site.RootWeb);
            }
            else
            {
                web = parent as Web;

                // Switch parser context
                parser.Rebase(web);
            }
            foreach (var customAction in customActions)
            {
                if (isNoScriptSite && Guid.Empty == customAction.ClientSideComponentId)
                {
                    scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_CustomActions_SkippingAddUpdateDueToNoScript, customAction.Name);
                    continue;
                }

                var caExists = false;
                if (site != null)
                {
                    caExists = site.CustomActionExists(customAction.Name);
                }
                else
                {
                    caExists = web.CustomActionExists(customAction.Name);
                }

                // If the CustomAction does not exist, we don't have to remove it, and it is enabled
                if (!caExists && !customAction.Remove && customAction.Enabled)
                {
                    // Then we add it to the target
                    var customActionEntity = new CustomActionEntity()
                    {
                        ClientSideComponentId         = customAction.ClientSideComponentId,
                        ClientSideComponentProperties = customAction.ClientSideComponentProperties != null?parser.ParseString(customAction.ClientSideComponentProperties) : customAction.ClientSideComponentProperties,
                                                            CommandUIExtension = customAction.CommandUIExtension != null?parser.ParseString(customAction.CommandUIExtension.ToString()) : string.Empty,
                                                                                     Description      = parser.ParseString(customAction.Description),
                                                                                     Group            = customAction.Group,
                                                                                     ImageUrl         = parser.ParseString(customAction.ImageUrl),
                                                                                     Location         = customAction.Location,
                                                                                     Name             = customAction.Name,
                                                                                     RegistrationId   = parser.ParseString(customAction.RegistrationId),
                                                                                     RegistrationType = customAction.RegistrationType,
                                                                                     Remove           = customAction.Remove,
                                                                                     Rights           = customAction.Rights,
                                                                                     ScriptBlock      = parser.ParseString(customAction.ScriptBlock),
                                                                                     ScriptSrc        = parser.ParseString(customAction.ScriptSrc),
                                                                                     Sequence         = customAction.Sequence,
                                                                                     Title            = parser.ParseString(customAction.Title),
                                                                                     Url = parser.ParseString(customAction.Url)
                    };


                    if (site != null)
                    {
                        scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_CustomActions_Adding_custom_action___0___to_scope_Site, customActionEntity.Name);
                        site.AddCustomAction(customActionEntity);
                        if ((!string.IsNullOrEmpty(customAction.Title) && customAction.Title.ContainsResourceToken()) ||
                            (!string.IsNullOrEmpty(customAction.Description) && customAction.Description.ContainsResourceToken()))
                        {
                            var uca = site.GetCustomActions().FirstOrDefault(uc => uc.Name == customAction.Name);
                            SetCustomActionResourceValues(parser, customAction, uca);
                        }
                    }
                    else
                    {
                        scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_CustomActions_Adding_custom_action___0___to_scope_Web, customActionEntity.Name);
                        web.AddCustomAction(customActionEntity);
                        if (customAction.Title.ContainsResourceToken() || customAction.Description.ContainsResourceToken())
                        {
                            var uca = web.GetCustomActions().FirstOrDefault(uc => uc.Name == customAction.Name);
                            SetCustomActionResourceValues(parser, customAction, uca);
                        }
                    }
                }
                else
                {
                    UserCustomAction existingCustomAction;
                    if (site != null)
                    {
                        existingCustomAction = site.GetCustomActions().FirstOrDefault(c => c.Name == customAction.Name);
                    }
                    else
                    {
                        existingCustomAction = web.GetCustomActions().FirstOrDefault(c => c.Name == customAction.Name);
                    }
                    if (existingCustomAction != null)
                    {
                        // If we have to remove the existing CustomAction
                        if (customAction.Remove)
                        {
                            // We simply remove it
                            existingCustomAction.DeleteObject();
                            existingCustomAction.Context.ExecuteQueryRetry();
                        }
                        else
                        {
                            UpdateCustomAction(parser, scope, customAction, existingCustomAction, isNoScriptSite);
                        }
                    }
                }
            }
        }
        private Tuple<List, TokenParser> UpdateList(Web web, List existingList, ListInstance templateList, TokenParser parser, PnPMonitoredScope scope, bool isNoScriptSite = false)
        {
            web.Context.Load(existingList,
                l => l.Title,
                l => l.Description,
                l => l.OnQuickLaunch,
                l => l.Hidden,
                l => l.ContentTypesEnabled,
                l => l.EnableAttachments,
                l => l.EnableVersioning,
                l => l.EnableFolderCreation,
                l => l.EnableModeration,
                l => l.EnableMinorVersions,
                l => l.ForceCheckout,
                l => l.DraftVersionVisibility,
                l => l.Views,
                l => l.DocumentTemplateUrl,
                l => l.RootFolder,
                l => l.BaseType,
                l => l.BaseTemplate
            #if !SP2013
            , l => l.MajorWithMinorVersionsLimit
            , l => l.MajorVersionLimit
            #endif
            );
            web.Context.ExecuteQueryRetry();

            if (existingList.BaseTemplate == templateList.TemplateType)
            {
                var isDirty = false;
                if (parser.ParseString(templateList.Title) != existingList.Title)
                {
                    var oldTitle = existingList.Title;
                    existingList.Title = parser.ParseString(templateList.Title);
                    if (!oldTitle.Equals(existingList.Title, StringComparison.OrdinalIgnoreCase))
                    {
                        parser.AddToken(new ListIdToken(web, existingList.Title, existingList.Id));
                        parser.AddToken(new ListUrlToken(web, existingList.Title, existingList.RootFolder.ServerRelativeUrl.Substring(web.ServerRelativeUrl.Length + 1)));
                    }
                    isDirty = true;
                }
                if (!string.IsNullOrEmpty(templateList.DocumentTemplate))
                {
                    if (existingList.DocumentTemplateUrl != parser.ParseString(templateList.DocumentTemplate))
                    {
                        existingList.DocumentTemplateUrl = parser.ParseString(templateList.DocumentTemplate);
                        isDirty = true;
                    }
                }
                if (!string.IsNullOrEmpty(templateList.Description) && templateList.Description != existingList.Description)
                {
                    existingList.Description = templateList.Description;
                    isDirty = true;
                }
                if (templateList.Hidden != existingList.Hidden)
                {
                    existingList.Hidden = templateList.Hidden;
                    isDirty = true;
                }
                if (templateList.OnQuickLaunch != existingList.OnQuickLaunch)
                {
                    existingList.OnQuickLaunch = templateList.OnQuickLaunch;
                    isDirty = true;
                }
                if (existingList.BaseTemplate != (int)ListTemplateType.Survey &&
                    templateList.ContentTypesEnabled != existingList.ContentTypesEnabled)
                {
                    existingList.ContentTypesEnabled = templateList.ContentTypesEnabled;
                    isDirty = true;
                }
                if (existingList.BaseTemplate != (int)ListTemplateType.Survey &&
                    existingList.BaseTemplate != (int)ListTemplateType.DocumentLibrary &&
                    existingList.BaseTemplate != (int)ListTemplateType.PictureLibrary)
                {
                    // https://msdn.microsoft.com/EN-US/library/microsoft.sharepoint.splist.enableattachments.aspx
                    // The EnableAttachments property does not apply to any list that has a base type of Survey, DocumentLibrary or PictureLibrary.
                    // If you set this property to true for either type of list, it throws an SPException.
                    if (templateList.EnableAttachments != existingList.EnableAttachments)
                    {
                        existingList.EnableAttachments = templateList.EnableAttachments;
                        isDirty = true;
                    }
                }
                if (existingList.BaseTemplate != (int)ListTemplateType.DiscussionBoard)
                {
                    if (templateList.EnableFolderCreation != existingList.EnableFolderCreation)
                    {
                        existingList.EnableFolderCreation = templateList.EnableFolderCreation;
                        isDirty = true;
                    }
                }
            #if !SP2013
                if (templateList.Title.ContainsResourceToken())
                {
                    if (existingList.TitleResource.SetUserResourceValue(templateList.Title, parser))
                    {
                        isDirty = true;
                    }
                }
            #endif
                if (existingList.EnableModeration != templateList.EnableModeration)
                {
                    existingList.EnableModeration = templateList.EnableModeration;
                    isDirty = true;
                }

                if (templateList.ForceCheckout != existingList.ForceCheckout)
                {
                    existingList.ForceCheckout = templateList.ForceCheckout;
                    isDirty = true;
                }

                if (templateList.EnableVersioning)
                {
                    if (existingList.EnableVersioning != templateList.EnableVersioning)
                    {
                        existingList.EnableVersioning = templateList.EnableVersioning;
                        isDirty = true;
                    }
            #if !SP2013
                    if (existingList.MajorVersionLimit != templateList.MaxVersionLimit)
                    {
                        existingList.MajorVersionLimit = templateList.MaxVersionLimit;
                        isDirty = true;
                    }
            #endif
                    if (existingList.BaseType == BaseType.DocumentLibrary)
                    {
                        // Only supported on Document Libraries
                        if (templateList.EnableMinorVersions != existingList.EnableMinorVersions)
                        {
                            existingList.EnableMinorVersions = templateList.EnableMinorVersions;
                            isDirty = true;
                        }

                        if ((DraftVisibilityType)templateList.DraftVersionVisibility != existingList.DraftVersionVisibility)
                        {
                            existingList.DraftVersionVisibility = (DraftVisibilityType)templateList.DraftVersionVisibility;
                            isDirty = true;
                        }

                        if (templateList.EnableMinorVersions)
                        {
                            if (templateList.MinorVersionLimit != existingList.MajorWithMinorVersionsLimit)
                            {
                                existingList.MajorWithMinorVersionsLimit = templateList.MinorVersionLimit;
                            }

                            if (DraftVisibilityType.Approver ==
                                (DraftVisibilityType)templateList.DraftVersionVisibility)
                            {
                                if (templateList.EnableModeration)
                                {
                                    if ((DraftVisibilityType)templateList.DraftVersionVisibility != existingList.DraftVersionVisibility)
                                    {
                                        existingList.DraftVersionVisibility = (DraftVisibilityType)templateList.DraftVersionVisibility;
                                        isDirty = true;
                                    }
                                }
                            }
                            else
                            {
                                if ((DraftVisibilityType)templateList.DraftVersionVisibility != existingList.DraftVersionVisibility)
                                {
                                    existingList.DraftVersionVisibility = (DraftVisibilityType)templateList.DraftVersionVisibility;
                                    isDirty = true;
                                }
                            }
                        }
                    }
                }
                else
                {
                    if (existingList.EnableVersioning != templateList.EnableVersioning)
                    {
                        existingList.EnableVersioning = templateList.EnableVersioning;
                        isDirty = true;
                    }
                }

                if (isDirty)
                {
                    existingList.Update();
                    web.Context.ExecuteQueryRetry();
                    isDirty = false;
                }

                #region UserCustomActions
                if (!isNoScriptSite)
                {
                    // Add any UserCustomActions
                    var existingUserCustomActions = existingList.UserCustomActions;
                    web.Context.Load(existingUserCustomActions);
                    web.Context.ExecuteQueryRetry();

                    foreach (CustomAction userCustomAction in templateList.UserCustomActions)
                    {
                        // Check for existing custom actions before adding (compare by custom action name)
                        if (!existingUserCustomActions.AsEnumerable().Any(uca => uca.Name == userCustomAction.Name))
                        {
                            CreateListCustomAction(existingList, parser, userCustomAction);
                            isDirty = true;
                        }
                        else
                        {
                            var existingCustomAction = existingUserCustomActions.AsEnumerable().FirstOrDefault(uca => uca.Name == userCustomAction.Name);
                            if (existingCustomAction != null)
                            {
                                isDirty = true;

                                // If the custom action already exists
                                if (userCustomAction.Remove)
                                {
                                    // And if we need to remove it, we simply delete it
                                    existingCustomAction.DeleteObject();
                                }
                                else
                                {
                                    // Otherwise we update it, and before we force the target
                                    // registration type and ID to avoid issues
                                    userCustomAction.RegistrationType = UserCustomActionRegistrationType.List;
                                    userCustomAction.RegistrationId = existingList.Id.ToString("B").ToUpper();
                                    ObjectCustomActions.UpdateCustomAction(parser, scope, userCustomAction, existingCustomAction);
                                    // Blank out these values again to avoid inconsistent domain model data
                                    userCustomAction.RegistrationType = UserCustomActionRegistrationType.None;
                                    userCustomAction.RegistrationId = null;
                                }
                            }
                        }
                    }

                    if (isDirty)
                    {
                        existingList.Update();
                        web.Context.ExecuteQueryRetry();
                        isDirty = false;
                    }
                }
                else
                {
                    scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ListInstances_SkipAddingOrUpdatingCustomActions);
                }
                #endregion

                if (existingList.ContentTypesEnabled)
                {
                    // Check if we need to add a content type

                    var existingContentTypes = existingList.ContentTypes;
                    web.Context.Load(existingContentTypes, cts => cts.Include(ct => ct.StringId));
                    web.Context.ExecuteQueryRetry();

                    var bindingsToAdd = templateList.ContentTypeBindings.Where(ctb => existingContentTypes.All(ct => !ctb.ContentTypeId.Equals(ct.StringId, StringComparison.InvariantCultureIgnoreCase))).ToList();
                    var defaultCtBinding = templateList.ContentTypeBindings.FirstOrDefault(ctb => ctb.Default == true);
                    var currentDefaultContentTypeId = existingContentTypes.First().StringId;

                    foreach (var ctb in bindingsToAdd)
                    {
                        var tempCT = web.GetContentTypeById(ctb.ContentTypeId, searchInSiteHierarchy: true);
                        if (tempCT != null)
                        {
                            // Get the name of the existing CT
                            var name = tempCT.EnsureProperty(ct => ct.Name);

                            // If the CT does not exist in the target list, and we don't have to remove it
                            if (!existingList.ContentTypeExistsByName(name) && !ctb.Remove)
                            {
                                existingList.AddContentTypeToListById(ctb.ContentTypeId, searchContentTypeInSiteHierarchy: true);
                            }
                            // Else if the CT exists in the target list, and we have to remove it
                            else if (existingList.ContentTypeExistsByName(name) && ctb.Remove)
                            {
                                // Then remove it from the target list
                                existingList.RemoveContentTypeByName(name);
                            }
                        }
                    }

                    // default ContentTypeBinding should be set last because
                    // list extension .SetDefaultContentTypeToList() re-sets
                    // the list.RootFolder UniqueContentTypeOrder property
                    // which may cause missing CTs from the "New Button"
                    if (defaultCtBinding != null)
                    {
                        // Only update the defualt contenttype when we detect a change in default value
                        if (!currentDefaultContentTypeId.Equals(defaultCtBinding.ContentTypeId, StringComparison.InvariantCultureIgnoreCase))
                        {
                            existingList.SetDefaultContentTypeToList(defaultCtBinding.ContentTypeId);
                        }
                    }
                }
                if (templateList.Security != null)
                {
                    existingList.SetSecurity(parser, templateList.Security);
                }
                return Tuple.Create(existingList, parser);
            }
            else
            {
                scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_ListInstances_List__0____1____2___exists_but_is_of_a_different_type__Skipping_list_, templateList.Title, templateList.Url, existingList.Id);
                WriteWarning(string.Format(CoreResources.Provisioning_ObjectHandlers_ListInstances_List__0____1____2___exists_but_is_of_a_different_type__Skipping_list_, templateList.Title, templateList.Url, existingList.Id), ProvisioningMessageType.Warning);
                return null;
            }
        }
Beispiel #56
0
        public override TokenParser ProvisionObjects(Web web, ProvisioningTemplate template, TokenParser parser, ProvisioningTemplateApplyingInformation applyingInformation)
        {
            using (var scope = new PnPMonitoredScope(this.Name))
            {
                if (template.WebSettings != null)
                {
                    // Check if this is not a noscript site as we're not allowed to update some properties
                    bool isNoScriptSite = web.IsNoScriptSite();

                    web.EnsureProperties(
                        w => w.NoCrawl,
                        w => w.CommentsOnSitePagesDisabled,
                        w => w.ExcludeFromOfflineClient,
                        w => w.MembersCanShare,
                        w => w.DisableFlows,
                        w => w.DisableAppViews,
                        w => w.HorizontalQuickLaunch,
                        w => w.SearchScope,
                        w => w.SearchBoxInNavBar,
                        w => w.RootFolder,
                        w => w.Title,
                        w => w.Description,
                        w => w.AlternateCssUrl,
                        w => w.WebTemplate,
                        w => w.HasUniqueRoleAssignments);

                    var webSettings = template.WebSettings;

                    // Since the IsSubSite function can trigger an executequery ensure it's called before any updates to the web object are done.
                    if (!web.IsSubSite() || (web.IsSubSite() && web.HasUniqueRoleAssignments))
                    {
                        String requestAccessEmailValue = parser.ParseString(webSettings.RequestAccessEmail);
                        if (!String.IsNullOrEmpty(requestAccessEmailValue) && requestAccessEmailValue.Length >= 255)
                        {
                            requestAccessEmailValue = requestAccessEmailValue.Substring(0, 255);
                        }
                        if (!String.IsNullOrEmpty(requestAccessEmailValue))
                        {
                            web.RequestAccessEmail = requestAccessEmailValue;

                            web.Update();
                            web.Context.ExecuteQueryRetry();
                        }
                    }

                    if (!isNoScriptSite)
                    {
                        web.NoCrawl = webSettings.NoCrawl;
                    }
                    else
                    {
                        scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_WebSettings_SkipNoCrawlUpdate);
                    }

                    if (web.CommentsOnSitePagesDisabled != webSettings.CommentsOnSitePagesDisabled)
                    {
                        web.CommentsOnSitePagesDisabled = webSettings.CommentsOnSitePagesDisabled;
                    }

                    if (web.ExcludeFromOfflineClient != webSettings.ExcludeFromOfflineClient)
                    {
                        web.ExcludeFromOfflineClient = webSettings.ExcludeFromOfflineClient;
                    }

                    if (web.MembersCanShare != webSettings.MembersCanShare)
                    {
                        web.MembersCanShare = webSettings.MembersCanShare;
                    }

                    if (web.DisableFlows != webSettings.DisableFlows)
                    {
                        web.DisableFlows = webSettings.DisableFlows;
                    }

                    if (web.DisableAppViews != webSettings.DisableAppViews)
                    {
                        web.DisableAppViews = webSettings.DisableAppViews;
                    }

                    if (web.HorizontalQuickLaunch != webSettings.HorizontalQuickLaunch)
                    {
                        web.HorizontalQuickLaunch = webSettings.HorizontalQuickLaunch;
                    }

                    if (web.SearchScope.ToString() != webSettings.SearchScope.ToString())
                    {
                        web.SearchScope = (SearchScopeType)Enum.Parse(typeof(SearchScopeType), webSettings.SearchScope.ToString(), true);
                    }

                    if (web.SearchBoxInNavBar.ToString() != webSettings.SearchBoxInNavBar.ToString())
                    {
                        web.SearchBoxInNavBar = (SearchBoxInNavBarType)Enum.Parse(typeof(SearchBoxInNavBarType), webSettings.SearchBoxInNavBar.ToString(), true);
                    }

                    string searchCenterUrl = parser.ParseString(webSettings.SearchCenterUrl);
                    if (!string.IsNullOrEmpty(searchCenterUrl) &&
                        web.GetWebSearchCenterUrl(true) != webSettings.SearchCenterUrl)
                    {
                        web.SetWebSearchCenterUrl(webSettings.SearchCenterUrl);
                    }

                    var masterUrl = parser.ParseString(webSettings.MasterPageUrl);
                    if (!string.IsNullOrEmpty(masterUrl))
                    {
                        if (!isNoScriptSite)
                        {
                            web.MasterUrl = masterUrl;
                        }
                        else
                        {
                            scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_WebSettings_SkipMasterPageUpdate);
                        }
                    }
                    var customMasterUrl = parser.ParseString(webSettings.CustomMasterPageUrl);
                    if (!string.IsNullOrEmpty(customMasterUrl))
                    {
                        if (!isNoScriptSite)
                        {
                            web.CustomMasterUrl = customMasterUrl;
                        }
                        else
                        {
                            scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_WebSettings_SkipCustomMasterPageUpdate);
                        }
                    }
                    if (!String.IsNullOrEmpty(webSettings.Title))
                    {
                        var newTitle = parser.ParseString(webSettings.Title);
                        if (newTitle != web.Title)
                        {
                            web.Title = newTitle;
                        }
                    }
                    if (!String.IsNullOrEmpty(webSettings.Description))
                    {
                        var newDescription = parser.ParseString(webSettings.Description);
                        if (newDescription != web.Description)
                        {
                            web.Description = newDescription;
                        }
                    }
                    if (webSettings.SiteLogo != null)
                    {
                        var logoUrl = parser.ParseString(webSettings.SiteLogo);
                        if (template.BaseSiteTemplate == "SITEPAGEPUBLISHING#0" && web.WebTemplate == "GROUP")
                        {
                            // logo provisioning throws when applying across base template IDs; provisioning fails in this case
                            // this is the error that is already (rightly so) shown beforehand in the console: WARNING: The source site from which the template was generated had a base template ID value of SITEPAGEPUBLISHING#0, while the current target site has a base template ID value of GROUP#0. This could cause potential issues while applying the template.
                            WriteMessage("Applying site logo across base template IDs is not possible. Skipping site logo provisioning.", ProvisioningMessageType.Warning);
                        }
                        else
                        {
                            // Modern site? Then we assume the SiteLogo is actually a filepath
                            if (web.WebTemplate == "GROUP")
                            {
                                if (!string.IsNullOrEmpty(logoUrl) && !logoUrl.ToLower().Contains("_api/groupservice/getgroupimage"))
                                {
                                    var fileBytes = ConnectorFileHelper.GetFileBytes(template.Connector, logoUrl);
                                    if (fileBytes != null && fileBytes.Length > 0)
                                    {
                                        var mimeType = "";
                                        var imgUrl   = logoUrl;
                                        if (imgUrl.Contains("?"))
                                        {
                                            imgUrl = imgUrl.Split(new[] { '?' })[0];
                                        }
                                        if (imgUrl.EndsWith(".gif", StringComparison.InvariantCultureIgnoreCase))
                                        {
                                            mimeType = "image/gif";
                                        }
                                        if (imgUrl.EndsWith(".png", StringComparison.InvariantCultureIgnoreCase))
                                        {
                                            mimeType = "image/png";
                                        }
                                        if (imgUrl.EndsWith(".jpg", StringComparison.InvariantCultureIgnoreCase))
                                        {
                                            mimeType = "image/jpeg";
                                        }
                                        Sites.SiteCollection.SetGroupImageAsync((ClientContext)web.Context, fileBytes, mimeType).GetAwaiter().GetResult();
                                    }
                                }
                            }
                            else
                            {
                                web.SiteLogoUrl = logoUrl;
                            }
                        }
                    }
                    var welcomePage = parser.ParseString(webSettings.WelcomePage);
                    if (!string.IsNullOrEmpty(welcomePage))
                    {
                        if (welcomePage != web.RootFolder.WelcomePage)
                        {
                            web.RootFolder.WelcomePage = welcomePage;
                            web.RootFolder.Update();
                        }
                    }
                    if (!string.IsNullOrEmpty(webSettings.AlternateCSS))
                    {
                        var newAlternateCssUrl = parser.ParseString(webSettings.AlternateCSS);
                        if (newAlternateCssUrl != web.AlternateCssUrl)
                        {
                            web.AlternateCssUrl = newAlternateCssUrl;
                        }
                    }

                    // Temporary disabled as this change is a breaking change for folks that have not set this property in their provisioning templates
                    //web.QuickLaunchEnabled = webSettings.QuickLaunchEnabled;

                    web.Update();
                    web.Context.ExecuteQueryRetry();

                    if (webSettings.HubSiteUrl != null)
                    {
                        if (TenantExtensions.IsCurrentUserTenantAdmin(web.Context as ClientContext))
                        {
                            var hubsiteUrl = parser.ParseString(webSettings.HubSiteUrl);
                            try
                            {
                                using (var tenantContext = web.Context.Clone(web.GetTenantAdministrationUrl(), applyingInformation.AccessTokens))
                                {
                                    var tenant = new Tenant(tenantContext);
                                    tenant.ConnectSiteToHubSite(web.Url, hubsiteUrl);
                                    tenantContext.ExecuteQueryRetry();
                                }
                            }
                            catch (Exception ex)
                            {
                                WriteMessage($"Hub site association failed: {ex.Message}", ProvisioningMessageType.Warning);
                            }
                        }
                        else
                        {
                            WriteMessage("You need to be a SharePoint admin when associating to a Hub site.", ProvisioningMessageType.Warning);
                        }
                    }
                }
            }

            return(parser);
        }
        private void CreateFolderInList(Microsoft.SharePoint.Client.Folder parentFolder, Model.Folder folder, TokenParser parser, PnPMonitoredScope scope)
        {
            // Determine the folder name, parsing any token
            String targetFolderName = parser.ParseString(folder.Name);

            // Check if the folder already exists
            if (parentFolder.FolderExists(targetFolderName))
            {
                // Log a warning if the folder already exists
                String warningFolderAlreadyExists = String.Format(CoreResources.Provisioning_ObjectHandlers_ListInstances_FolderAlreadyExists, targetFolderName, parentFolder.ServerRelativeUrl);
                scope.LogWarning(warningFolderAlreadyExists);
                WriteWarning(warningFolderAlreadyExists, ProvisioningMessageType.Warning);
            }

            // Create it or get a reference to it
            var currentFolder = parentFolder.EnsureFolder(targetFolderName);

            if (currentFolder != null)
            {
                // Handle any child-folder
                if (folder.Folders != null && folder.Folders.Count > 0)
                {
                    foreach (var childFolder in folder.Folders)
                    {
                        CreateFolderInList(currentFolder, childFolder, parser, scope);
                    }
                }

                // Handle current folder security
                if (folder.Security != null && folder.Security.RoleAssignments.Count != 0)
                {
                    var currentFolderItem = currentFolder.ListItemAllFields;
                    parentFolder.Context.Load(currentFolderItem);
                    parentFolder.Context.ExecuteQueryRetry();
                    currentFolderItem.SetSecurity(parser, folder.Security);
                }
            }
        }
Beispiel #58
0
        public override TokenParser ProvisionObjects(Web web, ProvisioningTemplate template, TokenParser parser, ProvisioningTemplateApplyingInformation applyingInformation)
        {
            using (var scope = new PnPMonitoredScope(this.Name))
            {
                var context = web.Context as ClientContext;

                web.EnsureProperties(w => w.ServerRelativeUrl, w => w.RootFolder.WelcomePage);

                // Check if this is not a noscript site as we're not allowed to update some properties
                bool isNoScriptSite = web.IsNoScriptSite();

                foreach (var page in template.Pages)
                {
                    var url = parser.ParseString(page.Url);

                    if (!url.ToLower().StartsWith(web.ServerRelativeUrl.ToLower()))
                    {
                        url = UrlUtility.Combine(web.ServerRelativeUrl, url);
                    }

                    var exists = true;
                    Microsoft.SharePoint.Client.File file = null;
                    try
                    {
                        file = web.GetFileByServerRelativeUrl(url);
                        web.Context.Load(file);
                        web.Context.ExecuteQueryRetry();
                    }
                    catch (ServerException ex)
                    {
                        if (ex.ServerErrorTypeName == "System.IO.FileNotFoundException")
                        {
                            exists = false;
                        }
                    }
                    if (exists)
                    {
                        if (page.Overwrite)
                        {
                            try
                            {
                                scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_Pages_Overwriting_existing_page__0_, url);

                                // determine url of current home page
                                string welcomePageUrl = web.RootFolder.WelcomePage;
                                string welcomePageServerRelativeUrl = welcomePageUrl != null
                                    ? UrlUtility.Combine(web.ServerRelativeUrl, web.RootFolder.WelcomePage)
                                    : null;

                                bool overwriteWelcomePage = string.Equals(url, welcomePageServerRelativeUrl, StringComparison.InvariantCultureIgnoreCase);

                                // temporarily reset home page so we can delete it
                                if (overwriteWelcomePage)
                                {
                                    web.SetHomePage(string.Empty);
                                }

                                file.DeleteObject();
                                web.Context.ExecuteQueryRetry();
                                web.AddWikiPageByUrl(url);
                                if (page.Layout == WikiPageLayout.Custom)
                                {
                                    web.AddLayoutToWikiPage(WikiPageLayout.OneColumn, url);
                                }
                                else
                                {
                                    web.AddLayoutToWikiPage(page.Layout, url);
                                }

                                if (overwriteWelcomePage)
                                {
                                    // restore welcome page to previous value
                                    web.SetHomePage(welcomePageUrl);
                                }
                            }
                            catch (Exception ex)
                            {
                                scope.LogError(CoreResources.Provisioning_ObjectHandlers_Pages_Overwriting_existing_page__0__failed___1_____2_, url, ex.Message, ex.StackTrace);
                            }
                        }
                    }
                    else
                    {
                        try
                        {
                            scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_Pages_Creating_new_page__0_, url);

                            web.AddWikiPageByUrl(url);
                            if (page.Layout == WikiPageLayout.Custom)
                            {
                                web.AddLayoutToWikiPage(WikiPageLayout.OneColumn, url);
                            }
                            else
                            {
                                web.AddLayoutToWikiPage(page.Layout, url);
                            }
                        }
                        catch (Exception ex)
                        {
                            scope.LogError(CoreResources.Provisioning_ObjectHandlers_Pages_Creating_new_page__0__failed___1_____2_, url, ex.Message, ex.StackTrace);
                        }
                    }

#pragma warning disable 618
                    if (page.WelcomePage)
#pragma warning restore 618
                    {
                        web.RootFolder.EnsureProperty(p => p.ServerRelativeUrl);
                        var rootFolderRelativeUrl = url.Substring(web.RootFolder.ServerRelativeUrl.Length);
                        web.SetHomePage(rootFolderRelativeUrl);
                    }

#if !SP2013
                    bool webPartsNeedLocalization = false;
#endif
                    if (page.WebParts != null & page.WebParts.Any())
                    {
                        if (!isNoScriptSite)
                        {
                            var existingWebParts = web.GetWebParts(url);

                            foreach (var webPart in page.WebParts)
                            {
                                if (existingWebParts.FirstOrDefault(w => w.WebPart.Title == parser.ParseString(webPart.Title)) == null)
                                {
                                    WebPartEntity wpEntity = new WebPartEntity();
                                    wpEntity.WebPartTitle = parser.ParseString(webPart.Title);
                                    wpEntity.WebPartXml   = parser.ParseXmlString(webPart.Contents.Trim(new[] { '\n', ' ' }), "~sitecollection", "~site");
                                    var wpd = web.AddWebPartToWikiPage(url, wpEntity, (int)webPart.Row, (int)webPart.Column, false);
#if !SP2013
                                    if (webPart.Title.ContainsResourceToken())
                                    {
                                        // update data based on where it was added - needed in order to localize wp title
#if !SP2016
                                        wpd.EnsureProperties(w => w.ZoneId, w => w.WebPart, w => w.WebPart.Properties);
                                        webPart.Zone = wpd.ZoneId;
#else
                                        wpd.EnsureProperties(w => w.WebPart, w => w.WebPart.Properties);
#endif
                                        webPart.Order            = (uint)wpd.WebPart.ZoneIndex;
                                        webPartsNeedLocalization = true;
                                    }
#endif
                                }
                            }

                            // Remove any existing WebPartIdToken tokens in the parser that were added by other pages. They won't apply to this page,
                            // and they'll cause issues if this page contains web parts with the same name as web parts on other pages.
                            parser.Tokens.RemoveAll(t => t is WebPartIdToken);

                            var allWebParts = web.GetWebParts(url);
                            foreach (var webpart in allWebParts)
                            {
                                parser.AddToken(new WebPartIdToken(web, webpart.WebPart.Title, webpart.Id));
                            }
                        }
                        else
                        {
                            scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_Pages_SkipAddingWebParts, page.Url);
                        }
                    }

#if !SP2013
                    if (webPartsNeedLocalization)
                    {
                        page.LocalizeWebParts(web, parser, scope);
                    }
#endif

                    file = web.GetFileByServerRelativeUrl(url);
                    file.EnsureProperty(f => f.ListItemAllFields);

                    if (page.Fields.Any())
                    {
                        var item = file.ListItemAllFields;
                        foreach (var fieldValue in page.Fields)
                        {
                            item[fieldValue.Key] = parser.ParseString(fieldValue.Value);
                        }
                        item.Update();
                        web.Context.ExecuteQueryRetry();
                    }
                    if (page.Security != null && page.Security.RoleAssignments.Count != 0)
                    {
                        web.Context.Load(file.ListItemAllFields);
                        web.Context.ExecuteQueryRetry();
                        file.ListItemAllFields.SetSecurity(parser, page.Security);
                    }
                }
            }
            return(parser);
        }
        public override ProvisioningTemplate ExtractObjects(Web web, ProvisioningTemplate template, ProvisioningTemplateCreationInformation creationInfo)
        {
            if (template.Workflows == null)
            {
                template.Workflows = new Workflows();
            }

            using (var scope = new PnPMonitoredScope(this.Name))
            {
                if (creationInfo.FileConnector == null)
                {
                    scope.LogWarning("Cannot export Workflow definitions without a FileConnector.");
                }
                else
                {
                    // Retrieve all the lists and libraries
                    var lists = web.Lists;
                    web.Context.Load(lists);
                    web.Context.ExecuteQuery();

                    // Retrieve the workflow definitions (including unpublished ones)
                    var definitions = web.GetWorkflowDefinitions(false);

                    template.Workflows.WorkflowDefinitions.AddRange(
                        from d in definitions
                        select new Model.WorkflowDefinition(d.Properties.TokenizeWorkflowDefinitionProperties(lists))
                        {
                            AssociationUrl = d.AssociationUrl,
                            Description = d.Description,
                            DisplayName = d.DisplayName,
                            DraftVersion = d.DraftVersion,
                            FormField = d.FormField,
                            Id = d.Id,
                            InitiationUrl = d.InitiationUrl,
                            Published = d.Published,
                            RequiresAssociationForm = d.RequiresAssociationForm,
                            RequiresInitiationForm = d.RequiresInitiationForm,
                            RestrictToScope = (!String.IsNullOrEmpty(d.RestrictToScope) && Guid.Parse(d.RestrictToScope) != web.Id) ? String.Format("{{listid:{0}}}", lists.First(l => l.Id == Guid.Parse(d.RestrictToScope)).Title) : null,
                            RestrictToType = !String.IsNullOrEmpty(d.RestrictToType) ? d.RestrictToType : "Universal",
                            XamlPath = d.Xaml.SaveXamlToFile(d.Id, creationInfo.FileConnector),
                        }
                        );

                    // Retrieve the workflow subscriptions
                    var subscriptions = web.GetWorkflowSubscriptions();

            #if CLIENTSDKV15
                    template.Workflows.WorkflowSubscriptions.AddRange(
                        from s in subscriptions
                        select new Model.WorkflowSubscription(s.PropertyDefinitions.TokenizeWorkflowSubscriptionProperties(lists))
                        {
                            DefinitionId = s.DefinitionId,
                            Enabled = s.Enabled,
                            EventSourceId = s.EventSourceId != web.Id ? String.Format("{{listid:{0}}}", lists.First(l => l.Id == s.EventSourceId).Title) : null,
                            EventTypes = s.EventTypes.ToList(),
                            ManualStartBypassesActivationLimit = s.ManualStartBypassesActivationLimit,
                            Name = s.Name,
                            ListId = s.EventSourceId != web.Id ? String.Format("{{listid:{0}}}", lists.First(l => l.Id == s.EventSourceId).Title) : null,
                            StatusFieldName = s.StatusFieldName,
                        }
                        );
            #else
                    template.Workflows.WorkflowSubscriptions.AddRange(
                        from s in subscriptions
                        select new Model.WorkflowSubscription(s.PropertyDefinitions.TokenizeWorkflowSubscriptionProperties(lists))
                        {
                            DefinitionId = s.DefinitionId,
                            Enabled = s.Enabled,
                            EventSourceId = s.EventSourceId != web.Id ? String.Format("{{listid:{0}}}", lists.First(l => l.Id == s.EventSourceId).Title) : null,
                            EventTypes = s.EventTypes.ToList(),
                            ManualStartBypassesActivationLimit = s.ManualStartBypassesActivationLimit,
                            Name = s.Name,
                            ListId  = s.EventSourceId != web.Id ? String.Format("{{listid:{0}}}", lists.First(l => l.Id == s.EventSourceId).Title) : null,
                            ParentContentTypeId = s.ParentContentTypeId,
                            StatusFieldName = s.StatusFieldName,
                        }
                        );
            #endif
                }
            }
            return template;
        }
Beispiel #60
0
        private Tuple <Guid, TokenParser> CreateTerm <T>(Web web, Model.Term modelTerm, TaxonomyItem parent,
                                                         TermStore termStore, TokenParser parser, PnPMonitoredScope scope) where T : TaxonomyItem
        {
            // If the term is a re-used term and the term is not a source term, skip for now and create later
            if (modelTerm.IsReused && !modelTerm.IsSourceTerm)
            {
                this.reusedTerms.Add(new ReusedTerm()
                {
                    ModelTerm = modelTerm,
                    Parent    = parent,
                    TermStore = termStore
                });
                return(null);
            }

            // Create new term
            Term term;

            if (modelTerm.Id == Guid.Empty)
            {
                modelTerm.Id = Guid.NewGuid();
            }

            if (parent is Term)
            {
                term = ((Term)parent).CreateTerm(parser.ParseString(modelTerm.Name), modelTerm.Language ?? termStore.DefaultLanguage, modelTerm.Id);
            }
            else
            {
                term = ((TermSet)parent).CreateTerm(parser.ParseString(modelTerm.Name), modelTerm.Language ?? termStore.DefaultLanguage, modelTerm.Id);
            }
            if (!string.IsNullOrEmpty(modelTerm.Description))
            {
                term.SetDescription(modelTerm.Description, modelTerm.Language ?? termStore.DefaultLanguage);
            }
            if (!string.IsNullOrEmpty(modelTerm.Owner))
            {
                term.Owner = modelTerm.Owner;
            }

            term.IsAvailableForTagging = modelTerm.IsAvailableForTagging;

            if (modelTerm.Properties.Any() || modelTerm.Labels.Any() || modelTerm.LocalProperties.Any())
            {
                if (modelTerm.Labels.Any())
                {
                    foreach (var label in modelTerm.Labels)
                    {
                        if ((label.IsDefaultForLanguage && label.Language != termStore.DefaultLanguage) || label.IsDefaultForLanguage == false)
                        {
                            term.CreateLabel(parser.ParseString(label.Value), label.Language, label.IsDefaultForLanguage);
                        }
                        else
                        {
                            scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_TermGroups_Skipping_label__0___label_is_to_set_to_default_for_language__1__while_the_default_termstore_language_is_also__1_, label.Value, label.Language);
                            WriteWarning(string.Format(CoreResources.Provisioning_ObjectHandlers_TermGroups_Skipping_label__0___label_is_to_set_to_default_for_language__1__while_the_default_termstore_language_is_also__1_, label.Value, label.Language), ProvisioningMessageType.Warning);
                        }
                    }
                }

                if (modelTerm.Properties.Any())
                {
                    foreach (var property in modelTerm.Properties)
                    {
                        term.SetCustomProperty(parser.ParseString(property.Key), parser.ParseString(property.Value));
                    }
                }
                if (modelTerm.LocalProperties.Any())
                {
                    foreach (var property in modelTerm.LocalProperties)
                    {
                        term.SetLocalCustomProperty(parser.ParseString(property.Key), parser.ParseString(property.Value));
                    }
                }
            }

            termStore.CommitAll();

            web.Context.Load(term);
            web.Context.ExecuteQueryRetry();

            // Deprecate term if needed
            if (modelTerm.IsDeprecated != term.IsDeprecated)
            {
                term.Deprecate(modelTerm.IsDeprecated);
                web.Context.ExecuteQueryRetry();
            }


            parser = this.CreateChildTerms(web, modelTerm, term, termStore, parser, scope);
            return(Tuple.Create(modelTerm.Id, parser));
        }