From f791a8ce95ff4c7e83dc04342b2fa0d8e333968b Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Tue, 4 Sep 2018 09:36:41 +0200 Subject: [PATCH 01/19] Hierarchy cmdlets --- .../PipeBinds/ProvisioningSequencePipebind.cs | 36 ++++ .../PipeBinds/ProvisioningSitePipeBind.cs | 35 ++++ ...PnP.PowerShell.2013.Commands.Format.ps1xml | 23 +++ ...PnP.PowerShell.2016.Commands.Format.ps1xml | 23 +++ ...P.PowerShell.Online.Commands.Format.ps1xml | 69 ++++++- .../Provisioning/AddProvisioningSequence.cs | 39 ++++ Commands/Provisioning/AddProvisioningSite.cs | 33 ++++ .../Provisioning/AddProvisioningSubSite.cs | 40 ++++ .../Provisioning/AddProvisioningTemplate.cs | 35 ++++ .../ApplyProvisioningHierarchy.cs | 174 ++++++++++++++++++ .../Provisioning/GetProvisioningSequence.cs | 39 ++++ Commands/Provisioning/GetProvisioningSite.cs | 39 ++++ .../NewProvisioningCommunicationSite.cs | 71 +++++++ .../Provisioning/NewProvisioningHierarchy.cs | 22 +++ .../Provisioning/NewProvisioningSequence.cs | 36 ++++ .../NewProvisioningTeamNoGroupSite.cs | 60 ++++++ .../NewProvisioningTeamNoGroupSubSite.cs | 62 +++++++ .../Provisioning/NewProvisioningTeamSite.cs | 61 ++++++ .../Provisioning/TestProvisioningHierarchy.cs | 91 +++++++++ .../SharePointPnP.PowerShell.Commands.csproj | 16 ++ Commands/Taxonomy/GetTaxonomySession.cs | 5 +- 21 files changed, 1007 insertions(+), 2 deletions(-) create mode 100644 Commands/Base/PipeBinds/ProvisioningSequencePipebind.cs create mode 100644 Commands/Base/PipeBinds/ProvisioningSitePipeBind.cs create mode 100644 Commands/Provisioning/AddProvisioningSequence.cs create mode 100644 Commands/Provisioning/AddProvisioningSite.cs create mode 100644 Commands/Provisioning/AddProvisioningSubSite.cs create mode 100644 Commands/Provisioning/AddProvisioningTemplate.cs create mode 100644 Commands/Provisioning/ApplyProvisioningHierarchy.cs create mode 100644 Commands/Provisioning/GetProvisioningSequence.cs create mode 100644 Commands/Provisioning/GetProvisioningSite.cs create mode 100644 Commands/Provisioning/NewProvisioningCommunicationSite.cs create mode 100644 Commands/Provisioning/NewProvisioningHierarchy.cs create mode 100644 Commands/Provisioning/NewProvisioningSequence.cs create mode 100644 Commands/Provisioning/NewProvisioningTeamNoGroupSite.cs create mode 100644 Commands/Provisioning/NewProvisioningTeamNoGroupSubSite.cs create mode 100644 Commands/Provisioning/NewProvisioningTeamSite.cs create mode 100644 Commands/Provisioning/TestProvisioningHierarchy.cs diff --git a/Commands/Base/PipeBinds/ProvisioningSequencePipebind.cs b/Commands/Base/PipeBinds/ProvisioningSequencePipebind.cs new file mode 100644 index 000000000..0e9bb5d75 --- /dev/null +++ b/Commands/Base/PipeBinds/ProvisioningSequencePipebind.cs @@ -0,0 +1,36 @@ +using OfficeDevPnP.Core.Framework.Provisioning.Model; +using System; +using System.Linq; + +namespace SharePointPnP.PowerShell.Commands.Base.PipeBinds +{ + public sealed class ProvisioningSequencePipeBind + { + private readonly ProvisioningSequence _sequence; + private readonly string _identity; + + public ProvisioningSequencePipeBind(ProvisioningSequence sequence) + { + _sequence = sequence; + } + + public ProvisioningSequencePipeBind(string identity) + { + _identity = identity; + } + + public ProvisioningSequence GetSequenceFromHierarchy(ProvisioningHierarchy hierarchy) + { + var id = string.Empty; + if(_sequence == null) + { + id = _identity; + } else + { + id = _sequence.ID; + } + return hierarchy.Sequences.FirstOrDefault(s => s.ID == id); + } + + } +} diff --git a/Commands/Base/PipeBinds/ProvisioningSitePipeBind.cs b/Commands/Base/PipeBinds/ProvisioningSitePipeBind.cs new file mode 100644 index 000000000..d986315ee --- /dev/null +++ b/Commands/Base/PipeBinds/ProvisioningSitePipeBind.cs @@ -0,0 +1,35 @@ +using OfficeDevPnP.Core.Framework.Provisioning.Model; +using System; +using System.Linq; + +namespace SharePointPnP.PowerShell.Commands.Base.PipeBinds +{ + public sealed class ProvisioningSitePipeBind + { + private readonly SiteCollection _site; + + public ProvisioningSitePipeBind(TeamSiteCollection site) + { + _site = site; + } + + public ProvisioningSitePipeBind(TeamNoGroupSiteCollection site) + { + _site = site; + } + + public ProvisioningSitePipeBind(CommunicationSiteCollection site) + { + _site = site; + } + + public SiteCollection Site => _site; + + public SiteCollection GetSiteFromSequence(ProvisioningSequence sequence) + { + return sequence.SiteCollections.FirstOrDefault(s => s.Id == _site.Id); + } + } +} + + diff --git a/Commands/ModuleFiles/SharePointPnP.PowerShell.2013.Commands.Format.ps1xml b/Commands/ModuleFiles/SharePointPnP.PowerShell.2013.Commands.Format.ps1xml index 2e25fa199..fbe3be86e 100644 --- a/Commands/ModuleFiles/SharePointPnP.PowerShell.2013.Commands.Format.ps1xml +++ b/Commands/ModuleFiles/SharePointPnP.PowerShell.2013.Commands.Format.ps1xml @@ -1270,5 +1270,28 @@ + + TaxonomySession + + Microsoft.SharePoint.Client.Taxonomy.TaxonomySession + + + + + + left + + + + + + + TermStores + + + + + + \ No newline at end of file diff --git a/Commands/ModuleFiles/SharePointPnP.PowerShell.2016.Commands.Format.ps1xml b/Commands/ModuleFiles/SharePointPnP.PowerShell.2016.Commands.Format.ps1xml index 2e25fa199..fbe3be86e 100644 --- a/Commands/ModuleFiles/SharePointPnP.PowerShell.2016.Commands.Format.ps1xml +++ b/Commands/ModuleFiles/SharePointPnP.PowerShell.2016.Commands.Format.ps1xml @@ -1270,5 +1270,28 @@ + + TaxonomySession + + Microsoft.SharePoint.Client.Taxonomy.TaxonomySession + + + + + + left + + + + + + + TermStores + + + + + + \ No newline at end of file diff --git a/Commands/ModuleFiles/SharePointPnP.PowerShell.Online.Commands.Format.ps1xml b/Commands/ModuleFiles/SharePointPnP.PowerShell.Online.Commands.Format.ps1xml index c90625aab..012d7475c 100644 --- a/Commands/ModuleFiles/SharePointPnP.PowerShell.Online.Commands.Format.ps1xml +++ b/Commands/ModuleFiles/SharePointPnP.PowerShell.Online.Commands.Format.ps1xml @@ -1766,5 +1766,72 @@ + + TaxonomySession + + Microsoft.SharePoint.Client.Taxonomy.TaxonomySession + + + + + + left + + + + + + + TermStores + + + + + + + + SiteCollection + + OfficeDevPnP.Core.Framework.Provisioning.Model.SiteCollection + + + + + + left + + + + left + + + + left + + + + left + + + + + + + Id + + + Title + + + Templates + + + Sites + + + + + + - \ No newline at end of file + diff --git a/Commands/Provisioning/AddProvisioningSequence.cs b/Commands/Provisioning/AddProvisioningSequence.cs new file mode 100644 index 000000000..85d149970 --- /dev/null +++ b/Commands/Provisioning/AddProvisioningSequence.cs @@ -0,0 +1,39 @@ +using OfficeDevPnP.Core.Framework.Provisioning.Model; +using SharePointPnP.PowerShell.CmdletHelpAttributes; +using System; +using System.Linq; +using System.Management.Automation; + +namespace SharePointPnP.PowerShell.Commands.Provisioning +{ + [Cmdlet(VerbsCommon.Add, "PnPProvisioningSequence", SupportsShouldProcess = true)] + [CmdletHelp("Adds a provisioning sequence object to a provisioning hierarchy", + Category = CmdletHelpCategory.Provisioning)] + [CmdletExample( + Code = @"PS:> Add-PnPProvisioningSequence -Hierarchy $myhierarchy -Sequence $mysequence", + Remarks = "Adds an existing sequence object to an existing hierarchy object", + SortOrder = 1)] + [CmdletExample( + Code = @"PS:> New-PnPProvisioningSequence -Id ""MySequence"" | Add-PnPProvisioningSequence -Hierarchy $hierarchy", + Remarks = "Creates a new instance of a provisioning sequence object and sets the Id to the value specified, then the sequence is added to an existing hierarchy object", + SortOrder = 2)] + public class AddProvisioningSequence : PnPCmdlet + { + [Parameter(Mandatory = true, HelpMessage = "The hierarchy to add the sequence to", ParameterSetName = ParameterAttribute.AllParameterSets)] + public ProvisioningHierarchy Hierarchy; + + [Parameter(Mandatory = true, HelpMessage = "Optional Id of the sequence", ParameterSetName = ParameterAttribute.AllParameterSets, ValueFromPipeline = true)] + public ProvisioningSequence Sequence; + protected override void ExecuteCmdlet() + { + if (Hierarchy.Sequences.FirstOrDefault(s => s.ID == Sequence.ID) == null) + { + Hierarchy.Sequences.Add(Sequence); + WriteObject(Hierarchy); + } else + { + WriteError(new ErrorRecord(new Exception($"Sequence with ID {Sequence.ID} already exists in hierarchy"), "DUPLICATESEQUENCEID", ErrorCategory.InvalidData, Sequence)); + } + } + } +} diff --git a/Commands/Provisioning/AddProvisioningSite.cs b/Commands/Provisioning/AddProvisioningSite.cs new file mode 100644 index 000000000..7cad90912 --- /dev/null +++ b/Commands/Provisioning/AddProvisioningSite.cs @@ -0,0 +1,33 @@ +using OfficeDevPnP.Core.Framework.Provisioning.Model; +using SharePointPnP.PowerShell.CmdletHelpAttributes; +using SharePointPnP.PowerShell.Commands.Base.PipeBinds; +using System.Management.Automation; + +namespace SharePointPnP.PowerShell.Commands.Provisioning +{ + [Cmdlet(VerbsCommon.Add, "PnPProvisioningSite", SupportsShouldProcess = true)] + [CmdletHelp("Adds a provisioning sequence object to a provisioning hierarchy", + Category = CmdletHelpCategory.Provisioning)] + [CmdletExample( + Code = @"PS:> Add-PnPProvisioningSequence -Hierarchy $myhierarchy -Sequence $mysequence", + Remarks = "Adds an existing sequence object to an existing hierarchy object", + SortOrder = 1)] + [CmdletExample( + Code = @"PS:> New-PnPProvisioningSequence -Id ""MySequence"" | Add-PnPProvisioningSequence -Hierarchy $hierarchy", + Remarks = "Creates a new instance of a provisioning sequence object and sets the Id to the value specified, then the sequence is added to an existing hierarchy object", + SortOrder = 2)] + public class AddProvisioningSite : PnPCmdlet + { + [Parameter(Mandatory = true)] + public ProvisioningSitePipeBind Site; + + [Parameter(Mandatory = true, HelpMessage = "The sequence to add the site to", ValueFromPipeline = true)] + public ProvisioningSequence Sequence; + + protected override void ExecuteCmdlet() + { + Sequence.SiteCollections.Add(Site.Site); + WriteObject(Sequence); + } + } +} diff --git a/Commands/Provisioning/AddProvisioningSubSite.cs b/Commands/Provisioning/AddProvisioningSubSite.cs new file mode 100644 index 000000000..f69322fef --- /dev/null +++ b/Commands/Provisioning/AddProvisioningSubSite.cs @@ -0,0 +1,40 @@ +using OfficeDevPnP.Core.Framework.Provisioning.Model; +using SharePointPnP.PowerShell.CmdletHelpAttributes; +using SharePointPnP.PowerShell.Commands.Base.PipeBinds; +using System; +using System.Linq; +using System.Management.Automation; + +namespace SharePointPnP.PowerShell.Commands.Provisioning +{ + [Cmdlet(VerbsCommon.Add, "PnPProvisioningSubSite", SupportsShouldProcess = true)] + [CmdletHelp("Adds a provisioning sequence object to a provisioning site object", + Category = CmdletHelpCategory.Provisioning)] + [CmdletExample( + Code = @"PS:> Add-PnPProvisioningSequence -Hierarchy $myhierarchy -Sequence $mysequence", + Remarks = "Adds an existing sequence object to an existing hierarchy object", + SortOrder = 1)] + [CmdletExample( + Code = @"PS:> New-PnPProvisioningSequence -Id ""MySequence"" | Add-PnPProvisioningSequence -Hierarchy $hierarchy", + Remarks = "Creates a new instance of a provisioning sequence object and sets the Id to the value specified, then the sequence is added to an existing hierarchy object", + SortOrder = 2)] + public class AddProvisioningSubSite : PnPCmdlet + { + [Parameter(Mandatory = true)] + public TeamNoGroupSubSite SubSite; + + [Parameter(Mandatory = true, HelpMessage = "The sequence to add the site to", ValueFromPipeline = true)] + public SiteCollection Site; + + protected override void ExecuteCmdlet() + { + if (Site.Sites.Cast().FirstOrDefault(s => s.Url == SubSite.Url) == null) + { + Site.Sites.Add(SubSite); + } else + { + WriteError(new ErrorRecord(new Exception($"Site with URL {SubSite.Url} already exists in sequence"), "DUPLICATEURL", ErrorCategory.InvalidData, SubSite)); + } + } + } +} diff --git a/Commands/Provisioning/AddProvisioningTemplate.cs b/Commands/Provisioning/AddProvisioningTemplate.cs new file mode 100644 index 000000000..19c65eab1 --- /dev/null +++ b/Commands/Provisioning/AddProvisioningTemplate.cs @@ -0,0 +1,35 @@ +using OfficeDevPnP.Core.Framework.Provisioning.Model; +using SharePointPnP.PowerShell.CmdletHelpAttributes; +using SharePointPnP.PowerShell.Commands.Base.PipeBinds; +using System; +using System.Linq; +using System.Management.Automation; + +namespace SharePointPnP.PowerShell.Commands.Provisioning +{ + [Cmdlet(VerbsCommon.Add, "PnPProvisioningTemplate", SupportsShouldProcess = true)] + [CmdletHelp("Adds a provisioning template object to a provisioning hierarchy", + Category = CmdletHelpCategory.Provisioning)] + [CmdletExample( + Code = @"PS:> Add-PnPProvisioningTemplate -Hierarchy $myhierarchy -Template $mytemplate", + Remarks = "Adds an existing sequence object to an existing hierarchy object", + SortOrder = 1)] + public class AddProvisioningTemplate : PnPCmdlet + { + [Parameter(Mandatory = true, HelpMessage = "The template to add to the hierarchy")] + public ProvisioningTemplate Template; + + [Parameter(Mandatory = true, HelpMessage = "The hierarchy to add the template to", ValueFromPipeline = true)] + public ProvisioningHierarchy Hierarchy; + + protected override void ExecuteCmdlet() + { + if(Hierarchy.Templates.FirstOrDefault(t => t.Id == Template.Id) == null) + { + Hierarchy.Templates.Add(Template); + } else { + WriteError(new ErrorRecord(new Exception($"Template with ID {Template.Id} already exists in hierarchy"), "DUPLICATETEMPLATE", ErrorCategory.InvalidData, Template)); + } + } + } +} diff --git a/Commands/Provisioning/ApplyProvisioningHierarchy.cs b/Commands/Provisioning/ApplyProvisioningHierarchy.cs new file mode 100644 index 000000000..4453df25d --- /dev/null +++ b/Commands/Provisioning/ApplyProvisioningHierarchy.cs @@ -0,0 +1,174 @@ +using Microsoft.SharePoint.Client; +using OfficeDevPnP.Core.Framework.Provisioning.Model; +using OfficeDevPnP.Core.Framework.Provisioning.ObjectHandlers; +using OfficeDevPnP.Core.Framework.Provisioning.Providers; +using SharePointPnP.PowerShell.CmdletHelpAttributes; +using SharePointPnP.PowerShell.Commands.Base; +using SharePointPnP.PowerShell.Commands.Base.PipeBinds; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management.Automation; + +namespace SharePointPnP.PowerShell.Commands.Provisioning +{ + [Cmdlet("Apply", "PnPProvisioningHierarchy", SupportsShouldProcess = true)] + [CmdletHelp("Adds a provisioning sequence object to a provisioning site object", + Category = CmdletHelpCategory.Provisioning)] + [CmdletExample( + Code = @"PS:> Add-PnPProvisioningSequence -Hierarchy $myhierarchy -Sequence $mysequence", + Remarks = "Adds an existing sequence object to an existing hierarchy object", + SortOrder = 1)] + [CmdletExample( + Code = @"PS:> New-PnPProvisioningSequence -Id ""MySequence"" | Add-PnPProvisioningSequence -Hierarchy $hierarchy", + Remarks = "Creates a new instance of a provisioning sequence object and sets the Id to the value specified, then the sequence is added to an existing hierarchy object", + SortOrder = 2)] + public class ApplyProvisioningHierarchy : PnPAdminCmdlet + { + private ProgressRecord progressRecord = new ProgressRecord(0, "Activity", "Status"); + private ProgressRecord subProgressRecord = new ProgressRecord(1, "Activity", "Status"); + + [Parameter(Mandatory = true)] + public ProvisioningHierarchy Hierarchy; + + [Parameter(Mandatory = false)] + public string SequenceId; + + [Parameter(Mandatory = false, HelpMessage = "Allows you to only process a specific part of the template. Notice that this might fail, as some of the handlers require other artifacts in place if they are not part of what your applying.", ParameterSetName = ParameterAttribute.AllParameterSets)] + public Handlers Handlers; + + [Parameter(Mandatory = false, HelpMessage = "Allows you to run all handlers, excluding the ones specified.", ParameterSetName = ParameterAttribute.AllParameterSets)] + public Handlers ExcludeHandlers; + + [Parameter(Mandatory = false, HelpMessage = "Allows you to specify ExtensbilityHandlers to execute while applying a template", ParameterSetName = ParameterAttribute.AllParameterSets)] + public ExtensibilityHandler[] ExtensibilityHandlers; + + [Parameter(Mandatory = false, HelpMessage = "Allows you to specify ITemplateProviderExtension to execute while applying a template.", ParameterSetName = ParameterAttribute.AllParameterSets)] + public ITemplateProviderExtension[] TemplateProviderExtensions; + + [Parameter(Mandatory = false, HelpMessage = "Specify this parameter if you want to overwrite and/or create properties that are known to be system entries (starting with vti_, dlc_, etc.)", ParameterSetName = ParameterAttribute.AllParameterSets)] + public SwitchParameter OverwriteSystemPropertyBagValues; + + [Parameter(Mandatory = false, HelpMessage = "Ignore duplicate data row errors when the data row in the template already exists.", ParameterSetName = ParameterAttribute.AllParameterSets)] + public SwitchParameter IgnoreDuplicateDataRowErrors; + + [Parameter(Mandatory = false, HelpMessage = "If set content types will be provisioned if the target web is a subweb.")] + public SwitchParameter ProvisionContentTypesToSubWebs; + + [Parameter(Mandatory = false, HelpMessage = "If set fields will be provisioned if the target web is a subweb.")] + public SwitchParameter ProvisionFieldsToSubWebs; + + [Parameter(Mandatory = false, HelpMessage = "Override the RemoveExistingNodes attribute in the Navigation elements of the template. If you specify this value the navigation nodes will always be removed before adding the nodes in the template")] + public SwitchParameter ClearNavigation; + + protected override void ExecuteCmdlet() + { + var applyingInformation = new ProvisioningTemplateApplyingInformation(); + + if (MyInvocation.BoundParameters.ContainsKey("Handlers")) + { + applyingInformation.HandlersToProcess = Handlers; + } + if (MyInvocation.BoundParameters.ContainsKey("ExcludeHandlers")) + { + foreach (var handler in (Handlers[])Enum.GetValues(typeof(Handlers))) + { + if (!ExcludeHandlers.Has(handler) && handler != Handlers.All) + { + Handlers = Handlers | handler; + } + } + applyingInformation.HandlersToProcess = Handlers; + } + + if (ExtensibilityHandlers != null) + { + applyingInformation.ExtensibilityHandlers = ExtensibilityHandlers.ToList(); + } + + applyingInformation.ProgressDelegate = (message, step, total) => + { + if (message != null) + { + var percentage = Convert.ToInt32((100 / Convert.ToDouble(total)) * Convert.ToDouble(step)); + progressRecord.Activity = $"Applying template to tenant"; + progressRecord.StatusDescription = message; + progressRecord.PercentComplete = percentage; + progressRecord.RecordType = ProgressRecordType.Processing; + WriteProgress(progressRecord); + } + }; + + var warningsShown = new List(); + + applyingInformation.MessagesDelegate = (message, type) => + { + switch (type) + { + case ProvisioningMessageType.Warning: + { + if (!warningsShown.Contains(message)) + { + WriteWarning(message); + warningsShown.Add(message); + } + break; + } + case ProvisioningMessageType.Progress: + { + if (message != null) + { + var activity = message; + if (message.IndexOf("|") > -1) + { + var messageSplitted = message.Split('|'); + if (messageSplitted.Length == 4) + { + var current = double.Parse(messageSplitted[2]); + var total = double.Parse(messageSplitted[3]); + subProgressRecord.RecordType = ProgressRecordType.Processing; + subProgressRecord.Activity = string.IsNullOrEmpty(messageSplitted[0]) ? "-" : messageSplitted[0]; + subProgressRecord.StatusDescription = string.IsNullOrEmpty(messageSplitted[1]) ? "-" : messageSplitted[1]; + subProgressRecord.PercentComplete = Convert.ToInt32((100 / total) * current); + WriteProgress(subProgressRecord); + } + else + { + subProgressRecord.Activity = "Processing"; + subProgressRecord.RecordType = ProgressRecordType.Processing; + subProgressRecord.StatusDescription = activity; + subProgressRecord.PercentComplete = 0; + WriteProgress(subProgressRecord); + } + } + else + { + subProgressRecord.Activity = "Processing"; + subProgressRecord.RecordType = ProgressRecordType.Processing; + subProgressRecord.StatusDescription = activity; + subProgressRecord.PercentComplete = 0; + WriteProgress(subProgressRecord); + } + } + break; + } + case ProvisioningMessageType.Completed: + { + + WriteProgress(new ProgressRecord(1, message, " ") { RecordType = ProgressRecordType.Completed }); + break; + } + } + }; + + applyingInformation.OverwriteSystemPropertyBagValues = OverwriteSystemPropertyBagValues; + applyingInformation.IgnoreDuplicateDataRowErrors = IgnoreDuplicateDataRowErrors; + applyingInformation.ClearNavigation = ClearNavigation; + applyingInformation.ProvisionContentTypesToSubWebs = ProvisionContentTypesToSubWebs; + applyingInformation.ProvisionFieldsToSubWebs = ProvisionFieldsToSubWebs; + + Tenant.ApplyProvisionHierarchy(Hierarchy, SequenceId, applyingInformation); + + } + } +} diff --git a/Commands/Provisioning/GetProvisioningSequence.cs b/Commands/Provisioning/GetProvisioningSequence.cs new file mode 100644 index 000000000..7faece6e4 --- /dev/null +++ b/Commands/Provisioning/GetProvisioningSequence.cs @@ -0,0 +1,39 @@ +using OfficeDevPnP.Core.Framework.Provisioning.Model; +using SharePointPnP.PowerShell.CmdletHelpAttributes; +using SharePointPnP.PowerShell.Commands.Base.PipeBinds; +using System; +using System.Management.Automation; + +namespace SharePointPnP.PowerShell.Commands.Provisioning +{ + [Cmdlet(VerbsCommon.Get, "PnPProvisioningSequence", SupportsShouldProcess = true)] + [CmdletHelp("Returns one ore more provisioning sequence object(s) from a provisioning hierarchy", + Category = CmdletHelpCategory.Provisioning)] + [CmdletExample( + Code = @"PS:> Get-PnPProvisioningSequence -Hierarchy $myhierarchy", + Remarks = "Returns all sequences from the specified hierarchy", + SortOrder = 1)] + [CmdletExample( + Code = @"PS:> Get-PnPProvisioningSequence -Hierarchy $myhierarchy -Identity ""mysequence""", + Remarks = "Returns the specified sequence from the specified hierarchy", + SortOrder = 2)] + public class GetProvisioningSequence : PnPCmdlet + { + [Parameter(Mandatory = true, HelpMessage = "The hierarchy to retrieve the sequence from", ParameterSetName = ParameterAttribute.AllParameterSets)] + public ProvisioningHierarchy Hierarchy; + + [Parameter(Mandatory = false, HelpMessage = "Optional Id of the sequence", ParameterSetName = ParameterAttribute.AllParameterSets, ValueFromPipeline = true)] + public ProvisioningSequencePipeBind Identity; + protected override void ExecuteCmdlet() + { + if (!MyInvocation.BoundParameters.ContainsKey("Identity")) + { + WriteObject(Hierarchy.Sequences, true); + } + else + { + WriteObject(Identity.GetSequenceFromHierarchy(Hierarchy)); + } + } + } +} diff --git a/Commands/Provisioning/GetProvisioningSite.cs b/Commands/Provisioning/GetProvisioningSite.cs new file mode 100644 index 000000000..b26defb41 --- /dev/null +++ b/Commands/Provisioning/GetProvisioningSite.cs @@ -0,0 +1,39 @@ +using OfficeDevPnP.Core.Framework.Provisioning.Model; +using SharePointPnP.PowerShell.CmdletHelpAttributes; +using SharePointPnP.PowerShell.Commands.Base.PipeBinds; +using System; +using System.Management.Automation; + +namespace SharePointPnP.PowerShell.Commands.Provisioning +{ + [Cmdlet(VerbsCommon.Get, "PnPProvisioningSite", SupportsShouldProcess = true)] + [CmdletHelp("Returns one ore more provisioning sequence object(s) from a provisioning hierarchy", + Category = CmdletHelpCategory.Provisioning)] + [CmdletExample( + Code = @"PS:> Get-PnPProvisioningSite -Sequence $mysequence", + Remarks = "Returns all sites from the specified sequence", + SortOrder = 1)] + [CmdletExample( + Code = @"PS:> Get-PnPProvisioningSite -Sequence $mysequence -Identity 8058ea99-af7b-4bb7-b12a-78f93398041e", + Remarks = "Returns the specified site from the specified sequence", + SortOrder = 2)] + public class GetProvisioningSite : PnPCmdlet + { + [Parameter(Mandatory = true, HelpMessage = "The sequence to retrieve the site from", ParameterSetName = ParameterAttribute.AllParameterSets)] + public ProvisioningSequence Sequence; + + [Parameter(Mandatory = false, HelpMessage = "Optional Id of the site", ParameterSetName = ParameterAttribute.AllParameterSets, ValueFromPipeline = true)] + public ProvisioningSitePipeBind Identity; + protected override void ExecuteCmdlet() + { + if (!MyInvocation.BoundParameters.ContainsKey("Identity")) + { + WriteObject(Sequence.SiteCollections, true); + } + else + { + WriteObject(Identity.GetSiteFromSequence(Sequence)); + } + } + } +} diff --git a/Commands/Provisioning/NewProvisioningCommunicationSite.cs b/Commands/Provisioning/NewProvisioningCommunicationSite.cs new file mode 100644 index 000000000..299b0e708 --- /dev/null +++ b/Commands/Provisioning/NewProvisioningCommunicationSite.cs @@ -0,0 +1,71 @@ +using OfficeDevPnP.Core.Framework.Provisioning.Model; +using SharePointPnP.PowerShell.CmdletHelpAttributes; +using System.Linq; +using System.Management.Automation; + +namespace SharePointPnP.PowerShell.Commands.Provisioning +{ + [Cmdlet(VerbsCommon.New, "PnPProvisioningCommunicationSite", SupportsShouldProcess = true)] + [CmdletHelp("Creates a communication site object", + Category = CmdletHelpCategory.Provisioning)] + [CmdletExample( + Code = @"PS:> $site = New-PnPProvisioningCommunicationSite -Url ""/sites/mycommunicationsite"" -Title ""My Team Site""", + Remarks = "Creates a new communication site object with the specified variables", + SortOrder = 1)] + public class NewProvisioningCommunicationSite : PnPCmdlet + { + [Parameter(Mandatory = true)] + public string Url; + + [Parameter(Mandatory = true)] + public string Title; + + [Parameter(Mandatory = false)] + public uint Language; + + [Parameter(Mandatory = false)] + public string Owner; + + [Parameter(Mandatory = false)] + public string Description; + + [Parameter(Mandatory = false)] + public string Classification; + + [Parameter(Mandatory = false)] + public string SiteDesignId; + + [Parameter(Mandatory = false)] + public SwitchParameter HubSite; + + [Parameter(Mandatory = false)] + public SwitchParameter AllowFileSharingForGuestUsers; + + [Parameter(Mandatory = false)] + public string[] TemplateIds; + + protected override void ExecuteCmdlet() + { + var site = new CommunicationSiteCollection + { + Url = Url, + Language = (int)Language, + Owner = Owner, + AllowFileSharingForGuestUsers = AllowFileSharingForGuestUsers.IsPresent, + Classification = Classification, + Description = Description, + IsHubSite = HubSite.IsPresent, + Title = Title, + }; + if(MyInvocation.BoundParameters.ContainsKey("SiteDesignId")) + { + site.SiteDesign = SiteDesignId; + } + if (TemplateIds != null) + { + site.Templates.AddRange(TemplateIds.ToList()); + } + WriteObject(site); + } + } +} diff --git a/Commands/Provisioning/NewProvisioningHierarchy.cs b/Commands/Provisioning/NewProvisioningHierarchy.cs new file mode 100644 index 000000000..9292094e9 --- /dev/null +++ b/Commands/Provisioning/NewProvisioningHierarchy.cs @@ -0,0 +1,22 @@ +using OfficeDevPnP.Core.Framework.Provisioning.Model; +using SharePointPnP.PowerShell.CmdletHelpAttributes; +using System.Management.Automation; + +namespace SharePointPnP.PowerShell.Commands.Provisioning +{ + [Cmdlet(VerbsCommon.New, "PnPProvisioningHierarchy", SupportsShouldProcess = true)] + [CmdletHelp("Creates a new provisioning hierarchy object", + Category = CmdletHelpCategory.Provisioning)] + [CmdletExample( + Code = @"PS:> $hierarchy = New-PnPProvisioningHierarchy", + Remarks = "Creates a new instance of a provisioning hierarchy object.", + SortOrder = 1)] + public class NewProvisioningHierarchy : PnPCmdlet + { + protected override void ExecuteCmdlet() + { + var result = new ProvisioningHierarchy(); + WriteObject(result); + } + } +} diff --git a/Commands/Provisioning/NewProvisioningSequence.cs b/Commands/Provisioning/NewProvisioningSequence.cs new file mode 100644 index 000000000..77230bcc6 --- /dev/null +++ b/Commands/Provisioning/NewProvisioningSequence.cs @@ -0,0 +1,36 @@ +using OfficeDevPnP.Core.Framework.Provisioning.Model; +using SharePointPnP.PowerShell.CmdletHelpAttributes; +using System; +using System.Management.Automation; + +namespace SharePointPnP.PowerShell.Commands.Provisioning +{ + [Cmdlet(VerbsCommon.New, "PnPProvisioningSequence", SupportsShouldProcess = true)] + [CmdletHelp("Creates a new provisioning sequence object", + Category = CmdletHelpCategory.Provisioning)] + [CmdletExample( + Code = @"PS:> $sequence = New-PnPProvisioningSequence", + Remarks = "Creates a new instance of a provisioning sequence object.", + SortOrder = 1)] + [CmdletExample( + Code = @"PS:> $sequence = New-PnPProvisioningSequence -Id ""MySequence""", + Remarks = "Creates a new instance of a provisioning sequence object and sets the Id to the value specified.", + SortOrder = 2)] + public class NewProvisioningSequence : PnPCmdlet + { + [Parameter(Mandatory = false, HelpMessage = "Optional Id of the sequence", ParameterSetName = ParameterAttribute.AllParameterSets)] + public string Id; + protected override void ExecuteCmdlet() + { + var result = new ProvisioningSequence(); + if (this.MyInvocation.BoundParameters.ContainsKey("Id")) + { + result.ID = Id; + } else + { + result.ID = $"sequence-{Guid.NewGuid()}"; + } + WriteObject(result); + } + } +} diff --git a/Commands/Provisioning/NewProvisioningTeamNoGroupSite.cs b/Commands/Provisioning/NewProvisioningTeamNoGroupSite.cs new file mode 100644 index 000000000..8aba83684 --- /dev/null +++ b/Commands/Provisioning/NewProvisioningTeamNoGroupSite.cs @@ -0,0 +1,60 @@ +using OfficeDevPnP.Core.Framework.Provisioning.Model; +using SharePointPnP.PowerShell.CmdletHelpAttributes; +using System.Linq; +using System.Management.Automation; + +namespace SharePointPnP.PowerShell.Commands.Provisioning +{ + [Cmdlet(VerbsCommon.New, "PnPProvisioningTeamGroupSite", SupportsShouldProcess = true)] + [CmdletHelp("Creates a team site without an Office 365 group object", + Category = CmdletHelpCategory.Provisioning)] + [CmdletExample( + Code = @"PS:> $site = New-PnPProvisioningTeamNoGroupSite -Alias ""MyTeamSite"" -Title ""My Team Site""", + Remarks = "Creates a new team site object with the specified variables", + SortOrder = 1)] + public class NewProvisioningTeamNoGroupSite : PnPCmdlet + { + [Parameter(Mandatory = true)] + public string Url; + + [Parameter(Mandatory = true)] + public string Title; + + [Parameter(Mandatory = true)] + public uint TimeZoneId; + + [Parameter(Mandatory = false)] + public uint Language; + + [Parameter(Mandatory = false)] + public string Owner; + + [Parameter(Mandatory = false)] + public string Description; + + [Parameter(Mandatory = false)] + public SwitchParameter HubSite; + + [Parameter(Mandatory = false)] + public string[] TemplateIds; + + protected override void ExecuteCmdlet() + { + var site = new TeamNoGroupSiteCollection + { + Url = Url, + Language = (int)Language, + Owner = Owner, + TimeZoneId = (int)TimeZoneId, + Description = Description, + IsHubSite = HubSite.IsPresent, + Title = Title + }; + if (TemplateIds != null) + { + site.Templates.AddRange(TemplateIds.ToList()); + } + WriteObject(site); + } + } +} diff --git a/Commands/Provisioning/NewProvisioningTeamNoGroupSubSite.cs b/Commands/Provisioning/NewProvisioningTeamNoGroupSubSite.cs new file mode 100644 index 000000000..b4e7f8d98 --- /dev/null +++ b/Commands/Provisioning/NewProvisioningTeamNoGroupSubSite.cs @@ -0,0 +1,62 @@ +using OfficeDevPnP.Core.Framework.Provisioning.Model; +using SharePointPnP.PowerShell.CmdletHelpAttributes; +using System.Linq; +using System.Management.Automation; + +namespace SharePointPnP.PowerShell.Commands.Provisioning +{ + [Cmdlet(VerbsCommon.New, "PnPProvisioningTeamNoGroupSubSite", SupportsShouldProcess = true)] + [CmdletHelp("Creates a team site subsite with no Office 365 group object", + Category = CmdletHelpCategory.Provisioning)] + [CmdletExample( + Code = @"PS:> $site = New-PnPProvisioningTeamNoGroupSubSite -Url ""MyTeamSubsite"" -Title ""My Team Site"" -TimeZoneId 4", + Remarks = "Creates a new team site subsite object with the specified variables", + SortOrder = 1)] + public class NewProvisioningTeamNoGroupSubSite : PnPCmdlet + { + + [Parameter(Mandatory = true)] + public string Url; + + [Parameter(Mandatory = true)] + public string Title; + + [Parameter(Mandatory = true)] + public uint TimeZoneId; + + [Parameter(Mandatory = false)] + public uint Language; + + [Parameter(Mandatory = false)] + public string Description; + + [Parameter(Mandatory = false)] + public string[] TemplateIds; + + [Parameter(Mandatory = false)] + public SwitchParameter QuickLaunchDisabled; + + [Parameter(Mandatory = false)] + public SwitchParameter UseDifferentPermissionsFromParentSite; + + protected override void ExecuteCmdlet() + { + SiteCollection c; + var site = new TeamNoGroupSubSite() + { + Url = Url, + Language = Language.ToString(), + QuickLaunchEnabled = !QuickLaunchDisabled.IsPresent, + UseSamePermissionsAsParentSite = !UseDifferentPermissionsFromParentSite.IsPresent, + TimeZoneId = (int)TimeZoneId, + Description = Description, + Title = Title + }; + if (TemplateIds != null) + { + site.Templates.AddRange(TemplateIds.ToList()); + } + WriteObject(site); + } + } +} diff --git a/Commands/Provisioning/NewProvisioningTeamSite.cs b/Commands/Provisioning/NewProvisioningTeamSite.cs new file mode 100644 index 000000000..97477d9ee --- /dev/null +++ b/Commands/Provisioning/NewProvisioningTeamSite.cs @@ -0,0 +1,61 @@ +using OfficeDevPnP.Core.Framework.Provisioning.Model; +using SharePointPnP.PowerShell.CmdletHelpAttributes; +using System.Linq; +using System.Management.Automation; + +namespace SharePointPnP.PowerShell.Commands.Provisioning +{ + [Cmdlet(VerbsCommon.New, "PnPProvisioningTeamSite", SupportsShouldProcess = true)] + [CmdletHelp("Creates a team site object", + Category = CmdletHelpCategory.Provisioning)] + [CmdletExample( + Code = @"PS:> $site = New-PnPProvisioningTeamSite -Alias ""MyTeamSite"" -Title ""My Team Site""", + Remarks = "Creates a new team site object with the specified variables", + SortOrder = 1)] + public class NewProvisioningTeamSite : PnPCmdlet + { + + [Parameter(Mandatory = true)] + public string Alias; + + [Parameter(Mandatory = true)] + public string Title; + + [Parameter(Mandatory = false)] + public string Description; + + [Parameter(Mandatory = false)] + public string DisplayName; + + [Parameter(Mandatory = false)] + public string Classification; + + [Parameter(Mandatory = false)] + public SwitchParameter Public; + + [Parameter(Mandatory = false)] + public SwitchParameter HubSite; + + [Parameter(Mandatory = false)] + public string[] TemplateIds; + + protected override void ExecuteCmdlet() + { + var site = new TeamSiteCollection + { + Alias = Alias, + Classification = Classification, + Description = Description, + DisplayName = DisplayName, + IsHubSite = HubSite.IsPresent, + IsPublic = Public.IsPresent, + Title = Title + }; + if (TemplateIds != null) + { + site.Templates.AddRange(TemplateIds.ToList()); + } + WriteObject(site); + } + } +} diff --git a/Commands/Provisioning/TestProvisioningHierarchy.cs b/Commands/Provisioning/TestProvisioningHierarchy.cs new file mode 100644 index 000000000..b339ff341 --- /dev/null +++ b/Commands/Provisioning/TestProvisioningHierarchy.cs @@ -0,0 +1,91 @@ +using OfficeDevPnP.Core.Framework.Provisioning.Model; +using SharePointPnP.PowerShell.CmdletHelpAttributes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management.Automation; + +namespace SharePointPnP.PowerShell.Commands.Provisioning +{ + [Cmdlet(VerbsDiagnostic.Test, "PnPProvisioningHierarchy", SupportsShouldProcess = true)] + [CmdletHelp("Tests a provisioning hierarchy for invalid references", + Category = CmdletHelpCategory.Provisioning)] + [CmdletExample( + Code = @"PS:> Test-PnPProvisioningHierarchy -Hierarchy $myhierarchy", + Remarks = "Checks for valid template references", + SortOrder = 1)] + public class TestProvisioningHierarchy : PnPCmdlet + { + [Parameter(Mandatory = true, HelpMessage = "The hierarchy to add the sequence to", ParameterSetName = ParameterAttribute.AllParameterSets)] + public ProvisioningHierarchy Hierarchy; + + protected override void ExecuteCmdlet() + { + List issues = new List(); + foreach(var sequence in Hierarchy.Sequences) + { + foreach (var site in sequence.SiteCollections) + { + foreach (var template in site.Templates) + { + if(Hierarchy.Templates.FirstOrDefault(t => t.Id == template) == null) + { + issues.Add($"Template {template} referenced in site {site.Id} is not present in hierarchy."); + } + } + foreach(var subsite in site.Sites.Cast()) + { + foreach (var template in subsite.Templates) + { + if (Hierarchy.Templates.FirstOrDefault(t => t.Id == template) == null) + { + issues.Add($"Template {template} referenced in subsite with url {subsite.Url} in site {site.Id} is not present in hierarchy"); + } + } + } + } + } + foreach(var template in Hierarchy.Templates) + { + var used = false; + foreach(var sequence in Hierarchy.Sequences) + { + foreach(var site in sequence.SiteCollections) + { + if(site.Templates.Contains(template.Id)) + { + used = true; + break; + } + + foreach(var subsite in site.Sites) + { + if(subsite.Templates.Contains(template.Id)) + { + used = true; + break; + } + } + if (used) + { + break; + } + } + if(used) + { + break; + } + } + if(!used) + { + issues.Add($"Template {template.Id} is not used by any site in the hierarchy."); + } + } + if(issues.Any()) + { + WriteObject(issues, true); + } + + } + } +} diff --git a/Commands/SharePointPnP.PowerShell.Commands.csproj b/Commands/SharePointPnP.PowerShell.Commands.csproj index 5edb933f0..8faa84282 100644 --- a/Commands/SharePointPnP.PowerShell.Commands.csproj +++ b/Commands/SharePointPnP.PowerShell.Commands.csproj @@ -464,6 +464,8 @@ + + @@ -497,6 +499,20 @@ + + + + + + + + + + + + + + diff --git a/Commands/Taxonomy/GetTaxonomySession.cs b/Commands/Taxonomy/GetTaxonomySession.cs index ca42fa9f3..758991de1 100644 --- a/Commands/Taxonomy/GetTaxonomySession.cs +++ b/Commands/Taxonomy/GetTaxonomySession.cs @@ -14,7 +14,10 @@ public class GetTaxonomySession : PnPWebCmdlet { protected override void ExecuteCmdlet() { - WriteObject(ClientContext.Site.GetTaxonomySession()); + var session = ClientContext.Site.GetTaxonomySession(); + ClientContext.Load(session.TermStores); + ClientContext.ExecuteQueryRetry(); + WriteObject(session); } } From efc7a3762290d0c300bf561ea5ec187c77a0a903 Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Thu, 6 Sep 2018 12:42:57 +0200 Subject: [PATCH 02/19] Hierarchy cmdlets --- .../Provisioning/AddProvisioningSequence.cs | 5 +- Commands/Provisioning/AddProvisioningSite.cs | 4 +- .../Provisioning/AddProvisioningSubSite.cs | 4 +- .../Provisioning/AddProvisioningTemplate.cs | 4 +- .../ApplyProvisioningHierarchy.cs | 168 +++++++++++++++++- .../Provisioning/ApplyProvisioningTemplate.cs | 22 +-- .../Provisioning/GetProvisioningSequence.cs | 4 +- Commands/Provisioning/GetProvisioningSite.cs | 4 +- .../NewProvisioningCommunicationSite.cs | 4 +- .../Provisioning/NewProvisioningHierarchy.cs | 20 ++- .../Provisioning/NewProvisioningSequence.cs | 4 +- .../NewProvisioningTeamNoGroupSite.cs | 6 +- .../NewProvisioningTeamNoGroupSubSite.cs | 4 +- .../Provisioning/NewProvisioningTeamSite.cs | 4 +- .../Provisioning/NewProvisioningTemplate.cs | 4 +- .../Provisioning/ReadProvisioningTemplate.cs | 3 +- .../Provisioning/SaveProvisioningTemplate.cs | 2 +- .../SetProvisioningTemplateMetadata.cs | 3 +- .../SharePointPnP.PowerShell.Commands.csproj | 1 + Commands/Utilities/FileUtilities.cs | 31 ++++ 20 files changed, 248 insertions(+), 53 deletions(-) create mode 100644 Commands/Utilities/FileUtilities.cs diff --git a/Commands/Provisioning/AddProvisioningSequence.cs b/Commands/Provisioning/AddProvisioningSequence.cs index 85d149970..a0342145d 100644 --- a/Commands/Provisioning/AddProvisioningSequence.cs +++ b/Commands/Provisioning/AddProvisioningSequence.cs @@ -17,14 +17,15 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning Code = @"PS:> New-PnPProvisioningSequence -Id ""MySequence"" | Add-PnPProvisioningSequence -Hierarchy $hierarchy", Remarks = "Creates a new instance of a provisioning sequence object and sets the Id to the value specified, then the sequence is added to an existing hierarchy object", SortOrder = 2)] - public class AddProvisioningSequence : PnPCmdlet + public class AddProvisioningSequence : PSCmdlet { [Parameter(Mandatory = true, HelpMessage = "The hierarchy to add the sequence to", ParameterSetName = ParameterAttribute.AllParameterSets)] public ProvisioningHierarchy Hierarchy; [Parameter(Mandatory = true, HelpMessage = "Optional Id of the sequence", ParameterSetName = ParameterAttribute.AllParameterSets, ValueFromPipeline = true)] public ProvisioningSequence Sequence; - protected override void ExecuteCmdlet() + + protected override void ProcessRecord() { if (Hierarchy.Sequences.FirstOrDefault(s => s.ID == Sequence.ID) == null) { diff --git a/Commands/Provisioning/AddProvisioningSite.cs b/Commands/Provisioning/AddProvisioningSite.cs index 7cad90912..472501c23 100644 --- a/Commands/Provisioning/AddProvisioningSite.cs +++ b/Commands/Provisioning/AddProvisioningSite.cs @@ -16,7 +16,7 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning Code = @"PS:> New-PnPProvisioningSequence -Id ""MySequence"" | Add-PnPProvisioningSequence -Hierarchy $hierarchy", Remarks = "Creates a new instance of a provisioning sequence object and sets the Id to the value specified, then the sequence is added to an existing hierarchy object", SortOrder = 2)] - public class AddProvisioningSite : PnPCmdlet + public class AddProvisioningSite : PSCmdlet { [Parameter(Mandatory = true)] public ProvisioningSitePipeBind Site; @@ -24,7 +24,7 @@ public class AddProvisioningSite : PnPCmdlet [Parameter(Mandatory = true, HelpMessage = "The sequence to add the site to", ValueFromPipeline = true)] public ProvisioningSequence Sequence; - protected override void ExecuteCmdlet() + protected override void ProcessRecord() { Sequence.SiteCollections.Add(Site.Site); WriteObject(Sequence); diff --git a/Commands/Provisioning/AddProvisioningSubSite.cs b/Commands/Provisioning/AddProvisioningSubSite.cs index f69322fef..75bd155a4 100644 --- a/Commands/Provisioning/AddProvisioningSubSite.cs +++ b/Commands/Provisioning/AddProvisioningSubSite.cs @@ -18,7 +18,7 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning Code = @"PS:> New-PnPProvisioningSequence -Id ""MySequence"" | Add-PnPProvisioningSequence -Hierarchy $hierarchy", Remarks = "Creates a new instance of a provisioning sequence object and sets the Id to the value specified, then the sequence is added to an existing hierarchy object", SortOrder = 2)] - public class AddProvisioningSubSite : PnPCmdlet + public class AddProvisioningSubSite : PSCmdlet { [Parameter(Mandatory = true)] public TeamNoGroupSubSite SubSite; @@ -26,7 +26,7 @@ public class AddProvisioningSubSite : PnPCmdlet [Parameter(Mandatory = true, HelpMessage = "The sequence to add the site to", ValueFromPipeline = true)] public SiteCollection Site; - protected override void ExecuteCmdlet() + protected override void ProcessRecord() { if (Site.Sites.Cast().FirstOrDefault(s => s.Url == SubSite.Url) == null) { diff --git a/Commands/Provisioning/AddProvisioningTemplate.cs b/Commands/Provisioning/AddProvisioningTemplate.cs index 19c65eab1..21c5e773d 100644 --- a/Commands/Provisioning/AddProvisioningTemplate.cs +++ b/Commands/Provisioning/AddProvisioningTemplate.cs @@ -14,7 +14,7 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning Code = @"PS:> Add-PnPProvisioningTemplate -Hierarchy $myhierarchy -Template $mytemplate", Remarks = "Adds an existing sequence object to an existing hierarchy object", SortOrder = 1)] - public class AddProvisioningTemplate : PnPCmdlet + public class AddProvisioningTemplate : PSCmdlet { [Parameter(Mandatory = true, HelpMessage = "The template to add to the hierarchy")] public ProvisioningTemplate Template; @@ -22,7 +22,7 @@ public class AddProvisioningTemplate : PnPCmdlet [Parameter(Mandatory = true, HelpMessage = "The hierarchy to add the template to", ValueFromPipeline = true)] public ProvisioningHierarchy Hierarchy; - protected override void ExecuteCmdlet() + protected override void ProcessRecord() { if(Hierarchy.Templates.FirstOrDefault(t => t.Id == Template.Id) == null) { diff --git a/Commands/Provisioning/ApplyProvisioningHierarchy.cs b/Commands/Provisioning/ApplyProvisioningHierarchy.cs index 4453df25d..369ea1dd1 100644 --- a/Commands/Provisioning/ApplyProvisioningHierarchy.cs +++ b/Commands/Provisioning/ApplyProvisioningHierarchy.cs @@ -1,12 +1,15 @@ using Microsoft.SharePoint.Client; +using OfficeDevPnP.Core.Framework.Provisioning.Connectors; using OfficeDevPnP.Core.Framework.Provisioning.Model; using OfficeDevPnP.Core.Framework.Provisioning.ObjectHandlers; using OfficeDevPnP.Core.Framework.Provisioning.Providers; +using OfficeDevPnP.Core.Framework.Provisioning.Providers.Xml; using SharePointPnP.PowerShell.CmdletHelpAttributes; using SharePointPnP.PowerShell.Commands.Base; -using SharePointPnP.PowerShell.Commands.Base.PipeBinds; +using SharePointPnP.PowerShell.Commands.Utilities; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Management.Automation; @@ -25,15 +28,24 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning SortOrder = 2)] public class ApplyProvisioningHierarchy : PnPAdminCmdlet { + private const string ParameterSet_PATH = "By Path"; + private const string ParameterSet_OBJECT = "By Object"; + private ProgressRecord progressRecord = new ProgressRecord(0, "Activity", "Status"); private ProgressRecord subProgressRecord = new ProgressRecord(1, "Activity", "Status"); - [Parameter(Mandatory = true)] + [Parameter(Mandatory = true, Position = 0, ValueFromPipelineByPropertyName = true, ValueFromPipeline = true, HelpMessage = "Path to the xml or pnp file containing the provisioning hierarchy.", ParameterSetName = ParameterSet_PATH)] + public string Path; + + [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true, ParameterSetName = ParameterSet_OBJECT)] public ProvisioningHierarchy Hierarchy; [Parameter(Mandatory = false)] public string SequenceId; + [Parameter(Mandatory = false, HelpMessage = "Root folder where resources/files that are being referenced in the template are located. If not specified the same folder as where the provisioning template is located will be used.", ParameterSetName = ParameterAttribute.AllParameterSets)] + public string ResourceFolder; + [Parameter(Mandatory = false, HelpMessage = "Allows you to only process a specific part of the template. Notice that this might fail, as some of the handlers require other artifacts in place if they are not part of what your applying.", ParameterSetName = ParameterAttribute.AllParameterSets)] public Handlers Handlers; @@ -167,8 +179,156 @@ protected override void ExecuteCmdlet() applyingInformation.ProvisionContentTypesToSubWebs = ProvisionContentTypesToSubWebs; applyingInformation.ProvisionFieldsToSubWebs = ProvisionFieldsToSubWebs; - Tenant.ApplyProvisionHierarchy(Hierarchy, SequenceId, applyingInformation); - + ProvisioningHierarchy hierarchyToApply = null; + + switch(ParameterSetName) + { + case ParameterSet_PATH: + { + hierarchyToApply = GetHierarchy(); + break; + } + case ParameterSet_OBJECT: + { + hierarchyToApply = Hierarchy; + if (ResourceFolder != null) + { + var fileSystemConnector = new FileSystemConnector(ResourceFolder, ""); + hierarchyToApply.Connector = fileSystemConnector; + } + else + { + if (Path != null) + { + if (!System.IO.Path.IsPathRooted(Path)) + { + Path = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, Path); + } + } + else + { + Path = SessionState.Path.CurrentFileSystemLocation.Path; + } + var fileInfo = new FileInfo(Path); + var fileConnector = new FileSystemConnector(fileInfo.DirectoryName, ""); + hierarchyToApply.Connector = fileConnector; + } + break; + } + } + if (!string.IsNullOrEmpty(SequenceId)) + { + Tenant.ApplyProvisionHierarchy(hierarchyToApply, SequenceId, applyingInformation); + } + else + { + foreach (var sequence in hierarchyToApply.Sequences) + { + Tenant.ApplyProvisionHierarchy(hierarchyToApply, sequence.ID, applyingInformation); + } + } + + } + + private ProvisioningHierarchy GetHierarchy() + { + ProvisioningHierarchy hierarchy = null; + FileConnectorBase fileConnector; + + bool templateFromFileSystem = !Path.ToLower().StartsWith("http"); + string templateFileName = System.IO.Path.GetFileName(Path); + if (templateFromFileSystem) + { + if (!System.IO.Path.IsPathRooted(Path)) + { + Path = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, Path); + } + if (!System.IO.File.Exists(Path)) + { + throw new FileNotFoundException($"File not found"); + } + if (!string.IsNullOrEmpty(ResourceFolder)) + { + if (!System.IO.Path.IsPathRooted(ResourceFolder)) + { + ResourceFolder = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, + ResourceFolder); + } + } + var fileInfo = new FileInfo(Path); + fileConnector = new FileSystemConnector(fileInfo.DirectoryName, ""); + } + else + { + Uri fileUri = new Uri(Path); + var webUrl = Microsoft.SharePoint.Client.Web.WebUrlFromFolderUrlDirect(ClientContext, fileUri); + var templateContext = ClientContext.Clone(webUrl.ToString()); + + var library = Path.ToLower().Replace(templateContext.Url.ToLower(), "").TrimStart('/'); + var idx = library.IndexOf("/", StringComparison.Ordinal); + library = library.Substring(0, idx); + + // This syntax creates a SharePoint connector regardless we have the -InputInstance argument or not + fileConnector = new SharePointConnector(templateContext, templateContext.Url, library); + } + + // If we don't have the -InputInstance parameter, we load the template from the source connector + + using (var stream = fileConnector.GetFileStream(templateFileName)) + { + var isOpenOfficeFile = FileUtilities.IsOpenOfficeFile(stream); + XMLTemplateProvider provider; + if (isOpenOfficeFile) + { + provider = new XMLOpenXMLTemplateProvider(new OpenXMLConnector(templateFileName, fileConnector)); + templateFileName = templateFileName.Substring(0, templateFileName.LastIndexOf(".", StringComparison.Ordinal)) + ".xml"; + } + else + { + if (templateFromFileSystem) + { + provider = new XMLFileSystemTemplateProvider(Path, ""); + } + else + { + throw new NotSupportedException("Only .pnp package files are supported from a SharePoint library"); + } + } + + var formatter = XMLPnPSchemaFormatter.LatestFormatter; + formatter.Initialize(provider); + + var serializer = (IProvisioningHierarchyFormatter)formatter; + + hierarchy = serializer.ToProvisioningHierarchy(provider.Connector.GetFileStream(Path)); + + hierarchy.Connector = fileConnector; + if (hierarchy == null) + { + // If we don't have the template, raise an error and exit + WriteError(new ErrorRecord(new Exception("The -Path parameter targets an invalid repository or template object."), "WRONG_PATH", ErrorCategory.SyntaxError, null)); + return null; + } + + //if (isOpenOfficeFile) + //{ + // hierarchy.Connector = provider.Connector; + //} + //else + //{ + // if (ResourceFolder != null) + // { + // var fileSystemConnector = new FileSystemConnector(ResourceFolder, ""); + // provisioningTemplate.Connector = fileSystemConnector; + // } + // else + // { + // provisioningTemplate.Connector = provider.Connector; + // } + //} + + return hierarchy; + } } } } diff --git a/Commands/Provisioning/ApplyProvisioningTemplate.cs b/Commands/Provisioning/ApplyProvisioningTemplate.cs index 15f068c61..a09a567d7 100644 --- a/Commands/Provisioning/ApplyProvisioningTemplate.cs +++ b/Commands/Provisioning/ApplyProvisioningTemplate.cs @@ -12,6 +12,7 @@ using OfficeDevPnP.Core.Framework.Provisioning.Providers; using SharePointPnP.PowerShell.Commands.Components; using System.Collections.Generic; +using SharePointPnP.PowerShell.Commands.Utilities; namespace SharePointPnP.PowerShell.Commands.Provisioning { @@ -152,7 +153,7 @@ protected override void ExecuteCmdlet() // If we don't have the -InputInstance parameter, we load the template from the source connector Stream stream = fileConnector.GetFileStream(templateFileName); - var isOpenOfficeFile = IsOpenOfficeFile(stream); + var isOpenOfficeFile = FileUtilities.IsOpenOfficeFile(stream); XMLTemplateProvider provider; if (isOpenOfficeFile) { @@ -355,23 +356,6 @@ protected override void ExecuteCmdlet() WriteProgress(new ProgressRecord(0, $"Applying template to {SelectedWeb.Url}", " ") { RecordType = ProgressRecordType.Completed }); } - internal static bool IsOpenOfficeFile(Stream stream) - { - bool istrue = false; - // SIG 50 4B 03 04 14 00 - - byte[] bytes = new byte[6]; - stream.Read(bytes, 0, 6); - var signature = string.Empty; - foreach (var b in bytes) - { - signature += b.ToString("X2"); - } - if (signature == "504B03041400") - { - istrue = true; - } - return istrue; - } + } } diff --git a/Commands/Provisioning/GetProvisioningSequence.cs b/Commands/Provisioning/GetProvisioningSequence.cs index 7faece6e4..f59079421 100644 --- a/Commands/Provisioning/GetProvisioningSequence.cs +++ b/Commands/Provisioning/GetProvisioningSequence.cs @@ -17,14 +17,14 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning Code = @"PS:> Get-PnPProvisioningSequence -Hierarchy $myhierarchy -Identity ""mysequence""", Remarks = "Returns the specified sequence from the specified hierarchy", SortOrder = 2)] - public class GetProvisioningSequence : PnPCmdlet + public class GetProvisioningSequence : PSCmdlet { [Parameter(Mandatory = true, HelpMessage = "The hierarchy to retrieve the sequence from", ParameterSetName = ParameterAttribute.AllParameterSets)] public ProvisioningHierarchy Hierarchy; [Parameter(Mandatory = false, HelpMessage = "Optional Id of the sequence", ParameterSetName = ParameterAttribute.AllParameterSets, ValueFromPipeline = true)] public ProvisioningSequencePipeBind Identity; - protected override void ExecuteCmdlet() + protected override void ProcessRecord() { if (!MyInvocation.BoundParameters.ContainsKey("Identity")) { diff --git a/Commands/Provisioning/GetProvisioningSite.cs b/Commands/Provisioning/GetProvisioningSite.cs index b26defb41..d736ac23f 100644 --- a/Commands/Provisioning/GetProvisioningSite.cs +++ b/Commands/Provisioning/GetProvisioningSite.cs @@ -17,14 +17,14 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning Code = @"PS:> Get-PnPProvisioningSite -Sequence $mysequence -Identity 8058ea99-af7b-4bb7-b12a-78f93398041e", Remarks = "Returns the specified site from the specified sequence", SortOrder = 2)] - public class GetProvisioningSite : PnPCmdlet + public class GetProvisioningSite : PSCmdlet { [Parameter(Mandatory = true, HelpMessage = "The sequence to retrieve the site from", ParameterSetName = ParameterAttribute.AllParameterSets)] public ProvisioningSequence Sequence; [Parameter(Mandatory = false, HelpMessage = "Optional Id of the site", ParameterSetName = ParameterAttribute.AllParameterSets, ValueFromPipeline = true)] public ProvisioningSitePipeBind Identity; - protected override void ExecuteCmdlet() + protected override void ProcessRecord() { if (!MyInvocation.BoundParameters.ContainsKey("Identity")) { diff --git a/Commands/Provisioning/NewProvisioningCommunicationSite.cs b/Commands/Provisioning/NewProvisioningCommunicationSite.cs index 299b0e708..78fe577d0 100644 --- a/Commands/Provisioning/NewProvisioningCommunicationSite.cs +++ b/Commands/Provisioning/NewProvisioningCommunicationSite.cs @@ -12,7 +12,7 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning Code = @"PS:> $site = New-PnPProvisioningCommunicationSite -Url ""/sites/mycommunicationsite"" -Title ""My Team Site""", Remarks = "Creates a new communication site object with the specified variables", SortOrder = 1)] - public class NewProvisioningCommunicationSite : PnPCmdlet + public class NewProvisioningCommunicationSite : PSCmdlet { [Parameter(Mandatory = true)] public string Url; @@ -44,7 +44,7 @@ public class NewProvisioningCommunicationSite : PnPCmdlet [Parameter(Mandatory = false)] public string[] TemplateIds; - protected override void ExecuteCmdlet() + protected override void ProcessRecord() { var site = new CommunicationSiteCollection { diff --git a/Commands/Provisioning/NewProvisioningHierarchy.cs b/Commands/Provisioning/NewProvisioningHierarchy.cs index 9292094e9..4a566ff04 100644 --- a/Commands/Provisioning/NewProvisioningHierarchy.cs +++ b/Commands/Provisioning/NewProvisioningHierarchy.cs @@ -11,11 +11,27 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning Code = @"PS:> $hierarchy = New-PnPProvisioningHierarchy", Remarks = "Creates a new instance of a provisioning hierarchy object.", SortOrder = 1)] - public class NewProvisioningHierarchy : PnPCmdlet + public class NewProvisioningHierarchy : PSCmdlet { - protected override void ExecuteCmdlet() + [Parameter(Mandatory = false)] + public string Author; + + [Parameter(Mandatory = false)] + public string Description; + + [Parameter(Mandatory = false)] + public string DisplayName; + + [Parameter(Mandatory = false)] + public string Generator; + + protected override void ProcessRecord() { var result = new ProvisioningHierarchy(); + result.Author = Author; + result.Description = Description; + result.DisplayName = DisplayName; + result.Generator = Generator; WriteObject(result); } } diff --git a/Commands/Provisioning/NewProvisioningSequence.cs b/Commands/Provisioning/NewProvisioningSequence.cs index 77230bcc6..f5f090cfe 100644 --- a/Commands/Provisioning/NewProvisioningSequence.cs +++ b/Commands/Provisioning/NewProvisioningSequence.cs @@ -16,11 +16,11 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning Code = @"PS:> $sequence = New-PnPProvisioningSequence -Id ""MySequence""", Remarks = "Creates a new instance of a provisioning sequence object and sets the Id to the value specified.", SortOrder = 2)] - public class NewProvisioningSequence : PnPCmdlet + public class NewProvisioningSequence : PSCmdlet { [Parameter(Mandatory = false, HelpMessage = "Optional Id of the sequence", ParameterSetName = ParameterAttribute.AllParameterSets)] public string Id; - protected override void ExecuteCmdlet() + protected override void ProcessRecord() { var result = new ProvisioningSequence(); if (this.MyInvocation.BoundParameters.ContainsKey("Id")) diff --git a/Commands/Provisioning/NewProvisioningTeamNoGroupSite.cs b/Commands/Provisioning/NewProvisioningTeamNoGroupSite.cs index 8aba83684..6a935e4c1 100644 --- a/Commands/Provisioning/NewProvisioningTeamNoGroupSite.cs +++ b/Commands/Provisioning/NewProvisioningTeamNoGroupSite.cs @@ -5,14 +5,14 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning { - [Cmdlet(VerbsCommon.New, "PnPProvisioningTeamGroupSite", SupportsShouldProcess = true)] + [Cmdlet(VerbsCommon.New, "PnPProvisioningTeamNoGroupSite", SupportsShouldProcess = true)] [CmdletHelp("Creates a team site without an Office 365 group object", Category = CmdletHelpCategory.Provisioning)] [CmdletExample( Code = @"PS:> $site = New-PnPProvisioningTeamNoGroupSite -Alias ""MyTeamSite"" -Title ""My Team Site""", Remarks = "Creates a new team site object with the specified variables", SortOrder = 1)] - public class NewProvisioningTeamNoGroupSite : PnPCmdlet + public class NewProvisioningTeamNoGroupSite : PSCmdlet { [Parameter(Mandatory = true)] public string Url; @@ -38,7 +38,7 @@ public class NewProvisioningTeamNoGroupSite : PnPCmdlet [Parameter(Mandatory = false)] public string[] TemplateIds; - protected override void ExecuteCmdlet() + protected override void ProcessRecord() { var site = new TeamNoGroupSiteCollection { diff --git a/Commands/Provisioning/NewProvisioningTeamNoGroupSubSite.cs b/Commands/Provisioning/NewProvisioningTeamNoGroupSubSite.cs index b4e7f8d98..5a3e4eb1d 100644 --- a/Commands/Provisioning/NewProvisioningTeamNoGroupSubSite.cs +++ b/Commands/Provisioning/NewProvisioningTeamNoGroupSubSite.cs @@ -12,7 +12,7 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning Code = @"PS:> $site = New-PnPProvisioningTeamNoGroupSubSite -Url ""MyTeamSubsite"" -Title ""My Team Site"" -TimeZoneId 4", Remarks = "Creates a new team site subsite object with the specified variables", SortOrder = 1)] - public class NewProvisioningTeamNoGroupSubSite : PnPCmdlet + public class NewProvisioningTeamNoGroupSubSite : PSCmdlet { [Parameter(Mandatory = true)] @@ -39,7 +39,7 @@ public class NewProvisioningTeamNoGroupSubSite : PnPCmdlet [Parameter(Mandatory = false)] public SwitchParameter UseDifferentPermissionsFromParentSite; - protected override void ExecuteCmdlet() + protected override void ProcessRecord() { SiteCollection c; var site = new TeamNoGroupSubSite() diff --git a/Commands/Provisioning/NewProvisioningTeamSite.cs b/Commands/Provisioning/NewProvisioningTeamSite.cs index 97477d9ee..d0d1b1081 100644 --- a/Commands/Provisioning/NewProvisioningTeamSite.cs +++ b/Commands/Provisioning/NewProvisioningTeamSite.cs @@ -12,7 +12,7 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning Code = @"PS:> $site = New-PnPProvisioningTeamSite -Alias ""MyTeamSite"" -Title ""My Team Site""", Remarks = "Creates a new team site object with the specified variables", SortOrder = 1)] - public class NewProvisioningTeamSite : PnPCmdlet + public class NewProvisioningTeamSite : PSCmdlet { [Parameter(Mandatory = true)] @@ -39,7 +39,7 @@ public class NewProvisioningTeamSite : PnPCmdlet [Parameter(Mandatory = false)] public string[] TemplateIds; - protected override void ExecuteCmdlet() + protected override void ProcessRecord() { var site = new TeamSiteCollection { diff --git a/Commands/Provisioning/NewProvisioningTemplate.cs b/Commands/Provisioning/NewProvisioningTemplate.cs index 8cd226ce2..a9d0ae020 100644 --- a/Commands/Provisioning/NewProvisioningTemplate.cs +++ b/Commands/Provisioning/NewProvisioningTemplate.cs @@ -11,9 +11,9 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning Code = @"PS:> $template = New-PnPProvisioningTemplate", Remarks = "Creates a new instance of a provisioning template object.", SortOrder = 1)] - public class NewProvisioningTemplate : PnPWebCmdlet + public class NewProvisioningTemplate : PSCmdlet { - protected override void ExecuteCmdlet() + protected override void ProcessRecord() { var result = new ProvisioningTemplate(); WriteObject(result); diff --git a/Commands/Provisioning/ReadProvisioningTemplate.cs b/Commands/Provisioning/ReadProvisioningTemplate.cs index b79461ae5..abd8d7eb2 100644 --- a/Commands/Provisioning/ReadProvisioningTemplate.cs +++ b/Commands/Provisioning/ReadProvisioningTemplate.cs @@ -3,6 +3,7 @@ using OfficeDevPnP.Core.Framework.Provisioning.Providers; using OfficeDevPnP.Core.Framework.Provisioning.Providers.Xml; using SharePointPnP.PowerShell.CmdletHelpAttributes; +using SharePointPnP.PowerShell.Commands.Utilities; using System; using System.IO; using System.Management.Automation; @@ -54,7 +55,7 @@ internal static ProvisioningTemplate LoadProvisioningTemplateFromFile(string tem // Load the provisioning template file Stream stream = fileConnector.GetFileStream(templateFileName); - var isOpenOfficeFile = ApplyProvisioningTemplate.IsOpenOfficeFile(stream); + var isOpenOfficeFile = FileUtilities.IsOpenOfficeFile(stream); XMLTemplateProvider provider; if (isOpenOfficeFile) diff --git a/Commands/Provisioning/SaveProvisioningTemplate.cs b/Commands/Provisioning/SaveProvisioningTemplate.cs index 5fd1f6f24..7e5eba290 100644 --- a/Commands/Provisioning/SaveProvisioningTemplate.cs +++ b/Commands/Provisioning/SaveProvisioningTemplate.cs @@ -14,7 +14,7 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning { [Cmdlet(VerbsData.Save, "PnPProvisioningTemplate")] - [CmdletHelp("Saves a PnP file to the file systems", + [CmdletHelp("Saves a PnP file to the file system", Category = CmdletHelpCategory.Provisioning)] [CmdletExample( Code = @"PS:> Save-PnPProvisioningTemplate -InputInstance $template -Out .\template.pnp", diff --git a/Commands/Provisioning/SetProvisioningTemplateMetadata.cs b/Commands/Provisioning/SetProvisioningTemplateMetadata.cs index 6806df4e9..5e4508e9e 100644 --- a/Commands/Provisioning/SetProvisioningTemplateMetadata.cs +++ b/Commands/Provisioning/SetProvisioningTemplateMetadata.cs @@ -8,6 +8,7 @@ using OfficeDevPnP.Core.Framework.Provisioning.Connectors; using System.Collections; using OfficeDevPnP.Core.Framework.Provisioning.Providers; +using SharePointPnP.PowerShell.Commands.Utilities; namespace SharePointPnP.PowerShell.Commands.Provisioning { @@ -86,7 +87,7 @@ protected override void ExecuteCmdlet() XMLTemplateProvider provider; ProvisioningTemplate provisioningTemplate; Stream stream = fileConnector.GetFileStream(templateFileName); - var isOpenOfficeFile = ApplyProvisioningTemplate.IsOpenOfficeFile(stream); + var isOpenOfficeFile = FileUtilities.IsOpenOfficeFile(stream); if (isOpenOfficeFile) { provider = new XMLOpenXMLTemplateProvider(new OpenXMLConnector(templateFileName, fileConnector)); diff --git a/Commands/SharePointPnP.PowerShell.Commands.csproj b/Commands/SharePointPnP.PowerShell.Commands.csproj index 8faa84282..eefdd3b58 100644 --- a/Commands/SharePointPnP.PowerShell.Commands.csproj +++ b/Commands/SharePointPnP.PowerShell.Commands.csproj @@ -658,6 +658,7 @@ + diff --git a/Commands/Utilities/FileUtilities.cs b/Commands/Utilities/FileUtilities.cs new file mode 100644 index 000000000..98db23b63 --- /dev/null +++ b/Commands/Utilities/FileUtilities.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SharePointPnP.PowerShell.Commands.Utilities +{ + public static class FileUtilities + { + internal static bool IsOpenOfficeFile(Stream stream) + { + bool istrue = false; + // SIG 50 4B 03 04 14 00 + + byte[] bytes = new byte[6]; + stream.Read(bytes, 0, 6); + var signature = string.Empty; + foreach (var b in bytes) + { + signature += b.ToString("X2"); + } + if (signature == "504B03041400") + { + istrue = true; + } + return istrue; + } + } +} From d6203808b12ef6fec23fec19f28f9dbafea79f0e Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Mon, 17 Sep 2018 18:25:32 +0200 Subject: [PATCH 03/19] fixed typo --- Commands/Provisioning/ReadProvisioningTemplate.cs | 4 ++-- Commands/Provisioning/SaveProvisioningTemplate.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Commands/Provisioning/ReadProvisioningTemplate.cs b/Commands/Provisioning/ReadProvisioningTemplate.cs index abd8d7eb2..8c759e2aa 100644 --- a/Commands/Provisioning/ReadProvisioningTemplate.cs +++ b/Commands/Provisioning/ReadProvisioningTemplate.cs @@ -17,11 +17,11 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning Category = CmdletHelpCategory.Provisioning)] [CmdletExample( Code = @"PS:> Read-PnPProvisioningTemplate -Path template.pnp", - Remarks = "Loads a PnP file from the file systems", + Remarks = "Loads a PnP file from the file system", SortOrder = 1)] [CmdletExample( Code = @"PS:> Read-PnPProvisioningTemplate -Path template.pnp -TemplateProviderExtensions $extensions", - Remarks = "Loads a PnP file from the file systems using some custom template provider extenions while loading the file.", + Remarks = "Loads a PnP file from the file system using some custom template provider extenions while loading the file.", SortOrder = 2)] public class ReadProvisioningTemplate : PSCmdlet { diff --git a/Commands/Provisioning/SaveProvisioningTemplate.cs b/Commands/Provisioning/SaveProvisioningTemplate.cs index 7e5eba290..035941840 100644 --- a/Commands/Provisioning/SaveProvisioningTemplate.cs +++ b/Commands/Provisioning/SaveProvisioningTemplate.cs @@ -14,11 +14,11 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning { [Cmdlet(VerbsData.Save, "PnPProvisioningTemplate")] - [CmdletHelp("Saves a PnP file to the file system", + [CmdletHelp("Saves a PnP provisioning tempalte to the file system", Category = CmdletHelpCategory.Provisioning)] [CmdletExample( Code = @"PS:> Save-PnPProvisioningTemplate -InputInstance $template -Out .\template.pnp", - Remarks = "Saves a PnP file to the file systems", + Remarks = "Saves a PnP provisioning template to the file system as a PnP file.", SortOrder = 1)] public class SaveProvisioningTemplate : PSCmdlet { From 723bb23eb769b3975f173757ba5b24b82b34b094 Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Mon, 17 Sep 2018 18:26:01 +0200 Subject: [PATCH 04/19] fixed property type --- Commands/Provisioning/NewProvisioningTeamNoGroupSubSite.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Commands/Provisioning/NewProvisioningTeamNoGroupSubSite.cs b/Commands/Provisioning/NewProvisioningTeamNoGroupSubSite.cs index 5a3e4eb1d..19ed1e442 100644 --- a/Commands/Provisioning/NewProvisioningTeamNoGroupSubSite.cs +++ b/Commands/Provisioning/NewProvisioningTeamNoGroupSubSite.cs @@ -45,7 +45,7 @@ protected override void ProcessRecord() var site = new TeamNoGroupSubSite() { Url = Url, - Language = Language.ToString(), + Language = (int)Language, QuickLaunchEnabled = !QuickLaunchDisabled.IsPresent, UseSamePermissionsAsParentSite = !UseDifferentPermissionsFromParentSite.IsPresent, TimeZoneId = (int)TimeZoneId, From 41c0a15f6e8bdec69bd1f90ccf25e0563b520717 Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Mon, 17 Sep 2018 18:26:21 +0200 Subject: [PATCH 05/19] added functionality to read and save provisioning hierarchies --- .../Provisioning/NewProvisioningTeamSite.cs | 6 +- .../Provisioning/ReadProvisioningHierarchy.cs | 68 ++++++++++++++ .../Provisioning/SaveProvisioningHierarchy.cs | 94 +++++++++++++++++++ .../SharePointPnP.PowerShell.Commands.csproj | 2 + 4 files changed, 167 insertions(+), 3 deletions(-) create mode 100644 Commands/Provisioning/ReadProvisioningHierarchy.cs create mode 100644 Commands/Provisioning/SaveProvisioningHierarchy.cs diff --git a/Commands/Provisioning/NewProvisioningTeamSite.cs b/Commands/Provisioning/NewProvisioningTeamSite.cs index d0d1b1081..558424982 100644 --- a/Commands/Provisioning/NewProvisioningTeamSite.cs +++ b/Commands/Provisioning/NewProvisioningTeamSite.cs @@ -22,10 +22,10 @@ public class NewProvisioningTeamSite : PSCmdlet public string Title; [Parameter(Mandatory = false)] - public string Description; + public string Description = ""; [Parameter(Mandatory = false)] - public string DisplayName; + public string DisplayName = ""; [Parameter(Mandatory = false)] public string Classification; @@ -54,7 +54,7 @@ protected override void ProcessRecord() if (TemplateIds != null) { site.Templates.AddRange(TemplateIds.ToList()); - } + } WriteObject(site); } } diff --git a/Commands/Provisioning/ReadProvisioningHierarchy.cs b/Commands/Provisioning/ReadProvisioningHierarchy.cs new file mode 100644 index 000000000..2d7b30868 --- /dev/null +++ b/Commands/Provisioning/ReadProvisioningHierarchy.cs @@ -0,0 +1,68 @@ +using OfficeDevPnP.Core.Framework.Provisioning.Connectors; +using OfficeDevPnP.Core.Framework.Provisioning.Model; +using OfficeDevPnP.Core.Framework.Provisioning.Providers; +using OfficeDevPnP.Core.Framework.Provisioning.Providers.Xml; +using SharePointPnP.PowerShell.CmdletHelpAttributes; +using SharePointPnP.PowerShell.Commands.Utilities; +using System; +using System.IO; +using System.Management.Automation; + +namespace SharePointPnP.PowerShell.Commands.Provisioning +{ + + [Cmdlet(VerbsCommunications.Read, "PnPProvisioningHierarchy")] + [CmdletHelp("Loads/Reads a PnP provisioning hierarchy from the file system and returns an in-memory instance of this template.", + Category = CmdletHelpCategory.Provisioning)] + [CmdletExample( + Code = @"PS:> Read-PnPProvisioningHierarchy -Path hierarchy.pnp", + Remarks = "Reads a PnP provisioning hierarchy file from the file system and returns an in-memory instance", + SortOrder = 1)] + public class ReadProvisioningHierarchy : PSCmdlet + { + [Parameter(Mandatory = true, Position = 0, HelpMessage = "Filename to read from, optionally including full path.")] + public string Path; + + [Parameter(Mandatory = false, HelpMessage = "Allows you to specify ITemplateProviderExtension to execute while loading the template.")] + public ITemplateProviderExtension[] TemplateProviderExtensions; + + protected override void ProcessRecord() + { + if (!System.IO.Path.IsPathRooted(Path)) + { + Path = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, Path); + } + WriteObject(LoadProvisioningHierarchyFromFile(Path, TemplateProviderExtensions)); + } + + internal static ProvisioningHierarchy LoadProvisioningHierarchyFromFile(string templatePath, ITemplateProviderExtension[] templateProviderExtensions) + { + // Prepare the File Connector + string templateFileName = System.IO.Path.GetFileName(templatePath); + + // Prepare the template path + var fileInfo = new FileInfo(templatePath); + FileConnectorBase fileConnector = new FileSystemConnector(fileInfo.DirectoryName, ""); + + // Load the provisioning template file + Stream stream = fileConnector.GetFileStream(templateFileName); + var isOpenOfficeFile = FileUtilities.IsOpenOfficeFile(stream); + + XMLTemplateProvider provider; + if (isOpenOfficeFile) + { + provider = new XMLOpenXMLTemplateProvider(new OpenXMLConnector(templateFileName, fileConnector)); + templateFileName = templateFileName.Substring(0, templateFileName.LastIndexOf(".", StringComparison.Ordinal)) + ".xml"; + } + else + { + provider = new XMLFileSystemTemplateProvider(fileConnector.Parameters[FileConnectorBase.CONNECTIONSTRING] + "", ""); + } + + ProvisioningHierarchy provisioningHierarchy = provider.GetHierarchy(templateFileName); + + // Return the result + return provisioningHierarchy; + } + } +} diff --git a/Commands/Provisioning/SaveProvisioningHierarchy.cs b/Commands/Provisioning/SaveProvisioningHierarchy.cs new file mode 100644 index 000000000..b8f6acff2 --- /dev/null +++ b/Commands/Provisioning/SaveProvisioningHierarchy.cs @@ -0,0 +1,94 @@ +using OfficeDevPnP.Core.Framework.Provisioning.Connectors; +using OfficeDevPnP.Core.Framework.Provisioning.Model; +using OfficeDevPnP.Core.Framework.Provisioning.Providers; +using OfficeDevPnP.Core.Framework.Provisioning.Providers.Xml; +using SharePointPnP.PowerShell.CmdletHelpAttributes; +using System; +using System.IO; +using System.Management.Automation; + +namespace SharePointPnP.PowerShell.Commands.Provisioning +{ + [Cmdlet(VerbsData.Save, "PnPProvisioningHierarchy")] + [CmdletHelp("Saves a PnP provisioning hierarchy to the file system", + Category = CmdletHelpCategory.Provisioning)] + [CmdletExample( + Code = @"PS:> Save-PnPProvisioningHierarchy -Hierarchy $hierarchy -Out .\hierarchy.pnp", + Remarks = "Saves a PnP provisioning hiearchy to the file system", + SortOrder = 1)] + public class SaveProvisioningHierarchy : PSCmdlet + { + [Parameter(Mandatory = true, HelpMessage = "Allows you to provide an in-memory instance of the ProvisioningHierarchy type of the PnP Core Component. When using this parameter, the -Out parameter refers to the path for saving the template and storing any supporting file for the template.")] + public ProvisioningHierarchy Hierarchy; + + [Parameter(Mandatory = true, Position = 0, HelpMessage = "Filename to write to, optionally including full path.")] + public string Out; + + [Parameter(Mandatory = false, HelpMessage = "Specifying the Force parameter will skip the confirmation question.")] + public SwitchParameter Force; + + //[Parameter(Mandatory = false, HelpMessage = "Allows you to specify the ITemplateProviderExtension to execute while saving a template.")] + //public ITemplateProviderExtension[] TemplateProviderExtensions; + + protected override void ProcessRecord() + { + // Determine the output file name and path + string outFileName = Path.GetFileName(Out); + + if (!Path.IsPathRooted(Out)) + { + Out = Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, Out); + } + + bool proceed = false; + + if (System.IO.File.Exists(Out)) + { + if (Force || ShouldContinue(string.Format(Properties.Resources.File0ExistsOverwrite, Out), + Properties.Resources.Confirm)) + { + proceed = true; + } + } + else + { + proceed = true; + } + + string outPath = new FileInfo(Out).DirectoryName; + + // Determine if it is an .XML or a .PNP file + var extension = ""; + if (proceed && outFileName != null) + { + if (outFileName.IndexOf(".", StringComparison.Ordinal) > -1) + { + extension = outFileName.Substring(outFileName.LastIndexOf(".", StringComparison.Ordinal)).ToLower(); + } + else + { + extension = ".pnp"; + } + } + + var fileSystemConnector = new FileSystemConnector(outPath, ""); + + ITemplateFormatter formatter = XMLPnPSchemaFormatter.LatestFormatter; + + if (extension == ".pnp") + { + // var connector = new OpenXMLConnector(Out, fileSystemConnector); + XMLTemplateProvider provider = new XMLOpenXMLTemplateProvider( + Out, fileSystemConnector); + var templateFileName = outFileName.Substring(0, outFileName.LastIndexOf(".", StringComparison.Ordinal)) + ".xml"; + provider.SaveAs(Hierarchy, templateFileName); + //connector.Commit(); + } + else + { + XMLTemplateProvider provider = new XMLFileSystemTemplateProvider(outPath, ""); + provider.SaveAs(Hierarchy, Out); + } + } + } +} diff --git a/Commands/SharePointPnP.PowerShell.Commands.csproj b/Commands/SharePointPnP.PowerShell.Commands.csproj index eefdd3b58..2625d8663 100644 --- a/Commands/SharePointPnP.PowerShell.Commands.csproj +++ b/Commands/SharePointPnP.PowerShell.Commands.csproj @@ -500,6 +500,8 @@ + + From 7e1ce12b0e929d6f5db6e3690aa497e160fcf000 Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Tue, 18 Sep 2018 17:32:33 +0200 Subject: [PATCH 06/19] market cmdlets as only avialable for SPO --- .../Provisioning/AddProvisioningSequence.cs | 11 +++-- Commands/Provisioning/AddProvisioningSite.cs | 6 ++- .../Provisioning/AddProvisioningSubSite.cs | 10 +++-- .../Provisioning/AddProvisioningTemplate.cs | 6 ++- .../ApplyProvisioningHierarchy.cs | 40 +++++++++++++++---- .../NewProvisioningCommunicationSite.cs | 6 ++- .../Provisioning/NewProvisioningHierarchy.cs | 6 ++- .../Provisioning/NewProvisioningSequence.cs | 6 ++- .../NewProvisioningTeamNoGroupSite.cs | 6 ++- .../NewProvisioningTeamNoGroupSubSite.cs | 6 ++- .../Provisioning/NewProvisioningTeamSite.cs | 6 ++- .../Provisioning/ReadProvisioningHierarchy.cs | 6 ++- .../Provisioning/SaveProvisioningHierarchy.cs | 6 ++- 13 files changed, 86 insertions(+), 35 deletions(-) diff --git a/Commands/Provisioning/AddProvisioningSequence.cs b/Commands/Provisioning/AddProvisioningSequence.cs index a0342145d..2ed6fabdf 100644 --- a/Commands/Provisioning/AddProvisioningSequence.cs +++ b/Commands/Provisioning/AddProvisioningSequence.cs @@ -1,4 +1,5 @@ -using OfficeDevPnP.Core.Framework.Provisioning.Model; +#if !ONPREMISES +using OfficeDevPnP.Core.Framework.Provisioning.Model; using SharePointPnP.PowerShell.CmdletHelpAttributes; using System; using System.Linq; @@ -8,7 +9,7 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning { [Cmdlet(VerbsCommon.Add, "PnPProvisioningSequence", SupportsShouldProcess = true)] [CmdletHelp("Adds a provisioning sequence object to a provisioning hierarchy", - Category = CmdletHelpCategory.Provisioning)] + Category = CmdletHelpCategory.Provisioning, SupportedPlatform = CmdletSupportedPlatform.Online)] [CmdletExample( Code = @"PS:> Add-PnPProvisioningSequence -Hierarchy $myhierarchy -Sequence $mysequence", Remarks = "Adds an existing sequence object to an existing hierarchy object", @@ -24,17 +25,19 @@ public class AddProvisioningSequence : PSCmdlet [Parameter(Mandatory = true, HelpMessage = "Optional Id of the sequence", ParameterSetName = ParameterAttribute.AllParameterSets, ValueFromPipeline = true)] public ProvisioningSequence Sequence; - + protected override void ProcessRecord() { if (Hierarchy.Sequences.FirstOrDefault(s => s.ID == Sequence.ID) == null) { Hierarchy.Sequences.Add(Sequence); WriteObject(Hierarchy); - } else + } + else { WriteError(new ErrorRecord(new Exception($"Sequence with ID {Sequence.ID} already exists in hierarchy"), "DUPLICATESEQUENCEID", ErrorCategory.InvalidData, Sequence)); } } } } +#endif \ No newline at end of file diff --git a/Commands/Provisioning/AddProvisioningSite.cs b/Commands/Provisioning/AddProvisioningSite.cs index 472501c23..cef2a936d 100644 --- a/Commands/Provisioning/AddProvisioningSite.cs +++ b/Commands/Provisioning/AddProvisioningSite.cs @@ -1,4 +1,5 @@ -using OfficeDevPnP.Core.Framework.Provisioning.Model; +#if !ONPREMISES +using OfficeDevPnP.Core.Framework.Provisioning.Model; using SharePointPnP.PowerShell.CmdletHelpAttributes; using SharePointPnP.PowerShell.Commands.Base.PipeBinds; using System.Management.Automation; @@ -7,7 +8,7 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning { [Cmdlet(VerbsCommon.Add, "PnPProvisioningSite", SupportsShouldProcess = true)] [CmdletHelp("Adds a provisioning sequence object to a provisioning hierarchy", - Category = CmdletHelpCategory.Provisioning)] + Category = CmdletHelpCategory.Provisioning, SupportedPlatform = CmdletSupportedPlatform.Online)] [CmdletExample( Code = @"PS:> Add-PnPProvisioningSequence -Hierarchy $myhierarchy -Sequence $mysequence", Remarks = "Adds an existing sequence object to an existing hierarchy object", @@ -31,3 +32,4 @@ protected override void ProcessRecord() } } } +#endif \ No newline at end of file diff --git a/Commands/Provisioning/AddProvisioningSubSite.cs b/Commands/Provisioning/AddProvisioningSubSite.cs index 75bd155a4..524a88728 100644 --- a/Commands/Provisioning/AddProvisioningSubSite.cs +++ b/Commands/Provisioning/AddProvisioningSubSite.cs @@ -1,6 +1,6 @@ -using OfficeDevPnP.Core.Framework.Provisioning.Model; +#if !ONPREMISES +using OfficeDevPnP.Core.Framework.Provisioning.Model; using SharePointPnP.PowerShell.CmdletHelpAttributes; -using SharePointPnP.PowerShell.Commands.Base.PipeBinds; using System; using System.Linq; using System.Management.Automation; @@ -9,7 +9,7 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning { [Cmdlet(VerbsCommon.Add, "PnPProvisioningSubSite", SupportsShouldProcess = true)] [CmdletHelp("Adds a provisioning sequence object to a provisioning site object", - Category = CmdletHelpCategory.Provisioning)] + Category = CmdletHelpCategory.Provisioning, SupportedPlatform = CmdletSupportedPlatform.Online)] [CmdletExample( Code = @"PS:> Add-PnPProvisioningSequence -Hierarchy $myhierarchy -Sequence $mysequence", Remarks = "Adds an existing sequence object to an existing hierarchy object", @@ -31,10 +31,12 @@ protected override void ProcessRecord() if (Site.Sites.Cast().FirstOrDefault(s => s.Url == SubSite.Url) == null) { Site.Sites.Add(SubSite); - } else + } + else { WriteError(new ErrorRecord(new Exception($"Site with URL {SubSite.Url} already exists in sequence"), "DUPLICATEURL", ErrorCategory.InvalidData, SubSite)); } } } } +#endif \ No newline at end of file diff --git a/Commands/Provisioning/AddProvisioningTemplate.cs b/Commands/Provisioning/AddProvisioningTemplate.cs index 21c5e773d..215ff1a1c 100644 --- a/Commands/Provisioning/AddProvisioningTemplate.cs +++ b/Commands/Provisioning/AddProvisioningTemplate.cs @@ -1,4 +1,5 @@ -using OfficeDevPnP.Core.Framework.Provisioning.Model; +#if !ONPREMISES +using OfficeDevPnP.Core.Framework.Provisioning.Model; using SharePointPnP.PowerShell.CmdletHelpAttributes; using SharePointPnP.PowerShell.Commands.Base.PipeBinds; using System; @@ -9,7 +10,7 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning { [Cmdlet(VerbsCommon.Add, "PnPProvisioningTemplate", SupportsShouldProcess = true)] [CmdletHelp("Adds a provisioning template object to a provisioning hierarchy", - Category = CmdletHelpCategory.Provisioning)] + Category = CmdletHelpCategory.Provisioning, SupportedPlatform = CmdletSupportedPlatform.Online)] [CmdletExample( Code = @"PS:> Add-PnPProvisioningTemplate -Hierarchy $myhierarchy -Template $mytemplate", Remarks = "Adds an existing sequence object to an existing hierarchy object", @@ -33,3 +34,4 @@ protected override void ProcessRecord() } } } +#endif \ No newline at end of file diff --git a/Commands/Provisioning/ApplyProvisioningHierarchy.cs b/Commands/Provisioning/ApplyProvisioningHierarchy.cs index 369ea1dd1..0d63c9b50 100644 --- a/Commands/Provisioning/ApplyProvisioningHierarchy.cs +++ b/Commands/Provisioning/ApplyProvisioningHierarchy.cs @@ -1,4 +1,5 @@ -using Microsoft.SharePoint.Client; +#if !ONPREMISES +using Microsoft.SharePoint.Client; using OfficeDevPnP.Core.Framework.Provisioning.Connectors; using OfficeDevPnP.Core.Framework.Provisioning.Model; using OfficeDevPnP.Core.Framework.Provisioning.ObjectHandlers; @@ -8,6 +9,7 @@ using SharePointPnP.PowerShell.Commands.Base; using SharePointPnP.PowerShell.Commands.Utilities; using System; +using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; @@ -17,15 +19,21 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning { [Cmdlet("Apply", "PnPProvisioningHierarchy", SupportsShouldProcess = true)] [CmdletHelp("Adds a provisioning sequence object to a provisioning site object", - Category = CmdletHelpCategory.Provisioning)] + Category = CmdletHelpCategory.Provisioning, SupportedPlatform = CmdletSupportedPlatform.Online)] [CmdletExample( - Code = @"PS:> Add-PnPProvisioningSequence -Hierarchy $myhierarchy -Sequence $mysequence", - Remarks = "Adds an existing sequence object to an existing hierarchy object", + Code = @"PS:> Apply-PnPProvisioningHierarchy -Path myfile.pnp", + Remarks = "Will read the provisioning hierarchy from the filesystem and will apply the sequences in the hierarchy", SortOrder = 1)] [CmdletExample( - Code = @"PS:> New-PnPProvisioningSequence -Id ""MySequence"" | Add-PnPProvisioningSequence -Hierarchy $hierarchy", - Remarks = "Creates a new instance of a provisioning sequence object and sets the Id to the value specified, then the sequence is added to an existing hierarchy object", - SortOrder = 2)] + Code = @"PS:> Apply-PnPProvisioningHierarchy -Path myfile.pnp -SequenceId ""mysequence""", + Remarks = "Will read the provisioning hierarchy from the filesystem and will apply the specified sequence in the hierarchy", + SortOrder = 1)] + [CmdletExample( + Code = @"PS:> Apply-PnPProvisioningHierarchy -Path myfile.pnp -Parameters @{""ListTitle""=""Projects"";""parameter2""=""a second value""}", + Remarks = @"Applies a provisioning hierarchy template to the current tenant. It will populate the parameter in the template the values as specified and in the template you can refer to those values with the {parameter:} token. + +For instance with the example above, specifying {parameter:ListTitle} in your template will translate to 'Projects' when applying the template. These tokens can be used in most string values in a template.", + SortOrder = 3)] public class ApplyProvisioningHierarchy : PnPAdminCmdlet { private const string ParameterSet_PATH = "By Path"; @@ -58,6 +66,9 @@ public class ApplyProvisioningHierarchy : PnPAdminCmdlet [Parameter(Mandatory = false, HelpMessage = "Allows you to specify ITemplateProviderExtension to execute while applying a template.", ParameterSetName = ParameterAttribute.AllParameterSets)] public ITemplateProviderExtension[] TemplateProviderExtensions; + [Parameter(Mandatory = false, HelpMessage = "Allows you to specify parameters that can be referred to in the hierarchy by means of the {parameter:} token. See examples on how to use this parameter.", ParameterSetName = ParameterAttribute.AllParameterSets)] + public Hashtable Parameters; + [Parameter(Mandatory = false, HelpMessage = "Specify this parameter if you want to overwrite and/or create properties that are known to be system entries (starting with vti_, dlc_, etc.)", ParameterSetName = ParameterAttribute.AllParameterSets)] public SwitchParameter OverwriteSystemPropertyBagValues; @@ -216,6 +227,20 @@ protected override void ExecuteCmdlet() break; } } + if (Parameters != null) + { + foreach (var parameter in Parameters.Keys) + { + if (hierarchyToApply.Parameters.ContainsKey(parameter.ToString())) + { + hierarchyToApply.Parameters[parameter.ToString()] = Parameters[parameter].ToString(); + } + else + { + hierarchyToApply.Parameters.Add(parameter.ToString(), Parameters[parameter].ToString()); + } + } + } if (!string.IsNullOrEmpty(SequenceId)) { Tenant.ApplyProvisionHierarchy(hierarchyToApply, SequenceId, applyingInformation); @@ -332,3 +357,4 @@ private ProvisioningHierarchy GetHierarchy() } } } +#endif \ No newline at end of file diff --git a/Commands/Provisioning/NewProvisioningCommunicationSite.cs b/Commands/Provisioning/NewProvisioningCommunicationSite.cs index 78fe577d0..26d811288 100644 --- a/Commands/Provisioning/NewProvisioningCommunicationSite.cs +++ b/Commands/Provisioning/NewProvisioningCommunicationSite.cs @@ -1,4 +1,5 @@ -using OfficeDevPnP.Core.Framework.Provisioning.Model; +#if !ONPREMISES +using OfficeDevPnP.Core.Framework.Provisioning.Model; using SharePointPnP.PowerShell.CmdletHelpAttributes; using System.Linq; using System.Management.Automation; @@ -7,7 +8,7 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning { [Cmdlet(VerbsCommon.New, "PnPProvisioningCommunicationSite", SupportsShouldProcess = true)] [CmdletHelp("Creates a communication site object", - Category = CmdletHelpCategory.Provisioning)] + Category = CmdletHelpCategory.Provisioning, SupportedPlatform = CmdletSupportedPlatform.Online)] [CmdletExample( Code = @"PS:> $site = New-PnPProvisioningCommunicationSite -Url ""/sites/mycommunicationsite"" -Title ""My Team Site""", Remarks = "Creates a new communication site object with the specified variables", @@ -69,3 +70,4 @@ protected override void ProcessRecord() } } } +#endif \ No newline at end of file diff --git a/Commands/Provisioning/NewProvisioningHierarchy.cs b/Commands/Provisioning/NewProvisioningHierarchy.cs index 4a566ff04..5094b4e04 100644 --- a/Commands/Provisioning/NewProvisioningHierarchy.cs +++ b/Commands/Provisioning/NewProvisioningHierarchy.cs @@ -1,4 +1,5 @@ -using OfficeDevPnP.Core.Framework.Provisioning.Model; +#if !ONPREMISES +using OfficeDevPnP.Core.Framework.Provisioning.Model; using SharePointPnP.PowerShell.CmdletHelpAttributes; using System.Management.Automation; @@ -6,7 +7,7 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning { [Cmdlet(VerbsCommon.New, "PnPProvisioningHierarchy", SupportsShouldProcess = true)] [CmdletHelp("Creates a new provisioning hierarchy object", - Category = CmdletHelpCategory.Provisioning)] + Category = CmdletHelpCategory.Provisioning, SupportedPlatform = CmdletSupportedPlatform.Online)] [CmdletExample( Code = @"PS:> $hierarchy = New-PnPProvisioningHierarchy", Remarks = "Creates a new instance of a provisioning hierarchy object.", @@ -36,3 +37,4 @@ protected override void ProcessRecord() } } } +#endif \ No newline at end of file diff --git a/Commands/Provisioning/NewProvisioningSequence.cs b/Commands/Provisioning/NewProvisioningSequence.cs index f5f090cfe..649aa803a 100644 --- a/Commands/Provisioning/NewProvisioningSequence.cs +++ b/Commands/Provisioning/NewProvisioningSequence.cs @@ -1,4 +1,5 @@ -using OfficeDevPnP.Core.Framework.Provisioning.Model; +#if !ONPREMISES +using OfficeDevPnP.Core.Framework.Provisioning.Model; using SharePointPnP.PowerShell.CmdletHelpAttributes; using System; using System.Management.Automation; @@ -7,7 +8,7 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning { [Cmdlet(VerbsCommon.New, "PnPProvisioningSequence", SupportsShouldProcess = true)] [CmdletHelp("Creates a new provisioning sequence object", - Category = CmdletHelpCategory.Provisioning)] + Category = CmdletHelpCategory.Provisioning, SupportedPlatform = CmdletSupportedPlatform.Online)] [CmdletExample( Code = @"PS:> $sequence = New-PnPProvisioningSequence", Remarks = "Creates a new instance of a provisioning sequence object.", @@ -34,3 +35,4 @@ protected override void ProcessRecord() } } } +#endif \ No newline at end of file diff --git a/Commands/Provisioning/NewProvisioningTeamNoGroupSite.cs b/Commands/Provisioning/NewProvisioningTeamNoGroupSite.cs index 6a935e4c1..759873a4b 100644 --- a/Commands/Provisioning/NewProvisioningTeamNoGroupSite.cs +++ b/Commands/Provisioning/NewProvisioningTeamNoGroupSite.cs @@ -1,4 +1,5 @@ -using OfficeDevPnP.Core.Framework.Provisioning.Model; +#if !ONPREMISES +using OfficeDevPnP.Core.Framework.Provisioning.Model; using SharePointPnP.PowerShell.CmdletHelpAttributes; using System.Linq; using System.Management.Automation; @@ -7,7 +8,7 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning { [Cmdlet(VerbsCommon.New, "PnPProvisioningTeamNoGroupSite", SupportsShouldProcess = true)] [CmdletHelp("Creates a team site without an Office 365 group object", - Category = CmdletHelpCategory.Provisioning)] + Category = CmdletHelpCategory.Provisioning, SupportedPlatform = CmdletSupportedPlatform.Online)] [CmdletExample( Code = @"PS:> $site = New-PnPProvisioningTeamNoGroupSite -Alias ""MyTeamSite"" -Title ""My Team Site""", Remarks = "Creates a new team site object with the specified variables", @@ -58,3 +59,4 @@ protected override void ProcessRecord() } } } +#endif \ No newline at end of file diff --git a/Commands/Provisioning/NewProvisioningTeamNoGroupSubSite.cs b/Commands/Provisioning/NewProvisioningTeamNoGroupSubSite.cs index 19ed1e442..9918c1469 100644 --- a/Commands/Provisioning/NewProvisioningTeamNoGroupSubSite.cs +++ b/Commands/Provisioning/NewProvisioningTeamNoGroupSubSite.cs @@ -1,4 +1,5 @@ -using OfficeDevPnP.Core.Framework.Provisioning.Model; +#if !ONPREMISES +using OfficeDevPnP.Core.Framework.Provisioning.Model; using SharePointPnP.PowerShell.CmdletHelpAttributes; using System.Linq; using System.Management.Automation; @@ -7,7 +8,7 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning { [Cmdlet(VerbsCommon.New, "PnPProvisioningTeamNoGroupSubSite", SupportsShouldProcess = true)] [CmdletHelp("Creates a team site subsite with no Office 365 group object", - Category = CmdletHelpCategory.Provisioning)] + Category = CmdletHelpCategory.Provisioning, SupportedPlatform = CmdletSupportedPlatform.Online)] [CmdletExample( Code = @"PS:> $site = New-PnPProvisioningTeamNoGroupSubSite -Url ""MyTeamSubsite"" -Title ""My Team Site"" -TimeZoneId 4", Remarks = "Creates a new team site subsite object with the specified variables", @@ -60,3 +61,4 @@ protected override void ProcessRecord() } } } +#endif \ No newline at end of file diff --git a/Commands/Provisioning/NewProvisioningTeamSite.cs b/Commands/Provisioning/NewProvisioningTeamSite.cs index 558424982..467e56ff6 100644 --- a/Commands/Provisioning/NewProvisioningTeamSite.cs +++ b/Commands/Provisioning/NewProvisioningTeamSite.cs @@ -1,4 +1,5 @@ -using OfficeDevPnP.Core.Framework.Provisioning.Model; +#if !ONPREMISES +using OfficeDevPnP.Core.Framework.Provisioning.Model; using SharePointPnP.PowerShell.CmdletHelpAttributes; using System.Linq; using System.Management.Automation; @@ -7,7 +8,7 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning { [Cmdlet(VerbsCommon.New, "PnPProvisioningTeamSite", SupportsShouldProcess = true)] [CmdletHelp("Creates a team site object", - Category = CmdletHelpCategory.Provisioning)] + Category = CmdletHelpCategory.Provisioning, SupportedPlatform = CmdletSupportedPlatform.Online)] [CmdletExample( Code = @"PS:> $site = New-PnPProvisioningTeamSite -Alias ""MyTeamSite"" -Title ""My Team Site""", Remarks = "Creates a new team site object with the specified variables", @@ -59,3 +60,4 @@ protected override void ProcessRecord() } } } +#endif \ No newline at end of file diff --git a/Commands/Provisioning/ReadProvisioningHierarchy.cs b/Commands/Provisioning/ReadProvisioningHierarchy.cs index 2d7b30868..1b811ab5c 100644 --- a/Commands/Provisioning/ReadProvisioningHierarchy.cs +++ b/Commands/Provisioning/ReadProvisioningHierarchy.cs @@ -1,4 +1,5 @@ -using OfficeDevPnP.Core.Framework.Provisioning.Connectors; +#if !ONPREMISES +using OfficeDevPnP.Core.Framework.Provisioning.Connectors; using OfficeDevPnP.Core.Framework.Provisioning.Model; using OfficeDevPnP.Core.Framework.Provisioning.Providers; using OfficeDevPnP.Core.Framework.Provisioning.Providers.Xml; @@ -13,7 +14,7 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning [Cmdlet(VerbsCommunications.Read, "PnPProvisioningHierarchy")] [CmdletHelp("Loads/Reads a PnP provisioning hierarchy from the file system and returns an in-memory instance of this template.", - Category = CmdletHelpCategory.Provisioning)] + Category = CmdletHelpCategory.Provisioning, SupportedPlatform = CmdletSupportedPlatform.Online)] [CmdletExample( Code = @"PS:> Read-PnPProvisioningHierarchy -Path hierarchy.pnp", Remarks = "Reads a PnP provisioning hierarchy file from the file system and returns an in-memory instance", @@ -66,3 +67,4 @@ internal static ProvisioningHierarchy LoadProvisioningHierarchyFromFile(string t } } } +#endif \ No newline at end of file diff --git a/Commands/Provisioning/SaveProvisioningHierarchy.cs b/Commands/Provisioning/SaveProvisioningHierarchy.cs index b8f6acff2..3ff5e92e6 100644 --- a/Commands/Provisioning/SaveProvisioningHierarchy.cs +++ b/Commands/Provisioning/SaveProvisioningHierarchy.cs @@ -1,4 +1,5 @@ -using OfficeDevPnP.Core.Framework.Provisioning.Connectors; +#if !ONPREMISES +using OfficeDevPnP.Core.Framework.Provisioning.Connectors; using OfficeDevPnP.Core.Framework.Provisioning.Model; using OfficeDevPnP.Core.Framework.Provisioning.Providers; using OfficeDevPnP.Core.Framework.Provisioning.Providers.Xml; @@ -11,7 +12,7 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning { [Cmdlet(VerbsData.Save, "PnPProvisioningHierarchy")] [CmdletHelp("Saves a PnP provisioning hierarchy to the file system", - Category = CmdletHelpCategory.Provisioning)] + Category = CmdletHelpCategory.Provisioning, SupportedPlatform = CmdletSupportedPlatform.Online)] [CmdletExample( Code = @"PS:> Save-PnPProvisioningHierarchy -Hierarchy $hierarchy -Out .\hierarchy.pnp", Remarks = "Saves a PnP provisioning hiearchy to the file system", @@ -92,3 +93,4 @@ protected override void ProcessRecord() } } } +#endif \ No newline at end of file From d672ea6a84abacbb63ac9a39106a53a0348f47d0 Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Tue, 18 Sep 2018 23:56:57 +0200 Subject: [PATCH 07/19] Implemented support to save hierarchy to pnp file format --- .../ApplyProvisioningHierarchy.cs | 102 +----------------- .../Provisioning/ReadProvisioningHierarchy.cs | 9 +- .../Provisioning/SaveProvisioningHierarchy.cs | 60 ++++++++++- 3 files changed, 69 insertions(+), 102 deletions(-) diff --git a/Commands/Provisioning/ApplyProvisioningHierarchy.cs b/Commands/Provisioning/ApplyProvisioningHierarchy.cs index 0d63c9b50..41be05e31 100644 --- a/Commands/Provisioning/ApplyProvisioningHierarchy.cs +++ b/Commands/Provisioning/ApplyProvisioningHierarchy.cs @@ -4,10 +4,8 @@ using OfficeDevPnP.Core.Framework.Provisioning.Model; using OfficeDevPnP.Core.Framework.Provisioning.ObjectHandlers; using OfficeDevPnP.Core.Framework.Provisioning.Providers; -using OfficeDevPnP.Core.Framework.Provisioning.Providers.Xml; using SharePointPnP.PowerShell.CmdletHelpAttributes; using SharePointPnP.PowerShell.Commands.Base; -using SharePointPnP.PowerShell.Commands.Utilities; using System; using System.Collections; using System.Collections.Generic; @@ -192,7 +190,7 @@ protected override void ExecuteCmdlet() ProvisioningHierarchy hierarchyToApply = null; - switch(ParameterSetName) + switch (ParameterSetName) { case ParameterSet_PATH: { @@ -257,103 +255,11 @@ protected override void ExecuteCmdlet() private ProvisioningHierarchy GetHierarchy() { - ProvisioningHierarchy hierarchy = null; - FileConnectorBase fileConnector; - - bool templateFromFileSystem = !Path.ToLower().StartsWith("http"); - string templateFileName = System.IO.Path.GetFileName(Path); - if (templateFromFileSystem) - { - if (!System.IO.Path.IsPathRooted(Path)) - { - Path = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, Path); - } - if (!System.IO.File.Exists(Path)) - { - throw new FileNotFoundException($"File not found"); - } - if (!string.IsNullOrEmpty(ResourceFolder)) - { - if (!System.IO.Path.IsPathRooted(ResourceFolder)) - { - ResourceFolder = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, - ResourceFolder); - } - } - var fileInfo = new FileInfo(Path); - fileConnector = new FileSystemConnector(fileInfo.DirectoryName, ""); - } - else - { - Uri fileUri = new Uri(Path); - var webUrl = Microsoft.SharePoint.Client.Web.WebUrlFromFolderUrlDirect(ClientContext, fileUri); - var templateContext = ClientContext.Clone(webUrl.ToString()); - - var library = Path.ToLower().Replace(templateContext.Url.ToLower(), "").TrimStart('/'); - var idx = library.IndexOf("/", StringComparison.Ordinal); - library = library.Substring(0, idx); - - // This syntax creates a SharePoint connector regardless we have the -InputInstance argument or not - fileConnector = new SharePointConnector(templateContext, templateContext.Url, library); - } - - // If we don't have the -InputInstance parameter, we load the template from the source connector - - using (var stream = fileConnector.GetFileStream(templateFileName)) + if(!System.IO.Path.IsPathRooted(Path)) { - var isOpenOfficeFile = FileUtilities.IsOpenOfficeFile(stream); - XMLTemplateProvider provider; - if (isOpenOfficeFile) - { - provider = new XMLOpenXMLTemplateProvider(new OpenXMLConnector(templateFileName, fileConnector)); - templateFileName = templateFileName.Substring(0, templateFileName.LastIndexOf(".", StringComparison.Ordinal)) + ".xml"; - } - else - { - if (templateFromFileSystem) - { - provider = new XMLFileSystemTemplateProvider(Path, ""); - } - else - { - throw new NotSupportedException("Only .pnp package files are supported from a SharePoint library"); - } - } - - var formatter = XMLPnPSchemaFormatter.LatestFormatter; - formatter.Initialize(provider); - - var serializer = (IProvisioningHierarchyFormatter)formatter; - - hierarchy = serializer.ToProvisioningHierarchy(provider.Connector.GetFileStream(Path)); - - hierarchy.Connector = fileConnector; - if (hierarchy == null) - { - // If we don't have the template, raise an error and exit - WriteError(new ErrorRecord(new Exception("The -Path parameter targets an invalid repository or template object."), "WRONG_PATH", ErrorCategory.SyntaxError, null)); - return null; - } - - //if (isOpenOfficeFile) - //{ - // hierarchy.Connector = provider.Connector; - //} - //else - //{ - // if (ResourceFolder != null) - // { - // var fileSystemConnector = new FileSystemConnector(ResourceFolder, ""); - // provisioningTemplate.Connector = fileSystemConnector; - // } - // else - // { - // provisioningTemplate.Connector = provider.Connector; - // } - //} - - return hierarchy; + Path = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, Path); } + return ReadProvisioningHierarchy.LoadProvisioningHierarchyFromFile(Path, TemplateProviderExtensions); } } } diff --git a/Commands/Provisioning/ReadProvisioningHierarchy.cs b/Commands/Provisioning/ReadProvisioningHierarchy.cs index 1b811ab5c..719952591 100644 --- a/Commands/Provisioning/ReadProvisioningHierarchy.cs +++ b/Commands/Provisioning/ReadProvisioningHierarchy.cs @@ -46,8 +46,11 @@ internal static ProvisioningHierarchy LoadProvisioningHierarchyFromFile(string t FileConnectorBase fileConnector = new FileSystemConnector(fileInfo.DirectoryName, ""); // Load the provisioning template file - Stream stream = fileConnector.GetFileStream(templateFileName); - var isOpenOfficeFile = FileUtilities.IsOpenOfficeFile(stream); + var isOpenOfficeFile = false; + using (var stream = fileConnector.GetFileStream(templateFileName)) + { + isOpenOfficeFile = FileUtilities.IsOpenOfficeFile(stream); + } XMLTemplateProvider provider; if (isOpenOfficeFile) @@ -61,7 +64,7 @@ internal static ProvisioningHierarchy LoadProvisioningHierarchyFromFile(string t } ProvisioningHierarchy provisioningHierarchy = provider.GetHierarchy(templateFileName); - + provisioningHierarchy.Connector = provider.Connector; // Return the result return provisioningHierarchy; } diff --git a/Commands/Provisioning/SaveProvisioningHierarchy.cs b/Commands/Provisioning/SaveProvisioningHierarchy.cs index 3ff5e92e6..ba0c5f26c 100644 --- a/Commands/Provisioning/SaveProvisioningHierarchy.cs +++ b/Commands/Provisioning/SaveProvisioningHierarchy.cs @@ -6,6 +6,7 @@ using SharePointPnP.PowerShell.CmdletHelpAttributes; using System; using System.IO; +using System.Linq; using System.Management.Automation; namespace SharePointPnP.PowerShell.Commands.Provisioning @@ -48,6 +49,7 @@ protected override void ProcessRecord() if (Force || ShouldContinue(string.Format(Properties.Resources.File0ExistsOverwrite, Out), Properties.Resources.Confirm)) { + System.IO.File.Delete(Out); proceed = true; } } @@ -78,11 +80,15 @@ protected override void ProcessRecord() if (extension == ".pnp") { - // var connector = new OpenXMLConnector(Out, fileSystemConnector); + // var connector = new OpenXMLConnector(Out, fileSystemConnector); XMLTemplateProvider provider = new XMLOpenXMLTemplateProvider( Out, fileSystemConnector); var templateFileName = outFileName.Substring(0, outFileName.LastIndexOf(".", StringComparison.Ordinal)) + ".xml"; provider.SaveAs(Hierarchy, templateFileName); + + ProcessFiles(Out, fileSystemConnector, provider.Connector); + + //provider.SaveAs(Hierarchy, templateFileName); //connector.Commit(); } else @@ -91,6 +97,58 @@ protected override void ProcessRecord() provider.SaveAs(Hierarchy, Out); } } + + private void ProcessFiles(string templateFileName, FileConnectorBase fileSystemConnector, FileConnectorBase connector) + { + var hierarchy = ReadProvisioningHierarchy.LoadProvisioningHierarchyFromFile(templateFileName, null); + if (Hierarchy.Tenant?.AppCatalog != null) + { + foreach (var app in Hierarchy.Tenant.AppCatalog.Packages) + { + AddFile(app.Src, hierarchy, fileSystemConnector, connector); + } + } + if(Hierarchy.Tenant?.SiteScripts != null) + { + foreach(var siteScript in Hierarchy.Tenant.SiteScripts) + { + AddFile(siteScript.JsonFilePath, hierarchy, fileSystemConnector, connector); + } + } + if (Hierarchy.Localizations != null && Hierarchy.Localizations.Any()) + { + foreach (var location in Hierarchy.Localizations) + { + AddFile(location.ResourceFile, hierarchy, fileSystemConnector, connector); + } + } + foreach (var template in Hierarchy.Templates) + { + if (template.Files.Any()) + { + foreach (var file in template.Files) + { + AddFile(file.Src, hierarchy, fileSystemConnector, connector); + } + } + } + + } + + private void AddFile(string sourceName, ProvisioningHierarchy hierarchy, FileConnectorBase fileSystemConnector, FileConnectorBase connector) + { + using (var fs = fileSystemConnector.GetFileStream(sourceName)) + { + var fileName = sourceName.IndexOf("\\") > 0 ? sourceName.Substring(sourceName.LastIndexOf("\\") + 1) : sourceName; + var folderName = sourceName.IndexOf("\\") > 0 ? sourceName.Substring(0, sourceName.LastIndexOf("\\")) : ""; + hierarchy.Connector.SaveFileStream(fileName, folderName, fs); + + if (hierarchy.Connector is ICommitableFileConnector) + { + ((ICommitableFileConnector)hierarchy.Connector).Commit(); + } + } + } } } #endif \ No newline at end of file From 2ebf15141ce40f4f5d12575661cb1c2c79abe018 Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Wed, 19 Sep 2018 09:48:19 +0200 Subject: [PATCH 08/19] updated to include filename when saving --- Commands/Provisioning/ReadProvisioningHierarchy.cs | 10 ++++++++-- Commands/Provisioning/SaveProvisioningHierarchy.cs | 10 +++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Commands/Provisioning/ReadProvisioningHierarchy.cs b/Commands/Provisioning/ReadProvisioningHierarchy.cs index 719952591..7c9fb7bd1 100644 --- a/Commands/Provisioning/ReadProvisioningHierarchy.cs +++ b/Commands/Provisioning/ReadProvisioningHierarchy.cs @@ -11,7 +11,6 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning { - [Cmdlet(VerbsCommunications.Read, "PnPProvisioningHierarchy")] [CmdletHelp("Loads/Reads a PnP provisioning hierarchy from the file system and returns an in-memory instance of this template.", Category = CmdletHelpCategory.Provisioning, SupportedPlatform = CmdletSupportedPlatform.Online)] @@ -57,11 +56,18 @@ internal static ProvisioningHierarchy LoadProvisioningHierarchyFromFile(string t { provider = new XMLOpenXMLTemplateProvider(new OpenXMLConnector(templateFileName, fileConnector)); templateFileName = templateFileName.Substring(0, templateFileName.LastIndexOf(".", StringComparison.Ordinal)) + ".xml"; + + var hierarchy = (provider as XMLOpenXMLTemplateProvider).GetHierarchy(); + if (hierarchy != null) + { + hierarchy.Connector = provider.Connector; + return hierarchy; + } } else { provider = new XMLFileSystemTemplateProvider(fileConnector.Parameters[FileConnectorBase.CONNECTIONSTRING] + "", ""); - } + } ProvisioningHierarchy provisioningHierarchy = provider.GetHierarchy(templateFileName); provisioningHierarchy.Connector = provider.Connector; diff --git a/Commands/Provisioning/SaveProvisioningHierarchy.cs b/Commands/Provisioning/SaveProvisioningHierarchy.cs index ba0c5f26c..05d4ca43c 100644 --- a/Commands/Provisioning/SaveProvisioningHierarchy.cs +++ b/Commands/Provisioning/SaveProvisioningHierarchy.cs @@ -81,11 +81,11 @@ protected override void ProcessRecord() if (extension == ".pnp") { // var connector = new OpenXMLConnector(Out, fileSystemConnector); - XMLTemplateProvider provider = new XMLOpenXMLTemplateProvider( - Out, fileSystemConnector); var templateFileName = outFileName.Substring(0, outFileName.LastIndexOf(".", StringComparison.Ordinal)) + ".xml"; - provider.SaveAs(Hierarchy, templateFileName); + XMLTemplateProvider provider = new XMLOpenXMLTemplateProvider( + Out, fileSystemConnector, templateFileName: templateFileName); + provider.SaveAs(Hierarchy, templateFileName); ProcessFiles(Out, fileSystemConnector, provider.Connector); //provider.SaveAs(Hierarchy, templateFileName); @@ -108,9 +108,9 @@ private void ProcessFiles(string templateFileName, FileConnectorBase fileSystemC AddFile(app.Src, hierarchy, fileSystemConnector, connector); } } - if(Hierarchy.Tenant?.SiteScripts != null) + if (Hierarchy.Tenant?.SiteScripts != null) { - foreach(var siteScript in Hierarchy.Tenant.SiteScripts) + foreach (var siteScript in Hierarchy.Tenant.SiteScripts) { AddFile(siteScript.JsonFilePath, hierarchy, fileSystemConnector, connector); } From 3efc56a3b68aeba54b833269977391dfca6e7f59 Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Wed, 19 Sep 2018 10:25:12 +0200 Subject: [PATCH 09/19] updated changelog --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3b635561..7fd770288 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,20 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ## [3.2.1810.0] Unreleased ### Added +- Add-PnPProvisioningSequence +- Add-PnPProvisioningSite +- Add-PnPProvisioningSubSite +- Apply-PnPProvisioningHierarchy +- Get-PnPProvisioningSite +- New-PnPProvisioningHierarchy +- New-PnPProvisioningSequence +- New-PnPProvisioningCommunicationSite +- New-PnPProvisioningTeamNoGroupSite +- New-PnPProvisioningTeamNoGroupSubSite +- New-PnPProvisioningTeamSite +- Read-PnPProvisioningHierarchy +- Save-PnPProvisioningHierarchy +- Test-PnPProvisioningHierarchy ### Changed From d0090f0882390c00a8f83a8ab476af7788609332 Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Fri, 21 Sep 2018 11:23:10 +0200 Subject: [PATCH 10/19] updated examples --- Commands/Admin/GetHubSite.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Commands/Admin/GetHubSite.cs b/Commands/Admin/GetHubSite.cs index 45b103d45..edf2b32c8 100644 --- a/Commands/Admin/GetHubSite.cs +++ b/Commands/Admin/GetHubSite.cs @@ -13,8 +13,8 @@ namespace SharePointPnP.PowerShell.Commands.Admin [CmdletHelp(@"Retrieve all or a specific hubsite.", Category = CmdletHelpCategory.TenantAdmin, SupportedPlatform = CmdletSupportedPlatform.Online)] - [CmdletExample(Code = @"PS:> Get-PnPStorageEntity", Remarks = "Returns all site storage entities/farm properties", SortOrder = 1)] - [CmdletExample(Code = @"PS:> Get-PnPTenantSite -Key MyKey", Remarks = "Returns the storage entity/farm property with the given key.", SortOrder = 2)] + [CmdletExample(Code = @"PS:> Get-PnPHubSite", Remarks = "Returns all hubsite properties", SortOrder = 1)] + [CmdletExample(Code = @"PS:> Get-PnPHubSite -Identity https://contoso.sharepoint.com/sites/myhubsite", Remarks = "Returns the properties of the specified hubsite", SortOrder = 2)] public class GetHubSite : PnPAdminCmdlet { [Parameter(Position = 0, ValueFromPipeline = true)] From a5780e30d3406107e7e7455af390f2b8450ac322 Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Mon, 1 Oct 2018 08:15:48 +0200 Subject: [PATCH 11/19] added ability to set sitelogo of modern team site --- CHANGELOG.md | 2 +- Commands/Site/SetPnPSite.cs | 41 +++++++++++++++++++++++++++++++------ 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fd770288..9efc5fe48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Test-PnPProvisioningHierarchy ### Changed - +- Updated Set-PnPSite to allow for setting of logo on modern team site ### Deprecated ### Contributors diff --git a/Commands/Site/SetPnPSite.cs b/Commands/Site/SetPnPSite.cs index 94343afda..f4d2b5202 100644 --- a/Commands/Site/SetPnPSite.cs +++ b/Commands/Site/SetPnPSite.cs @@ -1,9 +1,7 @@ #if !ONPREMISES -using System; -using System.Linq.Expressions; -using System.Management.Automation; using Microsoft.SharePoint.Client; using SharePointPnP.PowerShell.CmdletHelpAttributes; +using System.Management.Automation; namespace SharePointPnP.PowerShell.Commands.Site { @@ -35,20 +33,51 @@ public class SetSite : PnPCmdlet [Parameter(Mandatory = false, HelpMessage = "Disables flows for this site")] public SwitchParameter DisableFlows; + [Parameter(Mandatory = false, HelpMessage = "Sets the logo")] + public string LogoFilePath; + protected override void ExecuteCmdlet() { + var executeQueryRequired = false; var site = ClientContext.Site; if (MyInvocation.BoundParameters.ContainsKey("Classification")) { site.Classification = Classification; + executeQueryRequired = true; } if (MyInvocation.BoundParameters.ContainsKey("DisableFlows")) { site.DisableFlows = DisableFlows; + executeQueryRequired = true; + } + if (MyInvocation.BoundParameters.ContainsKey("LogoFilePath")) + { + var webTemplate = ClientContext.Web.EnsureProperty(w => w.WebTemplate); + if (webTemplate == "GROUP") + { + if (System.IO.Path.IsPathRooted(LogoFilePath)) + { + LogoFilePath = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, LogoFilePath); + } + if (System.IO.File.Exists(LogoFilePath)) + { + var bytes = System.IO.File.ReadAllBytes(LogoFilePath); + var mimeType = System.Web.MimeMapping.GetMimeMapping(LogoFilePath); + var result = OfficeDevPnP.Core.Sites.SiteCollection.SetGroupImage(ClientContext, bytes, mimeType).GetAwaiter().GetResult(); + } + else + { + throw new System.Exception("Logo file does not exist"); + } + } else + { + throw new System.Exception("Not an Office365 group enabled site."); + } + } + if (executeQueryRequired) + { + ClientContext.ExecuteQueryRetry(); } - - - ClientContext.ExecuteQueryRetry(); } } } From d1616aaa600ead42f4e464dd877577e9bf133e35 Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Mon, 1 Oct 2018 08:27:38 +0200 Subject: [PATCH 12/19] updated Set-PnPSite --- CHANGELOG.md | 3 ++- Commands/Properties/AssemblyInfo.cs | 4 ++-- Commands/SharePointPnP.PowerShell.Commands.csproj | 2 +- Commands/Site/{SetPnPSite.cs => SetSite.cs} | 6 +++++- Commands/Web/SetWeb.cs | 6 +++--- 5 files changed, 13 insertions(+), 8 deletions(-) rename Commands/Site/{SetPnPSite.cs => SetSite.cs} (91%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9efc5fe48..231c58f70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Test-PnPProvisioningHierarchy ### Changed -- Updated Set-PnPSite to allow for setting of logo on modern team site +- Updated Set-PnPSite to allow for setting of a logo on modern team site + ### Deprecated ### Contributors diff --git a/Commands/Properties/AssemblyInfo.cs b/Commands/Properties/AssemblyInfo.cs index 97eed533c..727af4580 100644 --- a/Commands/Properties/AssemblyInfo.cs +++ b/Commands/Properties/AssemblyInfo.cs @@ -44,6 +44,6 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("3.1.1809.0")] -[assembly: AssemblyFileVersion("3.1.1809.0")] +[assembly: AssemblyVersion("3.2.1810.0")] +[assembly: AssemblyFileVersion("3.2.1810.0")] [assembly: InternalsVisibleTo("SharePointPnP.PowerShell.Tests")] \ No newline at end of file diff --git a/Commands/SharePointPnP.PowerShell.Commands.csproj b/Commands/SharePointPnP.PowerShell.Commands.csproj index 2625d8663..0362cae42 100644 --- a/Commands/SharePointPnP.PowerShell.Commands.csproj +++ b/Commands/SharePointPnP.PowerShell.Commands.csproj @@ -790,7 +790,7 @@ - + diff --git a/Commands/Site/SetPnPSite.cs b/Commands/Site/SetSite.cs similarity index 91% rename from Commands/Site/SetPnPSite.cs rename to Commands/Site/SetSite.cs index f4d2b5202..f6e7ea65e 100644 --- a/Commands/Site/SetPnPSite.cs +++ b/Commands/Site/SetSite.cs @@ -25,6 +25,10 @@ namespace SharePointPnP.PowerShell.Commands.Site Code = @"PS:> Set-PnPSite -DisableFlows:$false", Remarks = "Enables Flows for this site", SortOrder = 3)] + [CmdletExample( + Code = @"PS:> Set-PnPSite -SiteLogoPath c:\images\mylogo.png", + Remarks = "Sets the logo if the site is a modern team site", + SortOrder = 4)] public class SetSite : PnPCmdlet { [Parameter(Mandatory = false, HelpMessage = "The classification to set")] @@ -33,7 +37,7 @@ public class SetSite : PnPCmdlet [Parameter(Mandatory = false, HelpMessage = "Disables flows for this site")] public SwitchParameter DisableFlows; - [Parameter(Mandatory = false, HelpMessage = "Sets the logo")] + [Parameter(Mandatory = false, HelpMessage = "Sets the logo if the site is modern team site. If you want to set the logo for a classic site, use Set-PnPWeb -SiteLogoUrl")] public string LogoFilePath; protected override void ExecuteCmdlet() diff --git a/Commands/Web/SetWeb.cs b/Commands/Web/SetWeb.cs index 2eee363ee..929d42e39 100644 --- a/Commands/Web/SetWeb.cs +++ b/Commands/Web/SetWeb.cs @@ -1,6 +1,6 @@ -using System.Management.Automation; -using Microsoft.SharePoint.Client; +using Microsoft.SharePoint.Client; using SharePointPnP.PowerShell.CmdletHelpAttributes; +using System.Management.Automation; namespace SharePointPnP.PowerShell.Commands { @@ -9,7 +9,7 @@ namespace SharePointPnP.PowerShell.Commands Category = CmdletHelpCategory.Webs)] public class SetWeb : PnPWebCmdlet { - [Parameter(Mandatory = false)] + [Parameter(Mandatory = false, HelpMessage = "Sets the logo of the web to the current url. If you want to set the logo to a modern team site, use Set-PnPSite -SiteLogoPath")] public string SiteLogoUrl; [Parameter(Mandatory = false)] From d6ba4d41eec27857351b760e7dee94b525ab009a Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Mon, 1 Oct 2018 08:33:36 +0200 Subject: [PATCH 13/19] fixed check issue --- Commands/Site/SetSite.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Commands/Site/SetSite.cs b/Commands/Site/SetSite.cs index f6e7ea65e..68fdbbe5f 100644 --- a/Commands/Site/SetSite.cs +++ b/Commands/Site/SetSite.cs @@ -59,7 +59,7 @@ protected override void ExecuteCmdlet() var webTemplate = ClientContext.Web.EnsureProperty(w => w.WebTemplate); if (webTemplate == "GROUP") { - if (System.IO.Path.IsPathRooted(LogoFilePath)) + if (!System.IO.Path.IsPathRooted(LogoFilePath)) { LogoFilePath = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, LogoFilePath); } From 4b6e6ca45555f411d2a9b9efdacbc9ca7c39c77e Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Wed, 3 Oct 2018 19:41:17 +0200 Subject: [PATCH 14/19] Updated to support file ref in sitelogo attribute on websettings --- Commands/Provisioning/SaveProvisioningHierarchy.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Commands/Provisioning/SaveProvisioningHierarchy.cs b/Commands/Provisioning/SaveProvisioningHierarchy.cs index 05d4ca43c..944d32838 100644 --- a/Commands/Provisioning/SaveProvisioningHierarchy.cs +++ b/Commands/Provisioning/SaveProvisioningHierarchy.cs @@ -124,6 +124,13 @@ private void ProcessFiles(string templateFileName, FileConnectorBase fileSystemC } foreach (var template in Hierarchy.Templates) { + if(template.WebSettings != null && template.WebSettings.SiteLogo != null) + { + if (!template.WebSettings.SiteLogo.StartsWith("https://",StringComparison.InvariantCultureIgnoreCase) && !template.WebSettings.SiteLogo.StartsWith("{")) + { + AddFile(template.WebSettings.SiteLogo, hierarchy, fileSystemConnector, connector); + } + } if (template.Files.Any()) { foreach (var file in template.Files) From b0ce2622ae54d54c130f85cb3e4795399f338b87 Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Wed, 3 Oct 2018 19:50:27 +0200 Subject: [PATCH 15/19] updated cmdlet to save hierarchy to check for sitelogo file references --- Commands/Provisioning/SaveProvisioningHierarchy.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Commands/Provisioning/SaveProvisioningHierarchy.cs b/Commands/Provisioning/SaveProvisioningHierarchy.cs index 944d32838..2672f45b3 100644 --- a/Commands/Provisioning/SaveProvisioningHierarchy.cs +++ b/Commands/Provisioning/SaveProvisioningHierarchy.cs @@ -85,6 +85,7 @@ protected override void ProcessRecord() XMLTemplateProvider provider = new XMLOpenXMLTemplateProvider( Out, fileSystemConnector, templateFileName: templateFileName); + WriteObject("Processing template"); provider.SaveAs(Hierarchy, templateFileName); ProcessFiles(Out, fileSystemConnector, provider.Connector); @@ -105,6 +106,7 @@ private void ProcessFiles(string templateFileName, FileConnectorBase fileSystemC { foreach (var app in Hierarchy.Tenant.AppCatalog.Packages) { + WriteObject($"Processing {app.Src}"); AddFile(app.Src, hierarchy, fileSystemConnector, connector); } } @@ -112,6 +114,7 @@ private void ProcessFiles(string templateFileName, FileConnectorBase fileSystemC { foreach (var siteScript in Hierarchy.Tenant.SiteScripts) { + WriteObject($"Processing {siteScript.JsonFilePath}"); AddFile(siteScript.JsonFilePath, hierarchy, fileSystemConnector, connector); } } @@ -119,6 +122,7 @@ private void ProcessFiles(string templateFileName, FileConnectorBase fileSystemC { foreach (var location in Hierarchy.Localizations) { + WriteObject($"Processing {location.ResourceFile}"); AddFile(location.ResourceFile, hierarchy, fileSystemConnector, connector); } } @@ -126,8 +130,15 @@ private void ProcessFiles(string templateFileName, FileConnectorBase fileSystemC { if(template.WebSettings != null && template.WebSettings.SiteLogo != null) { - if (!template.WebSettings.SiteLogo.StartsWith("https://",StringComparison.InvariantCultureIgnoreCase) && !template.WebSettings.SiteLogo.StartsWith("{")) + // is it a file? + var isFile = false; + using (var fileStream = fileSystemConnector.GetFileStream(template.WebSettings.SiteLogo)) { + isFile = fileStream != null; + } + if (isFile) + { + WriteObject($"Processing {template.WebSettings.SiteLogo}"); AddFile(template.WebSettings.SiteLogo, hierarchy, fileSystemConnector, connector); } } From 5f26660f7c917c6b56a527cebcc9c65b57dd7d44 Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Fri, 5 Oct 2018 10:22:15 +0200 Subject: [PATCH 16/19] updated documentation --- Commands/Provisioning/AddProvisioningSite.cs | 12 ++++-------- Commands/Provisioning/AddProvisioningSubSite.cs | 12 ++++-------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/Commands/Provisioning/AddProvisioningSite.cs b/Commands/Provisioning/AddProvisioningSite.cs index cef2a936d..e98888827 100644 --- a/Commands/Provisioning/AddProvisioningSite.cs +++ b/Commands/Provisioning/AddProvisioningSite.cs @@ -10,16 +10,12 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning [CmdletHelp("Adds a provisioning sequence object to a provisioning hierarchy", Category = CmdletHelpCategory.Provisioning, SupportedPlatform = CmdletSupportedPlatform.Online)] [CmdletExample( - Code = @"PS:> Add-PnPProvisioningSequence -Hierarchy $myhierarchy -Sequence $mysequence", - Remarks = "Adds an existing sequence object to an existing hierarchy object", - SortOrder = 1)] - [CmdletExample( - Code = @"PS:> New-PnPProvisioningSequence -Id ""MySequence"" | Add-PnPProvisioningSequence -Hierarchy $hierarchy", - Remarks = "Creates a new instance of a provisioning sequence object and sets the Id to the value specified, then the sequence is added to an existing hierarchy object", - SortOrder = 2)] + Code = @"PS:> Add-PnPProvisioningSite -Site $myteamsite -Sequence $mysequence", + Remarks = "Adds an existing site object to an existing hierarchy sequence", + SortOrder = 1)] public class AddProvisioningSite : PSCmdlet { - [Parameter(Mandatory = true)] + [Parameter(Mandatory = true, ValueFromPipeline = true)] public ProvisioningSitePipeBind Site; [Parameter(Mandatory = true, HelpMessage = "The sequence to add the site to", ValueFromPipeline = true)] diff --git a/Commands/Provisioning/AddProvisioningSubSite.cs b/Commands/Provisioning/AddProvisioningSubSite.cs index 524a88728..89669e8db 100644 --- a/Commands/Provisioning/AddProvisioningSubSite.cs +++ b/Commands/Provisioning/AddProvisioningSubSite.cs @@ -11,19 +11,15 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning [CmdletHelp("Adds a provisioning sequence object to a provisioning site object", Category = CmdletHelpCategory.Provisioning, SupportedPlatform = CmdletSupportedPlatform.Online)] [CmdletExample( - Code = @"PS:> Add-PnPProvisioningSequence -Hierarchy $myhierarchy -Sequence $mysequence", - Remarks = "Adds an existing sequence object to an existing hierarchy object", + Code = @"PS:> Add-PnPProvisioningSubSite -Site $mysite -SubSite $mysubsite", + Remarks = "Adds an existing subsite object to an existing hierarchy sequence site object", SortOrder = 1)] - [CmdletExample( - Code = @"PS:> New-PnPProvisioningSequence -Id ""MySequence"" | Add-PnPProvisioningSequence -Hierarchy $hierarchy", - Remarks = "Creates a new instance of a provisioning sequence object and sets the Id to the value specified, then the sequence is added to an existing hierarchy object", - SortOrder = 2)] public class AddProvisioningSubSite : PSCmdlet { - [Parameter(Mandatory = true)] + [Parameter(Mandatory = true, HelpMessage = "The subsite to add")] public TeamNoGroupSubSite SubSite; - [Parameter(Mandatory = true, HelpMessage = "The sequence to add the site to", ValueFromPipeline = true)] + [Parameter(Mandatory = true, HelpMessage = "The site to add the subsite to", ValueFromPipeline = true)] public SiteCollection Site; protected override void ProcessRecord() From 57288637608d9dba172d65372fe2ce39c9828eb6 Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Sun, 7 Oct 2018 22:12:19 +0200 Subject: [PATCH 17/19] updated GetTerm --- CHANGELOG.md | 2 + Commands/Taxonomy/GetTerm.cs | 170 +++++++++++++++++++++++------------ 2 files changed, 115 insertions(+), 57 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 231c58f70..355f9b88d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Changed - Updated Set-PnPSite to allow for setting of a logo on modern team site +- Updated Get-PnPTerm to allow for -IncludeChildTerms parameter, which will load, if available all child terms +- Updated Get-PnPTerm to allow for only specifying the id of a termset, without needing to require to specify the termset and termgroup. ### Deprecated diff --git a/Commands/Taxonomy/GetTerm.cs b/Commands/Taxonomy/GetTerm.cs index f9c2b47dc..433e90122 100644 --- a/Commands/Taxonomy/GetTerm.cs +++ b/Commands/Taxonomy/GetTerm.cs @@ -1,11 +1,11 @@ -using System; -using System.Linq; -using System.Linq.Expressions; -using System.Management.Automation; -using Microsoft.SharePoint.Client; +using Microsoft.SharePoint.Client; using Microsoft.SharePoint.Client.Taxonomy; using SharePointPnP.PowerShell.CmdletHelpAttributes; using SharePointPnP.PowerShell.Commands.Base.PipeBinds; +using System; +using System.Linq; +using System.Linq.Expressions; +using System.Management.Automation; namespace SharePointPnP.PowerShell.Commands.Taxonomy { @@ -32,24 +32,33 @@ namespace SharePointPnP.PowerShell.Commands.Taxonomy SortOrder = 2)] public class GetTerm : PnPRetrievalsCmdlet { - [Parameter(Mandatory = false, HelpMessage = "The Id or Name of a Term")] + private const string ParameterSet_TERM = "By Term Id"; + private const string ParameterSet_TERMSET = "By Termset"; + private Term term; + + [Parameter(Mandatory = true, HelpMessage = "The Id or Name of a Term", ParameterSetName = ParameterSet_TERM)] + [Parameter(Mandatory = false, HelpMessage = "The Id or Name of a Term", ParameterSetName = ParameterSet_TERMSET)] public GenericObjectNameIdPipeBind Identity; - [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, HelpMessage = "Name of the termset to check.")] + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, HelpMessage = "Name of the termset to check.", ParameterSetName = ParameterSet_TERMSET)] public TaxonomyItemPipeBind TermSet; - [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, HelpMessage = "Name of the termgroup to check.")] + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, HelpMessage = "Name of the termgroup to check.", ParameterSetName = ParameterSet_TERMSET)] public TermGroupPipeBind TermGroup; - [Parameter(Mandatory = false, HelpMessage = "Term store to check; if not specified the default term store is used.")] + [Parameter(Mandatory = false, HelpMessage = "Term store to check; if not specified the default term store is used.", ParameterSetName = ParameterSet_TERM)] + [Parameter(Mandatory = false, HelpMessage = "Term store to check; if not specified the default term store is used.", ParameterSetName = ParameterSet_TERMSET)] public GenericObjectNameIdPipeBind TermStore; - [Parameter(Mandatory = false, HelpMessage = "Find the first term recursivly matching the label in a term hierarchy.")] + [Parameter(Mandatory = false, HelpMessage = "Find the first term recursivly matching the label in a term hierarchy.", ParameterSetName = ParameterSet_TERMSET)] public SwitchParameter Recursive; + [Parameter(Mandatory = false, HelpMessage = "Includes the hierarchy of child terms if available", ParameterSetName = ParameterAttribute.AllParameterSets)] + public SwitchParameter IncludeChildTerms; + protected override void ExecuteCmdlet() { - DefaultRetrievalExpressions = new Expression>[] { g => g.Name, g => g.Id }; + DefaultRetrievalExpressions = new Expression>[] { g => g.Name, g => g.TermsCount, g => g.Id }; var taxonomySession = TaxonomySession.GetTaxonomySession(ClientContext); // Get Term Store TermStore termStore = null; @@ -76,72 +85,119 @@ protected override void ExecuteCmdlet() } } - TermGroup termGroup = null; - - if (TermGroup.Id != Guid.Empty) - { - termGroup = termStore.Groups.GetById(TermGroup.Id); - } - else if (!string.IsNullOrEmpty(TermGroup.Name)) - { - termGroup = termStore.Groups.GetByName(TermGroup.Name); - } - - TermSet termSet; - if (TermSet.Id != Guid.Empty) + if (ParameterSetName == ParameterSet_TERM) { - termSet = termGroup.TermSets.GetById(TermSet.Id); - } - else if (!string.IsNullOrEmpty(TermSet.Title)) - { - termSet = termGroup.TermSets.GetByName(TermSet.Title); + if (Identity.IdValue != Guid.Empty) + { + term = termStore.GetTerm(Identity.IdValue); + ClientContext.Load(term, RetrievalExpressions); + ClientContext.ExecuteQueryRetry(); + if (IncludeChildTerms.IsPresent && term.TermsCount > 0) + { + LoadChildTerms(term); + } + WriteObject(term); + } } else { - termSet = TermSet.Item; - } - if (Identity != null) - { - Term term = null; - if (Identity.IdValue != Guid.Empty) + TermGroup termGroup = null; + + if (TermGroup != null && TermGroup.Id != Guid.Empty) { - term = termSet.Terms.GetById(Identity.IdValue); + termGroup = termStore.Groups.GetById(TermGroup.Id); } - else + else if (TermGroup != null && !string.IsNullOrEmpty(TermGroup.Name)) + { + termGroup = termStore.Groups.GetByName(TermGroup.Name); + } + + TermSet termSet = null; + if (TermSet != null) + { + if (TermSet.Id != Guid.Empty) + { + termSet = termGroup.TermSets.GetById(TermSet.Id); + } + else if (!string.IsNullOrEmpty(TermSet.Title)) + { + termSet = termGroup.TermSets.GetByName(TermSet.Title); + } + else + { + termSet = TermSet.Item; + } + } + if (Identity != null) { - var termName = TaxonomyExtensions.NormalizeName(Identity.StringValue); - if (!Recursive) + term = null; + if (Identity.IdValue != Guid.Empty) { - term = termSet.Terms.GetByName(termName); + term = termStore.GetTerm(Identity.IdValue); } else { - var lmi = new LabelMatchInformation(ClientContext) + var termName = TaxonomyExtensions.NormalizeName(Identity.StringValue); + if (!Recursive) { - TrimUnavailable = true, - TermLabel = termName - }; + term = termSet.Terms.GetByName(termName); + } + else + { + var lmi = new LabelMatchInformation(ClientContext) + { + TrimUnavailable = true, + TermLabel = termName + }; - var termMatches = termSet.GetTerms(lmi); - ClientContext.Load(termMatches); - ClientContext.ExecuteQueryRetry(); + var termMatches = termSet.GetTerms(lmi); + ClientContext.Load(termMatches); + ClientContext.ExecuteQueryRetry(); - if (termMatches.AreItemsAvailable) + if (termMatches.AreItemsAvailable) + { + term = termMatches.FirstOrDefault(); + } + } + } + ClientContext.Load(term, RetrievalExpressions); + ClientContext.ExecuteQueryRetry(); + if (IncludeChildTerms.IsPresent && term.TermsCount > 0) + { + LoadChildTerms(term); + } + WriteObject(term); + } + else + { + var query = termSet.Terms.IncludeWithDefaultProperties(RetrievalExpressions); + var terms = ClientContext.LoadQuery(query); + ClientContext.ExecuteQueryRetry(); + if (IncludeChildTerms.IsPresent) + { + foreach (var collectionTerm in terms) { - term = termMatches.FirstOrDefault(); + if (collectionTerm.TermsCount > 0) + { + LoadChildTerms(collectionTerm); + } } } + WriteObject(terms, true); } - ClientContext.Load(term, RetrievalExpressions); - ClientContext.ExecuteQueryRetry(); - WriteObject(term); } - else + } + + private void LoadChildTerms(Term incomingTerm) + { + ClientContext.Load(incomingTerm.Terms, ts => ts.IncludeWithDefaultProperties(RetrievalExpressions)); + ClientContext.ExecuteQueryRetry(); + foreach (var childTerm in incomingTerm.Terms) { - var query = termSet.Terms.IncludeWithDefaultProperties(RetrievalExpressions); - var terms = ClientContext.LoadQuery(query); - ClientContext.ExecuteQueryRetry(); - WriteObject(terms, true); + if (childTerm.TermsCount > 0) + { + LoadChildTerms(childTerm); + } } } } From f8d0cbbe47af749340f47e2197887d78f5afaf68 Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Sun, 7 Oct 2018 22:32:43 +0200 Subject: [PATCH 18/19] updated output handling --- ...PnP.PowerShell.2013.Commands.Format.ps1xml | 13 +++++++++ ...PnP.PowerShell.2016.Commands.Format.ps1xml | 13 +++++++++ ...P.PowerShell.Online.Commands.Format.ps1xml | 27 ++++++++++++++----- Commands/Taxonomy/GetTerm.cs | 3 +++ 4 files changed, 49 insertions(+), 7 deletions(-) diff --git a/Commands/ModuleFiles/SharePointPnP.PowerShell.2013.Commands.Format.ps1xml b/Commands/ModuleFiles/SharePointPnP.PowerShell.2013.Commands.Format.ps1xml index fbe3be86e..ef4684197 100644 --- a/Commands/ModuleFiles/SharePointPnP.PowerShell.2013.Commands.Format.ps1xml +++ b/Commands/ModuleFiles/SharePointPnP.PowerShell.2013.Commands.Format.ps1xml @@ -795,6 +795,9 @@ 36 left + + + @@ -805,6 +808,16 @@ Id + + + if($_.IsObjectPropertyInstantiated("Terms")){ + $_.TermsCount + } + else{ + "$($_.TermsCount) (Not loaded)" + } + + diff --git a/Commands/ModuleFiles/SharePointPnP.PowerShell.2016.Commands.Format.ps1xml b/Commands/ModuleFiles/SharePointPnP.PowerShell.2016.Commands.Format.ps1xml index fbe3be86e..ef4684197 100644 --- a/Commands/ModuleFiles/SharePointPnP.PowerShell.2016.Commands.Format.ps1xml +++ b/Commands/ModuleFiles/SharePointPnP.PowerShell.2016.Commands.Format.ps1xml @@ -795,6 +795,9 @@ 36 left + + + @@ -805,6 +808,16 @@ Id + + + if($_.IsObjectPropertyInstantiated("Terms")){ + $_.TermsCount + } + else{ + "$($_.TermsCount) (Not loaded)" + } + + diff --git a/Commands/ModuleFiles/SharePointPnP.PowerShell.Online.Commands.Format.ps1xml b/Commands/ModuleFiles/SharePointPnP.PowerShell.Online.Commands.Format.ps1xml index 012d7475c..6e52cfe88 100644 --- a/Commands/ModuleFiles/SharePointPnP.PowerShell.Online.Commands.Format.ps1xml +++ b/Commands/ModuleFiles/SharePointPnP.PowerShell.Online.Commands.Format.ps1xml @@ -583,30 +583,30 @@ if($_.Name -eq ""){ - $_.ServerRelativeUrl.TrimEnd("/").Substring($_.ServerRelativeUrl.TrimEnd("/").LastIndexof("/") + 1) + $_.ServerRelativeUrl.TrimEnd("/").Substring($_.ServerRelativeUrl.TrimEnd("/").LastIndexof("/") + 1) } else{ - $_.Name + $_.Name } if($_.GetType().Name -eq "Folder" -and $_.Name -eq ""){ - "Subweb" + "Subweb" } else{ - $_.GetType().Name + $_.GetType().Name } if($_.GetType().Name -eq "File"){ - $_.Length + $_.Length } else{ - $_.ItemCount + $_.ItemCount } @@ -795,6 +795,9 @@ 36 left + + + @@ -805,6 +808,16 @@ Id + + + if($_.IsObjectPropertyInstantiated("Terms")){ + $_.TermsCount + } + else{ + "$($_.TermsCount) (Not loaded)" + } + + @@ -1685,7 +1698,7 @@ - + SPOWebAppServicePrincipalPermissionRequest Microsoft.Online.SharePoint.TenantAdministration.Internal.SPOWebAppServicePrincipalPermissionRequest diff --git a/Commands/Taxonomy/GetTerm.cs b/Commands/Taxonomy/GetTerm.cs index 433e90122..6282a4cfd 100644 --- a/Commands/Taxonomy/GetTerm.cs +++ b/Commands/Taxonomy/GetTerm.cs @@ -97,6 +97,9 @@ protected override void ExecuteCmdlet() LoadChildTerms(term); } WriteObject(term); + } else + { + WriteError(new ErrorRecord(new Exception("Insuffication parameters"), "INSUFFICIENTPARAMETERS", ErrorCategory.SyntaxError, this)); } } else From 324b92121a9118bed777889ca966f4e4838e9712 Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Mon, 8 Oct 2018 11:23:34 +0200 Subject: [PATCH 19/19] Added Get-PnPException cmdlet --- CHANGELOG.md | 29 ++++----- Commands/Base/GetException.cs | 48 ++++++++++++++ Commands/Model/PnPException.cs | 17 +++++ ...PnP.PowerShell.2013.Commands.Format.ps1xml | 23 +++++++ ...PnP.PowerShell.2016.Commands.Format.ps1xml | 23 +++++++ ...harePointPnP.PowerShell.Core.Format.ps1xml | 63 +++++++++++++++---- ...P.PowerShell.Online.Commands.Format.ps1xml | 27 +++++++- .../SharePointPnP.PowerShell.Commands.csproj | 2 + 8 files changed, 203 insertions(+), 29 deletions(-) create mode 100644 Commands/Base/GetException.cs create mode 100644 Commands/Model/PnPException.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 355f9b88d..211bcf726 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,20 +7,21 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ## [3.2.1810.0] Unreleased ### Added -- Add-PnPProvisioningSequence -- Add-PnPProvisioningSite -- Add-PnPProvisioningSubSite -- Apply-PnPProvisioningHierarchy -- Get-PnPProvisioningSite -- New-PnPProvisioningHierarchy -- New-PnPProvisioningSequence -- New-PnPProvisioningCommunicationSite -- New-PnPProvisioningTeamNoGroupSite -- New-PnPProvisioningTeamNoGroupSubSite -- New-PnPProvisioningTeamSite -- Read-PnPProvisioningHierarchy -- Save-PnPProvisioningHierarchy -- Test-PnPProvisioningHierarchy +- Add-PnPProvisioningSequence : Adds an in-memory sequence to an in-memory provisioning hierarchy +- Add-PnPProvisioningSite : Adds an in-memory site definition to a in-memory sequence +- Add-PnPProvisioningSubSite : Adds an in-memory sub site defintion to an in-memory site +- Apply-PnPProvisioningHierarchy : Applies a provisioninghierarchy with a site sequence to a tenant +- Get-PnPProvisioningSite : Returns a site as an in-memory object from a given provisioning hierarchy +- New-PnPProvisioningHierarchy : Creates a new in-memory provisioning hierarchy +- New-PnPProvisioningSequence : Creates a new in-memory provisioning sequence +- New-PnPProvisioningCommunicationSite : Creates a new in-memory communication site definition +- New-PnPProvisioningTeamNoGroupSite : Creates a new in-memory team site definition which has no associated office365 group +- New-PnPProvisioningTeamNoGroupSubSite : Creates a new in-memory team sub site definition which has no associated office365 group +- New-PnPProvisioningTeamSite : Creates a new in-memory team site definition +- Read-PnPProvisioningHierarchy : Reads an existing (file based) provisioning hierarchy into an in-memory instance +- Save-PnPProvisioningHierarchy : Saves an in-memory provisioning hierarchy to a pnp file +- Test-PnPProvisioningHierarchy : Tests an in-memory hierarchy if all template references are correct in the site sequence +- Get-PnPException : Returns the last occured exception that occured while using PowerShell. ### Changed - Updated Set-PnPSite to allow for setting of a logo on modern team site diff --git a/Commands/Base/GetException.cs b/Commands/Base/GetException.cs new file mode 100644 index 000000000..157a814b7 --- /dev/null +++ b/Commands/Base/GetException.cs @@ -0,0 +1,48 @@ +using SharePointPnP.PowerShell.CmdletHelpAttributes; +using SharePointPnP.PowerShell.Commands.Model; +using System.Collections; +using System.Collections.Generic; +using System.Management.Automation; + +namespace SharePointPnP.PowerShell.Commands.Base +{ + [Cmdlet(VerbsCommon.Get, "PnPException")] + [CmdletHelp("Returns the last exception that occured", + @"Returns the last exception which can be used while debugging PnP Cmdlets", + Category = CmdletHelpCategory.Base)] + [CmdletExample( + Code = @"PS:> Get-PnPException", + Remarks = "Returns the last exception", + SortOrder = 1)] + [CmdletExample( + Code = @"PS:> Get-PnPException -All", + Remarks = "Returns all exceptions that occurred", + SortOrder = 2)] + public class GetException : PSCmdlet + { + [Parameter(Mandatory = false, HelpMessage = "Show all exceptions")] + public SwitchParameter All; + + protected override void ProcessRecord() + { + var exceptions = (ArrayList)this.SessionState.PSVariable.Get("error").Value; + if (exceptions.Count > 0) + { + var output = new List(); + if (All.IsPresent) + { + foreach (ErrorRecord exception in exceptions) + { + output.Add(new PnPException() { Message = exception.Exception.Message, Stacktrace = exception.Exception.StackTrace, ScriptLineNumber = exception.InvocationInfo.ScriptLineNumber, InvocationInfo = exception.InvocationInfo }); + } + } + else + { + var exception = (ErrorRecord)exceptions[0]; + output.Add(new PnPException() { Message = exception.Exception.Message, Stacktrace = exception.Exception.StackTrace, ScriptLineNumber = exception.InvocationInfo.ScriptLineNumber, InvocationInfo = exception.InvocationInfo }); + } + WriteObject(output, true); + } + } + } +} diff --git a/Commands/Model/PnPException.cs b/Commands/Model/PnPException.cs new file mode 100644 index 000000000..4573b1e9c --- /dev/null +++ b/Commands/Model/PnPException.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management.Automation; +using System.Text; +using System.Threading.Tasks; + +namespace SharePointPnP.PowerShell.Commands.Model +{ + public class PnPException + { + public string Message; + public string Stacktrace; + public int ScriptLineNumber; + public InvocationInfo InvocationInfo; + } +} diff --git a/Commands/ModuleFiles/SharePointPnP.PowerShell.2013.Commands.Format.ps1xml b/Commands/ModuleFiles/SharePointPnP.PowerShell.2013.Commands.Format.ps1xml index ef4684197..1994af1e7 100644 --- a/Commands/ModuleFiles/SharePointPnP.PowerShell.2013.Commands.Format.ps1xml +++ b/Commands/ModuleFiles/SharePointPnP.PowerShell.2013.Commands.Format.ps1xml @@ -1306,5 +1306,28 @@ + + PnPException + + SharePointPnP.PowerShell.Commands.Model.PnPException + + + + + + + Message + + + Stacktrace + + + ScriptLineNumber + + + + + + \ No newline at end of file diff --git a/Commands/ModuleFiles/SharePointPnP.PowerShell.2016.Commands.Format.ps1xml b/Commands/ModuleFiles/SharePointPnP.PowerShell.2016.Commands.Format.ps1xml index ef4684197..1994af1e7 100644 --- a/Commands/ModuleFiles/SharePointPnP.PowerShell.2016.Commands.Format.ps1xml +++ b/Commands/ModuleFiles/SharePointPnP.PowerShell.2016.Commands.Format.ps1xml @@ -1306,5 +1306,28 @@ + + PnPException + + SharePointPnP.PowerShell.Commands.Model.PnPException + + + + + + + Message + + + Stacktrace + + + ScriptLineNumber + + + + + + \ No newline at end of file diff --git a/Commands/ModuleFiles/SharePointPnP.PowerShell.Core.Format.ps1xml b/Commands/ModuleFiles/SharePointPnP.PowerShell.Core.Format.ps1xml index b5da6dcc1..49ef10b99 100644 --- a/Commands/ModuleFiles/SharePointPnP.PowerShell.Core.Format.ps1xml +++ b/Commands/ModuleFiles/SharePointPnP.PowerShell.Core.Format.ps1xml @@ -589,30 +589,30 @@ if($_.Name -eq ""){ - $_.ServerRelativeUrl.TrimEnd("/").Substring($_.ServerRelativeUrl.TrimEnd("/").LastIndexOf("/") + 1) + $_.ServerRelativeUrl.TrimEnd("/").Substring($_.ServerRelativeUrl.TrimEnd("/").LastIndexOf("/") + 1) } else{ - $_.Name + $_.Name } if($_.GetType().Name -eq "Folder" -and $_.Name -eq ""){ - "Subweb" + "Subweb" } else{ - $_.GetType().Name + $_.GetType().Name } if($_.GetType().Name -eq "File"){ - $_.Length + $_.Length } else{ - $_.ItemCount + $_.ItemCount } @@ -801,6 +801,9 @@ 36 left + + + @@ -811,6 +814,16 @@ Id + + + if($_.IsObjectPropertyInstantiated("Terms")){ + $_.TermsCount + } + else{ + "$($_.TermsCount) (Not loaded)" + } + + @@ -1290,7 +1303,7 @@ left - + @@ -1366,7 +1379,8 @@ - + + TenantSiteDesign Microsoft.Online.SharePoint.TenantAdministration.TenantSiteDesign @@ -1439,7 +1453,7 @@ Version - + Content @@ -1515,7 +1529,7 @@ left - + left @@ -1541,7 +1555,7 @@ Order - + PropertiesJson @@ -1549,12 +1563,12 @@ - + NavigationNode Microsoft.SharePoint.Client.NavigationNode - + @@ -1780,5 +1794,28 @@ + + PnPException + + SharePointPnP.PowerShell.Commands.Model.PnPException + + + + + + + Message + + + Stacktrace + + + ScriptLineNumber + + + + + + \ No newline at end of file diff --git a/Commands/ModuleFiles/SharePointPnP.PowerShell.Online.Commands.Format.ps1xml b/Commands/ModuleFiles/SharePointPnP.PowerShell.Online.Commands.Format.ps1xml index 6e52cfe88..22675cb17 100644 --- a/Commands/ModuleFiles/SharePointPnP.PowerShell.Online.Commands.Format.ps1xml +++ b/Commands/ModuleFiles/SharePointPnP.PowerShell.Online.Commands.Format.ps1xml @@ -811,10 +811,10 @@ if($_.IsObjectPropertyInstantiated("Terms")){ - $_.TermsCount + $_.TermsCount } else{ - "$($_.TermsCount) (Not loaded)" + "$($_.TermsCount) (Not loaded)" } @@ -1846,5 +1846,28 @@ + + PnPException + + SharePointPnP.PowerShell.Commands.Model.PnPException + + + + + + + Message + + + Stacktrace + + + ScriptLineNumber + + + + + + diff --git a/Commands/SharePointPnP.PowerShell.Commands.csproj b/Commands/SharePointPnP.PowerShell.Commands.csproj index 0362cae42..fb50e70a3 100644 --- a/Commands/SharePointPnP.PowerShell.Commands.csproj +++ b/Commands/SharePointPnP.PowerShell.Commands.csproj @@ -459,6 +459,7 @@ + @@ -480,6 +481,7 @@ +