Files
Umbraco-CMS/src/Umbraco.Core/Services/Implement/PackagingService.cs

261 lines
12 KiB
C#
Raw Normal View History

2017-12-28 09:18:09 +01:00
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Semver;
using Umbraco.Core.Composing;
2017-12-28 09:18:09 +01:00
using Umbraco.Core.Events;
using Umbraco.Core.Exceptions;
using Umbraco.Core.IO;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Packaging;
using Umbraco.Core.Packaging;
namespace Umbraco.Core.Services.Implement
{
/// <summary>
/// Represents the Packaging Service, which provides import/export functionality for the Core models of the API
/// using xml representation. This is primarily used by the Package functionality.
/// </summary>
public class PackagingService : IPackagingService
{
private readonly IPackageInstallation _packageInstallation;
private readonly IAuditService _auditService;
private readonly ICreatedPackagesRepository _createdPackages;
private readonly IInstalledPackagesRepository _installedPackages;
Merge remote-tracking branch 'origin/dev-v7' into temp8 # Conflicts: # build/Modules/Umbraco.Build/Get-UmbracoBuildEnv.ps1 # build/NuSpecs/UmbracoCms.Core.nuspec # build/NuSpecs/UmbracoCms.nuspec # build/NuSpecs/tools/Readme.txt # src/Umbraco.Core/Configuration/UmbracoConfig.cs # src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs # src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs # src/Umbraco.Core/Constants-Conventions.cs # src/Umbraco.Core/Constants-System.cs # src/Umbraco.Core/IO/MediaFileSystem.cs # src/Umbraco.Core/Media/Exif/ImageFile.cs # src/Umbraco.Core/Models/Property.cs # src/Umbraco.Core/Models/PropertyTagBehavior.cs # src/Umbraco.Core/Models/PropertyTags.cs # src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenTwelveZero/SetDefaultTagsStorageType.cs # src/Umbraco.Core/Persistence/Repositories/AuditRepository.cs # src/Umbraco.Core/Persistence/Repositories/UserRepository.cs # src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs # src/Umbraco.Core/Security/AuthenticationExtensions.cs # src/Umbraco.Core/Security/BackOfficeCookieAuthenticationProvider.cs # src/Umbraco.Core/Services/Implement/PackagingService.cs # src/Umbraco.Core/Services/ServerRegistrationService.cs # src/Umbraco.Core/StringExtensions.cs # src/Umbraco.Core/packages.config # src/Umbraco.Tests/ApplicationUrlHelperTests.cs # src/Umbraco.Tests/Persistence/Repositories/AuditRepositoryTest.cs # src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs # src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs # src/Umbraco.Tests/packages.config # src/Umbraco.Web.UI.Client/package.json # src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js # src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagegravity.directive.js # src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtreeitem.directive.js # src/Umbraco.Web.UI.Client/src/common/resources/log.resource.js # src/Umbraco.Web.UI.Client/src/common/services/user.service.js # src/Umbraco.Web.UI.Client/src/less/belle.less # src/Umbraco.Web.UI.Client/src/less/components/card.less # src/Umbraco.Web.UI.Client/src/less/navs.less # src/Umbraco.Web.UI.Client/src/less/panel.less # src/Umbraco.Web.UI.Client/src/less/property-editors.less # src/Umbraco.Web.UI.Client/src/less/tree.less # src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.controller.js # src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.html # src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js # src/Umbraco.Web.UI.Client/src/views/common/overlays/iconpicker/iconpicker.controller.js # src/Umbraco.Web.UI.Client/src/views/common/overlays/iconpicker/iconpicker.html # src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.controller.js # src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.html # src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html # src/Umbraco.Web.UI.Client/src/views/components/notifications/umb-notifications.html # src/Umbraco.Web.UI.Client/src/views/components/umb-color-swatches.html # src/Umbraco.Web.UI.Client/src/views/components/umb-table.html # src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.html # src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js # src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.controller.js # src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html # src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js # src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html # src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js # src/Umbraco.Web.UI.Client/src/views/propertyeditors/textarea/textarea.html # src/Umbraco.Web.UI/Umbraco/config/lang/en.xml # src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml # src/Umbraco.Web.UI/config/umbracoSettings.Release.config # src/Umbraco.Web.UI/packages.config # src/Umbraco.Web.UI/web.Template.Debug.config # src/Umbraco.Web.UI/web.Template.config # src/Umbraco.Web/Editors/AuthenticationController.cs # src/Umbraco.Web/Editors/BackOfficeController.cs # src/Umbraco.Web/Editors/CanvasDesignerController.cs # src/Umbraco.Web/Editors/ContentController.cs # src/Umbraco.Web/Editors/DashboardController.cs # src/Umbraco.Web/Editors/LogController.cs # src/Umbraco.Web/Editors/MediaController.cs # src/Umbraco.Web/Install/InstallHelper.cs # src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs # src/Umbraco.Web/Media/EmbedProviders/AbstractOEmbedProvider.cs # src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs # src/Umbraco.Web/Models/Mapping/PreValueDisplayResolver.cs # src/Umbraco.Web/Mvc/MasterControllerFactory.cs # src/Umbraco.Web/PropertyEditors/FileUploadPropertyValueEditor.cs # src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs # src/Umbraco.Web/PropertyEditors/TextAreaPropertyEditor.cs # src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerPropertyConverter.cs # src/Umbraco.Web/PublishedCache/MemberPublishedContent.cs # src/Umbraco.Web/Routing/RedirectTrackingEventHandler.cs # src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs # src/Umbraco.Web/Scheduling/KeepAlive.cs # src/Umbraco.Web/Scheduling/LogScrubber.cs # src/Umbraco.Web/Scheduling/ScheduledPublishing.cs # src/Umbraco.Web/Scheduling/ScheduledTasks.cs # src/Umbraco.Web/Scheduling/Scheduler.cs # src/Umbraco.Web/Templates/TemplateUtilities.cs # src/Umbraco.Web/Trees/DataTypeTreeController.cs # src/Umbraco.Web/UmbracoModule.cs # src/Umbraco.Web/_Legacy/Packager/Installer.cs # src/Umbraco.Web/packages.config # src/Umbraco.Web/umbraco.presentation/keepAliveService.cs # src/Umbraco.Web/umbraco.presentation/umbraco/dashboard/FeedProxy.aspx.cs # src/umbraco.businesslogic/IO/IOHelper.cs # src/umbraco.cms/packages.config # src/umbraco.cms/umbraco.cms.csproj # src/umbraco.controls/packages.config # src/umbraco.controls/umbraco.controls.csproj # src/umbraco.editorControls/packages.config # src/umbraco.editorControls/umbraco.editorControls.csproj
2018-10-01 14:32:46 +02:00
private static HttpClient _httpClient;
2017-12-28 09:18:09 +01:00
public PackagingService(
IAuditService auditService,
ICreatedPackagesRepository createdPackages,
IInstalledPackagesRepository installedPackages,
IPackageInstallation packageInstallation)
{
_auditService = auditService;
_createdPackages = createdPackages;
_installedPackages = installedPackages;
_packageInstallation = packageInstallation;
2017-12-28 09:18:09 +01:00
}
#region Package Files
/// <inheritdoc />
2019-01-15 22:08:08 +11:00
public async Task<FileInfo> FetchPackageFileAsync(Guid packageId, Version umbracoVersion, int userId)
2017-12-28 09:18:09 +01:00
{
//includeHidden = true because we don't care if it's hidden we want to get the file regardless
2019-11-05 13:45:42 +01:00
var url = $"{Constants.PackageRepository.RestApiBaseUrl}/{packageId}?version={umbracoVersion.ToString(3)}&includeHidden=true&asFile=true";
byte[] bytes;
try
2017-12-28 09:18:09 +01:00
{
if (_httpClient == null)
2017-12-28 09:18:09 +01:00
{
_httpClient = new HttpClient();
2017-12-28 09:18:09 +01:00
}
bytes = await _httpClient.GetByteArrayAsync(url);
}
catch (HttpRequestException ex)
{
2019-01-22 18:03:39 -05:00
throw new ConnectionException("An error occurring downloading the package from " + url, ex);
}
2017-12-28 09:18:09 +01:00
2019-01-22 18:03:39 -05:00
//successful
if (bytes.Length > 0)
{
var packagePath = Current.IOHelper.MapPath(SystemDirectories.Packages);
2017-12-28 09:18:09 +01:00
// Check for package directory
if (Directory.Exists(packagePath) == false)
Directory.CreateDirectory(packagePath);
2017-12-28 09:18:09 +01:00
var packageFilePath = Path.Combine(packagePath, packageId + ".umb");
2017-12-28 09:18:09 +01:00
using (var fs1 = new FileStream(packageFilePath, FileMode.Create))
{
fs1.Write(bytes, 0, bytes.Length);
2019-01-15 22:08:08 +11:00
return new FileInfo(packageFilePath);
2017-12-28 09:18:09 +01:00
}
}
2019-11-05 13:45:42 +01:00
_auditService.Add(AuditType.PackagerInstall, userId, -1, "Package", $"Package {packageId} fetched from {Constants.PackageRepository.DefaultRepositoryId}");
return null;
2017-12-28 09:18:09 +01:00
}
#endregion
#region Installation
2019-01-15 22:08:08 +11:00
public CompiledPackage GetCompiledPackageInfo(FileInfo packageFile) => _packageInstallation.ReadPackage(packageFile);
2019-11-05 13:45:42 +01:00
public IEnumerable<string> InstallCompiledPackageFiles(PackageDefinition packageDefinition, FileInfo packageFile, int userId = Constants.Security.SuperUserId)
2017-12-28 09:18:09 +01:00
{
if (packageDefinition == null) throw new ArgumentNullException(nameof(packageDefinition));
if (packageDefinition.Id == default) throw new ArgumentException("The package definition has not been persisted");
if (packageDefinition.Name == default) throw new ArgumentException("The package definition has incomplete information");
2017-12-28 09:18:09 +01:00
2019-01-15 22:08:08 +11:00
var compiledPackage = GetCompiledPackageInfo(packageFile);
if (compiledPackage == null) throw new InvalidOperationException("Could not read the package file " + packageFile);
2018-03-22 17:41:13 +01:00
2019-01-15 22:08:08 +11:00
var files = _packageInstallation.InstallPackageFiles(packageDefinition, compiledPackage, userId).ToList();
SaveInstalledPackage(packageDefinition);
2017-12-28 09:18:09 +01:00
_auditService.Add(AuditType.PackagerInstall, userId, -1, "Package", $"Package files installed for package '{compiledPackage.Name}'.");
2017-12-28 09:18:09 +01:00
return files;
2017-12-28 09:18:09 +01:00
}
2019-11-05 13:45:42 +01:00
public InstallationSummary InstallCompiledPackageData(PackageDefinition packageDefinition, FileInfo packageFile, int userId = Constants.Security.SuperUserId)
2017-12-28 09:18:09 +01:00
{
if (packageDefinition == null) throw new ArgumentNullException(nameof(packageDefinition));
if (packageDefinition.Id == default) throw new ArgumentException("The package definition has not been persisted");
if (packageDefinition.Name == default) throw new ArgumentException("The package definition has incomplete information");
2017-12-28 09:18:09 +01:00
2019-01-15 22:08:08 +11:00
var compiledPackage = GetCompiledPackageInfo(packageFile);
if (compiledPackage == null) throw new InvalidOperationException("Could not read the package file " + packageFile);
2019-01-15 22:08:08 +11:00
if (ImportingPackage.IsRaisedEventCancelled(new ImportPackageEventArgs<string>(packageFile.Name, compiledPackage), this))
return new InstallationSummary { MetaData = compiledPackage };
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<InstallationSummary>(summary, compiledPackage, false), this);
return summary;
2017-12-28 09:18:09 +01:00
}
2019-11-05 13:45:42 +01:00
public UninstallationSummary UninstallPackage(string packageName, int userId = Constants.Security.SuperUserId)
2019-01-15 22:08:08 +11:00
{
//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);
var summary = new UninstallationSummary
{
MetaData = allPackageVersions[0]
};
2019-01-15 22:08:08 +11:00
var allSummaries = new List<UninstallationSummary>();
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);
}
2019-01-15 22:08:08 +11:00
// trigger the UninstalledPackage event
UninstalledPackage.RaiseEvent(new UninstallPackageEventArgs(allSummaries, false), this);
2019-01-15 22:08:08 +11:00
return summary;
}
2017-12-28 09:18:09 +01:00
#endregion
#region Created/Installed Package Repositories
2019-11-05 13:45:42 +01:00
public void DeleteCreatedPackage(int id, int userId = Constants.Security.SuperUserId)
{
var package = GetCreatedPackageById(id);
if (package == null) return;
_auditService.Add(AuditType.PackagerUninstall, userId, -1, "Package", $"Created package '{package.Name}' deleted. Package id: {package.Id}");
_createdPackages.Delete(id);
}
public IEnumerable<PackageDefinition> GetAllCreatedPackages() => _createdPackages.GetAll();
public PackageDefinition GetCreatedPackageById(int id) => _createdPackages.GetById(id);
public bool SaveCreatedPackage(PackageDefinition definition) => _createdPackages.SavePackage(definition);
public string ExportCreatedPackage(PackageDefinition definition) => _createdPackages.ExportPackage(definition);
public IEnumerable<PackageDefinition> GetAllInstalledPackages() => _installedPackages.GetAll();
public PackageDefinition GetInstalledPackageById(int id) => _installedPackages.GetById(id);
public IEnumerable<PackageDefinition> GetInstalledPackageByName(string name)
{
var found = _installedPackages.GetAll().Where(x => x.Name.InvariantEquals(name)).OrderByDescending(x => SemVersion.Parse(x.Version));
return found;
}
public PackageInstallType GetPackageInstallType(string packageName, SemVersion packageVersion, out PackageDefinition alreadyInstalled)
{
if (packageName == null) throw new ArgumentNullException(nameof(packageName));
if (packageVersion == null) throw new ArgumentNullException(nameof(packageVersion));
2019-11-05 12:54:22 +01:00
//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))
throw new InvalidOperationException("Could not parse the currently installed package version " + alreadyInstalled.Version);
//compare versions
if (installedVersion >= packageVersion) return PackageInstallType.AlreadyInstalled;
//it's an upgrade
return PackageInstallType.Upgrade;
}
public bool SaveInstalledPackage(PackageDefinition definition) => _installedPackages.SavePackage(definition);
2019-11-05 13:45:42 +01:00
public void DeleteInstalledPackage(int packageId, int userId = Constants.Security.SuperUserId)
{
var package = GetInstalledPackageById(packageId);
if (package == null) return;
_auditService.Add(AuditType.PackagerUninstall, userId, -1, "Package", $"Installed package '{package.Name}' deleted. Package id: {package.Id}");
_installedPackages.Delete(packageId);
}
2017-12-28 09:18:09 +01:00
#endregion
#region Event Handlers
/// <summary>
/// Occurs before Importing umbraco package
/// </summary>
2019-01-15 22:08:08 +11:00
public static event TypedEventHandler<IPackagingService, ImportPackageEventArgs<string>> ImportingPackage;
2017-12-28 09:18:09 +01:00
/// <summary>
/// Occurs after a package is imported
/// </summary>
public static event TypedEventHandler<IPackagingService, ImportPackageEventArgs<InstallationSummary>> ImportedPackage;
/// <summary>
/// Occurs after a package is uninstalled
/// </summary>
public static event TypedEventHandler<IPackagingService, UninstallPackageEventArgs> UninstalledPackage;
2017-12-28 09:18:09 +01:00
#endregion
2017-12-28 09:18:09 +01:00
}
}