Package installing is working

This commit is contained in:
Shannon
2019-01-15 22:08:08 +11:00
parent b3585b0083
commit 44ab0991ce
52 changed files with 669 additions and 795 deletions

View File

@@ -22,7 +22,7 @@ namespace Umbraco.Core.Packaging
_conflictingPackageData = conflictingPackageData;
}
public CompiledPackage ToCompiledPackage(XDocument xml, string packageFileName, string applicationRootFolder)
public CompiledPackage ToCompiledPackage(XDocument xml, FileInfo packageFile, string applicationRootFolder)
{
if (xml == null) throw new ArgumentNullException(nameof(xml));
if (xml.Root == null) throw new ArgumentException(nameof(xml), "The xml document is invalid");
@@ -39,7 +39,7 @@ namespace Umbraco.Core.Packaging
var def = new CompiledPackage
{
PackageFileName = packageFileName,
PackageFile = packageFile,
Name = package.Element("name")?.Value,
Author = author.Element("name")?.Value,
AuthorUrl = author.Element("website")?.Value,

View File

@@ -1,4 +1,5 @@
using System.Xml.Linq;
using System.Collections.Generic;
using System.Xml.Linq;
namespace Umbraco.Core.Packaging
{
@@ -10,7 +11,8 @@ namespace Umbraco.Core.Packaging
/// <param name="packageName">Name of the package.</param>
/// <param name="actionAlias">The action alias.</param>
/// <param name="actionXml">The action XML.</param>
void RunPackageAction(string packageName, string actionAlias, XElement actionXml);
/// <param name="errors"></param>
bool RunPackageAction(string packageName, string actionAlias, XElement actionXml, out IEnumerable<string> errors);
/// <summary>
/// Undos the package action with the specified action alias.
@@ -18,6 +20,7 @@ namespace Umbraco.Core.Packaging
/// <param name="packageName">Name of the package.</param>
/// <param name="actionAlias">The action alias.</param>
/// <param name="actionXml">The action XML.</param>
void UndoPackageAction(string packageName, string actionAlias, XElement actionXml);
/// <param name="errors"></param>
bool UndoPackageAction(string packageName, string actionAlias, XElement actionXml, out IEnumerable<string> errors);
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.IO;
using System.Xml.Linq;
using Umbraco.Core.Models.Packaging;
@@ -6,6 +7,14 @@ namespace Umbraco.Core.Packaging
{
public interface IPackageInstallation
{
/// <summary>
/// Uninstalls a package including all data, entities and files
/// </summary>
/// <param name="packageDefinition"></param>
/// <param name="userId"></param>
/// <returns></returns>
UninstallationSummary UninstallPackage(PackageDefinition packageDefinition, int userId);
/// <summary>
/// Installs a packages data and entities
/// </summary>
@@ -27,8 +36,8 @@ namespace Umbraco.Core.Packaging
/// <summary>
/// Reads the package (zip) file and returns the <see cref="CompiledPackage"/> model
/// </summary>
/// <param name="packageFileName"></param>
/// <param name="packageFile"></param>
/// <returns></returns>
CompiledPackage ReadPackage(string packageFileName);
CompiledPackage ReadPackage(FileInfo packageFile);
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using Umbraco.Core.Logging;
using Umbraco.Core._Legacy.PackageActions;
@@ -19,15 +20,10 @@ namespace Umbraco.Core.Packaging
_packageActions = packageActions;
}
/// <summary>
/// Runs the package action with the specified action alias.
/// </summary>
/// <param name="packageName">Name of the package.</param>
/// <param name="actionAlias">The action alias.</param>
/// <param name="actionXml">The action XML.</param>
public void RunPackageAction(string packageName, string actionAlias, XElement actionXml)
/// <inheritdoc />
public bool RunPackageAction(string packageName, string actionAlias, XElement actionXml, out IEnumerable<string> errors)
{
var e = new List<string>();
foreach (var ipa in _packageActions)
{
try
@@ -37,19 +33,19 @@ namespace Umbraco.Core.Packaging
}
catch (Exception ex)
{
e.Add($"{ipa.Alias()} - {ex.Message}");
_logger.Error<PackageActionRunner>(ex, "Error loading package action '{PackageActionAlias}' for package {PackageName}", ipa.Alias(), packageName);
}
}
errors = e;
return e.Count == 0;
}
/// <summary>
/// Undos the package action with the specified action alias.
/// </summary>
/// <param name="packageName">Name of the package.</param>
/// <param name="actionAlias">The action alias.</param>
/// <param name="actionXml">The action XML.</param>
public void UndoPackageAction(string packageName, string actionAlias, XElement actionXml)
/// <inheritdoc />
public bool UndoPackageAction(string packageName, string actionAlias, XElement actionXml, out IEnumerable<string> errors)
{
var e = new List<string>();
foreach (var ipa in _packageActions)
{
try
@@ -59,9 +55,12 @@ namespace Umbraco.Core.Packaging
}
catch (Exception ex)
{
e.Add($"{ipa.Alias()} - {ex.Message}");
_logger.Error<PackageActionRunner>(ex, "Error undoing package action '{PackageActionAlias}' for package {PackageName}", ipa.Alias(), packageName);
}
}
errors = e;
return e.Count == 0;
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web;
@@ -10,6 +11,7 @@ using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Entities;
using Umbraco.Core.Models.Packaging;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Implement;
@@ -43,6 +45,128 @@ namespace Umbraco.Core.Packaging
_contentService = contentService;
}
#region Uninstall
public UninstallationSummary UninstallPackageData(PackageDefinition package, int userId)
{
if (package == null) throw new ArgumentNullException(nameof(package));
var removedTemplates = new List<ITemplate>();
var removedMacros = new List<IMacro>();
var removedContentTypes = new List<IContentType>();
var removedDictionaryItems = new List<IDictionaryItem>();
var removedDataTypes = new List<IDataType>();
var removedLanguages = new List<ILanguage>();
//Uninstall templates
foreach (var item in package.Templates.ToArray())
{
if (int.TryParse(item, out var nId) == false) continue;
var found = _fileService.GetTemplate(nId);
if (found != null)
{
removedTemplates.Add(found);
_fileService.DeleteTemplate(found.Alias, userId);
}
package.Templates.Remove(nId.ToString());
}
//Uninstall macros
foreach (var item in package.Macros.ToArray())
{
if (int.TryParse(item, out var nId) == false) continue;
var macro = _macroService.GetById(nId);
if (macro != null)
{
removedMacros.Add(macro);
_macroService.Delete(macro, userId);
}
package.Macros.Remove(nId.ToString());
}
//Remove Document Types
var contentTypes = new List<IContentType>();
var contentTypeService = _contentTypeService;
foreach (var item in package.DocumentTypes.ToArray())
{
if (int.TryParse(item, out var nId) == false) continue;
var contentType = contentTypeService.Get(nId);
if (contentType == null) continue;
contentTypes.Add(contentType);
package.DocumentTypes.Remove(nId.ToString(CultureInfo.InvariantCulture));
}
//Order the DocumentTypes before removing them
if (contentTypes.Any())
{
//TODO: I don't think this ordering is necessary
var orderedTypes = (from contentType in contentTypes
orderby contentType.ParentId descending, contentType.Id descending
select contentType).ToList();
removedContentTypes.AddRange(orderedTypes);
contentTypeService.Delete(orderedTypes, userId);
}
//Remove Dictionary items
foreach (var item in package.DictionaryItems.ToArray())
{
if (int.TryParse(item, out var nId) == false) continue;
var di = _localizationService.GetDictionaryItemById(nId);
if (di != null)
{
removedDictionaryItems.Add(di);
_localizationService.Delete(di, userId);
}
package.DictionaryItems.Remove(nId.ToString());
}
//Remove Data types
foreach (var item in package.DataTypes.ToArray())
{
if (int.TryParse(item, out var nId) == false) continue;
var dtd = _dataTypeService.GetDataType(nId);
if (dtd != null)
{
removedDataTypes.Add(dtd);
_dataTypeService.Delete(dtd, userId);
}
package.DataTypes.Remove(nId.ToString());
}
//Remove Langs
foreach (var item in package.Languages.ToArray())
{
if (int.TryParse(item, out var nId) == false) continue;
var lang = _localizationService.GetLanguageById(nId);
if (lang != null)
{
removedLanguages.Add(lang);
_localizationService.Delete(lang, userId);
}
package.Languages.Remove(nId.ToString());
}
// create a summary of what was actually removed, for PackagingService.UninstalledPackage
var summary = new UninstallationSummary
{
MetaData = package,
TemplatesUninstalled = removedTemplates,
MacrosUninstalled = removedMacros,
DocumentTypesUninstalled = removedContentTypes,
DictionaryItemsUninstalled = removedDictionaryItems,
DataTypesUninstalled = removedDataTypes,
LanguagesUninstalled = removedLanguages,
};
return summary;
}
#endregion
#region Content
@@ -201,7 +325,7 @@ namespace Umbraco.Core.Packaging
#endregion
#region ContentTypes
#region DocumentTypes
public IEnumerable<IContentType> ImportDocumentType(XElement docTypeElement, int userId)
{
@@ -576,6 +700,7 @@ namespace Umbraco.Core.Packaging
// This means that the property will not be created.
if (dataTypeDefinition == null)
{
//TODO: We should expose this to the UI during install!
_logger.Warn<PackagingService>("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());

View File

@@ -91,7 +91,7 @@ namespace Umbraco.Core.Packaging
new XElement("datatypes", string.Join(",", def.DataTypes ?? Array.Empty<string>())),
new XElement("content",
new XAttribute("nodeId", def.ContentNodeId),
new XAttribute("nodeId", def.ContentNodeId ?? string.Empty),
new XAttribute("loadChildNodes", def.ContentLoadChildNodes)),
new XElement("templates", string.Join(",", def.Templates ?? Array.Empty<string>())),

View File

@@ -53,6 +53,32 @@ namespace Umbraco.Core.Packaging
}
}
public IEnumerable<string> UninstallFiles(PackageDefinition package)
{
var removedFiles = new List<string>();
foreach (var item in package.Files.ToArray())
{
removedFiles.Add(item.GetRelativePath());
//here we need to try to find the file in question as most packages does not support the tilde char
var file = IOHelper.FindFile(item);
if (file != null)
{
//TODO: Surely this should be ~/ ?
file = file.EnsureStartsWith("/");
var filePath = IOHelper.MapPath(file);
if (File.Exists(filePath))
File.Delete(filePath);
}
package.Files.Remove(file);
}
return removedFiles;
}
private static IEnumerable<(string packageUniqueFile, string appAbsolutePath)> AppendRootToDestination(string applicationRootFolder, IEnumerable<(string packageUniqueFile, string appRelativePath)> sourceDestination)
{
return

View File

@@ -18,7 +18,6 @@ namespace Umbraco.Core.Packaging
private readonly PackageFileInstallation _packageFileInstallation;
private readonly CompiledPackageXmlParser _parser;
private readonly IPackageActionRunner _packageActionRunner;
private readonly string _packagesFolderPath;
private readonly DirectoryInfo _packageExtractionFolder;
private readonly DirectoryInfo _applicationRootFolder;
@@ -29,9 +28,6 @@ namespace Umbraco.Core.Packaging
/// <param name="packageFileInstallation"></param>
/// <param name="parser"></param>
/// <param name="packageActionRunner"></param>
/// <param name="packagesFolderPath">
/// The relative path of the package storage folder (i.e. ~/App_Data/Packages )
/// </param>
/// <param name="applicationRootFolder">
/// The root folder of the application
/// </param>
@@ -39,27 +35,25 @@ namespace Umbraco.Core.Packaging
/// The destination root folder to extract the package files (generally the same as applicationRoot) but can be modified for testing
/// </param>
public PackageInstallation(PackageDataInstallation packageDataInstallation, PackageFileInstallation packageFileInstallation, CompiledPackageXmlParser parser, IPackageActionRunner packageActionRunner,
string packagesFolderPath, DirectoryInfo applicationRootFolder, DirectoryInfo packageExtractionFolder)
DirectoryInfo applicationRootFolder, DirectoryInfo packageExtractionFolder)
{
_packageExtraction = new PackageExtraction();
_packageFileInstallation = packageFileInstallation ?? throw new ArgumentNullException(nameof(packageFileInstallation));
_packageDataInstallation = packageDataInstallation ?? throw new ArgumentNullException(nameof(packageDataInstallation));
_parser = parser;
_packageActionRunner = packageActionRunner;
_packagesFolderPath = packagesFolderPath;
_applicationRootFolder = applicationRootFolder;
_packageExtractionFolder = packageExtractionFolder;
_parser = parser ?? throw new ArgumentNullException(nameof(parser));
_packageActionRunner = packageActionRunner ?? throw new ArgumentNullException(nameof(packageActionRunner));
_applicationRootFolder = applicationRootFolder ?? throw new ArgumentNullException(nameof(applicationRootFolder));
_packageExtractionFolder = packageExtractionFolder ?? throw new ArgumentNullException(nameof(packageExtractionFolder));
}
public CompiledPackage ReadPackage(string packageFileName)
public CompiledPackage ReadPackage(FileInfo packageFile)
{
if (packageFileName == null) throw new ArgumentNullException(nameof(packageFileName));
var packageZipFile = GetPackageZipFile(packageFileName);
var doc = GetConfigXmlDoc(packageZipFile);
if (packageFile == null) throw new ArgumentNullException(nameof(packageFile));
var doc = GetConfigXmlDoc(packageFile);
var compiledPackage = _parser.ToCompiledPackage(doc, Path.GetFileName(packageZipFile.FullName), _applicationRootFolder.FullName);
var compiledPackage = _parser.ToCompiledPackage(doc, packageFile, _applicationRootFolder.FullName);
ValidatePackageFile(packageZipFile, compiledPackage);
ValidatePackageFile(packageFile, compiledPackage);
return compiledPackage;
}
@@ -73,15 +67,28 @@ namespace Umbraco.Core.Packaging
if (packageDefinition.Name != compiledPackage.Name)
throw new InvalidOperationException("The package definition does not match the compiled package manifest");
var packageZipFile = GetPackageZipFile(compiledPackage.PackageFileName);
var packageZipFile = compiledPackage.PackageFile;
return _packageFileInstallation.InstallFiles(compiledPackage, packageZipFile, _packageExtractionFolder.FullName);
}
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);
//run actions before files are removed
summary.ActionErrors = UndoPackageActions(package, summary.Actions).ToList();
var filesRemoved = _packageFileInstallation.UninstallFiles(package);
summary.FilesUninstalled = filesRemoved;
return summary;
}
public InstallationSummary InstallPackageData(PackageDefinition packageDefinition, CompiledPackage compiledPackage, int userId)
{
//TODO: Update the PackageDefinition!
var installationSummary = new InstallationSummary
{
DataTypesInstalled = _packageDataInstallation.ImportDataTypes(compiledPackage.DataTypes.ToList(), userId),
@@ -101,10 +108,8 @@ namespace Umbraco.Core.Packaging
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());
@@ -115,15 +120,17 @@ namespace Umbraco.Core.Packaging
var contentInstalled = installationSummary.ContentInstalled.ToList();
packageDefinition.ContentNodeId = contentInstalled.Count > 0 ? contentInstalled[0].Id.ToInvariantString() : null;
RunPackageActions(packageDefinition, installationSummary.Actions);
//run package actions
installationSummary.ActionErrors = RunPackageActions(packageDefinition, installationSummary.Actions).ToList();
return installationSummary;
}
private void RunPackageActions(PackageDefinition packageDefinition, IEnumerable<PackageAction> actions)
private IEnumerable<string> RunPackageActions(PackageDefinition packageDefinition, IEnumerable<PackageAction> actions)
{
foreach (var n in actions)
{
//if there is an undo section then save it to the definition so we can run it at uninstallation
var undo = n.Undo;
if (undo)
packageDefinition.Actions += n.XmlData.ToString();
@@ -131,12 +138,28 @@ namespace Umbraco.Core.Packaging
//Run the actions tagged only for 'install'
if (n.RunAt != ActionRunAt.Install) continue;
if (n.Alias.IsNullOrWhiteSpace() == false)
_packageActionRunner.RunPackageAction(packageDefinition.Name, n.Alias, n.XmlData);
if (n.Alias.IsNullOrWhiteSpace()) continue;
//run the actions and report errors
if (!_packageActionRunner.RunPackageAction(packageDefinition.Name, n.Alias, n.XmlData, out var err))
foreach (var e in err) yield return e;
}
}
private FileInfo GetPackageZipFile(string packageFileName) => new FileInfo(IOHelper.MapPath(_packagesFolderPath).EnsureEndsWith('\\') + packageFileName);
private IEnumerable<string> UndoPackageActions(IPackageInfo packageDefinition, IEnumerable<PackageAction> actions)
{
foreach (var n in actions)
{
//Run the actions tagged only for 'uninstall'
if (n.RunAt != ActionRunAt.Uninstall) continue;
if (n.Alias.IsNullOrWhiteSpace()) continue;
//run the actions and report errors
if (!_packageActionRunner.UndoPackageAction(packageDefinition.Name, n.Alias, n.XmlData, out var err))
foreach (var e in err) yield return e;
}
}
private XDocument GetConfigXmlDoc(FileInfo packageFile)
{

View File

@@ -69,7 +69,7 @@ namespace Umbraco.Core.Packaging
_logger = logger;
_packageRepositoryFileName = packageRepositoryFileName;
_tempFolderPath = tempFolderPath ?? SystemDirectories.Data + "/TEMP/PackageFiles";
_tempFolderPath = tempFolderPath ?? SystemDirectories.TempData.EnsureEndsWith('/') + "PackageFiles";
_packagesFolderPath = packagesFolderPath ?? SystemDirectories.Packages;
_mediaFolderPath = mediaFolderPath ?? SystemDirectories.Media + "/created-packages";