From 94da9a66816734292471f7b600ff4d348e2954eb Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 16 Jan 2019 17:25:46 +1100 Subject: [PATCH] Gets package upgrading working along with package uninstallation with multiple versions --- .../Events/UninstallPackageEventArgs.cs | 11 +- .../Packaging/IPackageInstallation.cs | 2 +- .../Packaging/PackageDataInstallation.cs | 39 +++--- .../Packaging/PackageInstallation.cs | 5 +- .../Packaging/PackagesRepository.cs | 5 - .../Services/IPackagingService.cs | 32 +++-- .../Services/Implement/PackagingService.cs | 111 +++++++----------- .../src/views/packages/edit.html | 14 +-- src/Umbraco.Web/Editors/PackageController.cs | 5 +- .../Editors/PackageInstallController.cs | 17 +-- 10 files changed, 110 insertions(+), 131 deletions(-) diff --git a/src/Umbraco.Core/Events/UninstallPackageEventArgs.cs b/src/Umbraco.Core/Events/UninstallPackageEventArgs.cs index 2618d04e17..63c5ceaba0 100644 --- a/src/Umbraco.Core/Events/UninstallPackageEventArgs.cs +++ b/src/Umbraco.Core/Events/UninstallPackageEventArgs.cs @@ -3,16 +3,13 @@ using Umbraco.Core.Models.Packaging; namespace Umbraco.Core.Events { - public class UninstallPackageEventArgs : CancellableObjectEventArgs> + public class UninstallPackageEventArgs: CancellableObjectEventArgs> { - public UninstallPackageEventArgs(TEntity eventObject, IPackageInfo packageMetaData, bool canCancel) - : base(new[] { eventObject }, canCancel) + public UninstallPackageEventArgs(IEnumerable eventObject, bool canCancel) + : base(eventObject, canCancel) { - PackageMetaData = packageMetaData; } - public IPackageInfo PackageMetaData { get; } - - public IEnumerable UninstallationSummary => EventObject; + public IEnumerable UninstallationSummary => EventObject; } } diff --git a/src/Umbraco.Core/Packaging/IPackageInstallation.cs b/src/Umbraco.Core/Packaging/IPackageInstallation.cs index 5dae76674d..c85a4ccd5f 100644 --- a/src/Umbraco.Core/Packaging/IPackageInstallation.cs +++ b/src/Umbraco.Core/Packaging/IPackageInstallation.cs @@ -8,7 +8,7 @@ namespace Umbraco.Core.Packaging public interface IPackageInstallation { /// - /// Uninstalls a package including all data, entities and files + /// This will run the uninstallation sequence for this /// /// /// diff --git a/src/Umbraco.Core/Packaging/PackageDataInstallation.cs b/src/Umbraco.Core/Packaging/PackageDataInstallation.cs index 36066b104f..2e9fee9595 100644 --- a/src/Umbraco.Core/Packaging/PackageDataInstallation.cs +++ b/src/Umbraco.Core/Packaging/PackageDataInstallation.cs @@ -228,14 +228,17 @@ namespace Umbraco.Core.Packaging } var content = CreateContentFromXml(root, importedContentTypes[contentTypeAlias], null, parentId); + if (content == null) continue; + 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)); + + if (children.Count > 0) + contents.AddRange(CreateContentFromXml(children, content, importedContentTypes).WhereNotNull()); } return contents; } @@ -258,7 +261,7 @@ namespace Umbraco.Core.Packaging list.Add(content); //Recursive call - XElement child1 = child; + var child1 = child; var grandChildren = (from grand in child1.Elements() where (string)grand.Attribute("isDoc") == "" select grand).ToList(); @@ -272,37 +275,43 @@ namespace Umbraco.Core.Packaging private IContent CreateContentFromXml(XElement element, IContentType contentType, IContent parent, int parentId) { + var key = Guid.Empty; + if (element.Attribute("key") != null && Guid.TryParse(element.Attribute("key").Value, out key)) + { + //if a Key is supplied, then we need to check if the content already exists and if so we ignore the installation for this item + if (_contentService.GetById(key) != null) + return null; + } + 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 templateId = element.AttributeValue("template"); + var properties = from property in element.Elements() where property.Attribute("isDoc") == null select property; + var template = templateId.HasValue ? _fileService.GetTemplate(templateId.Value) : null; + IContent content = parent == null ? new Content(nodeName, parentId, contentType) { Level = int.Parse(level), - SortOrder = int.Parse(sortOrder) + SortOrder = int.Parse(sortOrder), + TemplateId = template?.Id, + Key = key } : new Content(nodeName, parent, contentType) { Level = int.Parse(level), - SortOrder = int.Parse(sortOrder) + SortOrder = int.Parse(sortOrder), + TemplateId = template?.Id, + Key = key }; - 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; diff --git a/src/Umbraco.Core/Packaging/PackageInstallation.cs b/src/Umbraco.Core/Packaging/PackageInstallation.cs index d7bb72836e..955662f3fe 100644 --- a/src/Umbraco.Core/Packaging/PackageInstallation.cs +++ b/src/Umbraco.Core/Packaging/PackageInstallation.cs @@ -76,12 +76,13 @@ namespace Umbraco.Core.Packaging return files; } + /// public UninstallationSummary UninstallPackage(PackageDefinition package, int userId) { //running this will update the PackageDefinition with the items being removed var summary = _packageDataInstallation.UninstallPackageData(package, userId); - summary.Actions = _parser.GetPackageActions(XElement.Parse(package.Actions), package.Name); + summary.Actions = CompiledPackageXmlParser.GetPackageActions(XElement.Parse(package.Actions), package.Name); //run actions before files are removed summary.ActionErrors = UndoPackageActions(package, summary.Actions).ToList(); @@ -109,7 +110,7 @@ namespace Umbraco.Core.Packaging 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.Actions = CompiledPackageXmlParser.GetPackageActions(XElement.Parse(compiledPackage.Actions), compiledPackage.Name); installationSummary.MetaData = compiledPackage; installationSummary.FilesInstalled = packageDefinition.Files; diff --git a/src/Umbraco.Core/Packaging/PackagesRepository.cs b/src/Umbraco.Core/Packaging/PackagesRepository.cs index af5c7ffded..249d02a320 100644 --- a/src/Umbraco.Core/Packaging/PackagesRepository.cs +++ b/src/Umbraco.Core/Packaging/PackagesRepository.cs @@ -120,11 +120,6 @@ namespace Umbraco.Core.Packaging if (definition.Id == default) { - //check if the name already exists - var existsByName = packagesXml.Root.Elements("package").Any(x => x.AttributeValue("name") == definition.Name); - if (existsByName) - return false; - //need to gen an id and persist // Find max id var maxId = packagesXml.Root.Elements("package").Max(x => x.AttributeValue("id")) ?? 0; diff --git a/src/Umbraco.Core/Services/IPackagingService.cs b/src/Umbraco.Core/Services/IPackagingService.cs index 7d4d70196d..c14882fe7a 100644 --- a/src/Umbraco.Core/Services/IPackagingService.cs +++ b/src/Umbraco.Core/Services/IPackagingService.cs @@ -37,15 +37,35 @@ namespace Umbraco.Core.Services /// InstallationSummary InstallCompiledPackageData(PackageDefinition packageDefinition, FileInfo packageFile, int userId = 0); - UninstallationSummary UninstallPackage(PackageDefinition packageDefinition, int userId = 0); + /// + /// Uninstalls all versions of the package by name + /// + /// + /// + /// + UninstallationSummary UninstallPackage(string packageName, int userId = 0); #endregion #region Installed Packages IEnumerable GetAllInstalledPackages(); + + /// + /// Returns the for the installation id + /// + /// + /// PackageDefinition GetInstalledPackageById(int id); - PackageDefinition GetInstalledPackageByName(string name); + + /// + /// Returns all for the package by name + /// + /// + /// + /// A list of all package definitions installed for this package (i.e. original install and any upgrades) + /// + IEnumerable GetInstalledPackageByName(string name); /// /// Returns a for a given package name and version @@ -57,14 +77,6 @@ namespace Umbraco.Core.Services PackageInstallType GetPackageInstallType(string packageName, SemVersion packageVersion, out PackageDefinition alreadyInstalled); void DeleteInstalledPackage(int packageId, int userId = 0); - /// - /// Merges the package definition information from the upgrade on to the original and returns the merged definition - /// - /// - /// - /// - PackageDefinition MergePackageDefinition(PackageDefinition original, PackageDefinition upgrade); - /// /// Persists a package definition to storage /// diff --git a/src/Umbraco.Core/Services/Implement/PackagingService.cs b/src/Umbraco.Core/Services/Implement/PackagingService.cs index 1da53c5e11..24ef818624 100644 --- a/src/Umbraco.Core/Services/Implement/PackagingService.cs +++ b/src/Umbraco.Core/Services/Implement/PackagingService.cs @@ -44,7 +44,7 @@ namespace Umbraco.Core.Services.Implement ICreatedPackagesRepository createdPackages, IInstalledPackagesRepository installedPackages, IPackageInstallation packageInstallation) - { + { _auditService = auditService; _createdPackages = createdPackages; _installedPackages = installedPackages; @@ -110,7 +110,7 @@ namespace Umbraco.Core.Services.Implement if (compiledPackage == null) throw new InvalidOperationException("Could not read the package file " + packageFile); var files = _packageInstallation.InstallPackageFiles(packageDefinition, compiledPackage, userId).ToList(); - + SaveInstalledPackage(packageDefinition); _auditService.Add(AuditType.PackagerInstall, userId, -1, "Package", $"Package files installed for package '{compiledPackage.Name}'."); @@ -141,16 +141,44 @@ namespace Umbraco.Core.Services.Implement return summary; } - public UninstallationSummary UninstallPackage(PackageDefinition package, int userId = 0) + public UninstallationSummary UninstallPackage(string packageName, int userId = 0) { - var summary = _packageInstallation.UninstallPackage(package, userId); - - SaveInstalledPackage(package); + //this is ordered by descending version + var allPackageVersions = GetInstalledPackageByName(packageName)?.ToList(); + if (allPackageVersions == null || allPackageVersions.Count == 0) + throw new InvalidOperationException("No installed package found by name " + packageName); - DeleteInstalledPackage(package.Id, userId); + var summary = new UninstallationSummary + { + MetaData = allPackageVersions[0] + }; + + var allSummaries = new List(); + + foreach (var packageVersion in allPackageVersions) + { + var versionUninstallSummary = _packageInstallation.UninstallPackage(packageVersion, userId); + + allSummaries.Add(versionUninstallSummary); + + //merge the summary + summary.ActionErrors = summary.ActionErrors.Concat(versionUninstallSummary.ActionErrors).Distinct().ToList(); + summary.Actions = summary.Actions.Concat(versionUninstallSummary.Actions).Distinct().ToList(); + summary.DataTypesUninstalled = summary.DataTypesUninstalled.Concat(versionUninstallSummary.DataTypesUninstalled).Distinct().ToList(); + summary.DictionaryItemsUninstalled = summary.DictionaryItemsUninstalled.Concat(versionUninstallSummary.DictionaryItemsUninstalled).Distinct().ToList(); + summary.DocumentTypesUninstalled = summary.DocumentTypesUninstalled.Concat(versionUninstallSummary.DocumentTypesUninstalled).Distinct().ToList(); + summary.FilesUninstalled = summary.FilesUninstalled.Concat(versionUninstallSummary.FilesUninstalled).Distinct().ToList(); + summary.LanguagesUninstalled = summary.LanguagesUninstalled.Concat(versionUninstallSummary.LanguagesUninstalled).Distinct().ToList(); + summary.MacrosUninstalled = summary.MacrosUninstalled.Concat(versionUninstallSummary.MacrosUninstalled).Distinct().ToList(); + summary.StylesheetsUninstalled = summary.StylesheetsUninstalled.Concat(versionUninstallSummary.StylesheetsUninstalled).Distinct().ToList(); + summary.TemplatesUninstalled = summary.TemplatesUninstalled.Concat(versionUninstallSummary.TemplatesUninstalled).Distinct().ToList(); + + SaveInstalledPackage(packageVersion); + DeleteInstalledPackage(packageVersion.Id, userId); + } // trigger the UninstalledPackage event - UninstalledPackage.RaiseEvent(new UninstallPackageEventArgs(summary, package, false), this); + UninstalledPackage.RaiseEvent(new UninstallPackageEventArgs(allSummaries, false), this); return summary; } @@ -181,9 +209,9 @@ namespace Umbraco.Core.Services.Implement public PackageDefinition GetInstalledPackageById(int id) => _installedPackages.GetById(id); - public PackageDefinition GetInstalledPackageByName(string name) + public IEnumerable GetInstalledPackageByName(string name) { - var found = _installedPackages.GetAll().FirstOrDefault(x => x.Name.InvariantEquals(name)); + var found = _installedPackages.GetAll().Where(x => x.Name.InvariantEquals(name)).OrderByDescending(x => SemVersion.Parse(x.Version)); return found; } @@ -192,7 +220,8 @@ namespace Umbraco.Core.Services.Implement if (packageName == null) throw new ArgumentNullException(nameof(packageName)); if (packageVersion == null) throw new ArgumentNullException(nameof(packageVersion)); - alreadyInstalled = GetInstalledPackageByName(packageName); + //get the latest version installed + alreadyInstalled = GetInstalledPackageByName(packageName)?.OrderByDescending(x => SemVersion.Parse(x.Version)).FirstOrDefault(); if (alreadyInstalled == null) return PackageInstallType.NewInstall; if (!SemVersion.TryParse(alreadyInstalled.Version, out var installedVersion)) @@ -205,62 +234,6 @@ namespace Umbraco.Core.Services.Implement return PackageInstallType.Upgrade; } - public PackageDefinition MergePackageDefinition(PackageDefinition original, PackageDefinition upgrade) - { - if (!SemVersion.TryParse(original.Version, out var originalVersion)) - throw new InvalidOperationException("Could not parse the original version"); - if(!SemVersion.TryParse(upgrade.Version, out var upgradeVersion)) - throw new InvalidOperationException("Could not parse the upgrade version"); - if (originalVersion >= upgradeVersion) - throw new InvalidOperationException("The upgrade version must be higher than the original version"); - if (!original.Name.InvariantEquals(upgrade.Name)) - throw new InvalidOperationException("Cannot merge the package definitions, the package name doesn't match"); - - var result = original.Clone(); - - result.PackagePath = upgrade.PackagePath; - result.PackageId = upgrade.PackageId; - - result.UmbracoVersion = upgrade.UmbracoVersion; - result.Version = upgrade.Version; - result.Url = upgrade.Url; - result.Readme = upgrade.Readme; - result.AuthorUrl = upgrade.AuthorUrl; - result.Author = upgrade.Author; - result.LicenseUrl = upgrade.LicenseUrl; - result.PackageId = upgrade.PackageId; - result.Control = upgrade.Control; - result.IconUrl = upgrade.IconUrl; - result.License = upgrade.License; - result.ContentNodeId = upgrade.ContentNodeId; - result.ContentLoadChildNodes = upgrade.ContentLoadChildNodes; - - result.Files = original.Files.Concat(upgrade.Files).Distinct(StringComparer.InvariantCultureIgnoreCase).ToList(); - result.DataTypes = original.DataTypes.Concat(upgrade.DataTypes).Distinct(StringComparer.InvariantCultureIgnoreCase).ToList(); - result.Templates = original.Templates.Concat(upgrade.Templates).Distinct(StringComparer.InvariantCultureIgnoreCase).ToList(); - result.Languages = original.Languages.Concat(upgrade.Languages).Distinct(StringComparer.InvariantCultureIgnoreCase).ToList(); - result.Macros = original.Macros.Concat(upgrade.Macros).Distinct(StringComparer.InvariantCultureIgnoreCase).ToList(); - result.Stylesheets = original.Stylesheets.Concat(upgrade.Stylesheets).Distinct(StringComparer.InvariantCultureIgnoreCase).ToList(); - result.DocumentTypes = original.DocumentTypes.Concat(upgrade.DocumentTypes).Distinct(StringComparer.InvariantCultureIgnoreCase).ToList(); - result.DictionaryItems = original.DictionaryItems.Concat(upgrade.DictionaryItems).Distinct(StringComparer.InvariantCultureIgnoreCase).ToList(); - - var originalActions = CompiledPackageXmlParser.GetPackageActions(XElement.Parse(result.Actions), result.Name); - var upgradeActions = CompiledPackageXmlParser.GetPackageActions(XElement.Parse(upgrade.Actions), result.Name).ToList(); - var upgradeActionsLookup = upgradeActions.ToLookup(x => x.Alias, x => x.XmlData.ToString()); - - foreach (var originalAction in originalActions) - { - var upgradeActionsWithAlias = upgradeActionsLookup[originalAction.Alias]; - - //check if the original action does not exist already in our upgrade actions - if(upgradeActionsWithAlias.All(upgradeActionXml => upgradeActionXml != originalAction.XmlData.ToString())) - upgradeActions.Add(originalAction); - } - result.Actions = new XElement("actions", upgradeActions.Select(x => x.XmlData)).ToString(); - - return result; - } - public bool SaveInstalledPackage(PackageDefinition definition) => _installedPackages.SavePackage(definition); public void DeleteInstalledPackage(int packageId, int userId = 0) @@ -289,10 +262,10 @@ namespace Umbraco.Core.Services.Implement /// /// Occurs after a package is uninstalled /// - public static event TypedEventHandler> UninstalledPackage; + public static event TypedEventHandler UninstalledPackage; #endregion - + } } diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html index a66eb94746..8ed1a43e8a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html @@ -133,7 +133,7 @@ -
+