Files
Umbraco-CMS/src/Umbraco.Web/Editors/PackageInstallController.cs

394 lines
16 KiB
C#
Raw Normal View History

2014-08-18 20:42:56 +02:00
using System;
using System.IO;
using System.Linq;
using System.Net;
2014-08-18 20:42:56 +02:00
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using Semver;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Web.Composing;
using Umbraco.Core.Configuration;
2016-06-13 19:43:04 +02:00
using Umbraco.Core.Logging;
using Umbraco.Core.Mapping;
2019-01-15 22:08:08 +11:00
using Umbraco.Core.Models.Editors;
using Umbraco.Core.Models.Packaging;
using Umbraco.Core.Packaging;
using Umbraco.Core.Persistence;
2014-08-18 20:42:56 +02:00
using Umbraco.Core.Services;
using Umbraco.Core.Strings;
using Umbraco.Web.JavaScript;
2014-08-18 20:42:56 +02:00
using Umbraco.Web.Models;
using Umbraco.Web.Models.ContentEditing;
2014-08-18 20:42:56 +02:00
using Umbraco.Web.Mvc;
using Umbraco.Web.WebApi;
2014-08-18 20:42:56 +02:00
using Umbraco.Web.WebApi.Filters;
2016-06-13 19:43:04 +02:00
using File = System.IO.File;
2014-08-18 20:42:56 +02:00
namespace Umbraco.Web.Editors
{
/// <summary>
/// A controller used for installing packages and managing all of the data in the packages section in the back office
/// </summary>
2014-08-18 20:42:56 +02:00
[PluginController("UmbracoApi")]
[UmbracoApplicationAuthorize(Core.Constants.Applications.Packages)]
2014-08-18 20:42:56 +02:00
public class PackageInstallController : UmbracoAuthorizedJsonController
{
2019-11-19 23:15:11 +00:00
private readonly IUmbracoVersion _umbracoVersion;
public PackageInstallController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor,
2019-01-18 15:05:20 +01:00
ISqlContext sqlContext, ServiceContext services, AppCaches appCaches,
IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, IShortStringHelper shortStringHelper, IUmbracoVersion umbracoVersion, UmbracoMapper umbracoMapper)
: base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, shortStringHelper, umbracoMapper)
{
2019-11-19 23:15:11 +00:00
_umbracoVersion = umbracoVersion;
}
/// <summary>
/// This checks if this package & version is already installed
/// </summary>
/// <param name="name"></param>
/// <param name="version"></param>
/// <returns></returns>
[HttpPost]
public IHttpActionResult ValidateInstalled(string name, string version)
{
var installType = Services.PackagingService.GetPackageInstallType(name, SemVersion.Parse(version), out _);
if (installType == PackageInstallType.AlreadyInstalled)
return BadRequest();
return Ok();
}
2016-06-13 19:43:04 +02:00
[HttpPost]
public IHttpActionResult Uninstall(int packageId)
{
2017-05-30 10:50:09 +02:00
try
{
var package = Services.PackagingService.GetInstalledPackageById(packageId);
if (package == null) return NotFound();
2016-06-13 19:43:04 +02:00
var summary = Services.PackagingService.UninstallPackage(package.Name, Security.GetUserId().ResultOr(0));
2016-06-13 19:43:04 +02:00
2017-05-30 10:50:09 +02:00
//now get all other packages by this name since we'll uninstall all versions
foreach (var installed in Services.PackagingService.GetAllInstalledPackages()
.Where(x => x.Name == package.Name && x.Id != package.Id))
2017-05-30 10:50:09 +02:00
{
//remove from the xml
Services.PackagingService.DeleteInstalledPackage(installed.Id, Security.GetUserId().ResultOr(0));
2017-05-30 10:50:09 +02:00
}
}
catch (Exception ex)
{
Logger.Error<PackageInstallController>(ex, "Failed to uninstall.");
2017-05-30 10:50:09 +02:00
throw;
}
2016-06-13 19:43:04 +02:00
return Ok();
}
2016-06-14 14:12:10 +02:00
private void PopulateFromPackageData(LocalPackageInstallModel model)
{
var zipFile = new FileInfo(Path.Combine(Current.IOHelper.MapPath(Core.Constants.SystemDirectories.Packages), model.ZipFileName));
2019-01-15 22:08:08 +11:00
var ins = Services.PackagingService.GetCompiledPackageInfo(zipFile);
2016-06-14 14:12:10 +02:00
model.Name = ins.Name;
model.Author = ins.Author;
model.AuthorUrl = ins.AuthorUrl;
2019-02-14 16:00:58 +01:00
model.Contributors = ins.Contributors;
model.IconUrl = ins.IconUrl;
2016-06-14 14:12:10 +02:00
model.License = ins.License;
model.LicenseUrl = ins.LicenseUrl;
model.Readme = ins.Readme;
model.ConflictingMacroAliases = ins.Warnings.ConflictingMacros.ToDictionary(x => x.Name, x => x.Alias);
model.ConflictingStyleSheetNames = ins.Warnings.ConflictingStylesheets.ToDictionary(x => x.Name, x => x.Alias); ;
model.ConflictingTemplateAliases = ins.Warnings.ConflictingTemplates.ToDictionary(x => x.Name, x => x.Alias); ;
model.ContainsUnsecureFiles = ins.Warnings.UnsecureFiles.Any();
2016-06-14 14:12:10 +02:00
model.Url = ins.Url;
model.Version = ins.Version;
model.UmbracoVersion = ins.UmbracoVersionRequirementsType == RequirementsType.Strict
? ins.UmbracoVersion.ToString(3)
2016-06-14 14:12:10 +02:00
: string.Empty;
2016-07-20 12:44:15 +02:00
2016-06-14 14:12:10 +02:00
//now we need to check for version comparison
model.IsCompatible = true;
if (ins.UmbracoVersionRequirementsType == RequirementsType.Strict)
2016-06-14 14:12:10 +02:00
{
var packageMinVersion = ins.UmbracoVersion;
2019-11-19 23:15:11 +00:00
if (_umbracoVersion.Current < packageMinVersion)
2016-06-14 14:12:10 +02:00
{
model.IsCompatible = false;
}
}
}
[HttpPost]
[FileUploadCleanupFilter(false)]
public async Task<LocalPackageInstallModel> UploadLocalPackage()
{
if (Request.Content.IsMimeMultipartContent() == false)
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
var root = Current.IOHelper.MapPath(Core.Constants.SystemDirectories.TempFileUploads);
//ensure it exists
Directory.CreateDirectory(root);
var provider = new MultipartFormDataStreamProvider(root);
var result = await Request.Content.ReadAsMultipartAsync(provider);
//must have a file
if (result.FileData.Count == 0)
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
var model = new LocalPackageInstallModel
{
2019-01-15 22:08:08 +11:00
//Generate a new package Id for this, we'll use this later for tracking, when persisting, saving the file, etc...
PackageGuid = Guid.NewGuid()
};
//get the files
foreach (var file in result.FileData)
{
2019-01-15 22:08:08 +11:00
var fileName = file.Headers.ContentDisposition.FileName.Trim('\"');
var ext = fileName.Substring(fileName.LastIndexOf('.') + 1).ToLower();
if (ext.InvariantEquals("zip") || ext.InvariantEquals("umb"))
{
2019-01-15 22:08:08 +11:00
//we always save package files to /App_Data/packages/package-guid.umb for processing as a standard so lets copy.
var packagesFolder = Current.IOHelper.MapPath(Core.Constants.SystemDirectories.Packages);
2019-01-15 22:08:08 +11:00
Directory.CreateDirectory(packagesFolder);
var packageFile = Path.Combine(packagesFolder, model.PackageGuid + ".umb");
File.Copy(file.LocalFileName, packageFile);
2016-07-20 12:44:15 +02:00
2019-01-15 22:08:08 +11:00
model.ZipFileName = Path.GetFileName(packageFile);
2014-08-18 20:42:56 +02:00
2019-01-15 22:08:08 +11:00
//add to the outgoing model so that all temp files are cleaned up
model.UploadedFiles.Add(new ContentPropertyFile
{
TempFilePath = file.LocalFileName
});
2016-06-14 14:12:10 +02:00
//Populate the model from the metadata in the package file (zip file)
PopulateFromPackageData(model);
2014-08-18 20:42:56 +02:00
var installType = Services.PackagingService.GetPackageInstallType(model.Name, SemVersion.Parse(model.Version), out var alreadyInstalled);
2016-07-20 12:44:15 +02:00
if (installType == PackageInstallType.AlreadyInstalled)
{
//this package is already installed
throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse(
2016-07-20 12:44:15 +02:00
Services.TextService.Localize("packager/packageAlreadyInstalled")));
}
2016-07-20 12:44:15 +02:00
model.OriginalVersion = installType == PackageInstallType.Upgrade ? alreadyInstalled.Version : null;
}
else
{
2020-01-20 11:37:19 +01:00
model.Notifications.Add(new BackOfficeNotification(
Services.TextService.Localize("speechBubbles/operationFailedHeader"),
Services.TextService.Localize("media/disallowedFileType"),
NotificationStyle.Warning));
}
2016-07-20 12:44:15 +02:00
}
return model;
}
/// <summary>
/// Gets the package from Our to install
/// </summary>
/// <param name="packageGuid"></param>
/// <returns></returns>
2014-08-18 20:42:56 +02:00
[HttpGet]
public async Task<LocalPackageInstallModel> Fetch(string packageGuid)
2014-08-18 20:42:56 +02:00
{
//Default path
2019-01-15 22:08:08 +11:00
string fileName = packageGuid + ".umb";
if (File.Exists(Path.Combine(Current.IOHelper.MapPath(Core.Constants.SystemDirectories.Packages), fileName)) == false)
2014-08-18 20:42:56 +02:00
{
2019-01-15 22:08:08 +11:00
var packageFile = await Services.PackagingService.FetchPackageFileAsync(
Guid.Parse(packageGuid),
2019-11-19 23:15:11 +00:00
_umbracoVersion.Current,
2019-01-15 22:08:08 +11:00
Security.GetUserId().ResultOr(0));
fileName = packageFile.Name;
2014-08-18 20:42:56 +02:00
}
2016-06-14 14:12:10 +02:00
var model = new LocalPackageInstallModel
{
PackageGuid = Guid.Parse(packageGuid),
2019-01-15 22:08:08 +11:00
ZipFileName = fileName
2016-06-14 14:12:10 +02:00
};
//Populate the model from the metadata in the package file (zip file)
PopulateFromPackageData(model);
var installType = Services.PackagingService.GetPackageInstallType(model.Name, SemVersion.Parse(model.Version), out var alreadyInstalled);
if (installType == PackageInstallType.AlreadyInstalled)
{
throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse(
Services.TextService.Localize("packager/packageAlreadyInstalled")));
}
model.OriginalVersion = installType == PackageInstallType.Upgrade ? alreadyInstalled.Version : null;
2016-06-14 14:12:10 +02:00
return model;
2014-08-18 20:42:56 +02:00
}
/// <summary>
/// Extracts the package zip and gets the packages information
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
2014-08-18 20:42:56 +02:00
[HttpPost]
public PackageInstallModel Import(PackageInstallModel model)
{
var zipFile = new FileInfo(Path.Combine(Current.IOHelper.MapPath(Core.Constants.SystemDirectories.Packages), model.ZipFileName));
2019-01-15 22:08:08 +11:00
var packageInfo = Services.PackagingService.GetCompiledPackageInfo(zipFile);
2016-06-14 14:12:10 +02:00
//now we need to check for version comparison
if (packageInfo.UmbracoVersionRequirementsType == RequirementsType.Strict)
2016-06-14 14:12:10 +02:00
{
var packageMinVersion = packageInfo.UmbracoVersion;
2019-11-19 23:15:11 +00:00
if (_umbracoVersion.Current < packageMinVersion)
throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse(
Services.TextService.Localize("packager/targetVersionMismatch", new[] {packageMinVersion.ToString()})));
2016-06-14 14:12:10 +02:00
}
var installType = Services.PackagingService.GetPackageInstallType(packageInfo.Name, SemVersion.Parse(packageInfo.Version), out var alreadyInstalled);
var packageDefinition = PackageDefinition.FromCompiledPackage(packageInfo);
2019-01-15 22:08:08 +11:00
packageDefinition.PackagePath = zipFile.FullName;
packageDefinition.PackageId = model.PackageGuid; //We must re-map the original package GUID that was generated
switch (installType)
{
case PackageInstallType.AlreadyInstalled:
throw new InvalidOperationException("The package is already installed");
case PackageInstallType.NewInstall:
case PackageInstallType.Upgrade:
//save to the installedPackages.config, this will create a new entry with a new Id
if (!Services.PackagingService.SaveInstalledPackage(packageDefinition))
throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse("Could not save the package"));
model.Id = packageDefinition.Id;
break;
default:
throw new ArgumentOutOfRangeException();
}
2014-08-18 20:42:56 +02:00
return model;
}
/// <summary>
/// Installs the package files
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
2014-08-18 20:42:56 +02:00
[HttpPost]
public PackageInstallModel InstallFiles(PackageInstallModel model)
{
var definition = Services.PackagingService.GetInstalledPackageById(model.Id);
if (definition == null) throw new InvalidOperationException("Not package definition found with id " + model.Id);
2019-01-15 22:08:08 +11:00
var zipFile = new FileInfo(definition.PackagePath);
var installedFiles = Services.PackagingService.InstallCompiledPackageFiles(definition, zipFile, Security.GetUserId().ResultOr(0));
2017-09-12 16:22:16 +02:00
//set a restarting marker and reset the app pool
2019-01-16 11:37:20 +01:00
UmbracoApplication.Restart(Request.TryGetHttpContext().Result);
2017-09-23 10:08:18 +02:00
2017-09-12 16:22:16 +02:00
model.IsRestarting = true;
return model;
}
[HttpPost]
public PackageInstallModel CheckRestart(PackageInstallModel model)
{
if (model.IsRestarting == false) return model;
//check for the key, if it's not there we're are restarted
if (Request.TryGetHttpContext().Result.Application.AllKeys.Contains("AppPoolRestarting") == false)
{
//reset it
model.IsRestarting = false;
}
2014-08-18 20:42:56 +02:00
return model;
}
/// <summary>
/// Installs the packages data/business logic
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
2014-08-18 20:42:56 +02:00
[HttpPost]
public PackageInstallModel InstallData(PackageInstallModel model)
{
var definition = Services.PackagingService.GetInstalledPackageById(model.Id);
if (definition == null) throw new InvalidOperationException("Not package definition found with id " + model.Id);
2019-01-15 22:08:08 +11:00
var zipFile = new FileInfo(definition.PackagePath);
var installSummary = Services.PackagingService.InstallCompiledPackageData(definition, zipFile, Security.GetUserId().ResultOr(0));
2014-08-18 20:42:56 +02:00
return model;
}
/// <summary>
/// Cleans up the package installation
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
2014-08-18 20:42:56 +02:00
[HttpPost]
public PackageInstallResult CleanUp(PackageInstallModel model)
2014-08-18 20:42:56 +02:00
{
2019-01-15 22:08:08 +11:00
var definition = Services.PackagingService.GetInstalledPackageById(model.Id);
if (definition == null) throw new InvalidOperationException("Not package definition found with id " + model.Id);
var zipFile = new FileInfo(definition.PackagePath);
var packageInfo = Services.PackagingService.GetCompiledPackageInfo(zipFile);
zipFile.Delete();
//bump cdf to be safe
var clientDependencyConfig = new ClientDependencyConfiguration(Logger);
var clientDependencyUpdated = clientDependencyConfig.UpdateVersionNumber(
2019-11-19 23:15:11 +00:00
_umbracoVersion.SemanticVersion, DateTime.UtcNow, "yyyyMMdd");
2016-06-14 14:12:10 +02:00
var redirectUrl = "";
if (!packageInfo.PackageView.IsNullOrWhiteSpace())
2016-06-14 14:12:10 +02:00
{
redirectUrl = $"/packages/packages/options/{model.Id}";
2016-06-14 14:12:10 +02:00
}
return new PackageInstallResult
{
Id = model.Id,
2019-01-15 22:08:08 +11:00
ZipFileName = model.ZipFileName,
PackageGuid = model.PackageGuid,
2016-06-14 14:12:10 +02:00
PostInstallationPath = redirectUrl
};
2016-06-14 14:12:10 +02:00
2014-08-18 20:42:56 +02:00
}
}
}