public override TokenParser ProvisionObjects(Web web, ProvisioningTemplate template, TokenParser parser, ProvisioningTemplateApplyingInformation applyingInformation)
        {
            using (var scope = new PnPMonitoredScope(this.Name))
            {
                // if this is a sub site then we're not provisioning fields. Technically this can be done but it's not a recommended practice
                if (web.IsSubSite() && !applyingInformation.ProvisionFieldsToSubWebs)
                {
                    scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_Fields_Context_web_is_subweb__skipping_site_columns);
                    WriteMessage("This template contains fields and you are provisioning to a subweb. If you still want to provision these fields, set the ProvisionFieldsToSubWebs property to true.", ProvisioningMessageType.Warning);
                    return(parser);
                }

                var existingFields = web.Fields;

                web.Context.Load(existingFields, fs => fs.Include(f => f.Id));
                web.Context.ExecuteQueryRetry();
                var existingFieldIds = existingFields.AsEnumerable <SPField>().Select(l => l.Id).ToList();
                var fields           = template.SiteFields;

                var currentFieldIndex = 0;
                foreach (var field in fields)
                {
                    currentFieldIndex++;

                    XElement templateFieldElement = XElement.Parse(parser.ParseXmlString(field.SchemaXml, "~sitecollection", "~site"));
                    var      fieldId           = templateFieldElement.Attribute("ID").Value;
                    var      fieldInternalName = templateFieldElement.Attribute("InternalName") != null?templateFieldElement.Attribute("InternalName").Value : "";

                    WriteMessage($"Field|{(!string.IsNullOrWhiteSpace(fieldInternalName) ? fieldInternalName : fieldId)}|{currentFieldIndex}|{fields.Count}", ProvisioningMessageType.Progress);
                    if (!existingFieldIds.Contains(Guid.Parse(fieldId)))
                    {
                        try
                        {
                            scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_Fields_Adding_field__0__to_site, fieldId);
                            CreateField(web, templateFieldElement, scope, parser, field.SchemaXml);
                        }
                        catch (Exception ex)
                        {
                            scope.LogError(CoreResources.Provisioning_ObjectHandlers_Fields_Adding_field__0__failed___1_____2_, fieldId, ex.Message, ex.StackTrace);
                            throw;
                        }
                    }
                    else
                    {
                        try
                        {
                            scope.LogDebug(CoreResources.Provisioning_ObjectHandlers_Fields_Updating_field__0__in_site, fieldId);
                            UpdateField(web, fieldId, templateFieldElement, scope, parser, field.SchemaXml);
                        }
                        catch (Exception ex)
                        {
                            scope.LogError(CoreResources.Provisioning_ObjectHandlers_Fields_Updating_field__0__failed___1_____2_, fieldId, ex.Message, ex.StackTrace);
                            throw;
                        }
                    }
                }
            }
            WriteMessage($"Done processing fields", ProvisioningMessageType.Completed);
            return(parser);
        }
Beispiel #2
0
        private static void CreateField(Web web, XElement templateFieldElement, PnPMonitoredScope scope, TokenParser parser, string originalFieldXml)
        {
            var fieldXml = parser.ParseXmlString(templateFieldElement.ToString(), "~sitecollection", "~site");

            if (IsFieldXmlValid(fieldXml, parser, web.Context))
            {
                var field = web.Fields.AddFieldAsXml(fieldXml, false, AddFieldOptions.AddFieldInternalNameHint);
                web.Context.Load(field, f => f.Id, f => f.TypeAsString, f => f.DefaultValue, f => f.InternalName, f => f.Title);
                web.Context.ExecuteQueryRetry();

                // Add newly created field to token set, this allows to create a field + use it in a formula in the same provisioning template
                parser.AddToken(new FieldTitleToken(web, field.InternalName, field.Title));

                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())
                    {
                        field.TitleResource.SetUserResourceValue(nameAttributeValue, parser);
                        isDirty = true;
                    }
                    var descriptionAttributeValue = originalFieldElement.Attribute("Description") != null?originalFieldElement.Attribute("Description").Value : "";

                    if (descriptionAttributeValue.ContainsResourceToken())
                    {
                        field.DescriptionResource.SetUserResourceValue(descriptionAttributeValue, parser);
                        isDirty = true;
                    }
                }
