From 65e98928d4b5bd96b7ec919978abead1f39d4171 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 14 Jan 2019 17:46:12 +1100 Subject: [PATCH] Package data installation is now all migrated --- .../Composing/Composers/ServicesComposer.cs | 7 +- src/Umbraco.Core/Composing/Current.cs | 4 +- src/Umbraco.Core/Constants-Packaging.cs | 55 - src/Umbraco.Core/Events/ImportEventArgs.cs | 84 -- .../Models/Packaging/CompiledPackage.cs | 6 +- .../Models/Packaging/InstallationSummary.cs | 2 +- .../Packaging/CompiledPackageXmlParser.cs | 49 +- .../Packaging/ConflictingPackageData.cs | 12 +- .../Packaging/IPackageActionRunner.cs | 23 + .../Packaging/IPackageInstallation.cs | 3 +- .../Packaging/PackageActionRunner.cs | 3 +- .../Packaging/PackageDataInstallation.cs | 1144 +++++++++++++++ .../Packaging/PackageInstallation.cs | 306 +--- .../Services/IPackagingService.cs | 75 - .../Services/Implement/PackagingService.cs | 1303 +---------------- src/Umbraco.Core/Umbraco.Core.csproj | 4 +- .../Packaging/PackageInstallationTest.cs | 12 +- .../Services/Importing/PackageImportTests.cs | 126 +- src/Umbraco.Tests/TestHelpers/TestObjects.cs | 27 +- src/Umbraco.Web/Composing/Current.cs | 2 +- .../Editors/ContentTypeController.cs | 29 +- .../Editors/PackageInstallController.cs | 5 +- src/Umbraco.Web/Umbraco.Web.csproj | 1 - src/Umbraco.Web/_Legacy/Packager/Installer.cs | 688 --------- 24 files changed, 1424 insertions(+), 2546 deletions(-) delete mode 100644 src/Umbraco.Core/Constants-Packaging.cs delete mode 100644 src/Umbraco.Core/Events/ImportEventArgs.cs create mode 100644 src/Umbraco.Core/Packaging/IPackageActionRunner.cs create mode 100644 src/Umbraco.Core/Packaging/PackageDataInstallation.cs delete mode 100644 src/Umbraco.Web/_Legacy/Packager/Installer.cs diff --git a/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs b/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs index ac8f4beeb0..a09462c806 100644 --- a/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs +++ b/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs @@ -59,18 +59,19 @@ namespace Umbraco.Core.Composing.Composers composition.RegisterUnique(); - composition.RegisterUnique(); + composition.RegisterUnique(); composition.RegisterUnique(); composition.RegisterUnique(); composition.RegisterUnique(factory => CreatePackageRepository(factory, "createdPackages.config")); composition.RegisterUnique(factory => CreatePackageRepository(factory, "installedPackages.config")); + composition.RegisterUnique(); composition.RegisterUnique(); var appRoot = new DirectoryInfo(IOHelper.GetRootDirectorySafe()); composition.RegisterUnique(factory => //factory required because we need to pass in a string path new PackageInstallation( - factory.GetInstance(), factory.GetInstance(), - factory.GetInstance(), + factory.GetInstance(), factory.GetInstance(), + factory.GetInstance(), factory.GetInstance(), SystemDirectories.Packages, appRoot, appRoot)); diff --git a/src/Umbraco.Core/Composing/Current.cs b/src/Umbraco.Core/Composing/Current.cs index 4bed76d86f..5c8351924f 100644 --- a/src/Umbraco.Core/Composing/Current.cs +++ b/src/Umbraco.Core/Composing/Current.cs @@ -162,8 +162,8 @@ namespace Umbraco.Core.Composing internal static PackageActionCollection PackageActions => Factory.GetInstance(); - internal static PackageActionRunner PackageActionRunner - => Factory.GetInstance(); + internal static IPackageActionRunner PackageActionRunner + => Factory.GetInstance(); internal static PropertyValueConverterCollection PropertyValueConverters => Factory.GetInstance(); diff --git a/src/Umbraco.Core/Constants-Packaging.cs b/src/Umbraco.Core/Constants-Packaging.cs deleted file mode 100644 index 37f2c23fa3..0000000000 --- a/src/Umbraco.Core/Constants-Packaging.cs +++ /dev/null @@ -1,55 +0,0 @@ -namespace Umbraco.Core -{ - public static partial class Constants - { - /// - /// Defines the constants used for Umbraco packages in the package.config xml - /// - public static class Packaging - { - public const string UmbPackageNodeName = "umbPackage"; - public const string DataTypesNodeName = "DataTypes"; - public const string PackageXmlFileName = "package.xml"; - public const string UmbracoPackageExtention = ".umb"; - public const string DataTypeNodeName = "DataType"; - public const string LanguagesNodeName = "Languages"; - public const string FilesNodeName = "files"; - public const string StylesheetsNodeName = "Stylesheets"; - public const string TemplatesNodeName = "Templates"; - public const string NameNodeName = "Name"; - public const string TemplateNodeName = "Template"; - public const string AliasNodeNameSmall = "alias"; - public const string AliasNodeNameCapital = "Alias"; - public const string DictionaryItemsNodeName = "DictionaryItems"; - public const string DictionaryItemNodeName = "DictionaryItem"; - public const string MacrosNodeName = "Macros"; - public const string DocumentsNodeName = "Documents"; - public const string DocumentSetNodeName = "DocumentSet"; - public const string DocumentTypesNodeName = "DocumentTypes"; - public const string DocumentTypeNodeName = "DocumentType"; - public const string FileNodeName = "file"; - public const string OrgNameNodeName = "orgName"; - public const string OrgPathNodeName = "orgPath"; - public const string GuidNodeName = "guid"; - public const string StylesheetNodeName = "styleSheet"; - public const string MacroNodeName = "macro"; - public const string InfoNodeName = "info"; - public const string PackageRequirementsMajorXpath = "./package/requirements/major"; - public const string PackageRequirementsMinorXpath = "./package/requirements/minor"; - public const string PackageRequirementsPatchXpath = "./package/requirements/patch"; - public const string PackageNameXpath = "./package/name"; - public const string PackageVersionXpath = "./package/version"; - public const string PackageUrlXpath = "./package/url"; - public const string PackageLicenseXpath = "./package/license"; - public const string PackageLicenseXpathUrlAttribute = "url"; - public const string AuthorNameXpath = "./author/name"; - public const string AuthorWebsiteXpath = "./author/website"; - public const string ReadmeXpath = "./readme"; - public const string ControlNodeName = "control"; - public const string ActionNodeName = "Action"; - public const string ActionsNodeName = "Actions"; - public const string UndoNodeAttribute = "undo"; - public const string RunatNodeAttribute = "runat"; - } - } -} diff --git a/src/Umbraco.Core/Events/ImportEventArgs.cs b/src/Umbraco.Core/Events/ImportEventArgs.cs deleted file mode 100644 index fb8f7d4936..0000000000 --- a/src/Umbraco.Core/Events/ImportEventArgs.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Xml.Linq; - -namespace Umbraco.Core.Events -{ - public class ImportEventArgs : CancellableEnumerableObjectEventArgs, IEquatable> - { - /// - /// Constructor accepting an XElement with the xml being imported - /// - /// - public ImportEventArgs(XElement xml) : base(new List(), true) - { - Xml = xml; - } - - /// - /// Constructor accepting a list of entities and an XElement with the imported xml - /// - /// - /// - /// - public ImportEventArgs(IEnumerable eventObject, XElement xml, bool canCancel) - : base(eventObject, canCancel) - { - Xml = xml; - } - - protected ImportEventArgs(IEnumerable eventObject, bool canCancel) : base(eventObject, canCancel) - { - } - - protected ImportEventArgs(IEnumerable eventObject) : base(eventObject) - { - } - - /// - /// Returns all entities that were imported during the operation - /// - public IEnumerable ImportedEntities - { - get { return EventObject; } - } - - /// - /// Returns the xml relating to the import event - /// - public XElement Xml { get; private set; } - - public bool Equals(ImportEventArgs other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return base.Equals(other) && Equals(Xml, other.Xml); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((ImportEventArgs) obj); - } - - public override int GetHashCode() - { - unchecked - { - return (base.GetHashCode() * 397) ^ (Xml != null ? Xml.GetHashCode() : 0); - } - } - - public static bool operator ==(ImportEventArgs left, ImportEventArgs right) - { - return Equals(left, right); - } - - public static bool operator !=(ImportEventArgs left, ImportEventArgs right) - { - return !Equals(left, right); - } - } -} diff --git a/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs b/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs index 96159d1bbf..266c1f5518 100644 --- a/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs +++ b/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs @@ -34,7 +34,11 @@ namespace Umbraco.Core.Models.Packaging public IEnumerable Macros { get; set; } //fixme: make strongly typed public IEnumerable Templates { get; set; } //fixme: make strongly typed public IEnumerable Stylesheets { get; set; } //fixme: make strongly typed - + public IEnumerable DataTypes { get; set; } //fixme: make strongly typed + public IEnumerable Languages { get; set; } //fixme: make strongly typed + public IEnumerable DictionaryItems { get; set; } //fixme: make strongly typed + public IEnumerable DocumentTypes { get; set; } //fixme: make strongly typed + public IEnumerable Documents { get; set; } //fixme: make strongly typed } public class CompiledPackageFile diff --git a/src/Umbraco.Core/Models/Packaging/InstallationSummary.cs b/src/Umbraco.Core/Models/Packaging/InstallationSummary.cs index 1405259da2..b14e3d2a92 100644 --- a/src/Umbraco.Core/Models/Packaging/InstallationSummary.cs +++ b/src/Umbraco.Core/Models/Packaging/InstallationSummary.cs @@ -18,7 +18,7 @@ namespace Umbraco.Core.Models.Packaging public IEnumerable MacrosInstalled { get; set; } = Enumerable.Empty(); public IEnumerable FilesInstalled { get; set; } = Enumerable.Empty(); public IEnumerable TemplatesInstalled { get; set; } = Enumerable.Empty(); - public IEnumerable ContentTypesInstalled { get; set; } = Enumerable.Empty(); + public IEnumerable DocumentTypesInstalled { get; set; } = Enumerable.Empty(); public IEnumerable StylesheetsInstalled { get; set; } = Enumerable.Empty(); public IEnumerable ContentInstalled { get; set; } = Enumerable.Empty(); public IEnumerable Actions { get; set; } = Enumerable.Empty(); diff --git a/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs index 44dbded5d2..76f206f478 100644 --- a/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs +++ b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs @@ -26,7 +26,7 @@ namespace Umbraco.Core.Packaging { if (xml == null) throw new ArgumentNullException(nameof(xml)); if (xml.Root == null) throw new ArgumentException(nameof(xml), "The xml document is invalid"); - if (xml.Root.Name != Constants.Packaging.UmbPackageNodeName) throw new FormatException("The xml document is invalid"); + if (xml.Root.Name != "umbPackage") throw new FormatException("The xml document is invalid"); var info = xml.Root.Element("info"); if (info == null) throw new FormatException("The xml document is invalid"); @@ -53,7 +53,6 @@ namespace Umbraco.Core.Packaging UmbracoVersionRequirementsType = requirements.AttributeValue("type").IsNullOrWhiteSpace() ? RequirementsType.Legacy : Enum.Parse(requirements.AttributeValue("type")), Control = package.Element("control")?.Value, Actions = xml.Element("Actions")?.ToString(SaveOptions.None) ?? "", //take the entire outer xml value - Files = xml.Root.Element("files")?.Elements("file")?.Select(x => new CompiledPackageFile { UniqueFileName = x.Element("guid")?.Value, @@ -64,7 +63,11 @@ namespace Umbraco.Core.Packaging Macros = xml.Element("Macros")?.Elements("macro") ?? Enumerable.Empty(), Templates = xml.Element("Templates")?.Elements("Template") ?? Enumerable.Empty(), Stylesheets = xml.Element("Stylesheets")?.Elements("styleSheet") ?? Enumerable.Empty(), - + DataTypes = xml.Element("DataTypes")?.Elements("DataType") ?? Enumerable.Empty(), + Languages = xml.Element("Languages")?.Elements("Language") ?? Enumerable.Empty(), + DictionaryItems = xml.Element("DictionaryItems")?.Elements("DictionaryItem") ?? Enumerable.Empty(), + DocumentTypes = xml.Element("DocumentTypes")?.Elements("DocumentType") ?? Enumerable.Empty(), + Documents = xml.Element("Documents")?.Elements("DocumentSet") ?? Enumerable.Empty(), }; def.Warnings = GetPreInstallWarnings(def, applicationRootFolder); @@ -133,9 +136,8 @@ namespace Umbraco.Core.Packaging { return pathElement.TrimStart(new[] { '\\', '/', '~' }).Replace("/", "\\"); } - - //fixme: This is duplicated in the parser - public static string UpdatePathPlaceholders(string path) + + private static string UpdatePathPlaceholders(string path) { if (path.Contains("[$")) { @@ -146,5 +148,40 @@ namespace Umbraco.Core.Packaging } return path; } + + public IEnumerable GetPackageActions(XElement actionsElement, string packageName) + { + if (actionsElement == null) { return new PackageAction[0]; } + + if (string.Equals("Actions", actionsElement.Name.LocalName) == false) + throw new ArgumentException($"Must be \"Actions\" as root", nameof(actionsElement)); + + return actionsElement.Elements("Action") + .Select(elemet => + { + var aliasAttr = elemet.Attribute("Alias"); + if (aliasAttr == null) + throw new ArgumentException("missing \"Alias\" atribute in alias element", nameof(actionsElement)); + + var packageAction = new PackageAction + { + XmlData = elemet, + Alias = aliasAttr.Value, + PackageName = packageName, + }; + + + var attr = elemet.Attribute("runat"); + + if (attr != null && Enum.TryParse(attr.Value, true, out ActionRunAt runAt)) { packageAction.RunAt = runAt; } + + attr = elemet.Attribute("undo"); + + if (attr != null && bool.TryParse(attr.Value, out var undo)) { packageAction.Undo = undo; } + + + return packageAction; + }).ToArray(); + } } } diff --git a/src/Umbraco.Core/Packaging/ConflictingPackageData.cs b/src/Umbraco.Core/Packaging/ConflictingPackageData.cs index 401d3b6a85..a37195806e 100644 --- a/src/Umbraco.Core/Packaging/ConflictingPackageData.cs +++ b/src/Umbraco.Core/Packaging/ConflictingPackageData.cs @@ -23,9 +23,9 @@ namespace Umbraco.Core.Packaging return stylesheetNodes .Select(n => { - var xElement = n.Element(Constants.Packaging.NameNodeName); + var xElement = n.Element("Name"); if (xElement == null) - throw new FormatException($"Missing \"{Constants.Packaging.NameNodeName}\" element"); + throw new FormatException("Missing \"Name\" element"); return _fileService.GetStylesheetByName(xElement.Value) as IFile; }) @@ -37,9 +37,9 @@ namespace Umbraco.Core.Packaging return templateNodes .Select(n => { - var xElement = n.Element(Constants.Packaging.AliasNodeNameCapital) ?? n.Element(Constants.Packaging.AliasNodeNameSmall); + var xElement = n.Element("Alias") ?? n.Element("alias"); if (xElement == null) - throw new FormatException($"missing a \"{Constants.Packaging.AliasNodeNameCapital}\" element"); + throw new FormatException("missing a \"Alias\" element"); return _fileService.GetTemplate(xElement.Value); }) @@ -51,9 +51,9 @@ namespace Umbraco.Core.Packaging return macroNodes .Select(n => { - var xElement = n.Element(Constants.Packaging.AliasNodeNameSmall) ?? n.Element(Constants.Packaging.AliasNodeNameCapital); + var xElement = n.Element("alias") ?? n.Element("Alias"); if (xElement == null) - throw new FormatException($"missing a \"{Constants.Packaging.AliasNodeNameSmall}\" element in {Constants.Packaging.AliasNodeNameSmall} element"); + throw new FormatException("missing a \"alias\" element in alias element"); return _macroService.GetByAlias(xElement.Value); }) diff --git a/src/Umbraco.Core/Packaging/IPackageActionRunner.cs b/src/Umbraco.Core/Packaging/IPackageActionRunner.cs new file mode 100644 index 0000000000..d5c6327115 --- /dev/null +++ b/src/Umbraco.Core/Packaging/IPackageActionRunner.cs @@ -0,0 +1,23 @@ +using System.Xml.Linq; + +namespace Umbraco.Core.Packaging +{ + public interface IPackageActionRunner + { + /// + /// Runs the package action with the specified action alias. + /// + /// Name of the package. + /// The action alias. + /// The action XML. + void RunPackageAction(string packageName, string actionAlias, XElement actionXml); + + /// + /// Undos the package action with the specified action alias. + /// + /// Name of the package. + /// The action alias. + /// The action XML. + void UndoPackageAction(string packageName, string actionAlias, XElement actionXml); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Packaging/IPackageInstallation.cs b/src/Umbraco.Core/Packaging/IPackageInstallation.cs index 8ef3546c3d..83a21dcd2f 100644 --- a/src/Umbraco.Core/Packaging/IPackageInstallation.cs +++ b/src/Umbraco.Core/Packaging/IPackageInstallation.cs @@ -4,9 +4,8 @@ using Umbraco.Core.Models.Packaging; namespace Umbraco.Core.Packaging { - internal interface IPackageInstallation + public interface IPackageInstallation { - /// /// Installs a packages data and entities /// diff --git a/src/Umbraco.Core/Packaging/PackageActionRunner.cs b/src/Umbraco.Core/Packaging/PackageActionRunner.cs index 64b913989c..37103b0473 100644 --- a/src/Umbraco.Core/Packaging/PackageActionRunner.cs +++ b/src/Umbraco.Core/Packaging/PackageActionRunner.cs @@ -5,11 +5,10 @@ using Umbraco.Core._Legacy.PackageActions; namespace Umbraco.Core.Packaging { - /// /// Package actions are executed on packge install / uninstall. /// - public sealed class PackageActionRunner + internal class PackageActionRunner : IPackageActionRunner { private readonly ILogger _logger; private readonly PackageActionCollection _packageActions; diff --git a/src/Umbraco.Core/Packaging/PackageDataInstallation.cs b/src/Umbraco.Core/Packaging/PackageDataInstallation.cs new file mode 100644 index 0000000000..8ef99502c8 --- /dev/null +++ b/src/Umbraco.Core/Packaging/PackageDataInstallation.cs @@ -0,0 +1,1144 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using System.Web; +using System.Xml.Linq; +using System.Xml.XPath; +using Umbraco.Core.Collections; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Entities; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; +using Umbraco.Core.Services.Implement; + +namespace Umbraco.Core.Packaging +{ + internal class PackageDataInstallation + { + private readonly ILogger _logger; + private readonly IFileService _fileService; + private readonly IMacroService _macroService; + private readonly ILocalizationService _localizationService; + private readonly IDataTypeService _dataTypeService; + private readonly PropertyEditorCollection _propertyEditors; + private readonly IEntityService _entityService; + private readonly IContentTypeService _contentTypeService; + private readonly IContentService _contentService; + + public PackageDataInstallation(ILogger logger, IFileService fileService, IMacroService macroService, ILocalizationService localizationService, + IDataTypeService dataTypeService, IEntityService entityService, IContentTypeService contentTypeService, + IContentService contentService, PropertyEditorCollection propertyEditors) + { + _logger = logger; + _fileService = fileService; + _macroService = macroService; + _localizationService = localizationService; + _dataTypeService = dataTypeService; + _propertyEditors = propertyEditors; + _entityService = entityService; + _contentTypeService = contentTypeService; + _contentService = contentService; + } + + #region Content + + + public IEnumerable ImportContent(IEnumerable element, IDictionary importedDocumentTypes, int userId) + { + return element.Elements("DocumentSet").SelectMany(x => ImportContent(x, -1, importedDocumentTypes, userId)); + } + + /// + /// Imports and saves package xml as + /// + /// Xml to import + /// Optional parent Id for the content being imported + /// A dictionary of already imported document types (basically used as a cache) + /// Optional Id of the user performing the import + /// An enumrable list of generated content + public IEnumerable ImportContent(XElement element, int parentId, IDictionary importedDocumentTypes, int userId) + { + var name = element.Name.LocalName; + if (name.Equals("DocumentSet")) + { + //This is a regular deep-structured import + var roots = from doc in element.Elements() + where (string)doc.Attribute("isDoc") == "" + select doc; + + var contents = ParseDocumentRootXml(roots, parentId, importedDocumentTypes).ToList(); + if (contents.Any()) + _contentService.Save(contents, userId); + + return contents; + } + + var attribute = element.Attribute("isDoc"); + if (attribute != null) + { + //This is a single doc import + var elements = new List { element }; + var contents = ParseDocumentRootXml(elements, parentId, importedDocumentTypes).ToList(); + if (contents.Any()) + _contentService.Save(contents, userId); + + return contents; + } + + throw new ArgumentException( + "The passed in XElement is not valid! It does not contain a root element called " + + "'DocumentSet' (for structured imports) nor is the first element a Document (for single document import)."); + } + + private IEnumerable ParseDocumentRootXml(IEnumerable roots, int parentId, IDictionary importedContentTypes) + { + var contents = new List(); + foreach (var root in roots) + { + var contentTypeAlias = root.Name.LocalName; + + if (!importedContentTypes.ContainsKey(contentTypeAlias)) + { + var contentType = FindContentTypeByAlias(contentTypeAlias); + importedContentTypes.Add(contentTypeAlias, contentType); + } + + var content = CreateContentFromXml(root, importedContentTypes[contentTypeAlias], null, parentId); + contents.Add(content); + + var children = (from child in root.Elements() + where (string)child.Attribute("isDoc") == "" + select child) + .ToList(); + if (children.Any()) + contents.AddRange(CreateContentFromXml(children, content, importedContentTypes)); + } + return contents; + } + + private IEnumerable CreateContentFromXml(IEnumerable children, IContent parent, IDictionary importedContentTypes) + { + var list = new List(); + foreach (var child in children) + { + string contentTypeAlias = child.Name.LocalName; + + if (importedContentTypes.ContainsKey(contentTypeAlias) == false) + { + var contentType = FindContentTypeByAlias(contentTypeAlias); + importedContentTypes.Add(contentTypeAlias, contentType); + } + + //Create and add the child to the list + var content = CreateContentFromXml(child, importedContentTypes[contentTypeAlias], parent, default); + list.Add(content); + + //Recursive call + XElement child1 = child; + var grandChildren = (from grand in child1.Elements() + where (string)grand.Attribute("isDoc") == "" + select grand).ToList(); + + if (grandChildren.Any()) + list.AddRange(CreateContentFromXml(grandChildren, content, importedContentTypes)); + } + + return list; + } + + private IContent CreateContentFromXml(XElement element, IContentType contentType, IContent parent, int parentId) + { + var id = element.Attribute("id").Value; + var level = element.Attribute("level").Value; + var sortOrder = element.Attribute("sortOrder").Value; + var nodeName = element.Attribute("nodeName").Value; + var path = element.Attribute("path").Value; + //TODO: Shouldn't we be using this value??? + var template = element.Attribute("template").Value; + var key = Guid.Empty; + + var properties = from property in element.Elements() + where property.Attribute("isDoc") == null + select property; + + IContent content = parent == null + ? new Content(nodeName, parentId, contentType) + { + Level = int.Parse(level), + SortOrder = int.Parse(sortOrder) + } + : new Content(nodeName, parent, contentType) + { + Level = int.Parse(level), + SortOrder = int.Parse(sortOrder) + }; + + if (element.Attribute("key") != null && Guid.TryParse(element.Attribute("key").Value, out key)) + { + // update the Guid (for UDI support) + content.Key = key; + } + + foreach (var property in properties) + { + string propertyTypeAlias = property.Name.LocalName; + if (content.HasProperty(propertyTypeAlias)) + { + var propertyValue = property.Value; + + var propertyType = contentType.PropertyTypes.FirstOrDefault(pt => pt.Alias == propertyTypeAlias); + + //set property value + content.SetValue(propertyTypeAlias, propertyValue); + } + } + + return content; + } + + #endregion + + #region ContentTypes + + public IEnumerable ImportDocumentType(XElement docTypeElement, int userId) + { + return ImportDocumentTypes(new []{ docTypeElement }, userId); + } + + /// + /// Imports and saves package xml as + /// + /// Xml to import + /// Optional id of the User performing the operation. Default is zero (admin). + /// An enumrable list of generated ContentTypes + public IEnumerable ImportDocumentTypes(IEnumerable docTypeElements, int userId) + { + return ImportDocumentTypes(docTypeElements.ToList(), true, userId); + } + + /// + /// Imports and saves package xml as + /// + /// Xml to import + /// Boolean indicating whether or not to import the + /// Optional id of the User performing the operation. Default is zero (admin). + /// An enumrable list of generated ContentTypes + public IEnumerable ImportDocumentTypes(IReadOnlyCollection unsortedDocumentTypes, bool importStructure, int userId) + { + var importedContentTypes = new Dictionary(); + + //When you are importing a single doc type we have to assume that the depedencies are already there. + //Otherwise something like uSync won't work. + var graph = new TopoGraph>(x => x.Key, x => x.Dependencies); + var isSingleDocTypeImport = unsortedDocumentTypes.Count == 1; + + var importedFolders = CreateContentTypeFolderStructure(unsortedDocumentTypes); + + if (isSingleDocTypeImport == false) + { + //NOTE Here we sort the doctype XElements based on dependencies + //before creating the doc types - this should also allow for a better structure/inheritance support. + foreach (var documentType in unsortedDocumentTypes) + { + var elementCopy = documentType; + var infoElement = elementCopy.Element("Info"); + var dependencies = new HashSet(); + + //Add the Master as a dependency + if (string.IsNullOrEmpty((string)infoElement.Element("Master")) == false) + { + dependencies.Add(infoElement.Element("Master").Value); + } + + //Add compositions as dependencies + var compositionsElement = infoElement.Element("Compositions"); + if (compositionsElement != null && compositionsElement.HasElements) + { + var compositions = compositionsElement.Elements("Composition"); + if (compositions.Any()) + { + foreach (var composition in compositions) + { + dependencies.Add(composition.Value); + } + } + } + + graph.AddItem(TopoGraph.CreateNode(infoElement.Element("Alias").Value, elementCopy, dependencies.ToArray())); + } + } + + //Sorting the Document Types based on dependencies - if its not a single doc type import ref. #U4-5921 + var documentTypes = isSingleDocTypeImport + ? unsortedDocumentTypes.ToList() + : graph.GetSortedItems().Select(x => x.Item).ToList(); + + //Iterate the sorted document types and create them as IContentType objects + foreach (var documentType in documentTypes) + { + var alias = documentType.Element("Info").Element("Alias").Value; + if (importedContentTypes.ContainsKey(alias) == false) + { + var contentType = _contentTypeService.Get(alias); + importedContentTypes.Add(alias, contentType == null + ? CreateContentTypeFromXml(documentType, importedContentTypes) + : UpdateContentTypeFromXml(documentType, contentType, importedContentTypes)); + } + } + + foreach (var contentType in importedContentTypes) + { + var ct = contentType.Value; + if (importedFolders.ContainsKey(ct.Alias)) + { + ct.ParentId = importedFolders[ct.Alias]; + } + } + + //Save the newly created/updated IContentType objects + var list = importedContentTypes.Select(x => x.Value).ToList(); + _contentTypeService.Save(list, userId); + + //Now we can finish the import by updating the 'structure', + //which requires the doc types to be saved/available in the db + if (importStructure) + { + var updatedContentTypes = new List(); + //Update the structure here - we can't do it untill all DocTypes have been created + foreach (var documentType in documentTypes) + { + var alias = documentType.Element("Info").Element("Alias").Value; + var structureElement = documentType.Element("Structure"); + //Ensure that we only update ContentTypes which has actual structure-elements + if (structureElement == null || structureElement.Elements("DocumentType").Any() == false) continue; + + var updated = UpdateContentTypesStructure(importedContentTypes[alias], structureElement, importedContentTypes); + updatedContentTypes.Add(updated); + } + //Update ContentTypes with a newly added structure/list of allowed children + if (updatedContentTypes.Any()) + _contentTypeService.Save(updatedContentTypes, userId); + } + + return list; + } + + private Dictionary CreateContentTypeFolderStructure(IEnumerable unsortedDocumentTypes) + { + var importedFolders = new Dictionary(); + foreach (var documentType in unsortedDocumentTypes) + { + var foldersAttribute = documentType.Attribute("Folders"); + var infoElement = documentType.Element("Info"); + if (foldersAttribute != null && infoElement != null + //don't import any folder if this is a child doc type - the parent doc type will need to + //exist which contains it's folders + && ((string)infoElement.Element("Master")).IsNullOrWhiteSpace()) + { + var alias = documentType.Element("Info").Element("Alias").Value; + var folders = foldersAttribute.Value.Split('/'); + var rootFolder = HttpUtility.UrlDecode(folders[0]); + //level 1 = root level folders, there can only be one with the same name + var current = _contentTypeService.GetContainers(rootFolder, 1).FirstOrDefault(); + + if (current == null) + { + var tryCreateFolder = _contentTypeService.CreateContainer(-1, rootFolder); + if (tryCreateFolder == false) + { + _logger.Error(tryCreateFolder.Exception, "Could not create folder: {FolderName}", rootFolder); + throw tryCreateFolder.Exception; + } + var rootFolderId = tryCreateFolder.Result.Entity.Id; + current = _contentTypeService.GetContainer(rootFolderId); + } + + importedFolders.Add(alias, current.Id); + + for (var i = 1; i < folders.Length; i++) + { + var folderName = HttpUtility.UrlDecode(folders[i]); + current = CreateContentTypeChildFolder(folderName, current); + importedFolders[alias] = current.Id; + } + } + } + + return importedFolders; + } + + private EntityContainer CreateContentTypeChildFolder(string folderName, IUmbracoEntity current) + { + var children = _entityService.GetChildren(current.Id).ToArray(); + var found = children.Any(x => x.Name.InvariantEquals(folderName)); + if (found) + { + var containerId = children.Single(x => x.Name.InvariantEquals(folderName)).Id; + return _contentTypeService.GetContainer(containerId); + } + + var tryCreateFolder = _contentTypeService.CreateContainer(current.Id, folderName); + if (tryCreateFolder == false) + { + _logger.Error(tryCreateFolder.Exception, "Could not create folder: {FolderName}", folderName); + throw tryCreateFolder.Exception; + } + return _contentTypeService.GetContainer(tryCreateFolder.Result.Entity.Id); + } + + private IContentType CreateContentTypeFromXml(XElement documentType, IReadOnlyDictionary importedContentTypes) + { + var infoElement = documentType.Element("Info"); + + //Name of the master corresponds to the parent + var masterElement = infoElement.Element("Master"); + IContentType parent = null; + if (masterElement != null) + { + var masterAlias = masterElement.Value; + parent = importedContentTypes.ContainsKey(masterAlias) + ? importedContentTypes[masterAlias] + : _contentTypeService.Get(masterAlias); + } + + var alias = infoElement.Element("Alias").Value; + var contentType = parent == null + ? new ContentType(-1) { Alias = alias } + : new ContentType(parent, alias); + + if (parent != null) + contentType.AddContentType(parent); + + return UpdateContentTypeFromXml(documentType, contentType, importedContentTypes); + } + + private IContentType UpdateContentTypeFromXml(XElement documentType, IContentType contentType, IReadOnlyDictionary importedContentTypes) + { + var infoElement = documentType.Element("Info"); + var defaultTemplateElement = infoElement.Element("DefaultTemplate"); + + contentType.Name = infoElement.Element("Name").Value; + contentType.Icon = infoElement.Element("Icon").Value; + contentType.Thumbnail = infoElement.Element("Thumbnail").Value; + contentType.Description = infoElement.Element("Description").Value; + + //NOTE AllowAtRoot is a new property in the package xml so we need to verify it exists before using it. + var allowAtRoot = infoElement.Element("AllowAtRoot"); + if (allowAtRoot != null) + contentType.AllowedAsRoot = allowAtRoot.Value.InvariantEquals("true"); + + //NOTE IsListView is a new property in the package xml so we need to verify it exists before using it. + var isListView = infoElement.Element("IsListView"); + if (isListView != null) + contentType.IsContainer = isListView.Value.InvariantEquals("true"); + + //Name of the master corresponds to the parent and we need to ensure that the Parent Id is set + var masterElement = infoElement.Element("Master"); + if (masterElement != null) + { + var masterAlias = masterElement.Value; + IContentType parent = importedContentTypes.ContainsKey(masterAlias) + ? importedContentTypes[masterAlias] + : _contentTypeService.Get(masterAlias); + + contentType.SetParent(parent); + } + + //Update Compositions on the ContentType to ensure that they are as is defined in the package xml + var compositionsElement = infoElement.Element("Compositions"); + if (compositionsElement != null && compositionsElement.HasElements) + { + var compositions = compositionsElement.Elements("Composition"); + if (compositions.Any()) + { + foreach (var composition in compositions) + { + var compositionAlias = composition.Value; + var compositionContentType = importedContentTypes.ContainsKey(compositionAlias) + ? importedContentTypes[compositionAlias] + : _contentTypeService.Get(compositionAlias); + var added = contentType.AddContentType(compositionContentType); + } + } + } + + UpdateContentTypesAllowedTemplates(contentType, infoElement.Element("AllowedTemplates"), defaultTemplateElement); + UpdateContentTypesTabs(contentType, documentType.Element("Tabs")); + UpdateContentTypesProperties(contentType, documentType.Element("GenericProperties")); + + return contentType; + } + + private void UpdateContentTypesAllowedTemplates(IContentType contentType, + XElement allowedTemplatesElement, XElement defaultTemplateElement) + { + if (allowedTemplatesElement != null && allowedTemplatesElement.Elements("Template").Any()) + { + var allowedTemplates = contentType.AllowedTemplates.ToList(); + foreach (var templateElement in allowedTemplatesElement.Elements("Template")) + { + var alias = templateElement.Value; + var template = _fileService.GetTemplate(alias.ToSafeAlias()); + if (template != null) + { + if (allowedTemplates.Any(x => x.Id == template.Id)) continue; + allowedTemplates.Add(template); + } + else + { + _logger.Warn("Packager: Error handling allowed templates. Template with alias '{TemplateAlias}' could not be found.", alias); + } + } + + contentType.AllowedTemplates = allowedTemplates; + } + + if (string.IsNullOrEmpty((string)defaultTemplateElement) == false) + { + var defaultTemplate = _fileService.GetTemplate(defaultTemplateElement.Value.ToSafeAlias()); + if (defaultTemplate != null) + { + contentType.SetDefaultTemplate(defaultTemplate); + } + else + { + _logger.Warn("Packager: Error handling default template. Default template with alias '{DefaultTemplateAlias}' could not be found.", defaultTemplateElement.Value); + } + } + } + + private void UpdateContentTypesTabs(IContentType contentType, XElement tabElement) + { + if (tabElement == null) + return; + + var tabs = tabElement.Elements("Tab"); + foreach (var tab in tabs) + { + var id = tab.Element("Id").Value;//Do we need to use this for tracking? + var caption = tab.Element("Caption").Value; + + if (contentType.PropertyGroups.Contains(caption) == false) + { + contentType.AddPropertyGroup(caption); + + } + + int sortOrder; + if (tab.Element("SortOrder") != null && int.TryParse(tab.Element("SortOrder").Value, out sortOrder)) + { + // Override the sort order with the imported value + contentType.PropertyGroups[caption].SortOrder = sortOrder; + } + } + } + + private void UpdateContentTypesProperties(IContentType contentType, XElement genericPropertiesElement) + { + var properties = genericPropertiesElement.Elements("GenericProperty"); + foreach (var property in properties) + { + var dataTypeDefinitionId = new Guid(property.Element("Definition").Value);//Unique Id for a DataTypeDefinition + + var dataTypeDefinition = _dataTypeService.GetDataType(dataTypeDefinitionId); + + //If no DataTypeDefinition with the guid from the xml wasn't found OR the ControlId on the DataTypeDefinition didn't match the DataType Id + //We look up a DataTypeDefinition that matches + + + //get the alias as a string for use below + var propertyEditorAlias = property.Element("Type").Value.Trim(); + + //If no DataTypeDefinition with the guid from the xml wasn't found OR the ControlId on the DataTypeDefinition didn't match the DataType Id + //We look up a DataTypeDefinition that matches + + if (dataTypeDefinition == null) + { + var dataTypeDefinitions = _dataTypeService.GetByEditorAlias(propertyEditorAlias); + if (dataTypeDefinitions != null && dataTypeDefinitions.Any()) + { + dataTypeDefinition = dataTypeDefinitions.FirstOrDefault(); + } + } + else if (dataTypeDefinition.EditorAlias != propertyEditorAlias) + { + var dataTypeDefinitions = _dataTypeService.GetByEditorAlias(propertyEditorAlias); + if (dataTypeDefinitions != null && dataTypeDefinitions.Any()) + { + dataTypeDefinition = dataTypeDefinitions.FirstOrDefault(); + } + } + + // For backwards compatibility, if no datatype with that ID can be found, we're letting this fail silently. + // This means that the property will not be created. + if (dataTypeDefinition == null) + { + _logger.Warn("Packager: Error handling creation of PropertyType '{PropertyType}'. Could not find DataTypeDefintion with unique id '{DataTypeDefinitionId}' nor one referencing the DataType with a property editor alias (or legacy control id) '{PropertyEditorAlias}'. Did the package creator forget to package up custom datatypes? This property will be converted to a label/readonly editor if one exists.", + property.Element("Name").Value, dataTypeDefinitionId, property.Element("Type").Value.Trim()); + + //convert to a label! + dataTypeDefinition = _dataTypeService.GetByEditorAlias(Constants.PropertyEditors.Aliases.NoEdit).FirstOrDefault(); + //if for some odd reason this isn't there then ignore + if (dataTypeDefinition == null) continue; + } + + var sortOrder = 0; + var sortOrderElement = property.Element("SortOrder"); + if (sortOrderElement != null) + int.TryParse(sortOrderElement.Value, out sortOrder); + var propertyType = new PropertyType(dataTypeDefinition, property.Element("Alias").Value) + { + Name = property.Element("Name").Value, + Description = (string)property.Element("Description"), + Mandatory = property.Element("Mandatory") != null ? property.Element("Mandatory").Value.ToLowerInvariant().Equals("true") : false, + ValidationRegExp = (string)property.Element("Validation"), + SortOrder = sortOrder + }; + + var tab = (string)property.Element("Tab"); + if (string.IsNullOrEmpty(tab)) + { + contentType.AddPropertyType(propertyType); + } + else + { + contentType.AddPropertyType(propertyType, tab); + } + } + } + + private IContentType UpdateContentTypesStructure(IContentType contentType, XElement structureElement, IReadOnlyDictionary importedContentTypes) + { + var allowedChildren = contentType.AllowedContentTypes.ToList(); + int sortOrder = allowedChildren.Any() ? allowedChildren.Last().SortOrder : 0; + foreach (var element in structureElement.Elements("DocumentType")) + { + var alias = element.Value; + + var allowedChild = importedContentTypes.ContainsKey(alias) ? importedContentTypes[alias] : _contentTypeService.Get(alias); + if (allowedChild == null) + { + _logger.Warn( + "Packager: Error handling DocumentType structure. DocumentType with alias '{DoctypeAlias}' could not be found and was not added to the structure for '{DoctypeStructureAlias}'.", + alias, contentType.Alias); + continue; + } + + if (allowedChildren.Any(x => x.Id.IsValueCreated && x.Id.Value == allowedChild.Id)) continue; + + allowedChildren.Add(new ContentTypeSort(new Lazy(() => allowedChild.Id), sortOrder, allowedChild.Alias)); + sortOrder++; + } + + contentType.AllowedContentTypes = allowedChildren; + return contentType; + } + + /// + /// Used during Content import to ensure that the ContentType of a content item exists + /// + /// + /// + private IContentType FindContentTypeByAlias(string contentTypeAlias) + { + var contentType = _contentTypeService.Get(contentTypeAlias); + + if (contentType == null) + throw new Exception($"ContentType matching the passed in Alias: '{contentTypeAlias}' was null"); + + return contentType; + } + + #endregion + + #region DataTypes + + /// + /// Imports and saves package xml as + /// + /// Xml to import + /// Optional id of the user + /// An enumrable list of generated DataTypeDefinitions + public IEnumerable ImportDataTypes(IReadOnlyCollection dataTypeElements, int userId) + { + var dataTypes = new List(); + + var importedFolders = CreateDataTypeFolderStructure(dataTypeElements); + + foreach (var dataTypeElement in dataTypeElements) + { + var dataTypeDefinitionName = dataTypeElement.AttributeValue("Name"); + + var dataTypeDefinitionId = dataTypeElement.AttributeValue("Definition"); + var databaseTypeAttribute = dataTypeElement.Attribute("DatabaseType"); + + var parentId = -1; + if (importedFolders.ContainsKey(dataTypeDefinitionName)) + parentId = importedFolders[dataTypeDefinitionName]; + + var definition = _dataTypeService.GetDataType(dataTypeDefinitionId); + //If the datatypedefinition doesn't already exist we create a new new according to the one in the package xml + if (definition == null) + { + var databaseType = databaseTypeAttribute?.Value.EnumParse(true) ?? ValueStorageType.Ntext; + + // the Id field is actually the string property editor Alias + // however, the actual editor with this alias could be installed with the package, and + // therefore not yet part of the _propertyEditors collection, so we cannot try and get + // the actual editor - going with a void editor + + var editorAlias = dataTypeElement.Attribute("Id")?.Value?.Trim(); + if (!_propertyEditors.TryGet(editorAlias, out var editor)) + editor = new VoidEditor(_logger) { Alias = editorAlias }; + + var dataType = new DataType(editor) + { + Key = dataTypeDefinitionId, + Name = dataTypeDefinitionName, + DatabaseType = databaseType, + ParentId = parentId + }; + + var configurationAttributeValue = dataTypeElement.Attribute("Configuration")?.Value; + if (!string.IsNullOrWhiteSpace(configurationAttributeValue)) + dataType.Configuration = editor.GetConfigurationEditor().FromDatabase(configurationAttributeValue); + + dataTypes.Add(dataType); + } + else + { + definition.ParentId = parentId; + _dataTypeService.Save(definition, userId); + } + } + + if (dataTypes.Count > 0) + { + _dataTypeService.Save(dataTypes, userId, true); + } + + return dataTypes; + } + + private Dictionary CreateDataTypeFolderStructure(IEnumerable datatypeElements) + { + var importedFolders = new Dictionary(); + foreach (var datatypeElement in datatypeElements) + { + var foldersAttribute = datatypeElement.Attribute("Folders"); + if (foldersAttribute != null) + { + var name = datatypeElement.Attribute("Name").Value; + var folders = foldersAttribute.Value.Split('/'); + var rootFolder = HttpUtility.UrlDecode(folders[0]); + //there will only be a single result by name for level 1 (root) containers + var current = _dataTypeService.GetContainers(rootFolder, 1).FirstOrDefault(); + + if (current == null) + { + var tryCreateFolder = _dataTypeService.CreateContainer(-1, rootFolder); + if (tryCreateFolder == false) + { + _logger.Error(tryCreateFolder.Exception, "Could not create folder: {FolderName}", rootFolder); + throw tryCreateFolder.Exception; + } + current = _dataTypeService.GetContainer(tryCreateFolder.Result.Entity.Id); + } + + importedFolders.Add(name, current.Id); + + for (var i = 1; i < folders.Length; i++) + { + var folderName = HttpUtility.UrlDecode(folders[i]); + current = CreateDataTypeChildFolder(folderName, current); + importedFolders[name] = current.Id; + } + } + } + + return importedFolders; + } + + private EntityContainer CreateDataTypeChildFolder(string folderName, IUmbracoEntity current) + { + var children = _entityService.GetChildren(current.Id).ToArray(); + var found = children.Any(x => x.Name.InvariantEquals(folderName)); + if (found) + { + var containerId = children.Single(x => x.Name.InvariantEquals(folderName)).Id; + return _dataTypeService.GetContainer(containerId); + } + + var tryCreateFolder = _dataTypeService.CreateContainer(current.Id, folderName); + if (tryCreateFolder == false) + { + _logger.Error(tryCreateFolder.Exception, "Could not create folder: {FolderName}", folderName); + throw tryCreateFolder.Exception; + } + return _dataTypeService.GetContainer(tryCreateFolder.Result.Entity.Id); + } + + #endregion + + #region Dictionary Items + + /// + /// Imports and saves the 'DictionaryItems' part of the package xml as a list of + /// + /// Xml to import + /// + /// An enumerable list of dictionary items + public IEnumerable ImportDictionaryItems(IEnumerable dictionaryItemElementList, int userId) + { + var languages = _localizationService.GetAllLanguages().ToList(); + return ImportDictionaryItems(dictionaryItemElementList, languages, null, userId); + } + + private IEnumerable ImportDictionaryItems(IEnumerable dictionaryItemElementList, List languages, Guid? parentId, int userId) + { + var items = new List(); + foreach (var dictionaryItemElement in dictionaryItemElementList) + items.AddRange(ImportDictionaryItem(dictionaryItemElement, languages, parentId, userId)); + + return items; + } + + private IEnumerable ImportDictionaryItem(XElement dictionaryItemElement, List languages, Guid? parentId, int userId) + { + var items = new List(); + + IDictionaryItem dictionaryItem; + var key = dictionaryItemElement.Attribute("Key").Value; + if (_localizationService.DictionaryItemExists(key)) + dictionaryItem = GetAndUpdateDictionaryItem(key, dictionaryItemElement, languages); + else + dictionaryItem = CreateNewDictionaryItem(key, dictionaryItemElement, languages, parentId); + _localizationService.Save(dictionaryItem, userId); + items.Add(dictionaryItem); + + items.AddRange(ImportDictionaryItems(dictionaryItemElement.Elements("DictionaryItem"), languages, dictionaryItem.Key, userId)); + return items; + } + + private IDictionaryItem GetAndUpdateDictionaryItem(string key, XElement dictionaryItemElement, List languages) + { + var dictionaryItem = _localizationService.GetDictionaryItemByKey(key); + var translations = dictionaryItem.Translations.ToList(); + foreach (var valueElement in dictionaryItemElement.Elements("Value").Where(v => DictionaryValueIsNew(translations, v))) + AddDictionaryTranslation(translations, valueElement, languages); + dictionaryItem.Translations = translations; + return dictionaryItem; + } + + private static DictionaryItem CreateNewDictionaryItem(string key, XElement dictionaryItemElement, List languages, Guid? parentId) + { + var dictionaryItem = parentId.HasValue ? new DictionaryItem(parentId.Value, key) : new DictionaryItem(key); + var translations = new List(); + + foreach (var valueElement in dictionaryItemElement.Elements("Value")) + AddDictionaryTranslation(translations, valueElement, languages); + + dictionaryItem.Translations = translations; + return dictionaryItem; + } + + private static bool DictionaryValueIsNew(IEnumerable translations, XElement valueElement) + { + return translations.All(t => + String.Compare(t.Language.IsoCode, valueElement.Attribute("LanguageCultureAlias").Value, + StringComparison.InvariantCultureIgnoreCase) != 0 + ); + } + + private static void AddDictionaryTranslation(ICollection translations, XElement valueElement, IEnumerable languages) + { + var languageId = valueElement.Attribute("LanguageCultureAlias").Value; + var language = languages.SingleOrDefault(l => l.IsoCode == languageId); + if (language == null) + return; + var translation = new DictionaryTranslation(language, valueElement.Value); + translations.Add(translation); + } + + #endregion + + #region Languages + + + /// + /// Imports and saves the 'Languages' part of a package xml as a list of + /// + /// Xml to import + /// Optional id of the User performing the operation + /// An enumerable list of generated languages + public IEnumerable ImportLanguages(IEnumerable languageElements, int userId) + { + var list = new List(); + foreach (var languageElement in languageElements) + { + var isoCode = languageElement.AttributeValue("CultureAlias"); + var existingLanguage = _localizationService.GetLanguageByIsoCode(isoCode); + if (existingLanguage != null) continue; + var langauge = new Language(isoCode) + { + CultureName = languageElement.AttributeValue("FriendlyName") + }; + _localizationService.Save(langauge, userId); + list.Add(langauge); + } + + return list; + } + + #endregion + + #region Macros + + /// + /// Imports and saves the 'Macros' part of a package xml as a list of + /// + /// Xml to import + /// Optional id of the User performing the operation + /// + public IEnumerable ImportMacros(IEnumerable macroElements, int userId) + { + var macros = macroElements.Select(ParseMacroElement).ToList(); + + foreach (var macro in macros) + { + var existing = _macroService.GetByAlias(macro.Alias); + if (existing != null) + macro.Id = existing.Id; + + _macroService.Save(macro, userId); + } + + return macros; + } + + private IMacro ParseMacroElement(XElement macroElement) + { + var macroName = macroElement.Element("name").Value; + var macroAlias = macroElement.Element("alias").Value; + var macroType = Enum.Parse(macroElement.Element("macroType").Value); + var macroSource = macroElement.Element("macroSource").Value; + + //Following xml elements are treated as nullable properties + var useInEditorElement = macroElement.Element("useInEditor"); + var useInEditor = false; + if (useInEditorElement != null && string.IsNullOrEmpty((string)useInEditorElement) == false) + { + useInEditor = bool.Parse(useInEditorElement.Value); + } + var cacheDurationElement = macroElement.Element("refreshRate"); + var cacheDuration = 0; + if (cacheDurationElement != null && string.IsNullOrEmpty((string)cacheDurationElement) == false) + { + cacheDuration = int.Parse(cacheDurationElement.Value); + } + var cacheByMemberElement = macroElement.Element("cacheByMember"); + var cacheByMember = false; + if (cacheByMemberElement != null && string.IsNullOrEmpty((string)cacheByMemberElement) == false) + { + cacheByMember = bool.Parse(cacheByMemberElement.Value); + } + var cacheByPageElement = macroElement.Element("cacheByPage"); + var cacheByPage = false; + if (cacheByPageElement != null && string.IsNullOrEmpty((string)cacheByPageElement) == false) + { + cacheByPage = bool.Parse(cacheByPageElement.Value); + } + var dontRenderElement = macroElement.Element("dontRender"); + var dontRender = true; + if (dontRenderElement != null && string.IsNullOrEmpty((string)dontRenderElement) == false) + { + dontRender = bool.Parse(dontRenderElement.Value); + } + + var existingMacro = _macroService.GetByAlias(macroAlias) as Macro; + var macro = existingMacro ?? new Macro(macroAlias, macroName, macroSource, macroType, + cacheByPage, cacheByMember, dontRender, useInEditor, cacheDuration); + + var properties = macroElement.Element("properties"); + if (properties != null) + { + int sortOrder = 0; + foreach (var property in properties.Elements()) + { + var propertyName = property.Attribute("name").Value; + var propertyAlias = property.Attribute("alias").Value; + var editorAlias = property.Attribute("propertyType").Value; + var sortOrderAttribute = property.Attribute("sortOrder"); + if (sortOrderAttribute != null) + { + sortOrder = int.Parse(sortOrderAttribute.Value); + } + + if (macro.Properties.Values.Any(x => string.Equals(x.Alias, propertyAlias, StringComparison.OrdinalIgnoreCase))) continue; + macro.Properties.Add(new MacroProperty(propertyAlias, propertyName, sortOrder, editorAlias)); + sortOrder++; + } + } + return macro; + } + + + + + + #endregion + + #region Stylesheets + + public IEnumerable ImportStylesheets(IEnumerable stylesheetElements, int userId) + { + var result = new List(); + + foreach (var n in stylesheetElements) + { + var stylesheetName = n.Element("Name")?.Value; + if (stylesheetName.IsNullOrWhiteSpace()) continue; + + var s = _fileService.GetStylesheetByName(stylesheetName); + if (s == null) + { + var fileName = n.Element("FileName")?.Value; + if (fileName == null) continue; + var content = n.Element("Content")?.Value; + if (content == null) continue; + + s = new Stylesheet(fileName) { Content = content }; + _fileService.SaveStylesheet(s); + } + + foreach (var prop in n.XPathSelectElements("Properties/Property")) + { + var alias = prop.Element("Alias")?.Value; + var sp = s.Properties.SingleOrDefault(p => p != null && p.Alias == alias); + var name = prop.Element("Name")?.Value; + if (sp == null) + { + sp = new StylesheetProperty(name, "#" + name.ToSafeAlias(), ""); + s.AddProperty(sp); + } + else + { + //sp.Text = name; + //Changing the name requires removing the current property and then adding another new one + if (sp.Name != name) + { + s.RemoveProperty(sp.Name); + var newProp = new StylesheetProperty(name, sp.Alias, sp.Value); + s.AddProperty(newProp); + sp = newProp; + } + } + sp.Alias = alias; + sp.Value = prop.Element("Value")?.Value; + } + _fileService.SaveStylesheet(s); + result.Add(s); + } + + return result; + } + + #endregion + + #region Templates + + public IEnumerable ImportTemplate(XElement templateElement, int userId) + { + return ImportTemplates(new[] {templateElement}, userId); + } + + /// + /// Imports and saves package xml as + /// + /// Xml to import + /// Optional user id + /// An enumrable list of generated Templates + public IEnumerable ImportTemplates(IReadOnlyCollection templateElements, int userId) + { + var templates = new List(); + + var graph = new TopoGraph>(x => x.Key, x => x.Dependencies); + + foreach (var tempElement in templateElements) + { + var dependencies = new List(); + var elementCopy = tempElement; + //Ensure that the Master of the current template is part of the import, otherwise we ignore this dependency as part of the dependency sorting. + if (string.IsNullOrEmpty((string)elementCopy.Element("Master")) == false && + templateElements.Any(x => (string)x.Element("Alias") == (string)elementCopy.Element("Master"))) + { + dependencies.Add((string)elementCopy.Element("Master")); + } + else if (string.IsNullOrEmpty((string)elementCopy.Element("Master")) == false && + templateElements.Any(x => (string)x.Element("Alias") == (string)elementCopy.Element("Master")) == false) + { + _logger.Info( + "Template '{TemplateAlias}' has an invalid Master '{TemplateMaster}', so the reference has been ignored.", + (string)elementCopy.Element("Alias"), + (string)elementCopy.Element("Master")); + } + + graph.AddItem(TopoGraph.CreateNode((string)elementCopy.Element("Alias"), elementCopy, dependencies)); + } + + //Sort templates by dependencies to a potential master template + var sorted = graph.GetSortedItems(); + foreach (var item in sorted) + { + var templateElement = item.Item; + + var templateName = templateElement.Element("Name").Value; + var alias = templateElement.Element("Alias").Value; + var design = templateElement.Element("Design").Value; + var masterElement = templateElement.Element("Master"); + + var isMasterPage = IsMasterPageSyntax(design); + var path = isMasterPage ? MasterpagePath(alias) : ViewPath(alias); + + var existingTemplate = _fileService.GetTemplate(alias) as Template; + var template = existingTemplate ?? new Template(templateName, alias); + template.Content = design; + if (masterElement != null && string.IsNullOrEmpty((string)masterElement) == false) + { + template.MasterTemplateAlias = masterElement.Value; + var masterTemplate = templates.FirstOrDefault(x => x.Alias == masterElement.Value); + if (masterTemplate != null) + template.MasterTemplateId = new Lazy(() => masterTemplate.Id); + } + templates.Add(template); + } + + if (templates.Any()) + _fileService.SaveTemplate(templates, userId); + + return templates; + } + + + private bool IsMasterPageSyntax(string code) + { + return Regex.IsMatch(code, @"<%@\s*Master", RegexOptions.IgnoreCase) || + code.InvariantContains(" /// Constructor /// - /// + /// /// /// + /// /// /// The relative path of the package storage folder (i.e. ~/App_Data/Packages ) /// @@ -36,13 +38,14 @@ namespace Umbraco.Core.Packaging /// /// The destination root folder to extract the package files (generally the same as applicationRoot) but can be modified for testing /// - public PackageInstallation(IPackagingService packagingService, PackageFileInstallation packageFileInstallation, CompiledPackageXmlParser parser, + public PackageInstallation(PackageDataInstallation packageDataInstallation, PackageFileInstallation packageFileInstallation, CompiledPackageXmlParser parser, IPackageActionRunner packageActionRunner, string packagesFolderPath, DirectoryInfo applicationRootFolder, DirectoryInfo packageExtractionFolder) { _packageExtraction = new PackageExtraction(); - _packageFileInstallation = packageFileInstallation; - _packagingService = packagingService ?? throw new ArgumentNullException(nameof(packagingService)); + _packageFileInstallation = packageFileInstallation ?? throw new ArgumentNullException(nameof(packageFileInstallation)); + _packageDataInstallation = packageDataInstallation ?? throw new ArgumentNullException(nameof(packageDataInstallation)); _parser = parser; + _packageActionRunner = packageActionRunner; _packagesFolderPath = packagesFolderPath; _applicationRootFolder = applicationRootFolder; _packageExtractionFolder = packageExtractionFolder; @@ -61,8 +64,6 @@ namespace Umbraco.Core.Packaging return compiledPackage; } - //fixme: Should we move all of the ImportXXXX methods here instead of on the IPackagingService? we don't want to have cicurlar refs - public IEnumerable InstallPackageFiles(PackageDefinition packageDefinition, CompiledPackage compiledPackage, int userId) { if (packageDefinition == null) throw new ArgumentNullException(nameof(packageDefinition)); @@ -79,118 +80,77 @@ namespace Umbraco.Core.Packaging public InstallationSummary InstallPackageData(PackageDefinition packageDefinition, CompiledPackage compiledPackage, int userId) { - //fixme: fill this in - throw new NotImplementedException(); + //TODO: Update the PackageDefinition! + + var installationSummary = new InstallationSummary + { + DataTypesInstalled = _packageDataInstallation.ImportDataTypes(compiledPackage.DataTypes.ToList(), userId), + LanguagesInstalled = _packageDataInstallation.ImportLanguages(compiledPackage.Languages, userId), + DictionaryItemsInstalled = _packageDataInstallation.ImportDictionaryItems(compiledPackage.DictionaryItems, userId), + MacrosInstalled = _packageDataInstallation.ImportMacros(compiledPackage.Macros, userId), + TemplatesInstalled = _packageDataInstallation.ImportTemplates(compiledPackage.Templates.ToList(), userId), + DocumentTypesInstalled = _packageDataInstallation.ImportDocumentTypes(compiledPackage.DocumentTypes, userId) + }; + + //we need a reference to the imported doc types to continue + var importedDocTypes = installationSummary.DocumentTypesInstalled.ToDictionary(x => x.Alias, x => x); + + installationSummary.StylesheetsInstalled = _packageDataInstallation.ImportStylesheets(compiledPackage.Stylesheets, userId); + installationSummary.ContentInstalled = _packageDataInstallation.ImportContent(compiledPackage.Documents, importedDocTypes, userId); + installationSummary.Actions = _parser.GetPackageActions(XElement.Parse(compiledPackage.Actions), compiledPackage.Name); + installationSummary.MetaData = compiledPackage; + //fixme: Verify that this will work! + installationSummary.FilesInstalled = packageDefinition.Files; + installationSummary.PackageInstalled = true; + + //make sure the definition is up to date with everything + + foreach (var x in installationSummary.DataTypesInstalled) packageDefinition.DataTypes.Add(x.Id.ToInvariantString()); + foreach (var x in installationSummary.LanguagesInstalled) packageDefinition.Languages.Add(x.Id.ToInvariantString()); + foreach (var x in installationSummary.DictionaryItemsInstalled) packageDefinition.DictionaryItems.Add(x.Id.ToInvariantString()); + foreach (var x in installationSummary.MacrosInstalled) packageDefinition.Macros.Add(x.Id.ToInvariantString()); + foreach (var x in installationSummary.TemplatesInstalled) packageDefinition.Templates.Add(x.Id.ToInvariantString()); + foreach (var x in installationSummary.DocumentTypesInstalled) packageDefinition.DocumentTypes.Add(x.Id.ToInvariantString()); + foreach (var x in installationSummary.StylesheetsInstalled) packageDefinition.Stylesheets.Add(x.Id.ToInvariantString()); + var contentInstalled = installationSummary.ContentInstalled.ToList(); + packageDefinition.ContentNodeId = contentInstalled.Count > 0 ? contentInstalled[0].Id.ToInvariantString() : null; + + RunPackageActions(packageDefinition, installationSummary.Actions); + + return installationSummary; } - //public InstallationSummary InstallPackage(FileInfo packageFile, int userId) - //{ - // XElement dataTypes; - // XElement languages; - // XElement dictionaryItems; - // XElement macroes; - // XElement files; - // XElement templates; - // XElement documentTypes; - // XElement styleSheets; - // XElement documentSet; - // XElement documents; - // XElement actions; - // IPackageInfo metaData; - // InstallationSummary installationSummary; + private void RunPackageActions(PackageDefinition packageDefinition, IEnumerable actions) + { + foreach (var n in actions) + { + var undo = n.Undo; + if (undo) + packageDefinition.Actions += n.XmlData.ToString(); - // try - // { - // XElement rootElement = GetConfigXmlElement(packageFile); - // PackageSupportedCheck(rootElement); - // PackageStructureSanityCheck(packageFile, rootElement); - // dataTypes = rootElement.Element(Constants.Packaging.DataTypesNodeName); - // languages = rootElement.Element(Constants.Packaging.LanguagesNodeName); - // dictionaryItems = rootElement.Element(Constants.Packaging.DictionaryItemsNodeName); - // macroes = rootElement.Element(Constants.Packaging.MacrosNodeName); - // files = rootElement.Element(Constants.Packaging.FilesNodeName); - // templates = rootElement.Element(Constants.Packaging.TemplatesNodeName); - // documentTypes = rootElement.Element(Constants.Packaging.DocumentTypesNodeName); - // styleSheets = rootElement.Element(Constants.Packaging.StylesheetsNodeName); - // documentSet = rootElement.Element(Constants.Packaging.DocumentSetNodeName); - // documents = rootElement.Element(Constants.Packaging.DocumentsNodeName); - // actions = rootElement.Element(Constants.Packaging.ActionsNodeName); + //Run the actions tagged only for 'install' + if (n.RunAt != ActionRunAt.Install) continue; - // metaData = GetMetaData(rootElement); - // installationSummary = new InstallationSummary {MetaData = metaData}; - // } - // catch (Exception e) - // { - // throw new Exception("Error reading " + packageFile, e); - // } - - // try - // { - // var dataTypeDefinitions = EmptyEnumerableIfNull(dataTypes) ?? InstallDataTypes(dataTypes, userId); - // installationSummary.DataTypesInstalled = dataTypeDefinitions; - - // var languagesInstalled = EmptyEnumerableIfNull(languages) ?? InstallLanguages(languages, userId); - // installationSummary.LanguagesInstalled = languagesInstalled; - - // var dictionaryInstalled = EmptyEnumerableIfNull(dictionaryItems) ?? InstallDictionaryItems(dictionaryItems); - // installationSummary.DictionaryItemsInstalled = dictionaryInstalled; - - // var macros = EmptyEnumerableIfNull(macroes) ?? InstallMacros(macroes, userId); - // installationSummary.MacrosInstalled = macros; - - // var templatesInstalled = EmptyEnumerableIfNull(templates) ?? InstallTemplats(templates, userId); - // installationSummary.TemplatesInstalled = templatesInstalled; - - // var documentTypesInstalled = EmptyEnumerableIfNull(documentTypes) ?? InstallDocumentTypes(documentTypes, userId); - // installationSummary.ContentTypesInstalled =documentTypesInstalled; - - // var stylesheetsInstalled = EmptyEnumerableIfNull(styleSheets) ?? InstallStylesheets(styleSheets); - // installationSummary.StylesheetsInstalled = stylesheetsInstalled; - - // var documentsInstalled = documents != null ? InstallDocuments(documents, userId) - // : EmptyEnumerableIfNull(documentSet) - // ?? InstallDocuments(documentSet, userId); - // installationSummary.ContentInstalled = documentsInstalled; - - // var packageActions = EmptyEnumerableIfNull(actions) ?? GetPackageActions(actions, metaData.Name); - // installationSummary.Actions = packageActions; - - // installationSummary.PackageInstalled = true; - - // return installationSummary; - // } - // catch (Exception e) - // { - // throw new Exception("Error installing package " + packageFile, e); - // } - //} + if (n.Alias.IsNullOrWhiteSpace() == false) + _packageActionRunner.RunPackageAction(packageDefinition.Name, n.Alias, n.XmlData); + } + } private FileInfo GetPackageZipFile(string packageFileName) => new FileInfo(IOHelper.MapPath(_packagesFolderPath).EnsureEndsWith('\\') + packageFileName); - private static IEnumerable EmptyEnumerableIfNull(object obj) - { - return obj == null ? Enumerable.Empty() : null; - } - private XDocument GetConfigXmlDoc(FileInfo packageFile) { - var configXmlContent = _packageExtraction.ReadTextFileFromArchive(packageFile, Constants.Packaging.PackageXmlFileName, out _); + var configXmlContent = _packageExtraction.ReadTextFileFromArchive(packageFile, "package.xml", out _); var document = XDocument.Parse(configXmlContent); if (document.Root == null || - document.Root.Name.LocalName.Equals(Constants.Packaging.UmbPackageNodeName) == false) + document.Root.Name.LocalName.Equals("umbPackage") == false) throw new FormatException("xml does not have a root node called \"umbPackage\""); return document; } - public XElement GetConfigXmlElement(FileInfo packageFile) - { - var document = GetConfigXmlDoc(packageFile); - return document.Root; - } - private void ValidatePackageFile(FileInfo packageFile, CompiledPackage package) { if (!(package.Files?.Count > 0)) return; @@ -205,8 +165,8 @@ namespace Umbraco.Core.Packaging string.Join(", ", missingFiles.Select( mf => { - var sd = sourceDestination.Single(fi => fi.packageUniqueFile == mf); - return $"source: \"{sd.packageUniqueFile}\" destination: \"{sd.appRelativePath}\""; + var (packageUniqueFile, appRelativePath) = sourceDestination.Single(fi => fi.packageUniqueFile == mf); + return $"source: \"{packageUniqueFile}\" destination: \"{appRelativePath}\""; }))); } @@ -219,143 +179,7 @@ namespace Umbraco.Core.Packaging } } - private static IEnumerable GetPackageActions(XElement actionsElement, string packageName) - { - if (actionsElement == null) { return new PackageAction[0]; } - - if (string.Equals(Constants.Packaging.ActionsNodeName, actionsElement.Name.LocalName) == false) - { - throw new ArgumentException("Must be \"" + Constants.Packaging.ActionsNodeName + "\" as root", - "actionsElement"); - } - - return actionsElement.Elements(Constants.Packaging.ActionNodeName) - .Select(elemet => - { - XAttribute aliasAttr = elemet.Attribute(Constants.Packaging.AliasNodeNameCapital); - if (aliasAttr == null) - throw new ArgumentException( - "missing \"" + Constants.Packaging.AliasNodeNameCapital + "\" atribute in alias element", - "actionsElement"); - - var packageAction = new PackageAction - { - XmlData = elemet, - Alias = aliasAttr.Value, - PackageName = packageName, - }; - - - var attr = elemet.Attribute(Constants.Packaging.RunatNodeAttribute); - - if (attr != null && Enum.TryParse(attr.Value, true, out ActionRunAt runAt)) { packageAction.RunAt = runAt; } - - attr = elemet.Attribute(Constants.Packaging.UndoNodeAttribute); - - if (attr != null && bool.TryParse(attr.Value, out var undo)) { packageAction.Undo = undo; } - - - return packageAction; - }).ToArray(); - } - - private IEnumerable InstallDocuments(XElement documentsElement, int userId = 0) - { - if ((string.Equals(Constants.Packaging.DocumentSetNodeName, documentsElement.Name.LocalName) == false) - && (string.Equals(Constants.Packaging.DocumentsNodeName, documentsElement.Name.LocalName) == false)) - { - throw new ArgumentException("Must be \"" + Constants.Packaging.DocumentsNodeName + "\" as root", - "documentsElement"); - } - - if (string.Equals(Constants.Packaging.DocumentSetNodeName, documentsElement.Name.LocalName)) - return _packagingService.ImportContent(documentsElement, -1, userId); - - return - documentsElement.Elements(Constants.Packaging.DocumentSetNodeName) - .SelectMany(documentSetElement => _packagingService.ImportContent(documentSetElement, -1, userId)) - .ToArray(); - } - - private IEnumerable InstallStylesheets(XElement styleSheetsElement) - { - if (string.Equals(Constants.Packaging.StylesheetsNodeName, styleSheetsElement.Name.LocalName) == false) - { - throw new ArgumentException("Must be \"" + Constants.Packaging.StylesheetsNodeName + "\" as root", - "styleSheetsElement"); - } - - // TODO: Call _packagingService when import stylesheets import has been implimentet - if (styleSheetsElement.HasElements == false) { return new List(); } - - throw new NotImplementedException("The packaging service do not yes have a method for importing stylesheets"); - } - - private IEnumerable InstallDocumentTypes(XElement documentTypes, int userId = 0) - { - if (string.Equals(Constants.Packaging.DocumentTypesNodeName, documentTypes.Name.LocalName) == false) - { - if (string.Equals(Constants.Packaging.DocumentTypeNodeName, documentTypes.Name.LocalName) == false) - throw new ArgumentException( - "Must be \"" + Constants.Packaging.DocumentTypesNodeName + "\" as root", "documentTypes"); - - documentTypes = new XElement(Constants.Packaging.DocumentTypesNodeName, documentTypes); - } - - return _packagingService.ImportContentTypes(documentTypes, userId); - } - - private IEnumerable InstallTemplates(XElement templateElement, int userId = 0) - { - if (string.Equals(Constants.Packaging.TemplatesNodeName, templateElement.Name.LocalName) == false) - { - throw new ArgumentException("Must be \"" + Constants.Packaging.TemplatesNodeName + "\" as root", - "templateElement"); - } - return _packagingService.ImportTemplates(templateElement, userId); - } - - private IEnumerable InstallMacros(XElement macroElements, int userId = 0) - { - if (string.Equals(Constants.Packaging.MacrosNodeName, macroElements.Name.LocalName) == false) - { - throw new ArgumentException("Must be \"" + Constants.Packaging.MacrosNodeName + "\" as root", - "macroElements"); - } - return _packagingService.ImportMacros(macroElements, userId); - } - - private IEnumerable InstallDictionaryItems(XElement dictionaryItemsElement) - { - if (string.Equals(Constants.Packaging.DictionaryItemsNodeName, dictionaryItemsElement.Name.LocalName) == - false) - { - throw new ArgumentException("Must be \"" + Constants.Packaging.DictionaryItemsNodeName + "\" as root", - "dictionaryItemsElement"); - } - return _packagingService.ImportDictionaryItems(dictionaryItemsElement); - } - - private IEnumerable InstallLanguages(XElement languageElement, int userId = 0) - { - if (string.Equals(Constants.Packaging.LanguagesNodeName, languageElement.Name.LocalName) == false) - { - throw new ArgumentException("Must be \"" + Constants.Packaging.LanguagesNodeName + "\" as root", "languageElement"); - } - return _packagingService.ImportLanguages(languageElement, userId); - } - - private IEnumerable InstallDataTypes(XElement dataTypeElements, int userId = 0) - { - if (string.Equals(Constants.Packaging.DataTypesNodeName, dataTypeElements.Name.LocalName) == false) - { - if (string.Equals(Constants.Packaging.DataTypeNodeName, dataTypeElements.Name.LocalName) == false) - { - throw new ArgumentException("Must be \"" + Constants.Packaging.DataTypeNodeName + "\" as root", "dataTypeElements"); - } - } - return _packagingService.ImportDataTypeDefinitions(dataTypeElements, userId); - } + } } diff --git a/src/Umbraco.Core/Services/IPackagingService.cs b/src/Umbraco.Core/Services/IPackagingService.cs index fcb0c66401..8aa936c8ff 100644 --- a/src/Umbraco.Core/Services/IPackagingService.cs +++ b/src/Umbraco.Core/Services/IPackagingService.cs @@ -72,81 +72,6 @@ namespace Umbraco.Core.Services #endregion - #region Importing - /// - /// Imports and saves package xml as - /// - /// Xml to import - /// Optional parent Id for the content being imported - /// Optional Id of the user performing the import - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated content - IEnumerable ImportContent(XElement element, int parentId = -1, int userId = 0, bool raiseEvents = true); - - /// - /// Imports and saves package xml as - /// - /// Xml to import - /// Optional id of the User performing the operation. Default is zero (admin) - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated ContentTypes - IEnumerable ImportContentTypes(XElement element, int userId = 0, bool raiseEvents = true); - - /// - /// Imports and saves package xml as - /// - /// Xml to import - /// Boolean indicating whether or not to import the - /// Optional id of the User performing the operation. Default is zero (admin) - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated ContentTypes - IEnumerable ImportContentTypes(XElement element, bool importStructure, int userId = 0, bool raiseEvents = true); - - /// - /// Imports and saves package xml as - /// - /// Xml to import - /// Optional id of the User performing the operation. Default is zero (admin). - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated DataTypeDefinitions - IEnumerable ImportDataTypeDefinitions(XElement element, int userId = 0, bool raiseEvents = true); - - /// - /// Imports and saves the 'DictionaryItems' part of the package xml as a list of - /// - /// Xml to import - /// Optional parameter indicating whether or not to raise events - /// An enumerable list of dictionary items - IEnumerable ImportDictionaryItems(XElement dictionaryItemElementList, bool raiseEvents = true); - - /// - /// Imports and saves the 'Languages' part of a package xml as a list of - /// - /// Xml to import - /// Optional id of the User performing the operation. Default is zero (admin) - /// Optional parameter indicating whether or not to raise events - /// An enumerable list of generated languages - IEnumerable ImportLanguages(XElement languageElementList, int userId = 0, bool raiseEvents = true); - - /// - /// Imports and saves the 'Macros' part of a package xml as a list of - /// - /// Xml to import - /// Optional id of the User performing the operation - /// Optional parameter indicating whether or not to raise events - /// - IEnumerable ImportMacros(XElement element, int userId = 0, bool raiseEvents = true); - - /// - /// Imports and saves package xml as - /// - /// Xml to import - /// Optional id of the User performing the operation. Default is zero (admin) - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated Templates - IEnumerable ImportTemplates(XElement element, int userId = 0, bool raiseEvents = true); - #endregion - /// /// This will fetch an Umbraco package file from the package repository and return the file name of the downloaded package /// diff --git a/src/Umbraco.Core/Services/Implement/PackagingService.cs b/src/Umbraco.Core/Services/Implement/PackagingService.cs index 7c379dc2dc..ba5d30f8d7 100644 --- a/src/Umbraco.Core/Services/Implement/PackagingService.cs +++ b/src/Umbraco.Core/Services/Implement/PackagingService.cs @@ -31,1097 +31,25 @@ namespace Umbraco.Core.Services.Implement /// public class PackagingService : IPackagingService { - //fixme: inject when ready to use this - private readonly IPackageInstallation _packageInstallation; - private readonly ILogger _logger; - private readonly IContentService _contentService; - private readonly IContentTypeService _contentTypeService; - private readonly IMacroService _macroService; - private readonly IDataTypeService _dataTypeService; - private readonly IFileService _fileService; - private readonly ILocalizationService _localizationService; - private readonly IEntityService _entityService; - private Dictionary _importedContentTypes; + private readonly IPackageInstallation _packageInstallation; private readonly IAuditService _auditService; - private readonly PropertyEditorCollection _propertyEditors; private readonly ICreatedPackagesRepository _createdPackages; private readonly IInstalledPackagesRepository _installedPackages; private static HttpClient _httpClient; public PackagingService( - ILogger logger, - IContentService contentService, - IContentTypeService contentTypeService, - IMacroService macroService, - IDataTypeService dataTypeService, - IFileService fileService, - ILocalizationService localizationService, - IEntityService entityService, IAuditService auditService, - PropertyEditorCollection propertyEditors, ICreatedPackagesRepository createdPackages, - IInstalledPackagesRepository installedPackages) - { - _logger = logger; - _contentService = contentService; - _contentTypeService = contentTypeService; - _macroService = macroService; - _dataTypeService = dataTypeService; - _fileService = fileService; - _localizationService = localizationService; - _entityService = entityService; + IInstalledPackagesRepository installedPackages, + IPackageInstallation packageInstallation) + { _auditService = auditService; - _propertyEditors = propertyEditors; _createdPackages = createdPackages; _installedPackages = installedPackages; - _importedContentTypes = new Dictionary(); + _packageInstallation = packageInstallation; } - #region Content - - - /// - /// Imports and saves package xml as - /// - /// Xml to import - /// Optional parent Id for the content being imported - /// Optional Id of the user performing the import - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated content - public IEnumerable ImportContent(XElement element, int parentId = -1, int userId = 0, bool raiseEvents = true) - { - if (raiseEvents) - { - if (ImportingContent.IsRaisedEventCancelled(new ImportEventArgs(element), this)) - return Enumerable.Empty(); - } - - var name = element.Name.LocalName; - if (name.Equals("DocumentSet")) - { - //This is a regular deep-structured import - var roots = from doc in element.Elements() - where (string)doc.Attribute("isDoc") == "" - select doc; - - var contents = ParseDocumentRootXml(roots, parentId).ToList(); - if (contents.Any()) - _contentService.Save(contents, userId); - - if (raiseEvents) - ImportedContent.RaiseEvent(new ImportEventArgs(contents, element, false), this); - return contents; - } - - var attribute = element.Attribute("isDoc"); - if (attribute != null) - { - //This is a single doc import - var elements = new List { element }; - var contents = ParseDocumentRootXml(elements, parentId).ToList(); - if (contents.Any()) - _contentService.Save(contents, userId); - - if (raiseEvents) - ImportedContent.RaiseEvent(new ImportEventArgs(contents, element, false), this); - return contents; - } - - throw new ArgumentException( - "The passed in XElement is not valid! It does not contain a root element called " + - "'DocumentSet' (for structured imports) nor is the first element a Document (for single document import)."); - } - - private IEnumerable ParseDocumentRootXml(IEnumerable roots, int parentId) - { - var contents = new List(); - foreach (var root in roots) - { - var contentTypeAlias = root.Name.LocalName; - - if (_importedContentTypes.ContainsKey(contentTypeAlias) == false) - { - var contentType = FindContentTypeByAlias(contentTypeAlias); - _importedContentTypes.Add(contentTypeAlias, contentType); - } - - var content = CreateContentFromXml(root, _importedContentTypes[contentTypeAlias], null, parentId); - contents.Add(content); - - var children = (from child in root.Elements() - where (string)child.Attribute("isDoc") == "" - select child) - .ToList(); - if (children.Any()) - contents.AddRange(CreateContentFromXml(children, content)); - } - return contents; - } - - private IEnumerable CreateContentFromXml(IEnumerable children, IContent parent) - { - var list = new List(); - foreach (var child in children) - { - string contentTypeAlias = child.Name.LocalName; - - if (_importedContentTypes.ContainsKey(contentTypeAlias) == false) - { - var contentType = FindContentTypeByAlias(contentTypeAlias); - _importedContentTypes.Add(contentTypeAlias, contentType); - } - - //Create and add the child to the list - var content = CreateContentFromXml(child, _importedContentTypes[contentTypeAlias], parent, default(int)); - list.Add(content); - - //Recursive call - XElement child1 = child; - var grandChildren = (from grand in child1.Elements() - where (string) grand.Attribute("isDoc") == "" - select grand).ToList(); - - if (grandChildren.Any()) - list.AddRange(CreateContentFromXml(grandChildren, content)); - } - - return list; - } - - private IContent CreateContentFromXml(XElement element, IContentType contentType, IContent parent, int parentId) - { - var id = element.Attribute("id").Value; - var level = element.Attribute("level").Value; - var sortOrder = element.Attribute("sortOrder").Value; - var nodeName = element.Attribute("nodeName").Value; - var path = element.Attribute("path").Value; - //TODO: Shouldn't we be using this value??? - var template = element.Attribute("template").Value; - var key = Guid.Empty; - - var properties = from property in element.Elements() - where property.Attribute("isDoc") == null - select property; - - IContent content = parent == null - ? new Content(nodeName, parentId, contentType) - { - Level = int.Parse(level), - SortOrder = int.Parse(sortOrder) - } - : new Content(nodeName, parent, contentType) - { - Level = int.Parse(level), - SortOrder = int.Parse(sortOrder) - }; - - if (element.Attribute("key") != null && Guid.TryParse(element.Attribute("key").Value, out key)) - { - // update the Guid (for UDI support) - content.Key = key; - } - - foreach (var property in properties) - { - string propertyTypeAlias = property.Name.LocalName; - if (content.HasProperty(propertyTypeAlias)) - { - var propertyValue = property.Value; - - var propertyType = contentType.PropertyTypes.FirstOrDefault(pt => pt.Alias == propertyTypeAlias); - - //set property value - content.SetValue(propertyTypeAlias, propertyValue); - } - } - - return content; - } - - #endregion - - #region ContentTypes - - - - /// - /// Imports and saves package xml as - /// - /// Xml to import - /// Optional id of the User performing the operation. Default is zero (admin). - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated ContentTypes - public IEnumerable ImportContentTypes(XElement element, int userId = 0, bool raiseEvents = true) - { - return ImportContentTypes(element, true, userId); - } - - /// - /// Imports and saves package xml as - /// - /// Xml to import - /// Boolean indicating whether or not to import the - /// Optional id of the User performing the operation. Default is zero (admin). - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated ContentTypes - public IEnumerable ImportContentTypes(XElement element, bool importStructure, int userId = 0, bool raiseEvents = true) - { - if (raiseEvents) - { - if (ImportingContentType.IsRaisedEventCancelled(new ImportEventArgs(element), this)) - return Enumerable.Empty(); - } - - var name = element.Name.LocalName; - if (name.Equals("DocumentTypes") == false && name.Equals("DocumentType") == false) - { - throw new ArgumentException("The passed in XElement is not valid! It does not contain a root element called 'DocumentTypes' for multiple imports or 'DocumentType' for a single import."); - } - - _importedContentTypes = new Dictionary(); - var unsortedDocumentTypes = name.Equals("DocumentTypes") - ? (from doc in element.Elements("DocumentType") select doc).ToList() - : new List { element }; - - //When you are importing a single doc type we have to assume that the depedencies are already there. - //Otherwise something like uSync won't work. - var graph = new TopoGraph>(x => x.Key, x => x.Dependencies); - var isSingleDocTypeImport = unsortedDocumentTypes.Count == 1; - - var importedFolders = CreateContentTypeFolderStructure(unsortedDocumentTypes); - - if (isSingleDocTypeImport == false) - { - //NOTE Here we sort the doctype XElements based on dependencies - //before creating the doc types - this should also allow for a better structure/inheritance support. - foreach (var documentType in unsortedDocumentTypes) - { - var elementCopy = documentType; - var infoElement = elementCopy.Element("Info"); - var dependencies = new HashSet(); - - //Add the Master as a dependency - if (string.IsNullOrEmpty((string)infoElement.Element("Master")) == false) - { - dependencies.Add(infoElement.Element("Master").Value); - } - - //Add compositions as dependencies - var compositionsElement = infoElement.Element("Compositions"); - if (compositionsElement != null && compositionsElement.HasElements) - { - var compositions = compositionsElement.Elements("Composition"); - if (compositions.Any()) - { - foreach (var composition in compositions) - { - dependencies.Add(composition.Value); - } - } - } - - graph.AddItem(TopoGraph.CreateNode(infoElement.Element("Alias").Value, elementCopy, dependencies.ToArray())); - } - } - - //Sorting the Document Types based on dependencies - if its not a single doc type import ref. #U4-5921 - var documentTypes = isSingleDocTypeImport - ? unsortedDocumentTypes.ToList() - : graph.GetSortedItems().Select(x => x.Item).ToList(); - - //Iterate the sorted document types and create them as IContentType objects - foreach (var documentType in documentTypes) - { - var alias = documentType.Element("Info").Element("Alias").Value; - if (_importedContentTypes.ContainsKey(alias) == false) - { - var contentType = _contentTypeService.Get(alias); - _importedContentTypes.Add(alias, contentType == null - ? CreateContentTypeFromXml(documentType) - : UpdateContentTypeFromXml(documentType, contentType)); - } - } - - foreach (var contentType in _importedContentTypes) - { - var ct = contentType.Value; - if (importedFolders.ContainsKey(ct.Alias)) - { - ct.ParentId = importedFolders[ct.Alias]; - } - } - - //Save the newly created/updated IContentType objects - var list = _importedContentTypes.Select(x => x.Value).ToList(); - _contentTypeService.Save(list, userId); - - //Now we can finish the import by updating the 'structure', - //which requires the doc types to be saved/available in the db - if (importStructure) - { - var updatedContentTypes = new List(); - //Update the structure here - we can't do it untill all DocTypes have been created - foreach (var documentType in documentTypes) - { - var alias = documentType.Element("Info").Element("Alias").Value; - var structureElement = documentType.Element("Structure"); - //Ensure that we only update ContentTypes which has actual structure-elements - if (structureElement == null || structureElement.Elements("DocumentType").Any() == false) continue; - - var updated = UpdateContentTypesStructure(_importedContentTypes[alias], structureElement); - updatedContentTypes.Add(updated); - } - //Update ContentTypes with a newly added structure/list of allowed children - if (updatedContentTypes.Any()) - _contentTypeService.Save(updatedContentTypes, userId); - } - - if (raiseEvents) - ImportedContentType.RaiseEvent(new ImportEventArgs(list, element, false), this); - - return list; - } - - private Dictionary CreateContentTypeFolderStructure(IEnumerable unsortedDocumentTypes) - { - var importedFolders = new Dictionary(); - foreach (var documentType in unsortedDocumentTypes) - { - var foldersAttribute = documentType.Attribute("Folders"); - var infoElement = documentType.Element("Info"); - if (foldersAttribute != null && infoElement != null - //don't import any folder if this is a child doc type - the parent doc type will need to - //exist which contains it's folders - && ((string)infoElement.Element("Master")).IsNullOrWhiteSpace()) - { - var alias = documentType.Element("Info").Element("Alias").Value; - var folders = foldersAttribute.Value.Split('/'); - var rootFolder = HttpUtility.UrlDecode(folders[0]); - //level 1 = root level folders, there can only be one with the same name - var current = _contentTypeService.GetContainers(rootFolder, 1).FirstOrDefault(); - - if (current == null) - { - var tryCreateFolder = _contentTypeService.CreateContainer(-1, rootFolder); - if (tryCreateFolder == false) - { - _logger.Error(tryCreateFolder.Exception, "Could not create folder: {FolderName}", rootFolder); - throw tryCreateFolder.Exception; - } - var rootFolderId = tryCreateFolder.Result.Entity.Id; - current = _contentTypeService.GetContainer(rootFolderId); - } - - importedFolders.Add(alias, current.Id); - - for (var i = 1; i < folders.Length; i++) - { - var folderName = HttpUtility.UrlDecode(folders[i]); - current = CreateContentTypeChildFolder(folderName, current); - importedFolders[alias] = current.Id; - } - } - } - - return importedFolders; - } - - private EntityContainer CreateContentTypeChildFolder(string folderName, IUmbracoEntity current) - { - var children = _entityService.GetChildren(current.Id).ToArray(); - var found = children.Any(x => x.Name.InvariantEquals(folderName)); - if (found) - { - var containerId = children.Single(x => x.Name.InvariantEquals(folderName)).Id; - return _contentTypeService.GetContainer(containerId); - } - - var tryCreateFolder = _contentTypeService.CreateContainer(current.Id, folderName); - if (tryCreateFolder == false) - { - _logger.Error(tryCreateFolder.Exception, "Could not create folder: {FolderName}", folderName); - throw tryCreateFolder.Exception; - } - return _contentTypeService.GetContainer(tryCreateFolder.Result.Entity.Id); - } - - private IContentType CreateContentTypeFromXml(XElement documentType) - { - var infoElement = documentType.Element("Info"); - - //Name of the master corresponds to the parent - var masterElement = infoElement.Element("Master"); - IContentType parent = null; - if (masterElement != null) - { - var masterAlias = masterElement.Value; - parent = _importedContentTypes.ContainsKey(masterAlias) - ? _importedContentTypes[masterAlias] - : _contentTypeService.Get(masterAlias); - } - - var alias = infoElement.Element("Alias").Value; - var contentType = parent == null - ? new ContentType(-1) { Alias = alias } - : new ContentType(parent, alias); - - if (parent != null) - contentType.AddContentType(parent); - - return UpdateContentTypeFromXml(documentType, contentType); - } - - private IContentType UpdateContentTypeFromXml(XElement documentType, IContentType contentType) - { - var infoElement = documentType.Element("Info"); - var defaultTemplateElement = infoElement.Element("DefaultTemplate"); - - contentType.Name = infoElement.Element("Name").Value; - contentType.Icon = infoElement.Element("Icon").Value; - contentType.Thumbnail = infoElement.Element("Thumbnail").Value; - contentType.Description = infoElement.Element("Description").Value; - - //NOTE AllowAtRoot is a new property in the package xml so we need to verify it exists before using it. - var allowAtRoot = infoElement.Element("AllowAtRoot"); - if (allowAtRoot != null) - contentType.AllowedAsRoot = allowAtRoot.Value.InvariantEquals("true"); - - //NOTE IsListView is a new property in the package xml so we need to verify it exists before using it. - var isListView = infoElement.Element("IsListView"); - if (isListView != null) - contentType.IsContainer = isListView.Value.InvariantEquals("true"); - - //Name of the master corresponds to the parent and we need to ensure that the Parent Id is set - var masterElement = infoElement.Element("Master"); - if (masterElement != null) - { - var masterAlias = masterElement.Value; - IContentType parent = _importedContentTypes.ContainsKey(masterAlias) - ? _importedContentTypes[masterAlias] - : _contentTypeService.Get(masterAlias); - - contentType.SetParent(parent); - } - - //Update Compositions on the ContentType to ensure that they are as is defined in the package xml - var compositionsElement = infoElement.Element("Compositions"); - if (compositionsElement != null && compositionsElement.HasElements) - { - var compositions = compositionsElement.Elements("Composition"); - if (compositions.Any()) - { - foreach (var composition in compositions) - { - var compositionAlias = composition.Value; - var compositionContentType = _importedContentTypes.ContainsKey(compositionAlias) - ? _importedContentTypes[compositionAlias] - : _contentTypeService.Get(compositionAlias); - var added = contentType.AddContentType(compositionContentType); - } - } - } - - UpdateContentTypesAllowedTemplates(contentType, infoElement.Element("AllowedTemplates"), defaultTemplateElement); - UpdateContentTypesTabs(contentType, documentType.Element("Tabs")); - UpdateContentTypesProperties(contentType, documentType.Element("GenericProperties")); - - return contentType; - } - - private void UpdateContentTypesAllowedTemplates(IContentType contentType, - XElement allowedTemplatesElement, XElement defaultTemplateElement) - { - if (allowedTemplatesElement != null && allowedTemplatesElement.Elements("Template").Any()) - { - var allowedTemplates = contentType.AllowedTemplates.ToList(); - foreach (var templateElement in allowedTemplatesElement.Elements("Template")) - { - var alias = templateElement.Value; - var template = _fileService.GetTemplate(alias.ToSafeAlias()); - if (template != null) - { - if (allowedTemplates.Any(x => x.Id == template.Id)) continue; - allowedTemplates.Add(template); - } - else - { - _logger.Warn("Packager: Error handling allowed templates. Template with alias '{TemplateAlias}' could not be found.", alias); - } - } - - contentType.AllowedTemplates = allowedTemplates; - } - - if (string.IsNullOrEmpty((string)defaultTemplateElement) == false) - { - var defaultTemplate = _fileService.GetTemplate(defaultTemplateElement.Value.ToSafeAlias()); - if (defaultTemplate != null) - { - contentType.SetDefaultTemplate(defaultTemplate); - } - else - { - _logger.Warn("Packager: Error handling default template. Default template with alias '{DefaultTemplateAlias}' could not be found.", defaultTemplateElement.Value); - } - } - } - - private void UpdateContentTypesTabs(IContentType contentType, XElement tabElement) - { - if (tabElement == null) - return; - - var tabs = tabElement.Elements("Tab"); - foreach (var tab in tabs) - { - var id = tab.Element("Id").Value;//Do we need to use this for tracking? - var caption = tab.Element("Caption").Value; - - if (contentType.PropertyGroups.Contains(caption) == false) - { - contentType.AddPropertyGroup(caption); - - } - - int sortOrder; - if (tab.Element("SortOrder") != null && int.TryParse(tab.Element("SortOrder").Value, out sortOrder)) - { - // Override the sort order with the imported value - contentType.PropertyGroups[caption].SortOrder = sortOrder; - } - } - } - - private void UpdateContentTypesProperties(IContentType contentType, XElement genericPropertiesElement) - { - var properties = genericPropertiesElement.Elements("GenericProperty"); - foreach (var property in properties) - { - var dataTypeDefinitionId = new Guid(property.Element("Definition").Value);//Unique Id for a DataTypeDefinition - - var dataTypeDefinition = _dataTypeService.GetDataType(dataTypeDefinitionId); - - //If no DataTypeDefinition with the guid from the xml wasn't found OR the ControlId on the DataTypeDefinition didn't match the DataType Id - //We look up a DataTypeDefinition that matches - - - //get the alias as a string for use below - var propertyEditorAlias = property.Element("Type").Value.Trim(); - - //If no DataTypeDefinition with the guid from the xml wasn't found OR the ControlId on the DataTypeDefinition didn't match the DataType Id - //We look up a DataTypeDefinition that matches - - if (dataTypeDefinition == null) - { - var dataTypeDefinitions = _dataTypeService.GetByEditorAlias(propertyEditorAlias); - if (dataTypeDefinitions != null && dataTypeDefinitions.Any()) - { - dataTypeDefinition = dataTypeDefinitions.FirstOrDefault(); - } - } - else if (dataTypeDefinition.EditorAlias != propertyEditorAlias) - { - var dataTypeDefinitions = _dataTypeService.GetByEditorAlias(propertyEditorAlias); - if (dataTypeDefinitions != null && dataTypeDefinitions.Any()) - { - dataTypeDefinition = dataTypeDefinitions.FirstOrDefault(); - } - } - - // For backwards compatibility, if no datatype with that ID can be found, we're letting this fail silently. - // This means that the property will not be created. - if (dataTypeDefinition == null) - { - _logger.Warn("Packager: Error handling creation of PropertyType '{PropertyType}'. Could not find DataTypeDefintion with unique id '{DataTypeDefinitionId}' nor one referencing the DataType with a property editor alias (or legacy control id) '{PropertyEditorAlias}'. Did the package creator forget to package up custom datatypes? This property will be converted to a label/readonly editor if one exists.", - property.Element("Name").Value, dataTypeDefinitionId, property.Element("Type").Value.Trim()); - - //convert to a label! - dataTypeDefinition = _dataTypeService.GetByEditorAlias(Constants.PropertyEditors.Aliases.NoEdit).FirstOrDefault(); - //if for some odd reason this isn't there then ignore - if (dataTypeDefinition == null) continue; - } - - var sortOrder = 0; - var sortOrderElement = property.Element("SortOrder"); - if (sortOrderElement != null) - int.TryParse(sortOrderElement.Value, out sortOrder); - var propertyType = new PropertyType(dataTypeDefinition, property.Element("Alias").Value) - { - Name = property.Element("Name").Value, - Description = (string)property.Element("Description"), - Mandatory = property.Element("Mandatory") != null ? property.Element("Mandatory").Value.ToLowerInvariant().Equals("true") : false, - ValidationRegExp = (string)property.Element("Validation"), - SortOrder = sortOrder - }; - - var tab = (string)property.Element("Tab"); - if (string.IsNullOrEmpty(tab)) - { - contentType.AddPropertyType(propertyType); - } - else - { - contentType.AddPropertyType(propertyType, tab); - } - } - } - - private IContentType UpdateContentTypesStructure(IContentType contentType, XElement structureElement) - { - var allowedChildren = contentType.AllowedContentTypes.ToList(); - int sortOrder = allowedChildren.Any() ? allowedChildren.Last().SortOrder : 0; - foreach (var element in structureElement.Elements("DocumentType")) - { - var alias = element.Value; - - var allowedChild = _importedContentTypes.ContainsKey(alias) ? _importedContentTypes[alias] : _contentTypeService.Get(alias); - if (allowedChild == null) - { - _logger.Warn( - "Packager: Error handling DocumentType structure. DocumentType with alias '{DoctypeAlias}' could not be found and was not added to the structure for '{DoctypeStructureAlias}'.", - alias, contentType.Alias); - continue; - } - - if (allowedChildren.Any(x => x.Id.IsValueCreated && x.Id.Value == allowedChild.Id)) continue; - - allowedChildren.Add(new ContentTypeSort(new Lazy(() => allowedChild.Id), sortOrder, allowedChild.Alias)); - sortOrder++; - } - - contentType.AllowedContentTypes = allowedChildren; - return contentType; - } - - /// - /// Used during Content import to ensure that the ContentType of a content item exists - /// - /// - /// - private IContentType FindContentTypeByAlias(string contentTypeAlias) - { - var contentType = _contentTypeService.Get(contentTypeAlias); - - if (contentType == null) - throw new Exception($"ContentType matching the passed in Alias: '{contentTypeAlias}' was null"); - - return contentType; - } - - #endregion - - #region DataTypes - - - - - - /// - /// Imports and saves package xml as - /// - /// Xml to import - /// Optional id of the user - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated DataTypeDefinitions - public IEnumerable ImportDataTypeDefinitions(XElement element, int userId = 0, bool raiseEvents = true) - { - if (raiseEvents) - { - if (ImportingDataType.IsRaisedEventCancelled(new ImportEventArgs(element), this)) - return Enumerable.Empty(); - } - - var name = element.Name.LocalName; - if (name.Equals("DataTypes") == false && name.Equals("DataType") == false) - { - throw new ArgumentException("The passed in XElement is not valid! It does not contain a root element called 'DataTypes' for multiple imports or 'DataType' for a single import."); - } - - var dataTypes = new List(); - var dataTypeElements = name.Equals("DataTypes") - ? (from doc in element.Elements("DataType") select doc).ToList() - : new List { element }; - - var importedFolders = CreateDataTypeFolderStructure(dataTypeElements); - - foreach (var dataTypeElement in dataTypeElements) - { - var dataTypeDefinitionName = dataTypeElement.Attribute("Name").Value; - - var dataTypeDefinitionId = new Guid(dataTypeElement.Attribute("Definition").Value); - var databaseTypeAttribute = dataTypeElement.Attribute("DatabaseType"); - - var parentId = -1; - if (importedFolders.ContainsKey(dataTypeDefinitionName)) - parentId = importedFolders[dataTypeDefinitionName]; - - var definition = _dataTypeService.GetDataType(dataTypeDefinitionId); - //If the datatypedefinition doesn't already exist we create a new new according to the one in the package xml - if (definition == null) - { - var databaseType = databaseTypeAttribute != null - ? databaseTypeAttribute.Value.EnumParse(true) - : ValueStorageType.Ntext; - - // the Id field is actually the string property editor Alias - // however, the actual editor with this alias could be installed with the package, and - // therefore not yet part of the _propertyEditors collection, so we cannot try and get - // the actual editor - going with a void editor - - var editorAlias = dataTypeElement.Attribute("Id")?.Value?.Trim(); - if (!_propertyEditors.TryGet(editorAlias, out var editor)) - editor = new VoidEditor(_logger) { Alias = editorAlias }; - - var dataType = new DataType(editor) - { - Key = dataTypeDefinitionId, - Name = dataTypeDefinitionName, - DatabaseType = databaseType, - ParentId = parentId - }; - - var configurationAttributeValue = dataTypeElement.Attribute("Configuration")?.Value; - if (!string.IsNullOrWhiteSpace(configurationAttributeValue)) - dataType.Configuration = editor.GetConfigurationEditor().FromDatabase(configurationAttributeValue); - - dataTypes.Add(dataType); - } - else - { - definition.ParentId = parentId; - _dataTypeService.Save(definition, userId); - } - } - - if (dataTypes.Count > 0) - { - _dataTypeService.Save(dataTypes, userId, true); - } - - if (raiseEvents) - ImportedDataType.RaiseEvent(new ImportEventArgs(dataTypes, element, false), this); - - return dataTypes; - } - - private Dictionary CreateDataTypeFolderStructure(IEnumerable datatypeElements) - { - var importedFolders = new Dictionary(); - foreach (var datatypeElement in datatypeElements) - { - var foldersAttribute = datatypeElement.Attribute("Folders"); - if (foldersAttribute != null) - { - var name = datatypeElement.Attribute("Name").Value; - var folders = foldersAttribute.Value.Split('/'); - var rootFolder = HttpUtility.UrlDecode(folders[0]); - //there will only be a single result by name for level 1 (root) containers - var current = _dataTypeService.GetContainers(rootFolder, 1).FirstOrDefault(); - - if (current == null) - { - var tryCreateFolder = _dataTypeService.CreateContainer(-1, rootFolder); - if (tryCreateFolder == false) - { - _logger.Error(tryCreateFolder.Exception, "Could not create folder: {FolderName}", rootFolder); - throw tryCreateFolder.Exception; - } - current = _dataTypeService.GetContainer(tryCreateFolder.Result.Entity.Id); - } - - importedFolders.Add(name, current.Id); - - for (var i = 1; i < folders.Length; i++) - { - var folderName = HttpUtility.UrlDecode(folders[i]); - current = CreateDataTypeChildFolder(folderName, current); - importedFolders[name] = current.Id; - } - } - } - - return importedFolders; - } - - private EntityContainer CreateDataTypeChildFolder(string folderName, IUmbracoEntity current) - { - var children = _entityService.GetChildren(current.Id).ToArray(); - var found = children.Any(x => x.Name.InvariantEquals(folderName)); - if (found) - { - var containerId = children.Single(x => x.Name.InvariantEquals(folderName)).Id; - return _dataTypeService.GetContainer(containerId); - } - - var tryCreateFolder = _dataTypeService.CreateContainer(current.Id, folderName); - if (tryCreateFolder == false) - { - _logger.Error(tryCreateFolder.Exception, "Could not create folder: {FolderName}", folderName); - throw tryCreateFolder.Exception; - } - return _dataTypeService.GetContainer(tryCreateFolder.Result.Entity.Id); - } - - #endregion - - #region Dictionary Items - - - - - - /// - /// Imports and saves the 'DictionaryItems' part of the package xml as a list of - /// - /// Xml to import - /// Optional parameter indicating whether or not to raise events - /// An enumerable list of dictionary items - public IEnumerable ImportDictionaryItems(XElement dictionaryItemElementList, bool raiseEvents = true) - { - if (raiseEvents) - { - if (ImportingDictionaryItem.IsRaisedEventCancelled(new ImportEventArgs(dictionaryItemElementList), this)) - return Enumerable.Empty(); - } - - var languages = _localizationService.GetAllLanguages().ToList(); - return ImportDictionaryItems(dictionaryItemElementList, languages, raiseEvents); - } - - private IEnumerable ImportDictionaryItems(XElement dictionaryItemElementList, List languages, bool raiseEvents, Guid? parentId = null) - { - var items = new List(); - foreach (var dictionaryItemElement in dictionaryItemElementList.Elements("DictionaryItem")) - items.AddRange(ImportDictionaryItem(dictionaryItemElement, languages, raiseEvents, parentId)); - - - if (raiseEvents) - ImportedDictionaryItem.RaiseEvent(new ImportEventArgs(items, dictionaryItemElementList, false), this); - - return items; - } - - private IEnumerable ImportDictionaryItem(XElement dictionaryItemElement, List languages, bool raiseEvents, Guid? parentId) - { - var items = new List(); - - IDictionaryItem dictionaryItem; - var key = dictionaryItemElement.Attribute("Key").Value; - if (_localizationService.DictionaryItemExists(key)) - dictionaryItem = GetAndUpdateDictionaryItem(key, dictionaryItemElement, languages); - else - dictionaryItem = CreateNewDictionaryItem(key, dictionaryItemElement, languages, parentId); - _localizationService.Save(dictionaryItem); - items.Add(dictionaryItem); - - items.AddRange(ImportDictionaryItems(dictionaryItemElement, languages, raiseEvents, dictionaryItem.Key)); - return items; - } - - private IDictionaryItem GetAndUpdateDictionaryItem(string key, XElement dictionaryItemElement, List languages) - { - var dictionaryItem = _localizationService.GetDictionaryItemByKey(key); - var translations = dictionaryItem.Translations.ToList(); - foreach (var valueElement in dictionaryItemElement.Elements("Value").Where(v => DictionaryValueIsNew(translations, v))) - AddDictionaryTranslation(translations, valueElement, languages); - dictionaryItem.Translations = translations; - return dictionaryItem; - } - - private static DictionaryItem CreateNewDictionaryItem(string key, XElement dictionaryItemElement, List languages, Guid? parentId) - { - var dictionaryItem = parentId.HasValue ? new DictionaryItem(parentId.Value, key) : new DictionaryItem(key); - var translations = new List(); - - foreach (var valueElement in dictionaryItemElement.Elements("Value")) - AddDictionaryTranslation(translations, valueElement, languages); - - dictionaryItem.Translations = translations; - return dictionaryItem; - } - - private static bool DictionaryValueIsNew(IEnumerable translations, XElement valueElement) - { - return translations.All(t => - String.Compare(t.Language.IsoCode, valueElement.Attribute("LanguageCultureAlias").Value, - StringComparison.InvariantCultureIgnoreCase) != 0 - ); - } - - private static void AddDictionaryTranslation(ICollection translations, XElement valueElement, IEnumerable languages) - { - var languageId = valueElement.Attribute("LanguageCultureAlias").Value; - var language = languages.SingleOrDefault(l => l.IsoCode == languageId); - if (language == null) - return; - var translation = new DictionaryTranslation(language, valueElement.Value); - translations.Add(translation); - } - - #endregion - - #region Languages - - - - - - /// - /// Imports and saves the 'Languages' part of a package xml as a list of - /// - /// Xml to import - /// Optional id of the User performing the operation - /// Optional parameter indicating whether or not to raise events - /// An enumerable list of generated languages - public IEnumerable ImportLanguages(XElement languageElementList, int userId = 0, bool raiseEvents = true) - { - if (raiseEvents) - { - if (ImportingLanguage.IsRaisedEventCancelled(new ImportEventArgs(languageElementList), this)) - return Enumerable.Empty(); - } - - var list = new List(); - foreach (var languageElement in languageElementList.Elements("Language")) - { - var isoCode = languageElement.Attribute("CultureAlias").Value; - var existingLanguage = _localizationService.GetLanguageByIsoCode(isoCode); - if (existingLanguage == null) - { - var langauge = new Language(isoCode) - { - CultureName = languageElement.Attribute("FriendlyName").Value - }; - _localizationService.Save(langauge); - list.Add(langauge); - } - } - - if (raiseEvents) - ImportedLanguage.RaiseEvent(new ImportEventArgs(list, languageElementList, false), this); - - return list; - } - - #endregion - - #region Macros - - /// - /// Imports and saves the 'Macros' part of a package xml as a list of - /// - /// Xml to import - /// Optional id of the User performing the operation - /// Optional parameter indicating whether or not to raise events - /// - public IEnumerable ImportMacros(XElement element, int userId = 0, bool raiseEvents = true) - { - if (raiseEvents) - { - if (ImportingMacro.IsRaisedEventCancelled(new ImportEventArgs(element), this)) - return Enumerable.Empty(); - } - - var name = element.Name.LocalName; - if (name.Equals("Macros") == false && name.Equals("macro") == false) - { - throw new ArgumentException("The passed in XElement is not valid! It does not contain a root element called 'Macros' for multiple imports or 'macro' for a single import."); - } - - var macroElements = name.Equals("Macros") - ? (from doc in element.Elements("macro") select doc).ToList() - : new List { element }; - - var macros = macroElements.Select(ParseMacroElement).ToList(); - - foreach (var macro in macros) - { - var existing = _macroService.GetByAlias(macro.Alias); - if (existing != null) - macro.Id = existing.Id; - - _macroService.Save(macro, userId); - } - - if (raiseEvents) - ImportedMacro.RaiseEvent(new ImportEventArgs(macros, element, false), this); - - return macros; - } - - private IMacro ParseMacroElement(XElement macroElement) - { - var macroName = macroElement.Element("name").Value; - var macroAlias = macroElement.Element("alias").Value; - var macroType = Enum.Parse(macroElement.Element("macroType").Value); - var macroSource = macroElement.Element("macroSource").Value; - - //Following xml elements are treated as nullable properties - var useInEditorElement = macroElement.Element("useInEditor"); - var useInEditor = false; - if (useInEditorElement != null && string.IsNullOrEmpty((string)useInEditorElement) == false) - { - useInEditor = bool.Parse(useInEditorElement.Value); - } - var cacheDurationElement = macroElement.Element("refreshRate"); - var cacheDuration = 0; - if (cacheDurationElement != null && string.IsNullOrEmpty((string)cacheDurationElement) == false) - { - cacheDuration = int.Parse(cacheDurationElement.Value); - } - var cacheByMemberElement = macroElement.Element("cacheByMember"); - var cacheByMember = false; - if (cacheByMemberElement != null && string.IsNullOrEmpty((string)cacheByMemberElement) == false) - { - cacheByMember = bool.Parse(cacheByMemberElement.Value); - } - var cacheByPageElement = macroElement.Element("cacheByPage"); - var cacheByPage = false; - if (cacheByPageElement != null && string.IsNullOrEmpty((string)cacheByPageElement) == false) - { - cacheByPage = bool.Parse(cacheByPageElement.Value); - } - var dontRenderElement = macroElement.Element("dontRender"); - var dontRender = true; - if (dontRenderElement != null && string.IsNullOrEmpty((string)dontRenderElement) == false) - { - dontRender = bool.Parse(dontRenderElement.Value); - } - - var existingMacro = _macroService.GetByAlias(macroAlias) as Macro; - var macro = existingMacro ?? new Macro(macroAlias, macroName, macroSource, macroType, - cacheByPage, cacheByMember, dontRender, useInEditor, cacheDuration); - - var properties = macroElement.Element("properties"); - if (properties != null) - { - int sortOrder = 0; - foreach (var property in properties.Elements()) - { - var propertyName = property.Attribute("name").Value; - var propertyAlias = property.Attribute("alias").Value; - var editorAlias = property.Attribute("propertyType").Value; - var sortOrderAttribute = property.Attribute("sortOrder"); - if (sortOrderAttribute != null) - { - sortOrder = int.Parse(sortOrderAttribute.Value); - } - - if (macro.Properties.Values.Any(x => string.Equals(x.Alias, propertyAlias, StringComparison.OrdinalIgnoreCase))) continue; - macro.Properties.Add(new MacroProperty(propertyAlias, propertyName, sortOrder, editorAlias)); - sortOrder++; - } - } - return macro; - } - - - - - - #endregion - #region Package Files /// @@ -1167,139 +95,6 @@ namespace Umbraco.Core.Services.Implement #endregion - #region Templates - - /// - /// Imports and saves package xml as - /// - /// Xml to import - /// Optional user id - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated Templates - public IEnumerable ImportTemplates(XElement element, int userId = 0, bool raiseEvents = true) - { - if (raiseEvents) - { - if (ImportingTemplate.IsRaisedEventCancelled(new ImportEventArgs(element), this)) - return Enumerable.Empty(); - } - - var name = element.Name.LocalName; - if (name.Equals("Templates") == false && name.Equals("Template") == false) - { - throw new ArgumentException("The passed in XElement is not valid! It does not contain a root element called 'Templates' for multiple imports or 'Template' for a single import."); - } - - var templates = new List(); - var templateElements = name.Equals("Templates") - ? (from doc in element.Elements("Template") select doc).ToList() - : new List { element }; - - var graph = new TopoGraph>(x => x.Key, x => x.Dependencies); - - foreach (XElement tempElement in templateElements) - { - var dependencies = new List(); - var elementCopy = tempElement; - //Ensure that the Master of the current template is part of the import, otherwise we ignore this dependency as part of the dependency sorting. - if (string.IsNullOrEmpty((string)elementCopy.Element("Master")) == false && - templateElements.Any(x => (string)x.Element("Alias") == (string)elementCopy.Element("Master"))) - { - dependencies.Add((string)elementCopy.Element("Master")); - } - else if (string.IsNullOrEmpty((string)elementCopy.Element("Master")) == false && - templateElements.Any(x => (string)x.Element("Alias") == (string)elementCopy.Element("Master")) == false) - { - _logger.Info( - "Template '{TemplateAlias}' has an invalid Master '{TemplateMaster}', so the reference has been ignored.", - (string) elementCopy.Element("Alias"), - (string) elementCopy.Element("Master")); - } - - graph.AddItem(TopoGraph.CreateNode((string) elementCopy.Element("Alias"), elementCopy, dependencies)); - } - - //Sort templates by dependencies to a potential master template - var sorted = graph.GetSortedItems(); - foreach (var item in sorted) - { - var templateElement = item.Item; - - var templateName = templateElement.Element("Name").Value; - var alias = templateElement.Element("Alias").Value; - var design = templateElement.Element("Design").Value; - var masterElement = templateElement.Element("Master"); - - var isMasterPage = IsMasterPageSyntax(design); - var path = isMasterPage ? MasterpagePath(alias) : ViewPath(alias); - - var existingTemplate = _fileService.GetTemplate(alias) as Template; - var template = existingTemplate ?? new Template(templateName, alias); - template.Content = design; - if (masterElement != null && string.IsNullOrEmpty((string)masterElement) == false) - { - template.MasterTemplateAlias = masterElement.Value; - var masterTemplate = templates.FirstOrDefault(x => x.Alias == masterElement.Value); - if (masterTemplate != null) - template.MasterTemplateId = new Lazy(() => masterTemplate.Id); - } - templates.Add(template); - } - - if (templates.Any()) - _fileService.SaveTemplate(templates, userId); - - if (raiseEvents) - ImportedTemplate.RaiseEvent(new ImportEventArgs(templates, element, false), this); - - return templates; - } - - - private bool IsMasterPageSyntax(string code) - { - return Regex.IsMatch(code, @"<%@\s*Master", RegexOptions.IgnoreCase) || - code.InvariantContains(" ImportStylesheets(XElement element, int userId = 0, bool raiseEvents = true) - { - - if (raiseEvents) - { - if (ImportingStylesheets.IsRaisedEventCancelled(new ImportEventArgs(element), this)) - return Enumerable.Empty(); - } - - IEnumerable styleSheets = Enumerable.Empty(); - - if (element.Elements().Any()) - throw new NotImplementedException("This needs to be implimentet"); - - - if (raiseEvents) - ImportingStylesheets.RaiseEvent(new ImportEventArgs(styleSheets, element, false), this); - - return styleSheets; - - } - - #endregion - #region Installation public CompiledPackage GetCompiledPackageInfo(string packageFileName) => _packageInstallation.ReadPackage(packageFileName); @@ -1317,8 +112,7 @@ namespace Umbraco.Core.Services.Implement SaveInstalledPackage(packageDefinition); - if (userId > -1) - _auditService.Add(AuditType.PackagerInstall, userId, -1, "Package", $"Package files installed for package '{compiledPackage.Name}'."); + _auditService.Add(AuditType.PackagerInstall, userId, -1, "Package", $"Package files installed for package '{compiledPackage.Name}'."); return files; } @@ -1337,6 +131,8 @@ namespace Umbraco.Core.Services.Implement var summary = _packageInstallation.InstallPackageData(packageDefinition, compiledPackage, userId); + SaveInstalledPackage(packageDefinition); + _auditService.Add(AuditType.PackagerInstall, userId, -1, "Package", $"Package data installed for package '{compiledPackage.Name}'."); ImportedPackage.RaiseEvent(new ImportPackageEventArgs(summary, compiledPackage, false), this); @@ -1383,15 +179,6 @@ namespace Umbraco.Core.Services.Implement #endregion - /// - /// This method can be used to trigger the 'ImportedPackage' event when a package is installed by something else but this service. - /// - /// - internal static void OnImportedPackage(ImportPackageEventArgs args) - { - ImportedPackage.RaiseEvent(args, null); - } - /// /// This method can be used to trigger the 'UninstalledPackage' event when a package is uninstalled by something else but this service. /// @@ -1402,80 +189,6 @@ namespace Umbraco.Core.Services.Implement } #region Event Handlers - /// - /// Occurs before Importing Content - /// - public static event TypedEventHandler> ImportingContent; - - /// - /// Occurs after Content is Imported and Saved - /// - public static event TypedEventHandler> ImportedContent; - - /// - /// Occurs before Importing ContentType - /// - public static event TypedEventHandler> ImportingContentType; - - /// - /// Occurs after ContentType is Imported and Saved - /// - public static event TypedEventHandler> ImportedContentType; - - /// - /// Occurs before Importing DataType - /// - public static event TypedEventHandler> ImportingDataType; - - /// - /// Occurs after DataType is Imported and Saved - /// - public static event TypedEventHandler> ImportedDataType; - - /// - /// Occurs before Importing DictionaryItem - /// - public static event TypedEventHandler> ImportingDictionaryItem; - - /// - /// Occurs after DictionaryItem is Imported and Saved - /// - public static event TypedEventHandler> ImportedDictionaryItem; - - /// - /// Occurs before Importing Macro - /// - public static event TypedEventHandler> ImportingMacro; - - /// - /// Occurs after Macro is Imported and Saved - /// - public static event TypedEventHandler> ImportedMacro; - - /// - /// Occurs before Importing Language - /// - public static event TypedEventHandler> ImportingLanguage; - - /// - /// Occurs after Language is Imported and Saved - /// - public static event TypedEventHandler> ImportedLanguage; - - /// - /// Occurs before Importing Template - /// - public static event TypedEventHandler> ImportingTemplate; - - /// - /// Occurs before Importing Stylesheets - /// - public static event TypedEventHandler> ImportingStylesheets; - - /// - /// Occurs after Template is Imported and Saved - /// - public static event TypedEventHandler> ImportedTemplate; /// /// Occurs before Importing umbraco package diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 2ee5d13f6d..b59a5190bd 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -320,7 +320,6 @@ - @@ -447,9 +446,11 @@ + + @@ -573,7 +574,6 @@ - diff --git a/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs b/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs index fe7a5fbe5c..964f0c47de 100644 --- a/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs +++ b/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs @@ -4,9 +4,11 @@ using System.Linq; using Moq; using NUnit.Framework; using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Models.Packaging; using Umbraco.Core.Packaging; +using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; using Umbraco.Tests.TestHelpers; @@ -38,10 +40,16 @@ namespace Umbraco.Tests.Packaging private CompiledPackageXmlParser Parser => new CompiledPackageXmlParser(new ConflictingPackageData(ServiceContext.MacroService, ServiceContext.FileService)); + private PackageDataInstallation PackageDataInstallation => new PackageDataInstallation( + Logger, ServiceContext.FileService, ServiceContext.MacroService, ServiceContext.LocalizationService, + ServiceContext.DataTypeService, ServiceContext.EntityService, + ServiceContext.ContentTypeService, ServiceContext.ContentService, + Factory.GetInstance()); + private IPackageInstallation PackageInstallation => new PackageInstallation( - ServiceContext.PackagingService, + PackageDataInstallation, new PackageFileInstallation(Parser, ProfilingLogger), - Parser, + Parser, Mock.Of(), packagesFolderPath: "~/Packaging/packages",//this is where our test zip file is applicationRootFolder: new DirectoryInfo(IOHelper.GetRootDirectorySafe()), packageExtractionFolder: new DirectoryInfo(IOHelper.MapPath("~/" + _testBaseFolder))); //we don't want to extract package files to the real root, so extract to a test folder diff --git a/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs b/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs index 84eb75a4f7..f29a43c504 100644 --- a/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs +++ b/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs @@ -8,6 +8,7 @@ using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Composing.Composers; using Umbraco.Core.Logging; +using Umbraco.Core.Packaging; using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; @@ -65,6 +66,8 @@ namespace Umbraco.Tests.Services.Importing Composition.ComposeFileSystems(); } + private PackageDataInstallation PackagingService => Factory.GetInstance(); + [Test] public void PackagingService_Can_Import_uBlogsy_ContentTypes_And_Verify_Structure() { @@ -74,12 +77,11 @@ namespace Umbraco.Tests.Services.Importing var dataTypeElement = xml.Descendants("DataTypes").First(); var templateElement = xml.Descendants("Templates").First(); var docTypeElement = xml.Descendants("DocumentTypes").First(); - var packagingService = ServiceContext.PackagingService; // Act - var dataTypes = packagingService.ImportDataTypeDefinitions(dataTypeElement); - var templates = packagingService.ImportTemplates(templateElement); - var contentTypes = packagingService.ImportContentTypes(docTypeElement); + var dataTypes = PackagingService.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); + var templates = PackagingService.ImportTemplates(templateElement.Elements("Template").ToList(), 0); + var contentTypes = PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); var numberOfTemplates = (from doc in templateElement.Elements("Template") select doc).Count(); var numberOfDocTypes = (from doc in docTypeElement.Elements("DocumentType") select doc).Count(); @@ -122,12 +124,11 @@ namespace Umbraco.Tests.Services.Importing var dataTypeElement = xml.Descendants("DataTypes").First(); var templateElement = xml.Descendants("Templates").First(); var docTypeElement = xml.Descendants("DocumentTypes").First(); - var packagingService = ServiceContext.PackagingService; // Act - var dataTypes = packagingService.ImportDataTypeDefinitions(dataTypeElement); - var templates = packagingService.ImportTemplates(templateElement); - var contentTypes = packagingService.ImportContentTypes(docTypeElement); + var dataTypes = PackagingService.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); + var templates = PackagingService.ImportTemplates(templateElement.Elements("Template").ToList(), 0); + var contentTypes = PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); // Assert var mRBasePage = contentTypes.First(x => x.Alias == "MRBasePage"); @@ -148,12 +149,11 @@ namespace Umbraco.Tests.Services.Importing var dataTypeElement = xml.Descendants("DataTypes").First(); var templateElement = xml.Descendants("Templates").First(); var docTypeElement = xml.Descendants("DocumentTypes").First(); - var packagingService = ServiceContext.PackagingService; // Act - var dataTypes = packagingService.ImportDataTypeDefinitions(dataTypeElement); - var templates = packagingService.ImportTemplates(templateElement); - var contentTypes = packagingService.ImportContentTypes(docTypeElement); + var dataTypes = PackagingService.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); + var templates = PackagingService.ImportTemplates(templateElement.Elements("Template").ToList(), 0); + var contentTypes = PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); var numberOfDocTypes = (from doc in docTypeElement.Elements("DocumentType") select doc).Count(); @@ -186,11 +186,11 @@ namespace Umbraco.Tests.Services.Importing string strXml = ImportResources.StandardMvc_Package; var xml = XElement.Parse(strXml); var element = xml.Descendants("Templates").First(); - var packagingService = ServiceContext.PackagingService; + var init = ServiceContext.FileService.GetTemplates().Count(); // Act - var templates = packagingService.ImportTemplates(element); + var templates = PackagingService.ImportTemplates(element.Elements("Template").ToList(), 0); var numberOfTemplates = (from doc in element.Elements("Template") select doc).Count(); var allTemplates = ServiceContext.FileService.GetTemplates(); @@ -209,11 +209,11 @@ namespace Umbraco.Tests.Services.Importing // Arrange string strXml = ImportResources.StandardMvc_Package; var xml = XElement.Parse(strXml); - var element = xml.Descendants("Templates").First().Element("Template"); - var packagingService = ServiceContext.PackagingService; + var element = xml.Descendants("Templates").First(); + // Act - var templates = packagingService.ImportTemplates(element); + var templates = PackagingService.ImportTemplate(element.Elements("Template").First(), 0); // Assert Assert.That(templates, Is.Not.Null); @@ -230,12 +230,12 @@ namespace Umbraco.Tests.Services.Importing var dataTypeElement = xml.Descendants("DataTypes").First(); var templateElement = xml.Descendants("Templates").First(); var docTypeElement = xml.Descendants("DocumentTypes").First(); - var packagingService = ServiceContext.PackagingService; + // Act - var dataTypeDefinitions = packagingService.ImportDataTypeDefinitions(dataTypeElement); - var templates = packagingService.ImportTemplates(templateElement); - var contentTypes = packagingService.ImportContentTypes(docTypeElement); + var dataTypeDefinitions = PackagingService.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); + var templates = PackagingService.ImportTemplates(templateElement.Elements("Template").ToList(), 0); + var contentTypes = PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); var numberOfDocTypes = (from doc in docTypeElement.Elements("DocumentType") select doc).Count(); // Assert @@ -268,13 +268,13 @@ namespace Umbraco.Tests.Services.Importing var docTypeElement = xml.Descendants("DocumentTypes").First(); // Act - var dataTypeDefinitions = ServiceContext.PackagingService.ImportDataTypeDefinitions(dataTypeElement); - var templates = ServiceContext.PackagingService.ImportTemplates(templateElement); - var contentTypes = ServiceContext.PackagingService.ImportContentTypes(docTypeElement); + var dataTypeDefinitions = PackagingService.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); + var templates = PackagingService.ImportTemplates(templateElement.Elements("Template").ToList(), 0); + var contentTypes = PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); var numberOfDocTypes = (from doc in docTypeElement.Elements("DocumentType") select doc).Count(); //Assert - Re-Import contenttypes doesn't throw - Assert.DoesNotThrow(() => ServiceContext.PackagingService.ImportContentTypes(docTypeElement)); + Assert.DoesNotThrow(() => PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0)); Assert.That(contentTypes.Count(), Is.EqualTo(numberOfDocTypes)); Assert.That(dataTypeDefinitions, Is.Not.Null); Assert.That(dataTypeDefinitions.Any(), Is.True); @@ -292,13 +292,13 @@ namespace Umbraco.Tests.Services.Importing var docTypeElement = xml.Descendants("DocumentTypes").First(); // Act - var dataTypeDefinitions = ServiceContext.PackagingService.ImportDataTypeDefinitions(dataTypeElement); - var templates = ServiceContext.PackagingService.ImportTemplates(templateElement); - var contentTypes = ServiceContext.PackagingService.ImportContentTypes(docTypeElement); + var dataTypeDefinitions = PackagingService.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); + var templates = PackagingService.ImportTemplates(templateElement.Elements("Template").ToList(), 0); + var contentTypes = PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); var numberOfDocTypes = (from doc in docTypeElement.Elements("DocumentType") select doc).Count(); //Assert - Re-Import contenttypes doesn't throw - Assert.DoesNotThrow(() => ServiceContext.PackagingService.ImportContentTypes(docTypeElement)); + Assert.DoesNotThrow(() => PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0)); Assert.That(contentTypes.Count(), Is.EqualTo(numberOfDocTypes)); Assert.That(dataTypeDefinitions, Is.Not.Null); Assert.That(dataTypeDefinitions.Any(), Is.True); @@ -314,12 +314,13 @@ namespace Umbraco.Tests.Services.Importing var dataTypeElement = xml.Descendants("DataTypes").First(); var docTypesElement = xml.Descendants("DocumentTypes").First(); var element = xml.Descendants("DocumentSet").First(); - var packagingService = ServiceContext.PackagingService; + // Act - var dataTypeDefinitions = packagingService.ImportDataTypeDefinitions(dataTypeElement); - var contentTypes = packagingService.ImportContentTypes(docTypesElement); - var contents = packagingService.ImportContent(element); + var dataTypeDefinitions = PackagingService.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); + var contentTypes = PackagingService.ImportDocumentTypes(docTypesElement.Elements("DocumentType"), 0); + var importedContentTypes = contentTypes.ToDictionary(x => x.Alias, x => x); + var contents = PackagingService.ImportContent(element, -1, importedContentTypes, 0); var numberOfDocs = (from doc in element.Descendants() where (string) doc.Attribute("isDoc") == "" select doc).Count(); @@ -347,12 +348,13 @@ namespace Umbraco.Tests.Services.Importing var dataTypeElement = xml.Descendants("DataTypes").First(); var docTypesElement = xml.Descendants("DocumentTypes").First(); var element = xml.Descendants("DocumentSet").First(); - var packagingService = ServiceContext.PackagingService; + // Act - var dataTypeDefinitions = packagingService.ImportDataTypeDefinitions(dataTypeElement); - var contentTypes = packagingService.ImportContentTypes(docTypesElement); - var contents = packagingService.ImportContent(element); + var dataTypeDefinitions = PackagingService.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); + var contentTypes = PackagingService.ImportDocumentTypes(docTypesElement.Elements("DocumentType"), 0); + var importedContentTypes = contentTypes.ToDictionary(x => x.Alias, x => x); + var contents = PackagingService.ImportContent(element, -1, importedContentTypes, 0); var numberOfDocs = (from doc in element.Descendants() where (string)doc.Attribute("isDoc") == "" select doc).Count(); @@ -382,10 +384,10 @@ namespace Umbraco.Tests.Services.Importing string strXml = ImportResources.XsltSearch_Package; var xml = XElement.Parse(strXml); var templateElement = xml.Descendants("Templates").First(); - var packagingService = ServiceContext.PackagingService; + // Act - var templates = packagingService.ImportTemplates(templateElement); + var templates = PackagingService.ImportTemplates(templateElement.Elements("Template").ToList(), 0); var numberOfTemplates = (from doc in templateElement.Elements("Template") select doc).Count(); // Assert @@ -399,10 +401,10 @@ namespace Umbraco.Tests.Services.Importing // Arrange string strXml = ImportResources.SingleDocType; var docTypeElement = XElement.Parse(strXml); - var packagingService = ServiceContext.PackagingService; + // Act - var contentTypes = packagingService.ImportContentTypes(docTypeElement); + var contentTypes = PackagingService.ImportDocumentType(docTypeElement, 0); // Assert Assert.That(contentTypes.Any(), Is.True); @@ -416,11 +418,11 @@ namespace Umbraco.Tests.Services.Importing // Arrange string strXml = ImportResources.SingleDocType; var docTypeElement = XElement.Parse(strXml); - var packagingService = ServiceContext.PackagingService; + var serializer = Factory.GetInstance(); // Act - var contentTypes = packagingService.ImportContentTypes(docTypeElement); + var contentTypes = PackagingService.ImportDocumentType(docTypeElement, 0); var contentType = contentTypes.FirstOrDefault(); var element = serializer.Serialize(contentType); @@ -442,8 +444,8 @@ namespace Umbraco.Tests.Services.Importing var docTypeElement = XElement.Parse(strXml); // Act - var contentTypes = ServiceContext.PackagingService.ImportContentTypes(docTypeElement); - var contentTypesUpdated = ServiceContext.PackagingService.ImportContentTypes(docTypeElement); + var contentTypes = PackagingService.ImportDocumentType(docTypeElement, 0); + var contentTypesUpdated = PackagingService.ImportDocumentType(docTypeElement, 0); // Assert Assert.That(contentTypes.Any(), Is.True); @@ -465,7 +467,7 @@ namespace Umbraco.Tests.Services.Importing var templateElement = newPackageXml.Descendants("Templates").First(); var templateElementUpdated = updatedPackageXml.Descendants("Templates").First(); - var packagingService = ServiceContext.PackagingService; + var fileService = ServiceContext.FileService; // kill default test data @@ -473,8 +475,8 @@ namespace Umbraco.Tests.Services.Importing // Act var numberOfTemplates = (from doc in templateElement.Elements("Template") select doc).Count(); - var templates = packagingService.ImportTemplates(templateElement); - var templatesAfterUpdate = packagingService.ImportTemplates(templateElementUpdated); + var templates = PackagingService.ImportTemplates(templateElement.Elements("Template").ToList(), 0); + var templatesAfterUpdate = PackagingService.ImportTemplates(templateElementUpdated.Elements("Template").ToList(), 0); var allTemplates = fileService.GetTemplates(); // Assert @@ -500,7 +502,7 @@ namespace Umbraco.Tests.Services.Importing AddLanguages(); // Act - ServiceContext.PackagingService.ImportDictionaryItems(dictionaryItemsElement); + PackagingService.ImportDictionaryItems(dictionaryItemsElement.Elements("DictionaryItem"), 0); // Assert AssertDictionaryItem("Parent", expectedEnglishParentValue, "en-GB"); @@ -522,7 +524,7 @@ namespace Umbraco.Tests.Services.Importing AddLanguages(); // Act - var dictionaryItems = ServiceContext.PackagingService.ImportDictionaryItems(dictionaryItemsElement); + var dictionaryItems = PackagingService.ImportDictionaryItems(dictionaryItemsElement.Elements("DictionaryItem"), 0); // Assert Assert.That(ServiceContext.LocalizationService.DictionaryItemExists(parentKey), "DictionaryItem parentKey does not exist"); @@ -551,7 +553,7 @@ namespace Umbraco.Tests.Services.Importing AddExistingEnglishAndNorwegianParentDictionaryItem(expectedEnglishParentValue, expectedNorwegianParentValue); // Act - ServiceContext.PackagingService.ImportDictionaryItems(dictionaryItemsElement); + PackagingService.ImportDictionaryItems(dictionaryItemsElement.Elements("DictionaryItem"), 0); // Assert AssertDictionaryItem("Parent", expectedEnglishParentValue, "en-GB"); @@ -576,7 +578,7 @@ namespace Umbraco.Tests.Services.Importing AddExistingEnglishParentDictionaryItem(expectedEnglishParentValue); // Act - ServiceContext.PackagingService.ImportDictionaryItems(dictionaryItemsElement); + PackagingService.ImportDictionaryItems(dictionaryItemsElement.Elements("DictionaryItem"), 0); // Assert AssertDictionaryItem("Parent", expectedEnglishParentValue, "en-GB"); @@ -593,7 +595,7 @@ namespace Umbraco.Tests.Services.Importing var LanguageItemsElement = newPackageXml.Elements("Languages").First(); // Act - var languages = ServiceContext.PackagingService.ImportLanguages(LanguageItemsElement); + var languages = PackagingService.ImportLanguages(LanguageItemsElement.Elements("Language"), 0); var allLanguages = ServiceContext.LocalizationService.GetAllLanguages(); // Assert @@ -611,10 +613,10 @@ namespace Umbraco.Tests.Services.Importing string strXml = ImportResources.uBlogsy_Package; var xml = XElement.Parse(strXml); var macrosElement = xml.Descendants("Macros").First(); - var packagingService = ServiceContext.PackagingService; + // Act - var macros = packagingService.ImportMacros(macrosElement).ToList(); + var macros = PackagingService.ImportMacros(macrosElement.Elements("macro"), 0).ToList(); // Assert Assert.That(macros.Any(), Is.True); @@ -633,10 +635,10 @@ namespace Umbraco.Tests.Services.Importing string strXml = ImportResources.XsltSearch_Package; var xml = XElement.Parse(strXml); var macrosElement = xml.Descendants("Macros").First(); - var packagingService = ServiceContext.PackagingService; + // Act - var macros = packagingService.ImportMacros(macrosElement).ToList(); + var macros = PackagingService.ImportMacros(macrosElement.Elements("macro"), 0).ToList(); // Assert Assert.That(macros.Any(), Is.True); @@ -657,11 +659,11 @@ namespace Umbraco.Tests.Services.Importing var xml = XElement.Parse(strXml); var templateElement = xml.Descendants("Templates").First(); var docTypeElement = xml.Descendants("DocumentTypes").First(); - var packagingService = ServiceContext.PackagingService; + // Act - var templates = packagingService.ImportTemplates(templateElement); - var contentTypes = packagingService.ImportContentTypes(docTypeElement); + var templates = PackagingService.ImportTemplates(templateElement.Elements("Template").ToList(), 0); + var contentTypes = PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); var numberOfDocTypes = (from doc in docTypeElement.Elements("DocumentType") select doc).Count(); // Assert @@ -685,10 +687,10 @@ namespace Umbraco.Tests.Services.Importing string strXml = ImportResources.CompositionsTestPackage_Random; var xml = XElement.Parse(strXml); var docTypeElement = xml.Descendants("DocumentTypes").First(); - var packagingService = ServiceContext.PackagingService; + // Act - var contentTypes = packagingService.ImportContentTypes(docTypeElement); + var contentTypes = PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); var numberOfDocTypes = (from doc in docTypeElement.Elements("DocumentType") select doc).Count(); // Assert diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects.cs b/src/Umbraco.Tests/TestHelpers/TestObjects.cs index f48b72daf9..412a492376 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects.cs @@ -22,6 +22,7 @@ using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; using Umbraco.Core.Strings; +using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Web.Services; namespace Umbraco.Tests.TestHelpers @@ -78,9 +79,11 @@ namespace Umbraco.Tests.TestHelpers /// A cache. /// A logger. /// + /// /// An event messages factory. /// Some url segment providers. - /// A container. + /// + /// A container. /// /// A ServiceContext. /// Should be used sparingly for integration tests only - for unit tests @@ -168,12 +171,22 @@ namespace Umbraco.Tests.TestHelpers GetRepo(c))); var macroService = GetLazyService(factory, c => new MacroService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c))); - var packagingService = GetLazyService(factory, c => new PackagingService( - logger, contentService.Value, contentTypeService.Value, macroService.Value, dataTypeService.Value, fileService.Value, localizationService.Value, entityService.Value, auditService.Value, new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())), - createdPackages: new PackagesRepository(contentService.Value, contentTypeService.Value, dataTypeService.Value, fileService.Value, macroService.Value, localizationService.Value, - new EntityXmlSerializer(contentService.Value, mediaService.Value, dataTypeService.Value, userService.Value, localizationService.Value, contentTypeService.Value, urlSegmentProviders), logger, "createdPackages.config"), - installedPackages: new PackagesRepository(contentService.Value, contentTypeService.Value, dataTypeService.Value, fileService.Value, macroService.Value, localizationService.Value, - new EntityXmlSerializer(contentService.Value, mediaService.Value, dataTypeService.Value, userService.Value, localizationService.Value, contentTypeService.Value, urlSegmentProviders), logger, "installedPackages.config"))); + var packagingService = GetLazyService(factory, c => + { + var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); + var compiledPackageXmlParser = new CompiledPackageXmlParser(new ConflictingPackageData(macroService.Value, fileService.Value)); + return new PackagingService( + auditService.Value, + new PackagesRepository(contentService.Value, contentTypeService.Value, dataTypeService.Value, fileService.Value, macroService.Value, localizationService.Value, + new EntityXmlSerializer(contentService.Value, mediaService.Value, dataTypeService.Value, userService.Value, localizationService.Value, contentTypeService.Value, urlSegmentProviders), logger, "createdPackages.config"), + new PackagesRepository(contentService.Value, contentTypeService.Value, dataTypeService.Value, fileService.Value, macroService.Value, localizationService.Value, + new EntityXmlSerializer(contentService.Value, mediaService.Value, dataTypeService.Value, userService.Value, localizationService.Value, contentTypeService.Value, urlSegmentProviders), logger, "installedPackages.config"), + new PackageInstallation( + new PackageDataInstallation(logger, fileService.Value, macroService.Value, localizationService.Value, dataTypeService.Value, entityService.Value, contentTypeService.Value, contentService.Value, propertyEditorCollection), + new PackageFileInstallation(compiledPackageXmlParser, new ProfilingLogger(logger, new TestProfiler())), + compiledPackageXmlParser, Mock.Of(), + "", null, null)); + }); var relationService = GetLazyService(factory, c => new RelationService(scopeProvider, logger, eventMessagesFactory, entityService.Value, GetRepo(c), GetRepo(c))); var treeService = GetLazyService(factory, c => new ApplicationTreeService(logger, cache, typeLoader)); var tagService = GetLazyService(factory, c => new TagService(scopeProvider, logger, eventMessagesFactory, GetRepo(c))); diff --git a/src/Umbraco.Web/Composing/Current.cs b/src/Umbraco.Web/Composing/Current.cs index daf152c29b..860d3ec88e 100644 --- a/src/Umbraco.Web/Composing/Current.cs +++ b/src/Umbraco.Web/Composing/Current.cs @@ -216,7 +216,7 @@ namespace Umbraco.Web.Composing internal static ManifestValueValidatorCollection ManifestValidators => CoreCurrent.ManifestValidators; - internal static PackageActionRunner PackageActionRunner => CoreCurrent.PackageActionRunner; + internal static IPackageActionRunner PackageActionRunner => CoreCurrent.PackageActionRunner; internal static PackageActionCollection PackageActions => CoreCurrent.PackageActions; diff --git a/src/Umbraco.Web/Editors/ContentTypeController.cs b/src/Umbraco.Web/Editors/ContentTypeController.cs index 0d56b34617..97f7ff3589 100644 --- a/src/Umbraco.Web/Editors/ContentTypeController.cs +++ b/src/Umbraco.Web/Editors/ContentTypeController.cs @@ -17,7 +17,9 @@ using Umbraco.Core.Dictionary; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Packaging; using Umbraco.Core.Persistence; +using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; using Umbraco.Web.Composing; @@ -46,10 +48,19 @@ namespace Umbraco.Web.Editors public class ContentTypeController : ContentTypeControllerBase { private readonly IEntityXmlSerializer _serializer; + private readonly PropertyEditorCollection _propertyEditors; - public ContentTypeController(IEntityXmlSerializer serializer, ICultureDictionaryFactory cultureDictionaryFactory, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, CacheHelper applicationCache, IProfilingLogger logger, IRuntimeState runtimeState) : base(cultureDictionaryFactory, globalSettings, umbracoContextAccessor, sqlContext, services, applicationCache, logger, runtimeState) + public ContentTypeController(IEntityXmlSerializer serializer, + ICultureDictionaryFactory cultureDictionaryFactory, + IGlobalSettings globalSettings, + IUmbracoContextAccessor umbracoContextAccessor, + ISqlContext sqlContext, PropertyEditorCollection propertyEditors, + ServiceContext services, CacheHelper applicationCache, + IProfilingLogger logger, IRuntimeState runtimeState) + : base(cultureDictionaryFactory, globalSettings, umbracoContextAccessor, sqlContext, services, applicationCache, logger, runtimeState) { _serializer = serializer; + _propertyEditors = propertyEditors; } public int GetCount() @@ -162,8 +173,8 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(HttpStatusCode.NotFound); } - var configuration = Current.Services.DataTypeService.GetDataType(id).Configuration; - var editor = Current.PropertyEditors[dataTypeDiff.EditorAlias]; + var configuration = Services.DataTypeService.GetDataType(id).Configuration; + var editor = _propertyEditors[dataTypeDiff.EditorAlias]; return new ContentPropertyDisplay() { @@ -488,14 +499,16 @@ namespace Umbraco.Web.Editors { return Request.CreateResponse(HttpStatusCode.NotFound); } + + var dataInstaller = new PackageDataInstallation(Logger, Services.FileService, Services.MacroService, Services.LocalizationService, + Services.DataTypeService, Services.EntityService, Services.ContentTypeService, Services.ContentService, _propertyEditors); - var xd = new XmlDocument(); - xd.XmlResolver = null; + var xd = new XmlDocument {XmlResolver = null}; xd.Load(filePath); - var userId = Security.GetUserId(); + var userId = Security.GetUserId().ResultOr(0); var element = XElement.Parse(xd.InnerXml); - Current.Services.PackagingService.ImportContentTypes(element, userId); + dataInstaller.ImportDocumentType(element, userId); // Try to clean up the temporary file. try @@ -504,7 +517,7 @@ namespace Umbraco.Web.Editors } catch (Exception ex) { - Current.Logger.Error(ex, "Error cleaning up temporary udt file in App_Data: {File}", filePath); + Logger.Error(ex, "Error cleaning up temporary udt file in App_Data: {File}", filePath); } return Request.CreateResponse(HttpStatusCode.OK); diff --git a/src/Umbraco.Web/Editors/PackageInstallController.cs b/src/Umbraco.Web/Editors/PackageInstallController.cs index 24cc999c6b..bfaec3f82c 100644 --- a/src/Umbraco.Web/Editors/PackageInstallController.cs +++ b/src/Umbraco.Web/Editors/PackageInstallController.cs @@ -42,11 +42,11 @@ namespace Umbraco.Web.Editors [UmbracoApplicationAuthorize(Core.Constants.Applications.Packages)] public class PackageInstallController : UmbracoAuthorizedJsonController { - private readonly PackageActionRunner _packageActionRunner; + private readonly IPackageActionRunner _packageActionRunner; public PackageInstallController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, CacheHelper applicationCache, - IProfilingLogger logger, IRuntimeState runtimeState, PackageActionRunner packageActionRunner) + IProfilingLogger logger, IRuntimeState runtimeState, IPackageActionRunner packageActionRunner) : base(globalSettings, umbracoContextAccessor, sqlContext, services, applicationCache, logger, runtimeState) { _packageActionRunner = packageActionRunner; @@ -259,6 +259,7 @@ namespace Umbraco.Web.Editors }; // trigger the UninstalledPackage event + // fixme: This all needs to be part of the service! PackagingService.OnUninstalledPackage(new UninstallPackageEventArgs(summary, package, false)); } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 64e17af221..89260e59f6 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -1136,7 +1136,6 @@ - diff --git a/src/Umbraco.Web/_Legacy/Packager/Installer.cs b/src/Umbraco.Web/_Legacy/Packager/Installer.cs deleted file mode 100644 index 89dff406b0..0000000000 --- a/src/Umbraco.Web/_Legacy/Packager/Installer.cs +++ /dev/null @@ -1,688 +0,0 @@ -//using System; -//using System.Collections.Generic; -//using System.Diagnostics; -//using System.Globalization; -//using System.IO; -//using System.Linq; -//using System.Net; -//using System.Xml; -//using System.Xml.Linq; -//using System.Xml.XPath; -//using ICSharpCode.SharpZipLib.Zip; -//using Umbraco.Core; -//using Umbraco.Core.Composing; -//using Umbraco.Core.Events; -//using Umbraco.Core.IO; -//using Umbraco.Core.Logging; -//using Umbraco.Core.Models; -//using Umbraco.Core.Models.Packaging; -//using Umbraco.Core.Packaging; -//using Umbraco.Core.Services.Implement; -//using File = System.IO.File; - -//namespace Umbraco.Web._Legacy.Packager -//{ -// /// -// /// The packager is a component which enables sharing of both data and functionality components between different umbraco installations. -// /// -// /// The output is a .umb (a zip compressed file) which contains the exported documents/medias/macroes/documentTypes (etc.) -// /// in a Xml document, along with the physical files used (images/usercontrols/xsl documents etc.) -// /// -// /// Partly implemented, import of packages is done, the export is *under construction*. -// /// -// /// -// /// Ruben Verborgh 31/12/2007: I had to change some code, I marked my changes with "DATALAYER". -// /// Reason: @@IDENTITY can't be used with the new datalayer. -// /// I wasn't able to test the code, since I'm not aware how the code functions. -// /// -// public class Installer -// { -// private const string PackageServer = "packages.umbraco.org"; - -// private readonly Dictionary _conflictingMacroAliases = new Dictionary(); -// private readonly Dictionary _conflictingTemplateAliases = new Dictionary(); -// private readonly Dictionary _conflictingStyleSheetNames = new Dictionary(); - -// private readonly int _currentUserId = -1; -// private static WebClient _webClient; - - -// public string Name { get; private set; } -// public string Version { get; private set; } -// public string Url { get; private set; } -// public string License { get; private set; } -// public string LicenseUrl { get; private set; } -// public string Author { get; private set; } -// public string AuthorUrl { get; private set; } -// public string ReadMe { get; private set; } -// public string Control { get; private set; } - -// public bool ContainsMacroConflict { get; private set; } -// public IDictionary ConflictingMacroAliases => _conflictingMacroAliases; - -// public bool ContainsUnsecureFiles { get; private set; } -// public List UnsecureFiles { get; } = new List(); - -// public bool ContainsTemplateConflicts { get; private set; } -// public IDictionary ConflictingTemplateAliases => _conflictingTemplateAliases; - -// public bool ContainsStyleSheeConflicts { get; private set; } -// public IDictionary ConflictingStyleSheetNames => _conflictingStyleSheetNames; - -// public int RequirementsMajor { get; private set; } -// public int RequirementsMinor { get; private set; } -// public int RequirementsPatch { get; private set; } - -// public RequirementsType RequirementsType { get; private set; } - -// public string IconUrl { get; private set; } - -// /// -// /// The xml of the compiled package -// /// -// public XDocument Config { get; private set; } - -// /// -// /// Constructor -// /// -// public Installer() -// { -// Initialize(); -// } - -// public Installer(int currentUserId) -// { -// Initialize(); -// _currentUserId = currentUserId; -// } - -// private void Initialize() -// { -// ContainsTemplateConflicts = false; -// ContainsUnsecureFiles = false; -// ContainsMacroConflict = false; -// ContainsStyleSheeConflicts = false; -// } - - - -// /// -// /// Constructor -// /// -// /// The name of the package -// /// The version of the package -// /// The url to a descriptionpage -// /// The license under which the package is released (preferably GPL ;)) -// /// The url to a licensedescription -// /// The original author of the package -// /// The url to the Authors website -// /// Umbraco version major -// /// Umbraco version minor -// /// Umbraco version patch -// /// The readme text -// /// The name of the usercontrol used to configure the package after install -// /// -// /// -// public Installer(string name, string version, string url, string license, string licenseUrl, string author, string authorUrl, int requirementsMajor, int requirementsMinor, int requirementsPatch, string readme, string control, RequirementsType requirementsType, string iconUrl) -// { -// ContainsTemplateConflicts = false; -// ContainsUnsecureFiles = false; -// ContainsMacroConflict = false; -// ContainsStyleSheeConflicts = false; -// this.Name = name; -// this.Version = version; -// this.Url = url; -// this.License = license; -// this.LicenseUrl = licenseUrl; -// this.RequirementsMajor = requirementsMajor; -// this.RequirementsMinor = requirementsMinor; -// this.RequirementsPatch = requirementsPatch; -// this.RequirementsType = requirementsType; -// this.Author = author; -// this.AuthorUrl = authorUrl; -// this.IconUrl = iconUrl; -// ReadMe = readme; -// this.Control = control; -// } - -// #region Public Methods - -// /// -// /// Imports the specified package -// /// -// /// Filename of the umbracopackage -// /// true if the input file should be deleted after import -// /// -// public string Import(string inputFile, bool deleteFile) -// { -// using (Current.ProfilingLogger.DebugDuration( -// $"Importing package file {inputFile}.", -// $"Package file {inputFile} imported.")) -// { -// var tempDir = ""; -// if (File.Exists(IOHelper.MapPath(SystemDirectories.Data + "/" + inputFile))) -// { -// var fi = new FileInfo(IOHelper.MapPath(SystemDirectories.Data + "/" + inputFile)); -// // Check if the file is a valid package -// if (fi.Extension.ToLower() == ".umb") -// { -// try -// { -// tempDir = UnPack(fi.FullName, deleteFile); -// LoadConfig(tempDir); -// } -// catch (Exception ex) -// { -// Current.Logger.Error(ex, "Error importing file {FileName}", fi.FullName); -// throw; -// } -// } -// else -// throw new Exception("Error - file isn't a package (doesn't have a .umb extension). Check if the file automatically got named '.zip' upon download."); -// } -// else -// throw new Exception("Error - file not found. Could find file named '" + IOHelper.MapPath(SystemDirectories.Data + Path.DirectorySeparatorChar + inputFile) + "'"); -// return tempDir; -// } - -// } - -// /// -// /// Imports the specified package -// /// -// /// Filename of the umbracopackage -// /// -// public string Import(string inputFile) -// { -// return Import(inputFile, true); -// } - -// public int CreateManifest(Guid guid) -// { -// //This is the new improved install rutine, which chops up the process into 3 steps, creating the manifest, moving files, and finally handling umb objects - -// var parser = new CompiledPackageXmlParser(); -// var def = parser.ToCompiledPackage(Config); - -// //create a new entry in the installedPackages.config -// var installedPackage = new PackageDefinition -// { -// Author = def.Author, -// AuthorUrl = def.AuthorUrl, -// Control = def.Control, -// IconUrl = def.IconUrl, -// License = def.License, -// LicenseUrl = def.LicenseUrl, -// Name = def.Name, -// Readme = def.Readme, -// UmbracoVersion = def.UmbracoVersion, -// Url = def.Url, -// Version = def.Version, -// PackageId = guid -// }; - -// if (!Current.Services.PackagingService.SaveInstalledPackage(installedPackage)) -// throw new InvalidOperationException("Could not save package definition"); - -// return installedPackage.Id; -// } - -// public void InstallFiles(int packageId, string tempDir) -// { -// var parser = new CompiledPackageXmlParser(); - -// using (Current.ProfilingLogger.DebugDuration( -// "Installing package files for package id " + packageId + " into temp folder " + tempDir, -// "Package file installation complete for package id " + packageId)) -// { -// //retrieve the manifest to continue installation -// var insPack = Current.Services.PackagingService.GetInstalledPackageById(packageId); - -// //TODO: Depending on some files, some files should be installed differently. -// //i.e. if stylsheets should probably be installed via business logic, media items should probably use the media IFileSystem! - -// // Move files -// //string virtualBasePath = System.Web.HttpContext.Current.Request.ApplicationPath; -// string basePath = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath; - -// var def = parser.ToCompiledPackage(Config); - -// try -// { -// foreach (var f in def.Files) -// { -// var destPath = GetFileName(basePath, f.OriginalPath); -// var sourceFile = GetFileName(tempDir, f.UniqueFileName); -// var destFile = GetFileName(destPath, f.OriginalName); - -// // Create the destination directory if it doesn't exist -// if (Directory.Exists(destPath) == false) -// Directory.CreateDirectory(destPath); -// //If a file with this name exists, delete it -// else if (File.Exists(destFile)) -// File.Delete(destFile); - -// // Copy the file -// // SJ: Note - this used to do a move but some packages included the same file to be -// // copied to multiple locations like so: -// // -// // -// // my-icon.png -// // /umbraco/Images/ -// // my-icon.png -// // -// // -// // my-icon.png -// // /App_Plugins/MyPlugin/Images -// // my-icon.png -// // -// // -// // Since this file unzips as a flat list of files, moving the file the first time means -// // that when you try to do that a second time, it would result in a FileNotFoundException -// File.Copy(sourceFile, destFile); - -// //PPH log file install -// insPack.Files.Add(f.OriginalPath.EnsureEndsWith('/') + f.OriginalName); - -// } - -// // Once we're done copying, remove all the files -// foreach (var f in def.Files) -// { -// var sourceFile = GetFileName(tempDir, f.UniqueFileName); -// if (File.Exists(sourceFile)) -// File.Delete(sourceFile); -// } -// } -// catch (Exception ex) -// { -// Current.Logger.Error(ex, "Package install error"); -// throw; -// } - -// // log that a user has install files -// if (_currentUserId > -1) -// { -// Current.Services.AuditService.Add(AuditType.PackagerInstall, -// _currentUserId, -// -1, "Package", string.Format("Package '{0}' installed. Package guid: {1}", insPack.Name, insPack.PackageId)); -// } - -// Current.Services.PackagingService.SaveInstalledPackage(insPack); -// } -// } - -// public void InstallBusinessLogic(int packageId, string tempDir) -// { -// using (Current.ProfilingLogger.DebugDuration( -// "Installing business logic for package id " + packageId + " into temp folder " + tempDir, -// "Package business logic installation complete for package id " + packageId)) -// { -// PackageDefinition insPack; -// try -// { -// //retrieve the manifest to continue installation -// insPack = Current.Services.PackagingService.GetInstalledPackageById(packageId); -// //bool saveNeeded = false; - -// // Get current user, with a fallback -// var currentUser = Current.Services.UserService.GetUserById(Constants.Security.SuperUserId); - -// //TODO: Get rid of this entire class! Until then all packages will be installed by the admin user - -// var rootElement = Config.Root; -// var packagingService = Current.Services.PackagingService; - -// //Perhaps it would have been a good idea to put the following into methods eh?!? - -// #region DataTypes -// var dataTypeElement = rootElement.Descendants("DataTypes").FirstOrDefault(); -// if (dataTypeElement != null) -// { -// var dataTypeDefinitions = packagingService.ImportDataTypeDefinitions(dataTypeElement, currentUser.Id); -// foreach (var dataTypeDefinition in dataTypeDefinitions) -// { -// insPack.DataTypes.Add(dataTypeDefinition.Id.ToString(CultureInfo.InvariantCulture)); -// } -// } -// #endregion - -// #region Languages -// var languageItemsElement = rootElement.Descendants("Languages").FirstOrDefault(); -// if (languageItemsElement != null) -// { -// var insertedLanguages = packagingService.ImportLanguages(languageItemsElement); -// foreach(var x in insertedLanguages.Select(l => l.Id.ToString(CultureInfo.InvariantCulture))) -// insPack.Languages.Add(x); -// } - -// #endregion - -// #region Dictionary items -// var dictionaryItemsElement = rootElement.Descendants("DictionaryItems").FirstOrDefault(); -// if (dictionaryItemsElement != null) -// { -// var insertedDictionaryItems = packagingService.ImportDictionaryItems(dictionaryItemsElement); -// foreach (var x in insertedDictionaryItems.Select(d => d.Id.ToString(CultureInfo.InvariantCulture))) -// insPack.DictionaryItems.Add(x); -// } -// #endregion - -// #region Macros -// var macroItemsElement = rootElement.Descendants("Macros").FirstOrDefault(); -// if (macroItemsElement != null) -// { -// var insertedMacros = packagingService.ImportMacros(macroItemsElement); -// foreach (var x in insertedMacros.Select(m => m.Id.ToString(CultureInfo.InvariantCulture))) -// insPack.Macros.Add(x); - -// } -// #endregion - -// #region Templates -// var templateElement = rootElement.Descendants("Templates").FirstOrDefault(); -// if (templateElement != null) -// { -// var templates = packagingService.ImportTemplates(templateElement, currentUser.Id); -// foreach (var template in templates) -// { -// insPack.Templates.Add(template.Id.ToString(CultureInfo.InvariantCulture)); -// } -// } -// #endregion - -// #region DocumentTypes -// //Check whether the root element is a doc type rather then a complete package -// var docTypeElement = rootElement.Name.LocalName.Equals("DocumentType") || -// rootElement.Name.LocalName.Equals("DocumentTypes") -// ? rootElement -// : rootElement.Descendants("DocumentTypes").FirstOrDefault(); - -// if (docTypeElement != null) -// { -// var contentTypes = packagingService.ImportContentTypes(docTypeElement, currentUser.Id); -// foreach (var contentType in contentTypes) -// { -// insPack.DocumentTypes.Add(contentType.Id.ToString(CultureInfo.InvariantCulture)); -// //saveNeeded = true; -// } -// } -// #endregion - -// #region Stylesheets -// foreach (var n in Config.Root.XPathSelectElements("Stylesheets/Stylesheet")) -// { -// string stylesheetName = n.Element("Name")?.Value; -// if (stylesheetName.IsNullOrWhiteSpace()) continue; - -// var s = Current.Services.FileService.GetStylesheetByName(stylesheetName); -// if (s == null) -// { -// var fileName = n.Element("FileName")?.Value; -// if (fileName == null) continue; -// var content = n.Element("Content")?.Value; -// if (content == null) continue; - -// s = new Stylesheet(fileName) { Content = content }; -// Current.Services.FileService.SaveStylesheet(s); -// } - -// foreach (var prop in n.XPathSelectElements("Properties/Property")) -// { -// string alias = prop.Element("Alias")?.Value; -// var sp = s.Properties.SingleOrDefault(p => p != null && p.Alias == alias); -// string name = prop.Element("Name")?.Value; -// if (sp == null) -// { -// //sp = StylesheetProperty.MakeNew( -// // name, -// // s, -// // u); - -// sp = new StylesheetProperty(name, "#" + name.ToSafeAlias(), ""); -// s.AddProperty(sp); -// } -// else -// { -// //sp.Text = name; -// //Changing the name requires removing the current property and then adding another new one -// if (sp.Name != name) -// { -// s.RemoveProperty(sp.Name); -// var newProp = new StylesheetProperty(name, sp.Alias, sp.Value); -// s.AddProperty(newProp); -// sp = newProp; -// } -// } -// sp.Alias = alias; -// sp.Value = prop.Element("Value")?.Value; -// } -// //s.saveCssToFile(); -// Current.Services.FileService.SaveStylesheet(s); - - - - -// insPack.Stylesheets.Add(s.Id.ToString(CultureInfo.InvariantCulture)); -// //saveNeeded = true; -// } - -// //if (saveNeeded) { insPack.Save(); saveNeeded = false; } -// #endregion - -// #region Documents -// var documentElement = rootElement.Descendants("DocumentSet").FirstOrDefault(); -// if (documentElement != null) -// { -// var content = packagingService.ImportContent(documentElement, -1, currentUser.Id); -// var firstContentItem = content.First(); -// insPack.ContentNodeId = firstContentItem.Id.ToString(CultureInfo.InvariantCulture); -// } -// #endregion - -// #region Package Actions -// foreach (var n in Config.Root.XPathSelectElements("Actions/Action")) -// { -// var undo = n.AttributeValue("undo"); -// if (undo == null || undo == "true") -// { -// insPack.Actions += n.ToString(); -// } - -// //Run the actions tagged only for 'install' -// var runat = n.AttributeValue("runat"); - -// if (runat != null && runat == "install") -// { -// var alias = n.AttributeValue("alias"); -// if (alias.IsNullOrWhiteSpace() == false) -// { -// Current.PackageActionRunner.RunPackageAction(insPack.Name, alias, n); -// } -// } -// } -// #endregion - -// Current.Services.PackagingService.SaveInstalledPackage(insPack); -// } -// catch (Exception ex) -// { -// Current.Logger.Error(ex, "Error installing businesslogic"); -// throw; -// } - -// OnPackageInstalled(insPack); -// } -// } - -// /// -// /// Remove the temp installation folder -// /// -// /// -// /// -// public void InstallCleanUp(int packageId, string tempDir) -// { -// if (Directory.Exists(tempDir)) -// { -// Directory.Delete(tempDir, true); -// } -// } - -// /// -// /// Reads the configuration of the package from the configuration xmldocument -// /// -// /// The folder to which the contents of the package is extracted -// public void LoadConfig(string tempDir) -// { -// Config = XDocument.Load(tempDir + Path.DirectorySeparatorChar + "package.xml"); - -// var parser = new CompiledPackageXmlParser(); -// var def = parser.ToCompiledPackage(Config); - -// Name = def.Name; -// Version = def.Version; -// Url = def.Url; -// License = def.License; -// LicenseUrl = def.LicenseUrl; - -// RequirementsMajor = def.UmbracoVersion.Major; -// RequirementsMinor = def.UmbracoVersion.Minor; -// RequirementsPatch = def.UmbracoVersion.Build; -// RequirementsType = def.UmbracoVersionRequirementsType; -// IconUrl = def.IconUrl; -// Author = def.Author; -// AuthorUrl = def.AuthorUrl; -// ReadMe = def.Readme; -// Control = def.Control; - -// var basePath = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath; -// var dllBinFiles = new List(); - -// foreach (var f in def.Files) -// { -// var badFile = false; -// var destPath = GetFileName(basePath, f.OriginalPath); -// var orgName = f.OriginalName; -// var destFile = GetFileName(destPath, orgName); - -// if (destPath.ToLower().Contains(IOHelper.DirSepChar + "app_code")) -// { -// badFile = true; -// } - -// if (destPath.ToLower().Contains(IOHelper.DirSepChar + "bin")) -// { -// badFile = true; -// } - -// if (destFile.ToLower().EndsWith(".dll")) -// { -// badFile = true; -// dllBinFiles.Add(Path.Combine(tempDir, orgName)); -// } - -// if (badFile) -// { -// ContainsUnsecureFiles = true; -// UnsecureFiles.Add(f.OriginalName); -// } -// } - - - -// //this will check for existing macros with the same alias -// //since we will not overwrite on import it's a good idea to inform the user what will be overwritten -// foreach (var n in Config.Root.XPathSelectElements("//macro")) -// { -// var alias = n.Element("alias")?.Value; -// if (!string.IsNullOrEmpty(alias)) -// { -// var m = Current.Services.MacroService.GetByAlias(alias); -// if (m != null) -// { -// ContainsMacroConflict = true; -// if (_conflictingMacroAliases.ContainsKey(m.Name) == false) -// { -// _conflictingMacroAliases.Add(m.Name, alias); -// } -// } -// } -// } - -// foreach (var n in Config.Root.XPathSelectElements("Templates/Template")) -// { -// var alias = n.Element("Alias")?.Value; -// if (!string.IsNullOrEmpty(alias)) -// { -// var t = Current.Services.FileService.GetTemplate(alias); -// if (t != null) -// { -// ContainsTemplateConflicts = true; -// if (_conflictingTemplateAliases.ContainsKey(t.Alias) == false) -// { -// _conflictingTemplateAliases.Add(t.Alias, alias); -// } -// } -// } -// } - -// foreach (var n in Config.Root.XPathSelectElements("Stylesheets/Stylesheet")) -// { -// var alias = n.Element("Name")?.Value; -// if (!string.IsNullOrEmpty(alias)) -// { -// var s = Current.Services.FileService.GetStylesheetByName(alias); -// if (s != null) -// { -// ContainsStyleSheeConflicts = true; -// if (_conflictingStyleSheetNames.ContainsKey(s.Alias) == false) -// { -// _conflictingStyleSheetNames.Add(s.Alias, alias); -// } -// } -// } -// } - - -// } - -// /// -// /// This uses the old method of fetching and only supports the packages.umbraco.org repository. -// /// -// /// -// /// -// public string Fetch(Guid Package) -// { -// // Check for package directory -// if (Directory.Exists(IOHelper.MapPath(SystemDirectories.Packages)) == false) -// Directory.CreateDirectory(IOHelper.MapPath(SystemDirectories.Packages)); - -// if (_webClient == null) -// _webClient = new WebClient(); - -// _webClient.DownloadFile( -// "http://" + PackageServer + "/fetch?package=" + Package.ToString(), -// IOHelper.MapPath(SystemDirectories.Packages + "/" + Package + ".umb")); - -// return "packages\\" + Package + ".umb"; -// } - -// #endregion - -// private void OnPackageInstalled(PackageDefinition insPack) -// { -// // getting an InstallationSummary for sending to the PackagingService.ImportedPackage event -// var fileService = Current.Services.FileService; -// var macroService = Current.Services.MacroService; -// var contentTypeService = Current.Services.ContentTypeService; -// var dataTypeService = Current.Services.DataTypeService; -// var localizationService = Current.Services.LocalizationService; - -// var installationSummary = InstallationSummary.FromPackageDefinition(insPack, contentTypeService, dataTypeService, fileService, localizationService, macroService); -// installationSummary.PackageInstalled = true; - -// var args = new ImportPackageEventArgs(installationSummary, insPack, false); -// PackagingService.OnImportedPackage(args); -// } -// } -//}