diff --git a/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.Common/Classes/Helpers/XmlUtility.cs b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.Common/Classes/Helpers/XmlUtility.cs new file mode 100644 index 000000000..57e772571 --- /dev/null +++ b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.Common/Classes/Helpers/XmlUtility.cs @@ -0,0 +1,77 @@ +using System; +using System.IO; +using System.Text; +using System.Xml; +using System.Xml.Serialization; + +namespace PNP.Deployer.Common +{ + // ======================================================= + /// + /// Simon-Pierre Plante (sp.plante@gmail.com) + /// + // ======================================================= + public static class XmlUtility + { + #region Public Methods + + // =========================================================================================================== + /// + /// Validates the specified XML file based on the specified XSD file + /// + /// The path of the XML file to validate + /// The path of the XSD file to be used for validation + // =========================================================================================================== + public static void ValidateSchema(string xmlPath, string xsdPath) + { + XmlDocument doc = new XmlDocument(); + doc.Load(xmlPath); + doc.Schemas.Add(null, xsdPath); + doc.Validate(null); + } + + + // =========================================================================================================== + /// + /// Deserializes the specified XML file into the desired type of object based on the specified XML file + /// + /// The type of object in which the XML needs to be deserialized + /// The path of the XML file that needs to be deserialized + /// The deserialized XML in the form of the requested type (T) + // =========================================================================================================== + public static T DeserializeXmlFile(string xmlPath) + { + T deserializedObject = default(T); + + XmlSerializer serializer = new XmlSerializer(typeof(T)); + XmlTextReader reader = new XmlTextReader(xmlPath); + deserializedObject = (T)serializer.Deserialize(reader); + + return deserializedObject; + } + + + // =========================================================================================================== + /// + /// Deserializes the specified XML file into the desired type of object based on the specified string reader + /// + /// The type of object in which the XML needs to be deserialized + /// A string that contains the XML that needs to be deserialized + /// The deserialized XML in the form of the requested type (T) + // =========================================================================================================== + public static T DeserializeXml(string xml) + { + T deserializedObject = default(T); + + using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(xml))) + { + XmlSerializer serializer = new XmlSerializer(typeof(T)); + deserializedObject = (T)serializer.Deserialize(stream); + } + + return deserializedObject; + } + + #endregion + } +} diff --git a/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.Common/PNP.Deployer.Common.csproj b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.Common/PNP.Deployer.Common.csproj new file mode 100644 index 000000000..8535218e4 --- /dev/null +++ b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.Common/PNP.Deployer.Common.csproj @@ -0,0 +1,54 @@ + + + + + Debug + AnyCPU + {0FE76181-46FE-451B-B7A0-83E8B455DA5D} + Library + Properties + PNP.Deployer.Common + PNP.Deployer.Common + v4.5.2 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.Common/Properties/AssemblyInfo.cs b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.Common/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..d6e02bb57 --- /dev/null +++ b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.Common/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("PNP.Deployer.Common")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("PNP.Deployer.Common")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("0fe76181-46fe-451b-b7a0-83e8b455da5d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// 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("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.ExtensibilityProviders.CSOM/PNP.Deployer.ExtensibilityProviders.CSOM.csproj b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.ExtensibilityProviders.CSOM/PNP.Deployer.ExtensibilityProviders.CSOM.csproj new file mode 100644 index 000000000..ffaefaeaf --- /dev/null +++ b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.ExtensibilityProviders.CSOM/PNP.Deployer.ExtensibilityProviders.CSOM.csproj @@ -0,0 +1,109 @@ + + + + + Debug + AnyCPU + {CA4E46F3-08F9-41E1-B90E-EDCDAD22EC22} + Library + Properties + PNP.Deployer.ExtensibilityProviders.CSOM + PNP.Deployer.ExtensibilityProviders.CSOM + v4.5.2 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + True + + + + + + ..\packages\SharePointPnPCore2013.2.6.1608.0\lib\net45\Microsoft.Online.SharePoint.Client.Tenant.dll + True + + + + + + + + + + + + ..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll + True + + + ..\packages\NLog.4.3.7\lib\net45\NLog.dll + True + + + ..\packages\SharePointPnPCore2013.2.6.1608.0\lib\net45\OfficeDevPnP.Core.dll + True + + + + + + + + ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll + True + + + + + + ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll + True + + + + + + + + + + + + + + + + {0fe76181-46fe-451b-b7a0-83e8b455da5d} + PNP.Deployer.Common + + + + + + + + \ No newline at end of file diff --git a/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.ExtensibilityProviders.CSOM/Properties/AssemblyInfo.cs b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.ExtensibilityProviders.CSOM/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..c300d83e2 --- /dev/null +++ b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.ExtensibilityProviders.CSOM/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("PNP.Deployer.ExtensibilityProviders.CSOM")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("PNP.Deployer.ExtensibilityProviders.CSOM")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("ca4e46f3-08f9-41e1-b90e-edcdad22ec22")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// 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("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.ExtensibilityProviders.CSOM/packages.config b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.ExtensibilityProviders.CSOM/packages.config new file mode 100644 index 000000000..99deb5d24 --- /dev/null +++ b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.ExtensibilityProviders.CSOM/packages.config @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.ExtensibilityProviders.SSOM/Classes/ExtensibilityProviders/SiteFieldsProvider.cs b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.ExtensibilityProviders.SSOM/Classes/ExtensibilityProviders/SiteFieldsProvider.cs new file mode 100644 index 000000000..81efdd1a0 --- /dev/null +++ b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.ExtensibilityProviders.SSOM/Classes/ExtensibilityProviders/SiteFieldsProvider.cs @@ -0,0 +1,117 @@ +using NLog; +using System; +using System.Globalization; +using System.Collections.Generic; +using OfficeDevPnP.Core.Diagnostics; +using OfficeDevPnP.Core.Framework.Provisioning.Model; +using OfficeDevPnP.Core.Framework.Provisioning.Extensibility; +using OfficeDevPnP.Core.Framework.Provisioning.ObjectHandlers; +using OfficeDevPnP.Core.Framework.Provisioning.ObjectHandlers.TokenDefinitions; +using Microsoft.SharePoint; +using Microsoft.SharePoint.Client; +using PNP.Deployer.Common; + + + +// ======================================================= +/// +/// Simon-Pierre Plante (sp.plante@gmail.com) +/// +// ======================================================= +namespace PNP.Deployer.ExtensibilityProviders.SSOM +{ + public class SiteFieldsProvider : IProvisioningExtensibilityHandler + { + #region Constants + + private const string ERROR_GENERAL = "An error occured while executing the SiteFieldsProvider : {0}"; + + #endregion + + + #region Private Members + + private static Logger logger = LogManager.GetCurrentClassLogger(); + + #endregion + + + #region Interface Methods + + public void Provision(ClientContext ctx, ProvisioningTemplate template, ProvisioningTemplateApplyingInformation applyingInformation, TokenParser tokenParser, PnPMonitoredScope scope, string configurationData) + { + logger.Info("Entering the SiteFields provider"); + + try + { + // ---------------------------------------------- + // Deserializes the configuration data + // ---------------------------------------------- + SiteFieldsConfigurationData configData = XmlUtility.DeserializeXml(configurationData); + + // ---------------------------------------------- + // Loads the site collection and root web + // ---------------------------------------------- + using (SPSite site = new SPSite(ctx.Url)) + { + using (SPWeb web = site.OpenWeb()) + { + // ---------------------------------------------- + // Loops through the nodes + // ---------------------------------------------- + foreach (Field configField in configData.Fields) + { + SPField field = web.Fields.GetFieldByInternalName(configField.Name); + + // ---------------------------------------------- + // Configures the field's title resource + // ---------------------------------------------- + ConfigureFieldTitleResource(field, configField); + } + } + } + } + catch(Exception e) + { + throw new Exception(String.Format(ERROR_GENERAL, e.Message), e.InnerException); + } + } + + + public ProvisioningTemplate Extract(ClientContext ctx, ProvisioningTemplate template, ProvisioningTemplateCreationInformation creationInformation, PnPMonitoredScope scope, string configurationData) + { + return template; + } + + + public IEnumerable GetTokens(ClientContext ctx, ProvisioningTemplate template, string configurationData) + { + return new List(); + } + + #endregion + + + #region Private Methods + + // =========================================================================================================== + /// + /// Configures the TitleResource of the given SPField based on the specified Field config + /// + /// The SPField that needs to be configured + /// The Field config + // =========================================================================================================== + private void ConfigureFieldTitleResource(SPField field, Field configField) + { + logger.Info("Configuring title resources for field '{0}'", field.InternalName); + + foreach(TitleResource titleResource in configField.TitleResources) + { + field.TitleResource.SetValueForUICulture(new CultureInfo(titleResource.LCID), titleResource.Value); + } + field.Update(); + } + + #endregion + } +} diff --git a/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.ExtensibilityProviders.SSOM/Classes/Serialization/SiteFieldsConfigurationData.cs b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.ExtensibilityProviders.SSOM/Classes/Serialization/SiteFieldsConfigurationData.cs new file mode 100644 index 000000000..49f823034 --- /dev/null +++ b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.ExtensibilityProviders.SSOM/Classes/Serialization/SiteFieldsConfigurationData.cs @@ -0,0 +1,56 @@ +using System; +using System.Xml.Serialization; +using System.Collections.Generic; + + +// ======================================================= +/// +/// Simon-Pierre Plante (sp.plante@gmail.com) +/// +// ======================================================= +namespace PNP.Deployer.ExtensibilityProviders.SSOM +{ + #region SiteFieldsConfigurationData + + [Serializable()] + [XmlRoot(ElementName = "ProviderConfiguration", Namespace = "http://PNP.Deployer/ProviderConfiguration")] + public class SiteFieldsConfigurationData + { + [XmlArray("Fields")] + [XmlArrayItem("Field", typeof(Field))] + public List Fields { get; set; } + } + + #endregion + + + #region Field + + [Serializable()] + public class Field + { + [XmlAttribute("Name")] + public string Name { get; set; } + + [XmlArray("TitleResources")] + [XmlArrayItem("TitleResource", typeof(TitleResource))] + public List TitleResources { get; set; } + } + + #endregion + + + #region TitleResource + + [Serializable()] + public class TitleResource + { + [XmlAttribute("LCID")] + public int LCID { get; set; } + + [XmlAttribute("Value")] + public string Value { get; set; } + } + + #endregion +} diff --git a/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.ExtensibilityProviders.SSOM/PNP.Deployer.ExtensibilityProviders.SSOM.csproj b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.ExtensibilityProviders.SSOM/PNP.Deployer.ExtensibilityProviders.SSOM.csproj new file mode 100644 index 000000000..70f7e9302 --- /dev/null +++ b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.ExtensibilityProviders.SSOM/PNP.Deployer.ExtensibilityProviders.SSOM.csproj @@ -0,0 +1,110 @@ + + + + + Debug + AnyCPU + {DC6CDDA2-4DB6-48A2-BBDB-9C05F84EB235} + Library + Properties + PNP.Deployer.ExtensibilityProviders.SSOM + PNP.Deployer.ExtensibilityProviders.SSOM + v4.5.2 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + True + + + + + + ..\packages\SharePointPnPCore2013.2.6.1608.0\lib\net45\Microsoft.Online.SharePoint.Client.Tenant.dll + True + + + + + + + + + + + + + ..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll + True + + + ..\packages\NLog.4.3.7\lib\net45\NLog.dll + True + + + ..\packages\SharePointPnPCore2013.2.6.1608.0\lib\net45\OfficeDevPnP.Core.dll + True + + + + + + + + ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll + True + + + + + + ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll + True + + + + + + + + + + + + + + + + + + + {0fe76181-46fe-451b-b7a0-83e8b455da5d} + PNP.Deployer.Common + + + + + \ No newline at end of file diff --git a/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.ExtensibilityProviders.SSOM/Properties/AssemblyInfo.cs b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.ExtensibilityProviders.SSOM/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..1384c0abd --- /dev/null +++ b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.ExtensibilityProviders.SSOM/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("PNP.Deployer.ExtensibilityProviders.SSOM")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("PNP.Deployer.ExtensibilityProviders.SSOM")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("dc6cdda2-4db6-48a2-bbdb-9c05f84eb235")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// 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("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.ExtensibilityProviders.SSOM/packages.config b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.ExtensibilityProviders.SSOM/packages.config new file mode 100644 index 000000000..99deb5d24 --- /dev/null +++ b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.ExtensibilityProviders.SSOM/packages.config @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.sln b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.sln new file mode 100644 index 000000000..497f809e3 --- /dev/null +++ b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer.sln @@ -0,0 +1,40 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PNP.Deployer", "PNP.Deployer\PNP.Deployer.csproj", "{C3C39474-E23C-4C7A-90C1-02153EC8E98A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PNP.Deployer.ExtensibilityProviders.CSOM", "PNP.Deployer.ExtensibilityProviders.CSOM\PNP.Deployer.ExtensibilityProviders.CSOM.csproj", "{CA4E46F3-08F9-41E1-B90E-EDCDAD22EC22}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PNP.Deployer.ExtensibilityProviders.SSOM", "PNP.Deployer.ExtensibilityProviders.SSOM\PNP.Deployer.ExtensibilityProviders.SSOM.csproj", "{DC6CDDA2-4DB6-48A2-BBDB-9C05F84EB235}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PNP.Deployer.Common", "PNP.Deployer.Common\PNP.Deployer.Common.csproj", "{0FE76181-46FE-451B-B7A0-83E8B455DA5D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C3C39474-E23C-4C7A-90C1-02153EC8E98A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C3C39474-E23C-4C7A-90C1-02153EC8E98A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C3C39474-E23C-4C7A-90C1-02153EC8E98A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C3C39474-E23C-4C7A-90C1-02153EC8E98A}.Release|Any CPU.Build.0 = Release|Any CPU + {CA4E46F3-08F9-41E1-B90E-EDCDAD22EC22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CA4E46F3-08F9-41E1-B90E-EDCDAD22EC22}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CA4E46F3-08F9-41E1-B90E-EDCDAD22EC22}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CA4E46F3-08F9-41E1-B90E-EDCDAD22EC22}.Release|Any CPU.Build.0 = Release|Any CPU + {DC6CDDA2-4DB6-48A2-BBDB-9C05F84EB235}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DC6CDDA2-4DB6-48A2-BBDB-9C05F84EB235}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DC6CDDA2-4DB6-48A2-BBDB-9C05F84EB235}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DC6CDDA2-4DB6-48A2-BBDB-9C05F84EB235}.Release|Any CPU.Build.0 = Release|Any CPU + {0FE76181-46FE-451B-B7A0-83E8B455DA5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0FE76181-46FE-451B-B7A0-83E8B455DA5D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0FE76181-46FE-451B-B7A0-83E8B455DA5D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0FE76181-46FE-451B-B7A0-83E8B455DA5D}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/App.config b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/App.config new file mode 100644 index 000000000..d0ec3e815 --- /dev/null +++ b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/App.config @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Deployer/Deployer.cs b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Deployer/Deployer.cs new file mode 100644 index 000000000..bb610d77e --- /dev/null +++ b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Deployer/Deployer.cs @@ -0,0 +1,160 @@ +using NLog; +using System; +using System.IO; +using System.Net; +using System.Configuration; +using OfficeDevPnP.Core.Framework.Provisioning.Providers.Xml; +using PNP.Deployer.Common; + +// ======================================================= +/// +/// Simon-Pierre Plante (sp.plante@gmail.com) +/// +// ======================================================= +namespace PNP.Deployer +{ + public class Deployer + { + #region Constants + + private const string APP_SETTING_SEQUENCES_FILE = "clientSequencesFile"; + private const string SEQUENCES_SCHEMA_FILE_NAME = "Sequences.xsd"; + private const string ERROR_FOLDER_INVALID = "The specified working directory '{0}' does not exist"; + private const string ERROR_SEQUENCES_NOT_FOUND = "Sequences configuration file was not found at path '{0}'"; + private const string ERROR_SEQUENCES_INVALID = "The {0} file is invalid : {1}"; + + #endregion + + + #region Private Members + + private static Logger logger = LogManager.GetCurrentClassLogger(); + + #endregion + + + #region Public Members + + public string WorkingDirectory { get; set; } + public EnvironmentType Environment { get; set; } + public ICredentials Credentials { get; set; } + public SequencesConfiguration SequencesConfig { get; set; } + + #endregion + + + #region Constructor + + // =========================================================================================================== + /// + /// Initializes the deployer based on the specified arguments + /// + /// The working directory on which to map the deployer + /// The type of environment on which the deployer is executed + /// The credential that must be used the get the SharePoint context + // =========================================================================================================== + public Deployer(string WorkingDirectory, EnvironmentType Environment, ICredentials Credentials) + { + // -------------------------------------------------- + // If the WorkingDirectory doesn't exist + // -------------------------------------------------- + if (!Directory.Exists(WorkingDirectory)) + throw new DeployerArgumentsException(String.Format(ERROR_FOLDER_INVALID, WorkingDirectory)); + + // -------------------------------------------------- + // Initializes the deployer's arguments + // -------------------------------------------------- + this.WorkingDirectory = WorkingDirectory; + logger.Info("Loaded 'WorkingDirectory' with value '{0}'", this.WorkingDirectory); + + this.Environment = Environment; + logger.Info("Loaded 'EnvironmentType' with value '{0}'", this.Environment); + + this.Credentials = Credentials; + logger.Info("Loaded 'Credentials' with success"); + } + + #endregion + + + #region Private Methods + + // =========================================================================================================== + /// + /// Throws an exception if the sequences file is missing or invalid + /// + /// The Sequences.xml file path + // =========================================================================================================== + private void ValidateSequencesFile(string sequencesFilePath) + { + logger.Info("Validating the '{0}' file", Path.GetFileName(sequencesFilePath)); + + // -------------------------------------------------- + // Throws an exception if Sequences.xml is not found + // -------------------------------------------------- + if (!File.Exists(sequencesFilePath)) + throw new FileNotFoundException(String.Format(ERROR_SEQUENCES_NOT_FOUND, sequencesFilePath)); + + // -------------------------------------------------- + // Throws an exception if Sequences.xml is invalid + // -------------------------------------------------- + XmlUtility.ValidateSchema(sequencesFilePath, Path.Combine(AppDomain.CurrentDomain.BaseDirectory, SEQUENCES_SCHEMA_FILE_NAME)); + + logger.Info("File '{0}' has completed XSD validation with success", Path.GetFileName(sequencesFilePath)); + } + + + // =========================================================================================================== + /// + /// Deserializes the sequences.xml file and stores a SequencesConfiguration object + /// + /// A SequencesConfiguration object + // =========================================================================================================== + private void LoadSequencesConfiguration() + { + // -------------------------------------------------- + // Validates the deployer's sequences file + // -------------------------------------------------- + string sequencesFilePath = Path.Combine(this.WorkingDirectory, ConfigurationManager.AppSettings[APP_SETTING_SEQUENCES_FILE]); + ValidateSequencesFile(sequencesFilePath); + + // -------------------------------------------------- + // Deserializes the Sequences.xml file + // -------------------------------------------------- + this.SequencesConfig = XmlUtility.DeserializeXmlFile(sequencesFilePath); + } + + #endregion + + + #region Public Methods + + // =========================================================================================================== + /// + /// Launches the deployer + /// + // =========================================================================================================== + public void Launch() + { + // -------------------------------------------------- + // Loads the sequences configuration + // -------------------------------------------------- + LoadSequencesConfiguration(); + + // -------------------------------------------------- + // Maps a template provider to the working directory + // -------------------------------------------------- + XMLTemplateProvider provider = new XMLFileSystemTemplateProvider(this.WorkingDirectory, string.Empty); + + // -------------------------------------------------- + // Launches the sequences + // -------------------------------------------------- + foreach (Sequence sequence in this.SequencesConfig.Sequences) + { + sequence.Launch(this.Credentials, provider); + } + } + + #endregion + } +} diff --git a/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Deployer/DeployerOptions.cs b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Deployer/DeployerOptions.cs new file mode 100644 index 000000000..fea765d91 --- /dev/null +++ b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Deployer/DeployerOptions.cs @@ -0,0 +1,117 @@ +using System; +using System.Reflection; +using CommandLine; +using CommandLine.Text; + + +// ======================================================= +/// +/// Simon-Pierre Plante (sp.plante@gmail.com) +/// +// ======================================================= +namespace PNP.Deployer +{ + public class DeployerOptions + { + #region Constants + + private const string HELP_ENVIRONMENT = "Whether the deployment occurs on a 'OnPrem' on 'Online' envrionment."; + private const string HELP_WORKING_DIRECTORY = "The working directory on which the deployer should be mapped to in order to deploy the artifacts."; + private const string HELP_PROMPT_CREDENTIALS = "Wheter there should be a prompt for credentials or not."; + + #endregion + + + #region Public Members + + // -------------------------------------------------- + // The environment on which the deployer is executed + // -------------------------------------------------- + [Option('e', "Environment", DefaultValue = EnvironmentType.OnPrem, HelpText = HELP_ENVIRONMENT, Required = false)] + public EnvironmentType Environment { get; set; } + + // -------------------------------------------------- + // The working directory that contains the artifacts + // -------------------------------------------------- + [Option('w', "WorkingDirectory", HelpText = HELP_WORKING_DIRECTORY, Required = true)] + public string WorkingDirectory { get; set; } + + // -------------------------------------------------- + // Whether to prompt for specific credentials or not + // -------------------------------------------------- + [Option('p', "PromptCredentials", DefaultValue = false, HelpText = HELP_PROMPT_CREDENTIALS, Required = false)] + public Boolean PromptCredentials { get; set; } + + // -------------------------------------------------- + // Stores the parser state for errors handling + // -------------------------------------------------- + [ParserState] + public IParserState LastParserState { get; set; } + + #endregion + + + #region Public Methods + + // =========================================================================================================== + /// + /// Default 'help' screen implementation for the console + /// + /// The default help screen + // =========================================================================================================== + [HelpOption(HelpText = "Display this deployer help screen")] + public string GetUsage() + { + + // -------------------------------------------------- + // Initializes the HelpText object + // -------------------------------------------------- + var help = new HelpText + { + AdditionalNewLineAfterOption = true, + AddDashesToOption = true + }; + + help.AddPreOptionsLine(string.Format(" PNP.Deployer (v.{0}) - By Simon-Pierre Plante", Assembly.GetExecutingAssembly().GetName().Version.ToString())); + help.AddPreOptionsLine(""); + help.AddPreOptionsLine(""); + + // -------------------------------------------------- + // Adds the error section if any + // -------------------------------------------------- + if (this.LastParserState != null && this.LastParserState.Errors.Count > 0 ) + { + + var errors = help.RenderParsingErrorsText(this, 4); + + if (!string.IsNullOrEmpty(errors)) + { + help.AddPreOptionsLine(" Error(s):"); + help.AddPreOptionsLine(""); + help.AddPreOptionsLine(errors); + help.AddPreOptionsLine(""); + } + } + + // -------------------------------------------------- + // Adds the Usage section + // -------------------------------------------------- + help.AddPreOptionsLine(" Usage:"); + help.AddPreOptionsLine(""); + help.AddPreOptionsLine(" PNP.Deployer.exe --WorkingDirectory \"C:\\DeploymentFolder\""); + help.AddPreOptionsLine(" [--PromptCredentials] [--Environment \"OnPrem|Online\"]"); + help.AddPreOptionsLine(""); + help.AddPreOptionsLine(""); + + // -------------------------------------------------- + // Adds the Options section + // -------------------------------------------------- + help.AddPreOptionsLine(" Options: "); + help.AddOptions(this); + + return help; + } + + #endregion + } +} diff --git a/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Enums/EnvironmentType.cs b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Enums/EnvironmentType.cs new file mode 100644 index 000000000..934ddb301 --- /dev/null +++ b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Enums/EnvironmentType.cs @@ -0,0 +1,13 @@ +// ======================================================= +/// +/// Simon-Pierre Plante (sp.plante@gmail.com) +/// +// ======================================================= +namespace PNP.Deployer +{ + public enum EnvironmentType + { + Online, + OnPrem + } +} diff --git a/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Enums/ExitCode.cs b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Enums/ExitCode.cs new file mode 100644 index 000000000..fd518ebe5 --- /dev/null +++ b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Enums/ExitCode.cs @@ -0,0 +1,13 @@ +// ======================================================= +/// +/// Simon-Pierre Plante (sp.plante@gmail.com) +/// +// ======================================================= +namespace PNP.Deployer +{ + public enum ExitCode : int + { + Success = 0, + Failure = 1 + } +} diff --git a/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Exceptions/DeployerArgumentsException.cs b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Exceptions/DeployerArgumentsException.cs new file mode 100644 index 000000000..3f7cfddc9 --- /dev/null +++ b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Exceptions/DeployerArgumentsException.cs @@ -0,0 +1,26 @@ +using System; +using System.Runtime.Serialization; + + +// ======================================================= +/// +/// Simon-Pierre Plante (sp.plante@gmail.com) +/// +// ======================================================= +namespace PNP.Deployer +{ + public class DeployerArgumentsException : Exception + { + #region Constructors + + public DeployerArgumentsException() : base() { } + + public DeployerArgumentsException(String message) : base(message) { } + + public DeployerArgumentsException(String message, Exception innerException) : base(message, innerException) { } + + public DeployerArgumentsException(SerializationInfo info, StreamingContext context) : base(info, context) { } + + #endregion + } +} diff --git a/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Extensions/LoggerExtensions.cs b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Extensions/LoggerExtensions.cs new file mode 100644 index 000000000..dd9629bcb --- /dev/null +++ b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Extensions/LoggerExtensions.cs @@ -0,0 +1,79 @@ +using NLog; +using System; + + +// ======================================================= +/// +/// Simon-Pierre Plante (sp.plante@gmail.com) +/// +// ======================================================= +namespace PNP.Deployer +{ + public static class LoggerExtensions + { + #region Public Methods + + // ============================================================================================================== + /// + /// Logs the specified message as a more visible "section" + /// + /// The current Logger + /// The message that needs to be logged as a "section" + /// The LogLevel in which the section needs to be logged + // ============================================================================================================== + public static void Section(this Logger logger, String message, LogLevel logLevel) + { + logger.Log(logLevel, "================================================"); + logger.Log(logLevel, message); + logger.Log(logLevel, "================================================"); + } + + + // ============================================================================================================== + /// + /// Logs the specified message as a more visible "section" + /// + /// The current Logger + /// The message that needs to be logged as a "section" + /// The LogLevel in which the section needs to be logged + /// An object array that contains zero or more objects to format + // ============================================================================================================== + public static void Section(this Logger logger, String message, LogLevel logLevel, params object[] args) + { + logger.Section(String.Format(message, args), logLevel); + } + + + // ============================================================================================================== + /// + /// Logs the specified message as a more visible "section" + /// + /// The current Logger + /// The message that needs to be logged as a "section" + /// The LogLevel in which the sub section needs to be logged + // ============================================================================================================== + public static void SubSection(this Logger logger, String message, LogLevel logLevel) + { + logger.Log(logLevel, "-----------------------------------"); + logger.Log(logLevel, message); + logger.Log(logLevel, "-----------------------------------"); + } + + + // ============================================================================================================== + /// + /// Logs the specified message as a more visible "section" + /// + /// The current Logger + /// The message that needs to be logged as a "section" + /// The LogLevel in which the sub section needs to be logged + /// An object array that contains zero or more objects to format + // ============================================================================================================== + public static void SubSection(this Logger logger, String message, LogLevel logLevel, params object[] args) + { + logger.SubSection(String.Format(message,args), logLevel); + } + + #endregion + } +} diff --git a/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Extensions/StringExtensions.cs b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Extensions/StringExtensions.cs new file mode 100644 index 000000000..e2b72f692 --- /dev/null +++ b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Extensions/StringExtensions.cs @@ -0,0 +1,36 @@ +using System.Security; + + +// ======================================================= +/// +/// Simon-Pierre Plante (sp.plante@gmail.com) +/// +// ======================================================= +namespace PNP.Deployer +{ + public static class StringExtensions + { + #region Public Methods + + // =========================================================================================================== + /// + /// Converts the current String into a SecureString object + /// + /// The current String + /// The current string converted as a SecureString object + // =========================================================================================================== + public static SecureString ToSecureString(this string str) + { + SecureString ss = new SecureString(); + + foreach(char c in str.ToCharArray()) + { + ss.AppendChar(c); + } + + return ss; + } + + #endregion + } +} diff --git a/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Helpers/ConsoleUtility.cs b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Helpers/ConsoleUtility.cs new file mode 100644 index 000000000..ffc4c8158 --- /dev/null +++ b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Helpers/ConsoleUtility.cs @@ -0,0 +1,96 @@ +using System; +using System.Security; + + +// ======================================================= +/// +/// Simon-Pierre Plante (sp.plante@gmail.com) +/// +// ======================================================= +namespace PNP.Deployer +{ + public static class ConsoleUtility + { + #region Private Methods + + // =========================================================================================================== + /// + /// Prompts the user with the specified label and returns it's input + /// + /// The label that will be prompted to the user + /// Whether the input should be of type 'password' or not + /// The user's input as a String + // =========================================================================================================== + private static string GetInput(string label, bool isPassword) + { + ConsoleColor defaultForeground = Console.ForegroundColor; + Console.ForegroundColor = ConsoleColor.DarkCyan; + Console.Write("{0} : ", label); + Console.ForegroundColor = defaultForeground; + + string value = ""; + + for (ConsoleKeyInfo keyInfo = Console.ReadKey(true); keyInfo.Key != ConsoleKey.Enter; keyInfo = Console.ReadKey(true)) + { + if (keyInfo.Key == ConsoleKey.Backspace) + { + if (value.Length > 0) + { + value = value.Remove(value.Length - 1); + Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop); + Console.Write(" "); + Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop); + } + } + else if (keyInfo.Key != ConsoleKey.Enter) + { + if (isPassword) + { + Console.Write("*"); + } + else + { + Console.Write(keyInfo.KeyChar); + } + value += keyInfo.KeyChar; + } + } + Console.WriteLine(""); + + return value; + } + + #endregion + + + #region Public Methods + + // =========================================================================================================== + /// + /// Prompts the user with the specified label and returns it's clear text answer + /// + /// The label that will be prompted to the user + /// The user's input as a clear string object + // =========================================================================================================== + public static string GetInputAsText(string label) + { + return GetInput(label, false); + } + + + // =========================================================================================================== + /// + /// Prompts the user with the specified label and returns it's secure answer + /// + /// The label that will be prompted to the user + /// The user's input as a SecureString object + // =========================================================================================================== + public static SecureString GetInputAsSecureString(string label) + { + string input = GetInput(label, true); + return input.ToSecureString(); + } + + #endregion + } +} diff --git a/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Helpers/FilesUtility.cs b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Helpers/FilesUtility.cs new file mode 100644 index 000000000..72c7460b1 --- /dev/null +++ b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Helpers/FilesUtility.cs @@ -0,0 +1,69 @@ +using System.IO; + + +namespace PNP.Deployer +{ + // ======================================================= + /// + /// Simon-Pierre Plante (sp.plante@gmail.com) + /// + // ======================================================= + public static class FilesUtility + { + #region Private Methods + + // =========================================================================================================== + /// + /// Copies recursively all files/folders from the specified source folder to the specified target folder + /// + /// The DirectoryInfo of the source folder + /// The DirectoryInfo of the target folder + // =========================================================================================================== + private static void CopyDirectoryRecursive(DirectoryInfo source, DirectoryInfo target) + { + // -------------------------------------------------- + // Creates the destination folder if needed + // -------------------------------------------------- + if (!target.Exists) + Directory.CreateDirectory(target.FullName); + + // -------------------------------------------------- + // Copies each file in the destination folder + // -------------------------------------------------- + foreach(FileInfo fileInfo in source.GetFiles()) + { + fileInfo.CopyTo(Path.Combine(target.FullName, fileInfo.Name), true); + } + + // -------------------------------------------------- + // Copies each folder in the destination folder + // -------------------------------------------------- + foreach (DirectoryInfo sourceSubDirInfo in source.GetDirectories()) + { + DirectoryInfo targetSubDirInfo = target.CreateSubdirectory(sourceSubDirInfo.Name); + CopyDirectoryRecursive(sourceSubDirInfo, targetSubDirInfo); + } + } + + #endregion + + + #region Public Methods + + // =========================================================================================================== + /// + /// Copies recursively all files/folders from the specified source folder to the specified target folder + /// + /// The source directory that needs to be copied + /// The target directory + // =========================================================================================================== + public static void CopyDirectory(string sourceDirectory, string destinationDirectory) + { + DirectoryInfo infoSourceDirectory = new DirectoryInfo(sourceDirectory); + DirectoryInfo infoDestinationDirectory = new DirectoryInfo(destinationDirectory); + CopyDirectoryRecursive(infoSourceDirectory, infoDestinationDirectory); + } + + #endregion + } +} diff --git a/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Sequences/Sequence.cs b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Sequences/Sequence.cs new file mode 100644 index 000000000..427b20f29 --- /dev/null +++ b/Samples/Provisioning.PnPDeployer.Console/PNP.Deployer/Classes/Sequences/Sequence.cs @@ -0,0 +1,96 @@ +using NLog; +using System; +using System.Net; +using System.Threading; +using System.Xml.Serialization; +using System.Collections.Generic; +using Microsoft.SharePoint.Client; +using OfficeDevPnP.Core.Framework.Provisioning.Providers.Xml; + + +// ======================================================= +/// +/// Simon-Pierre Plante (sp.plante@gmail.com) +/// +// ======================================================= +namespace PNP.Deployer +{ + [Serializable()] + public class Sequence + { + #region Private Members + + private static Logger logger = LogManager.GetCurrentClassLogger(); + + #endregion + + + #region Public Members + + [XmlAttribute("name")] + public string Name { get; set; } + + [XmlAttribute("description")] + public string Description { get; set; } + + [XmlAttribute("webUrl")] + public string WebUrl { get; set; } + + [XmlAttribute("ignore")] + public bool Ignore { get; set; } + + [XmlArray("templates")] + [XmlArrayItem("template", typeof(Template))] + public List