#endif
                if (isDirty)
                {
                    field.Update();
                    web.Context.ExecuteQueryRetry();
                }

                if (field.TypeAsString == "TaxonomyFieldType" || field.TypeAsString == "TaxonomyFieldTypeMulti")
                {
                    var taxField = web.Context.CastTo <TaxonomyField>(field);
                    if (!string.IsNullOrEmpty(field.DefaultValue))
                    {
                        ValidateTaxonomyFieldDefaultValue(taxField);
                    }
                    SetTaxonomyFieldOpenValue(taxField, originalFieldXml);
                }
            }
            else
            {
                // The field Xml was found invalid
                var tokenString = parser.GetLeftOverTokens(fieldXml).Aggregate(String.Empty, (acc, i) => acc + " " + i);
                scope.LogError("The field was found invalid: {0}", tokenString);
                throw new Exception($"The field was found invalid: {tokenString}");
            }
        }
Beispiel #3
0
        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;
                if (!String.IsNullOrEmpty(template.SiteSearchSettings))
                {
                    site.SetSearchConfiguration(parser.ParseXmlString(template.SiteSearchSettings));
                }

                if (!String.IsNullOrEmpty(template.WebSearchSettings))
                {
                    web.SetSearchConfiguration(parser.ParseXmlString(template.WebSearchSettings));
                }
            }

            return(parser);
        }
Beispiel #4
0
        internal static XElement GetSchemaXml(this Field templateField, TokenParser parser, params string[] tokensToSkip)
        {
            XElement schemaElement;

            if (!_fieldXmlDictionary.TryGetValue(templateField, out schemaElement))
            {
                schemaElement = XElement.Parse(parser.ParseXmlString(templateField.SchemaXml, tokensToSkip));
                _fieldXmlDictionary[templateField] = schemaElement;
            }
            return(schemaElement);
        }
Beispiel #5
0
        internal static Guid GetFieldId(this Field templateField, TokenParser parser)
        {
            XElement schemaElement;

            if (!_fieldXmlDictionary.TryGetValue(templateField, out schemaElement))
            {
                schemaElement = XElement.Parse(parser.ParseXmlString(templateField.SchemaXml));
                _fieldXmlDictionary[templateField] = schemaElement;
            }
            var id = (Guid)schemaElement.Attribute("ID");

            return(id);
        }
Beispiel #6
0
        internal static Step GetFieldProvisioningStep(this Field templateField, TokenParser parser)
        {
            XElement schemaElement;

            if (!_fieldXmlDictionary.TryGetValue(templateField, out schemaElement))
            {
                schemaElement = XElement.Parse(parser.ParseXmlString(templateField.SchemaXml));
                _fieldXmlDictionary[templateField] = schemaElement;
            }
            var type = (string)schemaElement.Attribute("Type");

            if (type != "Lookup" && type != "LookupMulti")
            {
                return(Step.ListAndStandardFields);
            }
            return(Step.LookupFields);
        }
Beispiel #7
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();
                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++;
                    WriteMessage($"File|{targetFileName}|{currentFileIndex}|{filesToProcess.Length}", ProvisioningMessageType.Progress);
                    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);

                    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
#if !SP2013
                        targetFile.EnsureProperties(p => p.UniqueId, p => p.ServerRelativeUrl);
                        parser.AddToken(new FileUniqueIdToken(web, targetFile.ServerRelativeUrl.Substring(web.ServerRelativeUrl.Length).TrimStart("/".ToCharArray()), targetFile.UniqueId));
                        parser.AddToken(new FileUniqueIdEncodedToken(web, targetFile.ServerRelativeUrl.Substring(web.ServerRelativeUrl.Length).TrimStart("/".ToCharArray()), targetFile.UniqueId));
#endif

#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.ParseXmlString(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
                                        wpd.EnsureProperties(w => w.ZoneId, w => w.WebPart, w => w.WebPart.Properties);
                                        webPart.Zone             = wpd.ZoneId;
                                        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);
                        }

                        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);
                        }
                    }

                    web = originalWeb; // restore context in case files are provisioned to the master page gallery #1059
                }
            }
            WriteMessage("Done processing files", ProvisioningMessageType.Completed);
            return(parser);
        }
