diff --git a/Commands/Base/PipeBinds/ClientSideComponentPipeBind.cs b/Commands/Base/PipeBinds/ClientSideComponentPipeBind.cs
new file mode 100644
index 000000000..785fbc921
--- /dev/null
+++ b/Commands/Base/PipeBinds/ClientSideComponentPipeBind.cs
@@ -0,0 +1,74 @@
+using Microsoft.SharePoint.Client;
+using OfficeDevPnP.Core.Pages;
+using SharePointPnP.PowerShell.Commands.ClientSidePages;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SharePointPnP.PowerShell.Commands.Base.PipeBinds
+{
+ public sealed class ClientSideComponentPipeBind
+ {
+ private readonly ClientSideComponent _component;
+ private string _name;
+ private Guid _id;
+
+ public ClientSideComponentPipeBind(ClientSideComponent component)
+ {
+ _component = component;
+ _id = Guid.Parse(_component.Id);
+ _name = _component.Name;
+ }
+
+ public ClientSideComponentPipeBind(string nameOrId)
+ {
+ _component = null;
+ if (!Guid.TryParse(nameOrId, out _id))
+ {
+ _name = nameOrId;
+ }
+ }
+
+ public ClientSideComponentPipeBind(Guid id)
+ {
+ _id = id;
+ _name = null;
+ _component = null;
+ }
+
+ public ClientSideComponent Component => _component;
+
+ public string Name => _component?.Name;
+
+ public string Id => _component == null ? Guid.Empty.ToString() : _component.Id;
+
+ public override string ToString() => Name;
+
+ internal ClientSideComponent GetComponent(ClientSidePage page)
+ {
+ if (_component != null)
+ {
+ return _component;
+ }
+ else if (!string.IsNullOrEmpty(_name))
+ {
+ ClientSideComponent com = page.AvailableClientSideComponents(_name).FirstOrDefault();
+ return com;
+ }
+ else if (_id != Guid.Empty)
+ {
+ string idAsString = _id.ToString();
+ var comQuery = from c in page.AvailableClientSideComponents(_name)
+ where c.Id == idAsString
+ select c;
+ return comQuery.FirstOrDefault();
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+}
diff --git a/Commands/Base/PipeBinds/ClientSidePagePipeBind.cs b/Commands/Base/PipeBinds/ClientSidePagePipeBind.cs
new file mode 100644
index 000000000..e20a8e4c1
--- /dev/null
+++ b/Commands/Base/PipeBinds/ClientSidePagePipeBind.cs
@@ -0,0 +1,58 @@
+using Microsoft.SharePoint.Client;
+using OfficeDevPnP.Core.Pages;
+using SharePointPnP.PowerShell.Commands.ClientSidePages;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SharePointPnP.PowerShell.Commands.Base.PipeBinds
+{
+ public sealed class ClientSidePagePipeBind
+ {
+ private readonly ClientSidePage _page;
+ private string _name;
+
+ public ClientSidePagePipeBind(ClientSidePage page)
+ {
+ _page = page;
+ _name = page.PageTitle;
+ }
+
+ public ClientSidePagePipeBind(string name)
+ {
+ _page = null;
+ _name = name;
+ }
+
+ public ClientSidePage Page => _page;
+
+ public string Name => ClientSidePageUtilities.EnsureCorrectPageName(_name);
+
+ public override string ToString() => Name;
+
+ internal ClientSidePage GetPage(ClientContext ctx)
+ {
+ if (_page != null)
+ {
+ return _page;
+ }
+ else if (!string.IsNullOrEmpty(_name))
+ {
+ try
+ {
+ return ClientSidePage.Load(ctx, Name);
+ }
+ catch (ArgumentException ex)
+ {
+ return null;
+ }
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+}
diff --git a/Commands/Base/PipeBinds/GenericPropertiesPipeBind.cs b/Commands/Base/PipeBinds/GenericPropertiesPipeBind.cs
new file mode 100644
index 000000000..733fecb7e
--- /dev/null
+++ b/Commands/Base/PipeBinds/GenericPropertiesPipeBind.cs
@@ -0,0 +1,67 @@
+using Microsoft.SharePoint.Client;
+using Newtonsoft.Json.Linq;
+using OfficeDevPnP.Core.Pages;
+using SharePointPnP.PowerShell.Commands.ClientSidePages;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Management.Automation;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SharePointPnP.PowerShell.Commands.Base.PipeBinds
+{
+ public sealed class PropertyBagPipeBind
+ {
+ private readonly Hashtable _hashtable;
+ private string _jsonString;
+ private JObject _jsonObject;
+
+ public PropertyBagPipeBind(Hashtable hashtable)
+ {
+ _hashtable = hashtable;
+ _jsonString = null;
+ _jsonObject = null;
+ }
+
+ public PropertyBagPipeBind(string json)
+ {
+ _hashtable = null;
+ _jsonString = json;
+ _jsonObject = JObject.Parse(json);
+ }
+
+ public string Json => _jsonString;
+
+ public JObject JsonObject => _jsonObject ?? HashtableToJsonObject(_hashtable);
+
+ public Hashtable Properties => _hashtable;
+
+ public override string ToString() => Json ?? HashtableToJsonString(_hashtable);
+
+ private string HashtableToJsonString(Hashtable hashtable)
+ {
+ return HashtableToJsonObject(hashtable).ToString();
+ }
+
+ private JObject HashtableToJsonObject(Hashtable hashtable)
+ {
+ var obj = new JObject();
+
+ foreach (var key in hashtable.Keys)
+ {
+ var rawValue = hashtable[key];
+
+ // To ensure the value is not serialized as PSObject
+ object value = rawValue is PSObject
+ ? ((PSObject)rawValue).BaseObject
+ : rawValue;
+
+ obj[key] = JToken.FromObject(value);
+ }
+ return obj;
+ }
+
+ }
+}
diff --git a/Commands/ClientSidePages/AddClientSidePage.cs b/Commands/ClientSidePages/AddClientSidePage.cs
new file mode 100644
index 000000000..b920831ea
--- /dev/null
+++ b/Commands/ClientSidePages/AddClientSidePage.cs
@@ -0,0 +1,99 @@
+#if !ONPREMISES
+using Microsoft.SharePoint.Client;
+using OfficeDevPnP.Core.Pages;
+using SharePointPnP.PowerShell.CmdletHelpAttributes;
+using System;
+using System.Management.Automation;
+
+namespace SharePointPnP.PowerShell.Commands.ClientSidePages
+{
+ [Cmdlet(VerbsCommon.Add, "PnPClientSidePage")]
+ [CmdletHelp("Adds a Client-Side Page",
+ Category = CmdletHelpCategory.ClientSidePages, SupportedPlatform = CmdletSupportedPlatform.Online)]
+ [CmdletExample(
+ Code = @"PS:> Add-PnPClientSidePage -PageName ""OurNewPage""",
+ Remarks = "Creates a new Client-Side page called 'OurNewPage'",
+ SortOrder = 1)]
+ public class AddClientSidePage : PnPWebCmdlet
+ {
+ [Parameter(Mandatory = true, HelpMessage = "Specifies the name of the page.")]
+ public string Name = null;
+
+ [Parameter(Mandatory = false, HelpMessage = "Specifies the layout type of the page.")]
+ public ClientSidePageLayoutType LayoutType = ClientSidePageLayoutType.Article;
+
+ [Parameter(Mandatory = false, HelpMessage = "Allows to promote the page for a specific purpose (HomePage | NewsPage)")]
+ public ClientSidePagePromoteType PromoteAs = ClientSidePagePromoteType.None;
+
+ [Parameter(Mandatory = false, HelpMessage = "Enables or Disables the comments on the page")]
+ public bool? CommentsEnabled = null;
+
+ [Parameter(Mandatory = false, HelpMessage = "Publishes the page once it is saved. Applicable to libraries set to create major and minor versions.")]
+ public SwitchParameter Publish;
+
+ [Parameter(Mandatory = false, HelpMessage = "Sets the message for publishing the page.")]
+ public string PublishMessage = string.Empty;
+
+ protected override void ExecuteCmdlet()
+ {
+
+ ClientSidePage clientSidePage = null;
+
+ // Check if the page exists
+
+ string name = ClientSidePageUtilities.EnsureCorrectPageName(Name);
+
+ bool pageExists = false;
+ try
+ {
+ ClientSidePage.Load(ClientContext, name);
+ pageExists = true;
+ }
+ catch { }
+
+ if(pageExists)
+ {
+ throw new Exception($"Page {name} already exists");
+ }
+
+ // Create a page that persists immediately
+ clientSidePage = SelectedWeb.AddClientSidePage(name);
+ clientSidePage.LayoutType = LayoutType;
+ clientSidePage.Save(name);
+
+ // If a specific promote type is specified, promote the page as Home or Article or ...
+ switch (PromoteAs)
+ {
+ case ClientSidePagePromoteType.HomePage:
+ clientSidePage.PromoteAsHomePage();
+ break;
+ case ClientSidePagePromoteType.NewsArticle:
+ clientSidePage.PromoteAsNewsArticle();
+ break;
+ case ClientSidePagePromoteType.None:
+ default:
+ break;
+ }
+
+ if (CommentsEnabled.HasValue)
+ {
+ if (CommentsEnabled.Value)
+ {
+ clientSidePage.EnableComments();
+ }
+ else
+ {
+ clientSidePage.DisableComments();
+ }
+ }
+
+ if (Publish)
+ {
+ clientSidePage.Publish(PublishMessage);
+ }
+
+ WriteObject(clientSidePage);
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/Commands/ClientSidePages/AddClientSidePageSection.cs b/Commands/ClientSidePages/AddClientSidePageSection.cs
new file mode 100644
index 000000000..c633efa2c
--- /dev/null
+++ b/Commands/ClientSidePages/AddClientSidePageSection.cs
@@ -0,0 +1,56 @@
+#if !ONPREMISES
+using OfficeDevPnP.Core.Pages;
+using SharePointPnP.PowerShell.CmdletHelpAttributes;
+using SharePointPnP.PowerShell.Commands.Base.PipeBinds;
+using System;
+using System.Management.Automation;
+
+namespace SharePointPnP.PowerShell.Commands.ClientSidePages
+{
+ [Cmdlet(VerbsCommon.Add, "PnPClientSidePageSection")]
+ [CmdletHelp("Adds a new section to a Client-Side page",
+ Category = CmdletHelpCategory.ClientSidePages, SupportedPlatform = CmdletSupportedPlatform.Online)]
+ [CmdletExample(
+ Code = @"PS:> Add-PnPClientSidePageSection -Page ""MyPage"" -SectionTemplate OneColumn",
+ Remarks = "Adds a new one-column section to the Client-Side page 'MyPage'",
+ SortOrder = 1)]
+ [CmdletExample(
+ Code = @"PS:> Add-PnPClientSidePageSection -Page ""MyPage"" -SectionTemplate ThreeColumn -Order 10",
+ Remarks = "Adds a new Three columns section to the Client-Side page 'MyPage' with an order index of 10",
+ SortOrder = 2)]
+ [CmdletExample(
+ Code = @"PS:> $page = Add-PnPClientSidePage -Name ""MyPage""
+PS> Add-PnPClientSidePageSection -Page $page -SectionTemplate OneColumn",
+ Remarks = "Adds a new one column section to the Client-Side page 'MyPage'",
+ SortOrder = 2)]
+ public class AddClientSidePageSection : PnPWebCmdlet
+ {
+ [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, HelpMessage = "The name of the page")]
+ public ClientSidePagePipeBind Page;
+
+ [Parameter(Mandatory = true, HelpMessage = "Specifies the columns template to use for the section.")]
+ public CanvasSectionTemplate SectionTemplate;
+
+ [Parameter(Mandatory = false, HelpMessage = "Sets the order of the section. (Default = 1)")]
+ public int Order = 1;
+
+
+ protected override void ExecuteCmdlet()
+ {
+ var clientSidePage = Page?.GetPage(ClientContext);
+
+ if (clientSidePage != null)
+ {
+ clientSidePage.AddSection(SectionTemplate, Order);
+ clientSidePage.Save();
+ }
+ else
+ {
+ // If the client side page object cannot be found
+ throw new Exception($"Page {Page} cannot be found.");
+ }
+
+ }
+ }
+}
+#endif
diff --git a/Commands/ClientSidePages/AddClientSideText.cs b/Commands/ClientSidePages/AddClientSideText.cs
new file mode 100644
index 000000000..b256798d6
--- /dev/null
+++ b/Commands/ClientSidePages/AddClientSideText.cs
@@ -0,0 +1,78 @@
+#if !ONPREMISES
+using OfficeDevPnP.Core.Pages;
+using SharePointPnP.PowerShell.CmdletHelpAttributes;
+using SharePointPnP.PowerShell.Commands.Base.PipeBinds;
+using System;
+using System.Management.Automation;
+
+namespace SharePointPnP.PowerShell.Commands.ClientSidePages
+{
+ [Cmdlet(VerbsCommon.Add, "PnPClientSideText")]
+ [CmdletHelp("Adds a Client-Side Page",
+ Category = CmdletHelpCategory.ClientSidePages, SupportedPlatform = CmdletSupportedPlatform.Online)]
+ [CmdletExample(
+ Code = @"PS:> Add-PnPClientSideText -Page ""OurNewPage"" -Text ""Hello World!""",
+ Remarks = "Adds the text 'Hello World!' to the Client-Side Page 'OurNewPage'",
+ SortOrder = 1)]
+ public class AddClientSideText : PnPWebCmdlet
+ {
+ [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, HelpMessage = "The name of the page.", ParameterSetName = "Default")]
+ [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, HelpMessage = "The name of the page.", ParameterSetName = "Positioned")]
+ public ClientSidePagePipeBind Page;
+
+ [Parameter(Mandatory = true, HelpMessage = "Specifies the text to display in the text area.", ParameterSetName = "Default")]
+ [Parameter(Mandatory = true, HelpMessage = "Specifies the text to display in the text area.", ParameterSetName = "Positioned")]
+ public string Text;
+
+ [Parameter(Mandatory = false, HelpMessage = "Sets the order of the text control. (Default = 1)", ParameterSetName = "Default")]
+ [Parameter(Mandatory = false, HelpMessage = "Sets the order of the text control. (Default = 1)", ParameterSetName = "Positioned")]
+ public int Order = 1;
+
+ [Parameter(Mandatory = true, HelpMessage = "Sets the section where to insert the text control.", ParameterSetName = "Positioned")]
+ public int Section;
+
+ [Parameter(Mandatory = true, HelpMessage = "Sets the column where to insert the text control.", ParameterSetName = "Positioned")]
+ public int Column;
+
+ protected override void ExecuteCmdlet()
+ {
+ if (MyInvocation.BoundParameters.ContainsKey("Section") && Section == 0)
+ {
+ throw new Exception("Section value should be at least 1 or higher");
+ }
+
+ if (MyInvocation.BoundParameters.ContainsKey("Column") && Column == 0)
+ {
+ throw new Exception("Column value should be at least 1 or higher");
+ }
+
+ var clientSidePage = Page.GetPage(ClientContext);
+
+ if (clientSidePage == null)
+ // If the client side page object cannot be found
+ throw new Exception($"Page {Page} cannot be found.");
+
+ var text = new ClientSideText() { Text = Text };
+ if (MyInvocation.BoundParameters.ContainsKey("Section"))
+ {
+ if (MyInvocation.BoundParameters.ContainsKey("Section"))
+ {
+ clientSidePage.AddControl(text,
+ clientSidePage.Sections[Section - 1].Columns[Column - 1], Order);
+ }
+ else
+ {
+ clientSidePage.AddControl(text, clientSidePage.Sections[Section - 1], Order);
+ }
+ }
+ else
+ {
+ clientSidePage.AddControl(text, Order);
+ }
+
+ // Save the page
+ clientSidePage.Save();
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/Commands/ClientSidePages/AddClientSideWebPart.cs b/Commands/ClientSidePages/AddClientSideWebPart.cs
new file mode 100644
index 000000000..0bde29d7b
--- /dev/null
+++ b/Commands/ClientSidePages/AddClientSideWebPart.cs
@@ -0,0 +1,123 @@
+#if !ONPREMISES
+using OfficeDevPnP.Core.Pages;
+using SharePointPnP.PowerShell.CmdletHelpAttributes;
+using SharePointPnP.PowerShell.Commands.Base.PipeBinds;
+using System;
+using System.Management.Automation;
+
+namespace SharePointPnP.PowerShell.Commands.ClientSidePages
+{
+ [Cmdlet(VerbsCommon.Add, "PnPClientSideWebPart")]
+ [CmdletHelp("Adds a Client-Side Component to a page",
+ Category = CmdletHelpCategory.ClientSidePages, SupportedPlatform = CmdletSupportedPlatform.Online)]
+ [CmdletExample(
+ Code = @"PS:> Add-PnPClientSideWebPart -Page ""OurNewPage"" -DefaultWebPartType BingMap",
+ Remarks = "Adds a built-in Client-Side component 'BingMap' to the page called 'OurNewPage'",
+ SortOrder = 2)]
+ [CmdletExample(
+ Code = @"PS:> Add-PnPClientSideWebPart -Page ""OurNewPage"" -Component ""HelloWorld""",
+ Remarks = "Adds a Client-Side component 'HelloWorld' to the page called 'OurNewPage'",
+ SortOrder = 2)]
+ [CmdletExample(
+ Code = @"PS:> Add-PnPClientSideWebPart -Page ""OurNewPage"" -Component ""HelloWorld"" -Section 1 -Column 2",
+ Remarks = "Adds a Client-Side component 'HelloWorld' to the page called 'OurNewPage' in section 1 and column 2",
+ SortOrder = 3)]
+ public class AddClientSideWebPart : PnPWebCmdlet
+ {
+ [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, HelpMessage = "The name of the page.", ParameterSetName = "DefaultBuiltIn")]
+ [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, HelpMessage = "The name of the page.", ParameterSetName = "Default3rdParty")]
+ [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, HelpMessage = "The name of the page.", ParameterSetName = "PositionedBuiltIn")]
+ [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, HelpMessage = "The name of the page.", ParameterSetName = "Positioned3rdParty")]
+ public ClientSidePagePipeBind Page;
+
+ [Parameter(Mandatory = true, HelpMessage = "Defines a default WebPart type to insert.", ParameterSetName = "DefaultBuiltIn")]
+ [Parameter(Mandatory = true, HelpMessage = "Defines a default WebPart type to insert.", ParameterSetName = "PositionedBuiltIn")]
+ public DefaultClientSideWebParts DefaultWebPartType;
+
+ [Parameter(Mandatory = true, HelpMessage = "Specifies the component instance or Id to add.", ParameterSetName = "Default3rdParty")]
+ [Parameter(Mandatory = true, HelpMessage = "Specifies the component instance or Id to add.", ParameterSetName = "Positioned3rdParty")]
+ public ClientSideComponentPipeBind Component;
+
+ [Parameter(Mandatory = false, HelpMessage = @"The properties of the WebPart", ParameterSetName = "DefaultBuiltIn")]
+ [Parameter(Mandatory = false, HelpMessage = @"The properties of the WebPart", ParameterSetName = "Default3rdParty")]
+ [Parameter(Mandatory = false, HelpMessage = @"The properties of the WebPart", ParameterSetName = "PositionedBuiltIn")]
+ [Parameter(Mandatory = false, HelpMessage = @"The properties of the WebPart", ParameterSetName = "Positioned3rdParty")]
+ public PropertyBagPipeBind WebPartProperties;
+
+ [Parameter(Mandatory = false, HelpMessage = "Sets the order of the WebPart control. (Default = 1)", ParameterSetName = "DefaultBuiltIn")]
+ [Parameter(Mandatory = false, HelpMessage = "Sets the order of the WebPart control. (Default = 1)", ParameterSetName = "Default3rdParty")]
+ [Parameter(Mandatory = false, HelpMessage = "Sets the order of the WebPart control. (Default = 1)", ParameterSetName = "PositionedBuiltIn")]
+ [Parameter(Mandatory = false, HelpMessage = "Sets the order of the WebPart control. (Default = 1)", ParameterSetName = "Positioned3rdParty")]
+ public int Order = 1;
+
+ [Parameter(Mandatory = true, HelpMessage = "Sets the section where to insert the WebPart control.", ParameterSetName = "PositionedBuiltIn")]
+ [Parameter(Mandatory = true, HelpMessage = "Sets the section where to insert the WebPart control.", ParameterSetName = "Positioned3rdParty")]
+ public int Section;
+
+ [Parameter(Mandatory = true, HelpMessage = "Sets the column where to insert the WebPart control.", ParameterSetName = "PositionedBuiltIn")]
+ [Parameter(Mandatory = true, HelpMessage = "Sets the column where to insert the WebPart control.", ParameterSetName = "Positioned3rdParty")]
+ public int Column;
+
+ protected override void ExecuteCmdlet()
+ {
+ if (MyInvocation.BoundParameters.ContainsKey("Section") && Section == 0)
+ {
+ throw new Exception("Section value should be at least 1 or higher");
+ }
+
+ if (MyInvocation.BoundParameters.ContainsKey("Column") && Column == 0)
+ {
+ throw new Exception("Column value should be at least 1 or higher");
+ }
+
+ var clientSidePage = Page.GetPage(ClientContext);
+ // If the client side page object cannot be found
+ if (clientSidePage == null)
+ {
+ throw new Exception($"Page {Page} cannot be found.");
+ }
+
+ ClientSideWebPart webpart = null;
+ if (MyInvocation.BoundParameters.ContainsKey("DefaultWebPartType"))
+ {
+ webpart = clientSidePage.InstantiateDefaultWebPart(DefaultWebPartType);
+ }
+ else
+ {
+ webpart = new ClientSideWebPart(Component.GetComponent(clientSidePage));
+ }
+
+ if (WebPartProperties != null)
+ {
+ if (WebPartProperties.Properties != null)
+ {
+ webpart.Properties.Merge(WebPartProperties.JsonObject);
+ }
+ else if (!string.IsNullOrEmpty(WebPartProperties.Json))
+ {
+ webpart.PropertiesJson = WebPartProperties.Json;
+ }
+ }
+
+ if (MyInvocation.BoundParameters.ContainsKey("Section"))
+ {
+ if (MyInvocation.BoundParameters.ContainsKey("Column"))
+ {
+ clientSidePage.AddControl(webpart,
+ clientSidePage.Sections[Section - 1].Columns[Column - 1], Order);
+ }
+ else
+ {
+ clientSidePage.AddControl(webpart, clientSidePage.Sections[Section - 1], Order);
+ }
+ }
+ else
+ {
+ clientSidePage.AddControl(webpart, Order);
+ }
+
+ clientSidePage.Save();
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/Commands/ClientSidePages/ClientSidePageUtilities.cs b/Commands/ClientSidePages/ClientSidePageUtilities.cs
new file mode 100644
index 000000000..22568b850
--- /dev/null
+++ b/Commands/ClientSidePages/ClientSidePageUtilities.cs
@@ -0,0 +1,16 @@
+#if !ONPREMISES
+
+namespace SharePointPnP.PowerShell.Commands.ClientSidePages
+{
+ internal static class ClientSidePageUtilities
+ {
+ public static string EnsureCorrectPageName(string pageName)
+ {
+ if (pageName != null && !pageName.EndsWith(".aspx"))
+ pageName += ".aspx";
+
+ return pageName;
+ }
+ }
+}
+#endif
diff --git a/Commands/ClientSidePages/GetAvailableClientSideComponents.cs b/Commands/ClientSidePages/GetAvailableClientSideComponents.cs
new file mode 100644
index 000000000..61a4a85f8
--- /dev/null
+++ b/Commands/ClientSidePages/GetAvailableClientSideComponents.cs
@@ -0,0 +1,48 @@
+#if !ONPREMISES
+using SharePointPnP.PowerShell.CmdletHelpAttributes;
+using SharePointPnP.PowerShell.Commands.Base.PipeBinds;
+using System.Linq;
+using System.Management.Automation;
+
+namespace SharePointPnP.PowerShell.Commands.ClientSidePages
+{
+ [Cmdlet(VerbsCommon.Get, "PnPAvailableClientSideComponents")]
+ [CmdletHelp("Gets the available client side components on a particular page",
+ Category = CmdletHelpCategory.ClientSidePages, SupportedPlatform = CmdletSupportedPlatform.Online)]
+ [CmdletExample(
+ Code = @"PS:> Get-PnPAvailableClientSideComponents -Identity ""MyPage.aspx""",
+ Remarks = "Gets the list of available client side components on the page 'MyPage.aspx'",
+ SortOrder = 1)]
+ [CmdletExample(
+ Code = @"PS:> Get-PnPAvailableClientSideComponents $page",
+ Remarks = "Gets the list of available client side components on the page contained in the $page variable",
+ SortOrder = 2)]
+ [CmdletExample(
+ Code = @"PS:> Get-PnPAvailableClientSideComponents -Identity ""MyPage.aspx"" -ComponentName ""HelloWorld""",
+ Remarks = "Gets the client side component 'HelloWorld' if available on the page 'MyPage.aspx'",
+ SortOrder = 3)]
+ public class GetAvailableClientSideComponents : PnPWebCmdlet
+ {
+ [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, HelpMessage = "The name of the page.")]
+ public ClientSidePagePipeBind Page;
+
+ [Parameter(Mandatory = false, HelpMessage = "Specifies the component instance or Id to look for.")]
+ public ClientSideComponentPipeBind Component;
+
+ protected override void ExecuteCmdlet()
+ {
+ var clientSidePage = Page.GetPage(ClientContext);
+
+ if (Component == null)
+ {
+ var allComponents = clientSidePage.AvailableClientSideComponents().Where(c => c.ComponentType == 1);
+ WriteObject(allComponents, true);
+ }
+ else
+ {
+ WriteObject(Component.GetComponent(clientSidePage));
+ }
+ }
+ }
+}
+#endif
diff --git a/Commands/ClientSidePages/GetClientSidePage.cs b/Commands/ClientSidePages/GetClientSidePage.cs
new file mode 100644
index 000000000..f6400e89e
--- /dev/null
+++ b/Commands/ClientSidePages/GetClientSidePage.cs
@@ -0,0 +1,36 @@
+#if !ONPREMISES
+using SharePointPnP.PowerShell.CmdletHelpAttributes;
+using SharePointPnP.PowerShell.Commands.Base.PipeBinds;
+using System;
+using System.Management.Automation;
+
+namespace SharePointPnP.PowerShell.Commands.ClientSidePages
+{
+ [Cmdlet(VerbsCommon.Get, "PnPClientSidePage")]
+ [CmdletHelp("Gets a Client-Side Page",
+ Category = CmdletHelpCategory.ClientSidePages, SupportedPlatform = CmdletSupportedPlatform.Online)]
+ [CmdletExample(
+ Code = @"PS:> Get-PnPClientSidePage -Identity ""MyPage.aspx""",
+ Remarks = "Gets the Modern Page (Client-Side) called 'MyPage.aspx' in the current SharePoint site",
+ SortOrder = 2)]
+ [CmdletExample(
+ Code = @"PS:> Get-PnPClientSidePage ""MyPage""",
+ Remarks = "Gets the Modern Page (Client-Side) called 'MyPage.aspx' in the current SharePoint site",
+ SortOrder = 2)]
+ public class GetClientSidePage : PnPWebCmdlet
+ {
+ [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, HelpMessage = "The name of the page")]
+ public ClientSidePagePipeBind Identity;
+
+ protected override void ExecuteCmdlet()
+ {
+ var clientSidePage = Identity.GetPage(ClientContext);
+
+ if (clientSidePage == null)
+ throw new Exception($"Page '{Identity?.Name}' does not exist");
+
+ WriteObject(clientSidePage);
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/Commands/ClientSidePages/RemoveClientSidePage.cs b/Commands/ClientSidePages/RemoveClientSidePage.cs
new file mode 100644
index 000000000..ce90b7b7d
--- /dev/null
+++ b/Commands/ClientSidePages/RemoveClientSidePage.cs
@@ -0,0 +1,42 @@
+#if !ONPREMISES
+using SharePointPnP.PowerShell.CmdletHelpAttributes;
+using SharePointPnP.PowerShell.Commands.Base.PipeBinds;
+using SharePointPnP.PowerShell.Commands.Properties;
+using System;
+using System.Management.Automation;
+
+namespace SharePointPnP.PowerShell.Commands.ClientSidePages
+{
+ [Cmdlet(VerbsCommon.Remove, "PnPClientSidePage")]
+ [CmdletHelp("Removes a Client-Side Page",
+ Category = CmdletHelpCategory.ClientSidePages, SupportedPlatform = CmdletSupportedPlatform.Online)]
+ [CmdletExample(
+ Code = @"PS:> Remove-PnPClientSidePage -Identity ""MyPage""",
+ Remarks = "Removes the Client-Side page called 'MyPage.aspx'",
+ SortOrder = 1)]
+ [CmdletExample(
+ Code = @"PS:> Remove-PnPClientSidePage $page",
+ Remarks = "Removes the specified Client-Side page which is contained in the $page variable.",
+ SortOrder = 2)]
+ public class RemoveClientSidePage : PnPWebCmdlet
+ {
+ [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, HelpMessage = "The name of the page")]
+ public ClientSidePagePipeBind Identity;
+
+ [Parameter(Mandatory = false, HelpMessage = "Specifying the Force parameter will skip the confirmation question.")]
+ public SwitchParameter Force;
+
+ protected override void ExecuteCmdlet()
+ {
+ if (Force || ShouldContinue(Resources.RemoveClientSidePage, Resources.Confirm))
+ {
+ var clientSidePage = Identity.GetPage(ClientContext);
+ if (clientSidePage == null)
+ throw new Exception($"Page '{Identity?.Name}' does not exist");
+
+ clientSidePage.Delete();
+ }
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/Commands/ClientSidePages/SetClientSidePage.cs b/Commands/ClientSidePages/SetClientSidePage.cs
new file mode 100644
index 000000000..126f11771
--- /dev/null
+++ b/Commands/ClientSidePages/SetClientSidePage.cs
@@ -0,0 +1,92 @@
+#if !ONPREMISES
+using OfficeDevPnP.Core.Pages;
+using SharePointPnP.PowerShell.CmdletHelpAttributes;
+using SharePointPnP.PowerShell.Commands.Base.PipeBinds;
+using System;
+using System.Management.Automation;
+
+namespace SharePointPnP.PowerShell.Commands.ClientSidePages
+{
+ [Cmdlet(VerbsCommon.Set, "PnPClientSidePage")]
+ [CmdletHelp("Sets parameters of a Client-Side Page",
+ Category = CmdletHelpCategory.ClientSidePages, SupportedPlatform = CmdletSupportedPlatform.Online)]
+ [CmdletExample(
+ Code = @"PS:> Set-PnPClientSidePage -Identity ""MyPage"" -LayoutType Home",
+ Remarks = "Updates the properties of the Client-Side page called 'MyPage'",
+ SortOrder = 1)]
+ public class SetClientSidePage : PnPWebCmdlet
+ {
+ [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, HelpMessage = "The name/identity of the page")]
+ public ClientSidePagePipeBind Identity;
+
+ [Parameter(Mandatory = false, HelpMessage = "Sets the name of the page.")]
+ public string Name = null;
+
+ [Parameter(Mandatory = false, HelpMessage = "Sets the layout type of the page. (Default = Article)")]
+ public ClientSidePageLayoutType LayoutType = ClientSidePageLayoutType.Article;
+
+ [Parameter(Mandatory = false, HelpMessage = "Allows to promote the page for a specific purpose (HomePage | NewsPage)")]
+ public ClientSidePagePromoteType PromoteAs = ClientSidePagePromoteType.None;
+
+ [Parameter(Mandatory = false, HelpMessage = "Enables or Disables the comments on the page")]
+ public bool? CommentsEnabled = null;
+
+ [Parameter(Mandatory = false, HelpMessage = "Publishes the page once it is saved.")]
+ public SwitchParameter Publish;
+
+ [Parameter(Mandatory = false, HelpMessage = "Sets the message for publishing the page.")]
+ public string PublishMessage = string.Empty;
+
+ protected override void ExecuteCmdlet()
+ {
+
+ ClientSidePage clientSidePage = Identity?.GetPage(ClientContext);
+
+ if (clientSidePage == null)
+ // If the client side page object cannot be found
+ throw new Exception($"Page {Identity?.Name} cannot be found.");
+
+ // We need to have the page name, if not found, raise an error
+ string name = ClientSidePageUtilities.EnsureCorrectPageName(Name ?? Identity?.Name);
+ if (name == null)
+ throw new Exception("Insufficient arguments to add a client side page");
+
+ clientSidePage.LayoutType = LayoutType;
+ clientSidePage.Save(name);
+
+ // If a specific promote type is specified, promote the page as Home or Article or ...
+ switch (PromoteAs)
+ {
+ case ClientSidePagePromoteType.HomePage:
+ clientSidePage.PromoteAsHomePage();
+ break;
+ case ClientSidePagePromoteType.NewsArticle:
+ clientSidePage.PromoteAsNewsArticle();
+ break;
+ case ClientSidePagePromoteType.None:
+ default:
+ break;
+ }
+
+ if (CommentsEnabled.HasValue)
+ {
+ if (CommentsEnabled.Value)
+ {
+ clientSidePage.EnableComments();
+ }
+ else
+ {
+ clientSidePage.DisableComments();
+ }
+ }
+
+ if (Publish)
+ {
+ clientSidePage.Publish(PublishMessage);
+ }
+
+ WriteObject(clientSidePage);
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/Commands/Enums/ClientSidePagePromoteType.cs b/Commands/Enums/ClientSidePagePromoteType.cs
new file mode 100644
index 000000000..b9137b3c3
--- /dev/null
+++ b/Commands/Enums/ClientSidePagePromoteType.cs
@@ -0,0 +1,17 @@
+#if !ONPREMISES
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SharePointPnP.PowerShell.Commands.ClientSidePages
+{
+ public enum ClientSidePagePromoteType
+ {
+ None = 0,
+ HomePage = 1,
+ NewsArticle = 2,
+ }
+}
+#endif
diff --git a/Commands/ModuleFiles/SharePointPnP.PowerShell.Online.Commands.Format.ps1xml b/Commands/ModuleFiles/SharePointPnP.PowerShell.Online.Commands.Format.ps1xml
index bb2cb6e98..534544dd3 100644
--- a/Commands/ModuleFiles/SharePointPnP.PowerShell.Online.Commands.Format.ps1xml
+++ b/Commands/ModuleFiles/SharePointPnP.PowerShell.Online.Commands.Format.ps1xml
@@ -1270,5 +1270,42 @@
+
+ ClientSideComponent
+
+ OfficeDevPnP.Core.Pages.ClientSideComponent
+
+
+
+
+
+ left
+
+
+
+ left
+
+
+
+ left
+
+
+
+
+
+
+ Id
+
+
+ Name
+
+
+ (ConvertFrom-Json $_.Manifest).alias
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Commands/Properties/Resources.Designer.cs b/Commands/Properties/Resources.Designer.cs
index c4e19c603..3019863f4 100644
--- a/Commands/Properties/Resources.Designer.cs
+++ b/Commands/Properties/Resources.Designer.cs
@@ -341,6 +341,15 @@ internal class Resources {
}
}
+ ///
+ /// Looks up a localized string similar to Remove Client-Side Page?.
+ ///
+ internal static string RemoveClientSidePage {
+ get {
+ return ResourceManager.GetString("RemoveClientSidePage", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Remove content type?.
///
diff --git a/Commands/Properties/Resources.resx b/Commands/Properties/Resources.resx
index bc0c741fc..a13f33bb5 100644
--- a/Commands/Properties/Resources.resx
+++ b/Commands/Properties/Resources.resx
@@ -289,6 +289,9 @@
Remove Webhook Subscription '{0}' from {1} '{2}' ?
+
+ Remove Client-Side Page?
+
Remove User with Id {0}, LoginName '{1}', Email '{2}' from the User Information List?
diff --git a/Commands/SharePointPnP.PowerShell.Commands.csproj b/Commands/SharePointPnP.PowerShell.Commands.csproj
index 0b0a2a153..cfc6f0d29 100644
--- a/Commands/SharePointPnP.PowerShell.Commands.csproj
+++ b/Commands/SharePointPnP.PowerShell.Commands.csproj
@@ -406,6 +406,9 @@
+
+
+
@@ -482,6 +485,16 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/Documentation/AddPnPClientSidePage.md b/Documentation/AddPnPClientSidePage.md
new file mode 100644
index 000000000..3fa67eb62
--- /dev/null
+++ b/Documentation/AddPnPClientSidePage.md
@@ -0,0 +1,32 @@
+# Add-PnPClientSidePage
+Adds a Client-Side Page
+>*Only available for SharePoint Online*
+## Syntax
+```powershell
+Add-PnPClientSidePage -Name
+ [-LayoutType ]
+ [-PromoteAs ]
+ [-CommentsEnabled ]
+ [-Publish []]
+ [-PublishMessage ]
+ [-Web ]
+```
+
+
+## Parameters
+Parameter|Type|Required|Description
+---------|----|--------|-----------
+|Name|String|True|Specifies the name of the page.|
+|CommentsEnabled|Nullable`1|False|Enables or Disables the comments on the page|
+|LayoutType|ClientSidePageLayoutType|False|Specifies the layout type of the page.|
+|PromoteAs|ClientSidePagePromoteType|False|Allows to promote the page for a specific purpose (HomePage | NewsPage)|
+|Publish|SwitchParameter|False|Publishes the page once it is saved. Applicable to libraries set to create major and minor versions.|
+|PublishMessage|String|False|Sets the message for publishing the page.|
+|Web|WebPipeBind|False|The web to apply the command to. Omit this parameter to use the current web.|
+## Examples
+
+### Example 1
+```powershell
+PS:> Add-PnPClientSidePage -PageName "OurNewPage"
+```
+Creates a new Client-Side page called 'OurNewPage'
diff --git a/Documentation/AddPnPClientSidePageSection.md b/Documentation/AddPnPClientSidePageSection.md
new file mode 100644
index 000000000..882be7f8a
--- /dev/null
+++ b/Documentation/AddPnPClientSidePageSection.md
@@ -0,0 +1,39 @@
+# Add-PnPClientSidePageSection
+Adds a new section to a Client-Side page
+>*Only available for SharePoint Online*
+## Syntax
+```powershell
+Add-PnPClientSidePageSection -SectionTemplate
+ -Page
+ [-Order ]
+ [-Web ]
+```
+
+
+## Parameters
+Parameter|Type|Required|Description
+---------|----|--------|-----------
+|Page|ClientSidePagePipeBind|True|The name of the page|
+|SectionTemplate|CanvasSectionTemplate|True|Specifies the columns template to use for the section.|
+|Order|Int|False|Sets the order of the section. (Default = 1)|
+|Web|WebPipeBind|False|The web to apply the command to. Omit this parameter to use the current web.|
+## Examples
+
+### Example 1
+```powershell
+PS:> Add-PnPClientSidePageSection -Page "MyPage" -SectionTemplate OneColumn
+```
+Adds a new one-column section to the Client-Side page 'MyPage'
+
+### Example 2
+```powershell
+PS:> Add-PnPClientSidePageSection -Page "MyPage" -SectionTemplate ThreeColumn -Order 10
+```
+Adds a new Three columns section to the Client-Side page 'MyPage' with an order index of 10
+
+### Example 3
+```powershell
+PS:> $page = Add-PnPClientSidePage -Name "MyPage"
+PS> Add-PnPClientSidePageSection -Page $page -SectionTemplate OneColumn
+```
+Adds a new one column section to the Client-Side page 'MyPage'
diff --git a/Documentation/AddPnPClientSideText.md b/Documentation/AddPnPClientSideText.md
new file mode 100644
index 000000000..8dcc0b687
--- /dev/null
+++ b/Documentation/AddPnPClientSideText.md
@@ -0,0 +1,38 @@
+# Add-PnPClientSideText
+Adds a Client-Side Page
+>*Only available for SharePoint Online*
+## Syntax
+```powershell
+Add-PnPClientSideText -Text
+ -Page
+ [-Order ]
+ [-Web ]
+```
+
+
+```powershell
+Add-PnPClientSideText -Text
+ -Section
+ -Column
+ -Page
+ [-Order ]
+ [-Web ]
+```
+
+
+## Parameters
+Parameter|Type|Required|Description
+---------|----|--------|-----------
+|Column|Int|True|Sets the column where to insert the text control.|
+|Page|ClientSidePagePipeBind|True|The name of the page.|
+|Section|Int|True|Sets the section where to insert the text control.|
+|Text|String|True|Specifies the text to display in the text area.|
+|Order|Int|False|Sets the order of the text control. (Default = 1)|
+|Web|WebPipeBind|False|The web to apply the command to. Omit this parameter to use the current web.|
+## Examples
+
+### Example 1
+```powershell
+PS:> Add-PnPClientSideText -Page "OurNewPage" -Text "Hello World!"
+```
+Adds the text 'Hello World!' to the Client-Side Page 'OurNewPage'
diff --git a/Documentation/AddPnPClientSideWebPart.md b/Documentation/AddPnPClientSideWebPart.md
new file mode 100644
index 000000000..1c9f10c3d
--- /dev/null
+++ b/Documentation/AddPnPClientSideWebPart.md
@@ -0,0 +1,74 @@
+# Add-PnPClientSideWebPart
+Adds a Client-Side Component to a page
+>*Only available for SharePoint Online*
+## Syntax
+```powershell
+Add-PnPClientSideWebPart -DefaultWebPartType
+ -Page
+ [-WebPartProperties ]
+ [-Order ]
+ [-Web ]
+```
+
+
+```powershell
+Add-PnPClientSideWebPart -Component
+ -Page
+ [-WebPartProperties ]
+ [-Order ]
+ [-Web ]
+```
+
+
+```powershell
+Add-PnPClientSideWebPart -DefaultWebPartType
+ -Section
+ -Column
+ -Page
+ [-WebPartProperties ]
+ [-Order ]
+ [-Web ]
+```
+
+
+```powershell
+Add-PnPClientSideWebPart -Component
+ -Section
+ -Column
+ -Page
+ [-WebPartProperties ]
+ [-Order ]
+ [-Web ]
+```
+
+
+## Parameters
+Parameter|Type|Required|Description
+---------|----|--------|-----------
+|Column|Int|True|Sets the column where to insert the WebPart control.|
+|Component|ClientSideComponentPipeBind|True|Specifies the component instance or Id to add.|
+|DefaultWebPartType|DefaultClientSideWebParts|True|Defines a default WebPart type to insert.|
+|Page|ClientSidePagePipeBind|True|The name of the page.|
+|Section|Int|True|Sets the section where to insert the WebPart control.|
+|Order|Int|False|Sets the order of the WebPart control. (Default = 1)|
+|Web|WebPipeBind|False|The web to apply the command to. Omit this parameter to use the current web.|
+|WebPartProperties|PropertyBagPipeBind|False|The properties of the WebPart|
+## Examples
+
+### Example 1
+```powershell
+PS:> Add-PnPClientSideWebPart -Page "OurNewPage" -DefaultWebPartType BingMap
+```
+Adds a built-in Client-Side component 'BingMap' to the page called 'OurNewPage'
+
+### Example 2
+```powershell
+PS:> Add-PnPClientSideWebPart -Page "OurNewPage" -Component "HelloWorld"
+```
+Adds a Client-Side component 'HelloWorld' to the page called 'OurNewPage'
+
+### Example 3
+```powershell
+PS:> Add-PnPClientSideWebPart -Page "OurNewPage" -Component "HelloWorld" -Section 1 -Column 2
+```
+Adds a Client-Side component 'HelloWorld' to the page called 'OurNewPage' in section 1 and column 2
diff --git a/Documentation/GetPnPAvailableClientSideComponents.md b/Documentation/GetPnPAvailableClientSideComponents.md
new file mode 100644
index 000000000..ea450318b
--- /dev/null
+++ b/Documentation/GetPnPAvailableClientSideComponents.md
@@ -0,0 +1,36 @@
+# Get-PnPAvailableClientSideComponents
+Gets the available client side components on a particular page
+>*Only available for SharePoint Online*
+## Syntax
+```powershell
+Get-PnPAvailableClientSideComponents -Page
+ [-Component ]
+ [-Web ]
+```
+
+
+## Parameters
+Parameter|Type|Required|Description
+---------|----|--------|-----------
+|Page|ClientSidePagePipeBind|True|The name of the page.|
+|Component|ClientSideComponentPipeBind|False|Specifies the component instance or Id to look for.|
+|Web|WebPipeBind|False|The web to apply the command to. Omit this parameter to use the current web.|
+## Examples
+
+### Example 1
+```powershell
+PS:> Get-PnPAvailableClientSideComponents -Identity "MyPage.aspx"
+```
+Gets the list of available client side components on the page 'MyPage.aspx'
+
+### Example 2
+```powershell
+PS:> Get-PnPAvailableClientSideComponents $page
+```
+Gets the list of available client side components on the page contained in the $page variable
+
+### Example 3
+```powershell
+PS:> Get-PnPAvailableClientSideComponents -Identity "MyPage.aspx" -ComponentName "HelloWorld"
+```
+Gets the client side component 'HelloWorld' if available on the page 'MyPage.aspx'
diff --git a/Documentation/GetPnPClientSidePage.md b/Documentation/GetPnPClientSidePage.md
new file mode 100644
index 000000000..f2479752b
--- /dev/null
+++ b/Documentation/GetPnPClientSidePage.md
@@ -0,0 +1,28 @@
+# Get-PnPClientSidePage
+Gets a Client-Side Page
+>*Only available for SharePoint Online*
+## Syntax
+```powershell
+Get-PnPClientSidePage -Identity
+ [-Web ]
+```
+
+
+## Parameters
+Parameter|Type|Required|Description
+---------|----|--------|-----------
+|Identity|ClientSidePagePipeBind|True|The name of the page|
+|Web|WebPipeBind|False|The web to apply the command to. Omit this parameter to use the current web.|
+## Examples
+
+### Example 1
+```powershell
+PS:> Get-PnPClientSidePage -Identity "MyPage.aspx"
+```
+Gets the Modern Page (Client-Side) called 'MyPage.aspx' in the current SharePoint site
+
+### Example 2
+```powershell
+PS:> Get-PnPClientSidePage "MyPage"
+```
+Gets the Modern Page (Client-Side) called 'MyPage.aspx' in the current SharePoint site
diff --git a/Documentation/MSDN/Client-SidePages-category.md b/Documentation/MSDN/Client-SidePages-category.md
new file mode 100644
index 000000000..5f56553ac
--- /dev/null
+++ b/Documentation/MSDN/Client-SidePages-category.md
@@ -0,0 +1,11 @@
+# Client-Side Pages
+Cmdlet|Description|Platform
+:-----|:----------|:-------
+**[Get‑PnPAvailableClientSideComponents](GetPnPAvailableClientSideComponents.md)** |Gets the available client side components on a particular page|SharePoint Online
+**[Add‑PnPClientSidePage](AddPnPClientSidePage.md)** |Adds a Client-Side Page|SharePoint Online
+**[Get‑PnPClientSidePage](GetPnPClientSidePage.md)** |Gets a Client-Side Page|SharePoint Online
+**[Remove‑PnPClientSidePage](RemovePnPClientSidePage.md)** |Removes a Client-Side Page|SharePoint Online
+**[Set‑PnPClientSidePage](SetPnPClientSidePage.md)** |Sets parameters of a Client-Side Page|SharePoint Online
+**[Add‑PnPClientSidePageSection](AddPnPClientSidePageSection.md)** |Adds a new section to a Client-Side page|SharePoint Online
+**[Add‑PnPClientSideText](AddPnPClientSideText.md)** |Adds a Client-Side Page|SharePoint Online
+**[Add‑PnPClientSideWebPart](AddPnPClientSideWebPart.md)** |Adds a Client-Side Component to a page|SharePoint Online
diff --git a/Documentation/MSDN/PnP-PowerShell-Overview.md b/Documentation/MSDN/PnP-PowerShell-Overview.md
index 1031b8269..43a62bca6 100644
--- a/Documentation/MSDN/PnP-PowerShell-Overview.md
+++ b/Documentation/MSDN/PnP-PowerShell-Overview.md
@@ -127,6 +127,19 @@ Cmdlet|Description|Platform
**[Set‑PnPTheme](SetPnPTheme.md)** |Sets the theme of the current web.|All
+### Client-Side Pages
+Cmdlet|Description|Platform
+:-----|:----------|:-------
+**[Get‑PnPAvailableClientSideComponents](GetPnPAvailableClientSideComponents.md)** |Gets the available client side components on a particular page|SharePoint Online
+**[Add‑PnPClientSidePage](AddPnPClientSidePage.md)** |Adds a Client-Side Page|SharePoint Online
+**[Get‑PnPClientSidePage](GetPnPClientSidePage.md)** |Gets a Client-Side Page|SharePoint Online
+**[Remove‑PnPClientSidePage](RemovePnPClientSidePage.md)** |Removes a Client-Side Page|SharePoint Online
+**[Set‑PnPClientSidePage](SetPnPClientSidePage.md)** |Sets parameters of a Client-Side Page|SharePoint Online
+**[Add‑PnPClientSidePageSection](AddPnPClientSidePageSection.md)** |Adds a new section to a Client-Side page|SharePoint Online
+**[Add‑PnPClientSideText](AddPnPClientSideText.md)** |Adds a Client-Side Page|SharePoint Online
+**[Add‑PnPClientSideWebPart](AddPnPClientSideWebPart.md)** |Adds a Client-Side Component to a page|SharePoint Online
+
+
### Content Types
Cmdlet|Description|Platform
:-----|:----------|:-------
diff --git a/Documentation/MSDN/TOC.md b/Documentation/MSDN/TOC.md
index 177f04f04..db7fc9e42 100644
--- a/Documentation/MSDN/TOC.md
+++ b/Documentation/MSDN/TOC.md
@@ -35,6 +35,15 @@
### [Enable-PnPResponsiveUI](EnablePnPResponsiveUI.md)
### [Get-PnPTheme](GetPnPTheme.md)
### [Set-PnPTheme](SetPnPTheme.md)
+## [Client-Side Pages](Client-SidePages-category.md)
+### [Get-PnPAvailableClientSideComponents](GetPnPAvailableClientSideComponents.md)
+### [Add-PnPClientSidePage](AddPnPClientSidePage.md)
+### [Get-PnPClientSidePage](GetPnPClientSidePage.md)
+### [Remove-PnPClientSidePage](RemovePnPClientSidePage.md)
+### [Set-PnPClientSidePage](SetPnPClientSidePage.md)
+### [Add-PnPClientSidePageSection](AddPnPClientSidePageSection.md)
+### [Add-PnPClientSideText](AddPnPClientSideText.md)
+### [Add-PnPClientSideWebPart](AddPnPClientSideWebPart.md)
## [Content Types](ContentTypes-category.md)
### [Add-PnPContentType](AddPnPContentType.md)
### [Get-PnPContentType](GetPnPContentType.md)
diff --git a/Documentation/RemovePnPClientSidePage.md b/Documentation/RemovePnPClientSidePage.md
new file mode 100644
index 000000000..fb197f896
--- /dev/null
+++ b/Documentation/RemovePnPClientSidePage.md
@@ -0,0 +1,30 @@
+# Remove-PnPClientSidePage
+Removes a Client-Side Page
+>*Only available for SharePoint Online*
+## Syntax
+```powershell
+Remove-PnPClientSidePage -Identity
+ [-Force []]
+ [-Web ]
+```
+
+
+## Parameters
+Parameter|Type|Required|Description
+---------|----|--------|-----------
+|Identity|ClientSidePagePipeBind|True|The name of the page|
+|Force|SwitchParameter|False|Specifying the Force parameter will skip the confirmation question.|
+|Web|WebPipeBind|False|The web to apply the command to. Omit this parameter to use the current web.|
+## Examples
+
+### Example 1
+```powershell
+PS:> Remove-PnPClientSidePage -Identity "MyPage"
+```
+Removes the Client-Side page called 'MyPage.aspx'
+
+### Example 2
+```powershell
+PS:> Remove-PnPClientSidePage $page
+```
+Removes the specified Client-Side page which is contained in the $page variable.
diff --git a/Documentation/SetPnPClientSidePage.md b/Documentation/SetPnPClientSidePage.md
new file mode 100644
index 000000000..cbdcbfc98
--- /dev/null
+++ b/Documentation/SetPnPClientSidePage.md
@@ -0,0 +1,34 @@
+# Set-PnPClientSidePage
+Sets parameters of a Client-Side Page
+>*Only available for SharePoint Online*
+## Syntax
+```powershell
+Set-PnPClientSidePage -Identity
+ [-Name ]
+ [-LayoutType ]
+ [-PromoteAs ]
+ [-CommentsEnabled ]
+ [-Publish []]
+ [-PublishMessage ]
+ [-Web ]
+```
+
+
+## Parameters
+Parameter|Type|Required|Description
+---------|----|--------|-----------
+|Identity|ClientSidePagePipeBind|True|The name/identity of the page|
+|CommentsEnabled|Nullable`1|False|Enables or Disables the comments on the page|
+|LayoutType|ClientSidePageLayoutType|False|Sets the layout type of the page. (Default = Article)|
+|Name|String|False|Sets the name of the page.|
+|PromoteAs|ClientSidePagePromoteType|False|Allows to promote the page for a specific purpose (HomePage | NewsPage)|
+|Publish|SwitchParameter|False|Publishes the page once it is saved.|
+|PublishMessage|String|False|Sets the message for publishing the page.|
+|Web|WebPipeBind|False|The web to apply the command to. Omit this parameter to use the current web.|
+## Examples
+
+### Example 1
+```powershell
+PS:> Set-PnPClientSidePage -Identity "MyPage" -LayoutType Home
+```
+Updates the properties of the Client-Side page called 'MyPage'
diff --git a/Documentation/readme.md b/Documentation/readme.md
index 2e6e3cf09..212e843f0 100644
--- a/Documentation/readme.md
+++ b/Documentation/readme.md
@@ -47,6 +47,17 @@ Cmdlet|Description|Platforms
**[Enable‑PnPResponsiveUI](EnablePnPResponsiveUI.md)** |Enables the PnP Responsive UI implementation on a classic SharePoint Site|All
**[Get‑PnPTheme](GetPnPTheme.md)** |Returns the current theme/composed look of the current web.|All
**[Set‑PnPTheme](SetPnPTheme.md)** |Sets the theme of the current web.|All
+## Client-Side Pages
+Cmdlet|Description|Platforms
+:-----|:----------|:--------
+**[Get‑PnPAvailableClientSideComponents](GetPnPAvailableClientSideComponents.md)** |Gets the available client side components on a particular page|SharePoint Online
+**[Add‑PnPClientSidePage](AddPnPClientSidePage.md)** |Adds a Client-Side Page|SharePoint Online
+**[Get‑PnPClientSidePage](GetPnPClientSidePage.md)** |Gets a Client-Side Page|SharePoint Online
+**[Remove‑PnPClientSidePage](RemovePnPClientSidePage.md)** |Removes a Client-Side Page|SharePoint Online
+**[Set‑PnPClientSidePage](SetPnPClientSidePage.md)** |Sets parameters of a Client-Side Page|SharePoint Online
+**[Add‑PnPClientSidePageSection](AddPnPClientSidePageSection.md)** |Adds a new section to a Client-Side page|SharePoint Online
+**[Add‑PnPClientSideText](AddPnPClientSideText.md)** |Adds a Client-Side Page|SharePoint Online
+**[Add‑PnPClientSideWebPart](AddPnPClientSideWebPart.md)** |Adds a Client-Side Component to a page|SharePoint Online
## Content Types
Cmdlet|Description|Platforms
:-----|:----------|:--------
diff --git a/HelpAttributes/CmdletHelpCategory.cs b/HelpAttributes/CmdletHelpCategory.cs
index 3e82aa3d3..db261496f 100644
--- a/HelpAttributes/CmdletHelpCategory.cs
+++ b/HelpAttributes/CmdletHelpCategory.cs
@@ -44,6 +44,8 @@ public enum CmdletHelpCategory
[EnumMember(Value = "SharePoint WebHooks")]
Webhooks = 25,
[EnumMember(Value = "Records Management")]
- RecordsManagement = 26
+ RecordsManagement = 26,
+ [EnumMember(Value = "Client-Side Pages")]
+ ClientSidePages = 27
}
}
diff --git a/Tests/ModernPagesTests.cs b/Tests/ModernPagesTests.cs
new file mode 100644
index 000000000..1ef903a1b
--- /dev/null
+++ b/Tests/ModernPagesTests.cs
@@ -0,0 +1,250 @@
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Management.Automation.Runspaces;
+using Microsoft.SharePoint.Client;
+using System.Linq;
+using OfficeDevPnP.Core.Pages;
+using System.Collections;
+
+namespace SharePointPnP.PowerShell.Tests
+{
+ [TestClass]
+ public class ModernPagesTests
+ {
+
+ public const string PageTestNewDefaultName = "Page 1.aspx";
+ public const string PageTestNewName = "NewPage.aspx";
+ public const string PageTestWithoutExtensionName = "PageWithoutExtensionTest";
+ public const string PageTestWithExtensionName = "PageWithExtensionTest.aspx";
+ public const string PagePipedTestName = "PagePipedTest.aspx";
+ public const string PageGetTestName = "PageGetTest.aspx";
+ public const string PageRemoveTestName = "PageRemoveTest.aspx";
+ public const string PageSetTestName = "PageSetTest.aspx";
+ public const string PageSet2TestName = "PageSet2Test.aspx";
+ public const string PageNotExistingTestName = "PageNotExisting.aspx";
+ public const string PageAddSectionTestName = "PageAddSection.aspx";
+ public const string PageAddWebPartTestName = "PageAddWebPart.aspx";
+
+ private void CleanupPageIfExists(ClientContext ctx, string pageName)
+ {
+ try
+ {
+ pageName = pageName.EndsWith(".aspx") ? pageName : pageName + ".aspx";
+ var p = ClientSidePage.Load(ctx, pageName);
+ p.Delete();
+ }
+ catch (Exception) { }
+ }
+
+ [TestCleanup]
+ public void Cleanup()
+ {
+ using (var ctx = TestCommon.CreateClientContext())
+ {
+ // Delete all the test pages if they exist
+ CleanupPageIfExists(ctx, PageTestNewDefaultName);
+ CleanupPageIfExists(ctx, PageTestNewName);
+ CleanupPageIfExists(ctx, PageTestWithoutExtensionName);
+ CleanupPageIfExists(ctx, PageTestWithExtensionName);
+ CleanupPageIfExists(ctx, PagePipedTestName);
+ CleanupPageIfExists(ctx, PageGetTestName);
+ CleanupPageIfExists(ctx, PageRemoveTestName);
+ CleanupPageIfExists(ctx, PageSetTestName);
+ CleanupPageIfExists(ctx, PageSet2TestName);
+ CleanupPageIfExists(ctx, PageAddSectionTestName);
+ CleanupPageIfExists(ctx, PageAddWebPartTestName);
+ }
+ }
+
+ [TestMethod]
+ public void NewClientSidePageTest()
+ {
+ using (var scope = new PSTestScope(true))
+ {
+ var results = scope.ExecuteCommand("New-PnPClientSidePage");
+ var page = results[0].BaseObject as ClientSidePage;
+ Assert.IsNotNull(page);
+ }
+ }
+
+ [TestMethod]
+ public void NewClientSidePageWithNameTest()
+ {
+ using (var scope = new PSTestScope(true))
+ {
+ var results = scope.ExecuteCommand("New-PnPClientSidePage",
+ new CommandParameter("Name", PageTestNewName));
+
+ var page = results[0].BaseObject as ClientSidePage;
+ Assert.IsNotNull(page);
+ }
+ }
+
+ [TestMethod]
+ public void AddClientSidePageWithNameWithoutExtensionTest()
+ {
+ using (var scope = new PSTestScope(true))
+ {
+ var results = scope.ExecuteCommand("Add-PnPClientSidePage",
+ new CommandParameter("Name", PageTestWithoutExtensionName));
+
+ var page = results[0].BaseObject as ClientSidePage;
+ string pageName = page.PageListItem["FileLeafRef"] as string;
+ Assert.IsTrue(page != null && pageName == PageTestWithoutExtensionName + ".aspx");
+ }
+ }
+
+ [TestMethod]
+ public void AddClientSidePageWithNameWithExtensionTest()
+ {
+ using (var scope = new PSTestScope(true))
+ {
+ var results = scope.ExecuteCommand("Add-PnPClientSidePage",
+ new CommandParameter("Name", PageTestWithExtensionName));
+
+ var page = results[0].BaseObject as ClientSidePage;
+ string pageName = page.PageListItem["FileLeafRef"] as string;
+ Assert.IsTrue(page != null && pageName == PageTestWithExtensionName);
+ }
+ }
+
+
+ [TestMethod]
+ public void GetClientSidePageTest()
+ {
+ using (var scope = new PSTestScope(true))
+ {
+ using (var ctx = TestCommon.CreateClientContext())
+ {
+ ctx.Web.AddClientSidePage(PageGetTestName, true);
+ }
+
+ var results = scope.ExecuteCommand("Get-PnPClientSidePage",
+ new CommandParameter("Identity", PageGetTestName));
+
+ var page = results[0].BaseObject as ClientSidePage;
+ string pageName = page.PageListItem["FileLeafRef"] as string;
+ Assert.IsTrue(page != null && pageName == PageGetTestName);
+ }
+ }
+
+
+ [TestMethod]
+ public void GetClientSidePageNotExistingTest()
+ {
+ using (var scope = new PSTestScope(true))
+ {
+ try
+ {
+ scope.ExecuteCommand("Get-PnPClientSidePage",
+ new CommandParameter("Identity", PageNotExistingTestName));
+ Assert.Fail();
+ }
+ catch (Exception)
+ {
+ // An exception should be thrown
+ Assert.IsTrue(true);
+ }
+
+ }
+ }
+
+ [TestMethod]
+ public void SetClientSidePageTest()
+ {
+ using (var scope = new PSTestScope(true))
+ {
+ using (var ctx = TestCommon.CreateClientContext())
+ {
+ ctx.Web.AddClientSidePage(PageSetTestName, true);
+
+
+ var results = scope.ExecuteCommand("Set-PnPClientSidePage",
+ new CommandParameter("Identity", PageSetTestName),
+ new CommandParameter("LayoutType", ClientSidePageLayoutType.Home),
+ new CommandParameter("Name", PageSet2TestName));
+
+ var page = ClientSidePage.Load(ctx, PageSet2TestName);
+
+ Assert.IsTrue(page.LayoutType == ClientSidePageLayoutType.Home);
+ }
+ }
+ }
+
+ // TODO Add more test cases
+
+ [TestMethod]
+ [ExpectedException(typeof(ArgumentException),
+ "The page does not exist.")]
+ public void RemoveClientSidePageTest()
+ {
+ using (var scope = new PSTestScope(true))
+ {
+ using (var ctx = TestCommon.CreateClientContext())
+ {
+ ctx.Web.AddClientSidePage(PageRemoveTestName, true);
+
+ scope.ExecuteCommand("Remove-PnPClientSidePage",
+ new CommandParameter("Identity", PageRemoveTestName),
+ new CommandParameter("Force"));
+
+
+ var p = ClientSidePage.Load(ctx, PageRemoveTestName);
+ }
+ }
+ }
+
+ [TestMethod]
+ public void AddClientSidePageSectionTest()
+ {
+ using (var scope = new PSTestScope(true))
+ {
+ using (var ctx = TestCommon.CreateClientContext())
+ {
+ ctx.Web.AddClientSidePage(PageAddSectionTestName, true);
+
+
+ var results = scope.ExecuteCommand("Add-PnPClientSidePageSection",
+ new CommandParameter("Page", PageAddSectionTestName),
+ new CommandParameter("SectionTemplate", CanvasSectionTemplate.ThreeColumn),
+ new CommandParameter("Order", 10));
+
+ var page = ClientSidePage.Load(ctx, PageAddSectionTestName);
+
+ Assert.IsTrue(page.Sections[0].Columns.Count == 3);
+ }
+ }
+ }
+
+ [TestMethod]
+ public void AddClientSideWebPartTest()
+ {
+ using (var scope = new PSTestScope(true))
+ {
+ using (var ctx = TestCommon.CreateClientContext())
+ {
+ ctx.Web.AddClientSidePage(PageAddWebPartTestName, true);
+
+ var results = scope.ExecuteCommand("Add-PnPClientSideWebPart",
+ new CommandParameter("Page", PageAddWebPartTestName),
+ new CommandParameter("DefaultWebPartType", DefaultClientSideWebParts.Image),
+ new CommandParameter("WebPartProperties", new Hashtable()
+ {
+ {"imageSourceType", 2},
+ {"siteId", "c827cb03-d059-4956-83d0-cd60e02e3b41" },
+ {"webId","9fafd7c0-e8c3-4a3c-9e87-4232c481ca26" },
+ {"listId","78d1b1ac-7590-49e7-b812-55f37c018c4b" },
+ {"uniqueId","3C27A419-66D0-4C36-BF24-BD6147719052" },
+ {"imgWidth", 500 },
+ {"imgHeight", 235 }
+ }
+ ));
+
+ var page = ClientSidePage.Load(ctx, PageAddWebPartTestName);
+
+ Assert.AreEqual(page.Controls.Count , 1);
+ }
+ }
+ }
+ }
+}
diff --git a/Tests/SharePointPnP.PowerShell.Tests.csproj b/Tests/SharePointPnP.PowerShell.Tests.csproj
index b42b56bb7..8b14f168f 100644
--- a/Tests/SharePointPnP.PowerShell.Tests.csproj
+++ b/Tests/SharePointPnP.PowerShell.Tests.csproj
@@ -272,6 +272,7 @@
+