Beispiel #8
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?
                {
                    var listIdentifier = templateFieldElement.Attribute("List") != null?templateFieldElement.Attribute("List").Value : null;

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

                    if (IsFieldXmlValid(parser.ParseXmlString(originalFieldXml), 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 (string.Equals(templateFieldElement.Attribute("Type").Value, "Calculated", StringComparison.OrdinalIgnoreCase))
                        {
                            var fieldRefsElement = existingFieldElement.Descendants("FieldRefs").FirstOrDefault();
                            if (fieldRefsElement != null)
                            {
                                fieldRefsElement.Remove();
                            }
                        }

                        if (existingFieldElement.Attribute("Version") != null)
                        {
                            existingFieldElement.Attributes("Version").Remove();
                        }
                        existingField.SchemaXml = parser.ParseXmlString(existingFieldElement.ToString(), "~sitecollection", "~site");
                        existingField.UpdateAndPushChanges(true);
                        web.Context.Load(existingField, f => f.TypeAsString, f => f.DefaultValue);
                        try
                        {
                            web.Context.ExecuteQueryRetry();
                        }
                        catch (ServerException se)
                        {
                            if (se.ServerErrorTypeName == "Microsoft.SharePoint.Client.ClientServiceTimeoutException")
                            {
                                string fieldName = existingFieldElement.Attribute("Name") != null?existingFieldElement.Attribute("Name").Value : existingFieldElement.Attribute("StaticName").Value;

                                WriteMessage(string.Format(CoreResources.Provisioning_ObjectHandlers_Fields_Updating_field__0__timeout, fieldName), ProvisioningMessageType.Warning);
                                scope.LogWarning(CoreResources.Provisioning_ObjectHandlers_Fields_Updating_field__0__timeout, fieldName);

                                web.Context.Load(existingField, f => f.TypeAsString, f => f.DefaultValue);
                                web.Context.ExecuteQueryRetry();
                            }
                            else
                            {
                                throw;
                            }
                        }

                        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())
                            {
                                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.ExecuteQueryRetry();
                        }
                        if ((existingField.TypeAsString == "TaxonomyFieldType" || existingField.TypeAsString == "TaxonomyFieldTypeMulti"))
                        {
                            var taxField = web.Context.CastTo <TaxonomyField>(existingField);
                            if (!string.IsNullOrEmpty(existingField.DefaultValue))
                            {
                                ValidateTaxonomyFieldDefaultValue(taxField);
                            }
                            SetTaxonomyFieldOpenValue(taxField, originalFieldXml);
                        }
                    }
                    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($"The field was found invalid: {tokenString}");
                    }
                }
                else
                {
                    var fieldName = existingFieldElement.Attribute("Name") != null?existingFieldElement.Attribute("Name").Value : existingFieldElement.Attribute("StaticName").Value;

                    WriteMessage(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 #9
0
        public override TokenParser ProvisionObjects(Web web, ProvisioningTemplate template, TokenParser parser, ProvisioningTemplateApplyingInformation applyingInformation)
        {
            using (var scope = new PnPMonitoredScope(this.Name))
            {
                // Get a reference to infrastructural services
                WorkflowServicesManager servicesManager = null;

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

                if (servicesManager != null)
                {
                    var deploymentService   = servicesManager.GetWorkflowDeploymentService();
                    var subscriptionService = servicesManager.GetWorkflowSubscriptionService();

                    // Pre-load useful properties
                    web.EnsureProperty(w => w.Id);

                    // Provision Workflow Definitions
                    foreach (var templateDefinition in template.Workflows.WorkflowDefinitions)
                    {
                        // Load the Workflow Definition XAML
                        Stream   xamlStream = template.Connector.GetFileStream(templateDefinition.XamlPath);
                        XElement xaml       = XElement.Load(xamlStream);

                        int retryCount    = 5;
                        int retryAttempts = 1;
                        int delay         = 2000;

                        while (retryAttempts <= retryCount)
                        {
                            try
                            {
                                var workflowDefinition = deploymentService.GetDefinition(templateDefinition.Id);
                                web.Context.Load(workflowDefinition);
                                web.Context.ExecuteQueryRetry();

                                // If Definition does not exist, create it
                                // Prevent an exception if already exists
                                if (workflowDefinition.ServerObjectIsNull())
                                {
                                    // Create the WorkflowDefinition instance
                                    workflowDefinition = new Microsoft.SharePoint.Client.WorkflowServices.WorkflowDefinition(web.Context)
                                    {
                                        AssociationUrl          = templateDefinition.AssociationUrl,
                                        Description             = templateDefinition.Description,
                                        DisplayName             = templateDefinition.DisplayName,
                                        FormField               = templateDefinition.FormField,
                                        DraftVersion            = templateDefinition.DraftVersion,
                                        Id                      = templateDefinition.Id,
                                        InitiationUrl           = templateDefinition.InitiationUrl,
                                        RequiresAssociationForm = templateDefinition.RequiresAssociationForm,
                                        RequiresInitiationForm  = templateDefinition.RequiresInitiationForm,
                                        RestrictToScope         = parser.ParseString(templateDefinition.RestrictToScope),
                                        RestrictToType          = templateDefinition.RestrictToType != "Universal" ? templateDefinition.RestrictToType : null,
                                        Xaml                    = parser.ParseXmlString(xaml.ToString()),
                                    };

                                    // Save the Workflow Definition
                                    var newDefinition = deploymentService.SaveDefinition(workflowDefinition);
                                    web.Context.ExecuteQueryRetry();

                                    // Let's publish the Workflow Definition, if needed
                                    if (templateDefinition.Published)
                                    {
                                        deploymentService.PublishDefinition(newDefinition.Value);
                                        web.Context.ExecuteQueryRetry();
                                    }
                                }
                                else
                                {
                                    // Otherwise update the XAML definition
                                    workflowDefinition.AssociationUrl          = templateDefinition.AssociationUrl;
                                    workflowDefinition.Description             = templateDefinition.Description;
                                    workflowDefinition.DisplayName             = templateDefinition.DisplayName;
                                    workflowDefinition.FormField               = templateDefinition.FormField;
                                    workflowDefinition.DraftVersion            = templateDefinition.DraftVersion;
                                    workflowDefinition.InitiationUrl           = templateDefinition.InitiationUrl;
                                    workflowDefinition.RequiresAssociationForm = templateDefinition.RequiresAssociationForm;
                                    workflowDefinition.RequiresInitiationForm  = templateDefinition.RequiresInitiationForm;
                                    workflowDefinition.RestrictToScope         = parser.ParseString(templateDefinition.RestrictToScope);
                                    workflowDefinition.RestrictToType          = templateDefinition.RestrictToType != "Universal" ? templateDefinition.RestrictToType : null;
                                    workflowDefinition.Xaml = parser.ParseXmlString(xaml.ToString());

                                    var updatedDefinition = deploymentService.SaveDefinition(workflowDefinition);
                                    web.Context.ExecuteQueryRetry();

                                    // Let's publish the Workflow Definition, if needed
                                    if (templateDefinition.Published)
                                    {
                                        deploymentService.PublishDefinition(updatedDefinition.Value);
                                        web.Context.ExecuteQueryRetry();
                                    }
                                }

                                // If we are here, we have the workflow definition
                                // and we did not have any exception.
                                // Thus, we can exit from the loop
                                break;
                            }
                            catch (Exception ex)
                            {
                                // check exception is due to connection closed issue
                                if (ex is ServerException && ((ServerException)ex).ServerErrorCode == -2130575223 &&
                                    ((ServerException)ex).ServerErrorTypeName.Equals("Microsoft.SharePoint.SPException", StringComparison.InvariantCultureIgnoreCase) &&
                                    ((ServerException)ex).Message.Contains("A connection that was expected to be kept alive was closed by the server.")
                                    )
                                {
                                    WriteMessage($"Connection closed whilst adding Workflow Definition, trying again in {delay}ms", ProvisioningMessageType.Warning);

                                    Thread.Sleep(delay);

                                    retryAttempts++;
                                    delay = delay * 2; // double delay for next retry
                                }
                                else
                                {
                                    throw;
                                }
                            }
                        }
                    }


                    // get existing subscriptions
                    var existingWorkflowSubscriptions = web.GetWorkflowSubscriptions();

                    foreach (var subscription in template.Workflows.WorkflowSubscriptions)
                    {
                        Microsoft.SharePoint.Client.WorkflowServices.WorkflowSubscription workflowSubscription = null;

                        // Check if the subscription already exists before adding it, and
                        // if already exists a subscription with the same name and with the same DefinitionId,
                        // it is a duplicate and we just need to update it
                        string subscriptionName;
                        if (subscription.PropertyDefinitions.TryGetValue("SharePointWorkflowContext.Subscription.Name", out subscriptionName) &&
                            existingWorkflowSubscriptions.Any(s => s.PropertyDefinitions["SharePointWorkflowContext.Subscription.Name"] == subscriptionName && s.DefinitionId == subscription.DefinitionId))
                        {
                            // Thus, delete it before adding it again!
                            WriteMessage($"Workflow Subscription '{subscription.Name}' already exists. It will be updated.", ProvisioningMessageType.Warning);
                            workflowSubscription = existingWorkflowSubscriptions.FirstOrDefault((s => s.PropertyDefinitions["SharePointWorkflowContext.Subscription.Name"] == subscriptionName && s.DefinitionId == subscription.DefinitionId));
                        }

                        if (workflowSubscription != null)
                        {
                            // Update The existing subscription instead of delete the existing one.
                            // Only update the following properties
                            workflowSubscription.Enabled    = subscription.Enabled;
                            workflowSubscription.EventTypes = subscription.EventTypes;
                            workflowSubscription.ManualStartBypassesActivationLimit = subscription.ManualStartBypassesActivationLimit;
                            workflowSubscription.StatusFieldName = subscription.StatusFieldName;
                        }
                        else
                        {
                            // Create the WorkflowDefinition instance
                            workflowSubscription =
                                new Microsoft.SharePoint.Client.WorkflowServices.WorkflowSubscription(web.Context)
                            {
                                DefinitionId  = subscription.DefinitionId,
                                Enabled       = subscription.Enabled,
                                EventSourceId = (!String.IsNullOrEmpty(subscription.EventSourceId)) ? Guid.Parse(parser.ParseString(subscription.EventSourceId)) : web.Id,
                                EventTypes    = subscription.EventTypes,
#if !ONPREMISES
                                ParentContentTypeId = subscription.ParentContentTypeId,
#endif
                                ManualStartBypassesActivationLimit = subscription.ManualStartBypassesActivationLimit,
                                Name            = subscription.Name,
                                StatusFieldName = subscription.StatusFieldName,
                            };

                            foreach (var propertyDefinition in subscription.PropertyDefinitions
                                     .Where(d => d.Key == "TaskListId" ||
                                            d.Key == "HistoryListId" ||
                                            d.Key == "SharePointWorkflowContext.Subscription.Id" ||
                                            d.Key == "SharePointWorkflowContext.Subscription.Name" ||
                                            d.Key == "CreatedBySPD" ||
                                            d.Key == "StatusColumnCreated")) // If set to "0" the status column will be created automatically.
                            {
                                workflowSubscription.SetProperty(propertyDefinition.Key, parser.ParseString(propertyDefinition.Value));
                            }
                        }

                        if (!String.IsNullOrEmpty(subscription.ListId))
                        {
                            // It is a List Workflow
                            Guid targetListId = Guid.Parse(parser.ParseString(subscription.ListId));
                            subscriptionService.PublishSubscriptionForList(workflowSubscription, targetListId);
                        }
                        else
                        {
                            // It is a Site Workflow
                            subscriptionService.PublishSubscription(workflowSubscription);
                        }

                        web.Context.ExecuteQueryRetry();
                    }
                }
            }

            return(parser);
        }
Beispiel #10
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 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));
                }

                var filesToProcess   = template.Files.Union(directoryFiles).ToArray();
                var currentFileIndex = 0;
                foreach (var file in filesToProcess)
                {
                    var targetFileName = !String.IsNullOrEmpty(file.TargetFileName) ? file.TargetFileName : file.Src;

                    currentFileIndex++;
                    WriteMessage($"File|{targetFileName}|{currentFileIndex}|{filesToProcess.Length}", ProvisioningMessageType.Progress);
                    var folderName = parser.ParseString(file.Folder);

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

                    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 = 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_, targetFileName);
                            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.ParseXmlString(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);
                        }
                    }
                }
            }
            WriteMessage("Done processing files", ProvisioningMessageType.Completed);
            return(parser);
        }