From a86a5d3db21913a5d787d9b20fbd8cdf95fb1352 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 11 Jul 2016 14:34:02 +0200 Subject: [PATCH] U4-8636 New Packager - Do not allow a package to be installed multiple times adds better localization support for installing/uninstalling and validates the installation target --- .../src/common/resources/package.resource.js | 9 +++ .../dashboard/dashboard.tabs.controller.js | 12 +-- .../views/install-local.controller.js | 13 ++-- .../packager/views/installed.controller.js | 6 +- .../views/packager/views/repo.controller.js | 44 +++++++---- .../src/views/packager/views/repo.html | 12 ++- src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 7 ++ .../umbraco/config/lang/en_us.xml | 8 ++ .../Editors/PackageInstallController.cs | 75 ++++++++++++++----- .../developer/Packages/installer.aspx.cs | 3 +- 10 files changed, 137 insertions(+), 52 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/package.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/package.resource.js index bfb801e536..9dae2008e2 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/package.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/package.resource.js @@ -24,6 +24,15 @@ function packageResource($q, $http, umbDataFormatter, umbRequestHelper) { 'Failed to get installed packages'); }, + validateInstalled: function (name, version) { + return umbRequestHelper.resourcePromise( + $http.post( + umbRequestHelper.getApiUrl( + "packageInstallApiBaseUrl", + "ValidateInstalled", { name: name, version: version })), + 'Failed to validate package ' + name); + }, + deleteCreatedPackage: function (packageId) { return umbRequestHelper.resourcePromise( $http.post( diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js b/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js index 42ceb86964..53c0e0419d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js @@ -17,28 +17,28 @@ function startUpVideosDashboardController($scope, xmlhelper, $log, $http) { angular.module("umbraco").controller("Umbraco.Dashboard.StartupVideosController", startUpVideosDashboardController); -function FormsController($scope, $route, $cookieStore, packageResource) { +function FormsController($scope, $route, $cookieStore, packageResource, localizationService) { $scope.installForms = function(){ - $scope.state = "Installng package"; + $scope.state = localizationService.localize("packager_installStateDownloading"); packageResource .fetch("CD44CF39-3D71-4C19-B6EE-948E1FAF0525") .then(function(pack) { - $scope.state = "importing"; + $scope.state = localizationService.localize("packager_installStateImporting"); return packageResource.import(pack); }, $scope.error) .then(function(pack) { - $scope.state = "Installing"; + $scope.state = localizationService.localize("packager_installStateInstalling"); return packageResource.installFiles(pack); }, $scope.error) .then(function(pack) { - $scope.state = "Restarting, please wait..."; + $scope.state = localizationService.localize("packager_installStateRestarting"); return packageResource.installData(pack); }, $scope.error) .then(function(pack) { - $scope.state = "All done, your browser will now refresh"; + $scope.state = localizationService.localize("packager_installStateComplete"); return packageResource.cleanUp(pack); }, $scope.error) diff --git a/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.controller.js b/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.controller.js index 63a270386d..0f3455203d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function PackagesInstallLocalController($scope, $route, $location, Upload, umbRequestHelper, packageResource, localStorageService, $timeout, $window) { + function PackagesInstallLocalController($scope, $route, $location, Upload, umbRequestHelper, packageResource, localStorageService, $timeout, $window, localizationService) { var vm = this; vm.state = "upload"; @@ -9,7 +9,8 @@ vm.localPackage = {}; vm.installPackage = installPackage; vm.installState = { - status: "" + status: "", + progress:0 }; vm.zipFile = { uploadStatus: "idle", @@ -98,7 +99,7 @@ } function installPackage() { - vm.installState.status = "Importing"; + vm.installState.status = localizationService.localize("packager_installStateImporting"); vm.installState.progress = "0"; //TODO: If any of these fail, will they keep calling the next one? @@ -106,19 +107,19 @@ .import(vm.localPackage) .then(function(pack) { vm.installState.progress = "25"; - vm.installState.status = "Installing..."; + vm.installState.status = localizationService.localize("packager_installStateInstalling"); vm.installState.progress = "50"; return packageResource.installFiles(pack); }, installError) .then(function(pack) { - vm.installState.status = "Restarting, please wait..."; + vm.installState.status = localizationService.localize("packager_installStateRestarting"); vm.installState.progress = "75"; return packageResource.installData(pack); }, installError) .then(function(pack) { - vm.installState.status = "All done, your browser will now refresh, please wait..."; + vm.installState.status = localizationService.localize("packager_installStateComplete"); vm.installState.progress = "100"; return packageResource.cleanUp(pack); }, diff --git a/src/Umbraco.Web.UI.Client/src/views/packager/views/installed.controller.js b/src/Umbraco.Web.UI.Client/src/views/packager/views/installed.controller.js index a1bc40ece0..ce1d2cca0f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packager/views/installed.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packager/views/installed.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function PackagesInstalledController($scope, $route, $location, packageResource, $timeout, $window, localStorageService) { + function PackagesInstalledController($scope, $route, $location, packageResource, $timeout, $window, localStorageService, localizationService) { var vm = this; @@ -28,14 +28,14 @@ } function uninstallPackage(installedPackage) { - vm.installState.status = "Uninstalling package..."; + vm.installState.status = localizationService.localize("packager_installStateUninstalling"); vm.installState.progress = "0"; packageResource.uninstall(installedPackage.id) .then(function () { if (installedPackage.files.length > 0) { - vm.installState.status = "All done, your browser will now refresh"; + vm.installState.status = localizationService.localize("packager_installStateComplete"); vm.installState.progress = "100"; //set this flag so that on refresh it shows the installed packages list diff --git a/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.controller.js b/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.controller.js index 6aae118085..dc6dfe7da4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function PackagesRepoController($scope, $route, $location, $timeout, ourPackageRepositoryResource, $q, packageResource, localStorageService) { + function PackagesRepoController($scope, $route, $location, $timeout, ourPackageRepositoryResource, $q, packageResource, localStorageService, localizationService) { var vm = this; @@ -15,7 +15,9 @@ }; vm.searchQuery = ""; vm.installState = { - status: "" + status: "", + progress: 0, + type: "ok" }; vm.selectCategory = selectCategory; vm.showPackageDetails = showPackageDetails; @@ -107,11 +109,20 @@ function showPackageDetails(selectedPackage) { ourPackageRepositoryResource.getDetails(selectedPackage.id) - .then(function(pack) { - vm.package = pack; - vm.packageViewState = "packageDetails"; + .then(function (pack) { + packageResource.validateInstalled(pack.name, pack.latestVersion) + .then(function() { + //ok, can install + vm.package = pack; + vm.package.isValid = true; + vm.packageViewState = "packageDetails"; + }, function() { + //nope, cannot install + vm.package = pack; + vm.package.isValid = false; + vm.packageViewState = "packageDetails"; + }) }); - } function setPackageViewState(state) { @@ -154,35 +165,42 @@ vm.loading = false; vm.localPackage = pack; vm.localPackage.allowed = true; - }, - error); + }, function (evt, status, headers, config) { + + if (status == 400) { + //it's a validation error + vm.installState.type = "error"; + vm.zipFile.serverErrorMessage = evt.message; + } + }); } function error(e, args) { - + //This will return a rejection meaning that the promise change above will stop + return $q.reject(); } function installPackage(selectedPackage) { - vm.installState.status = "importing..."; + vm.installState.status = localizationService.localize("packager_installStateImporting"); vm.installState.progress = "0"; packageResource .import(selectedPackage) .then(function(pack) { - vm.installState.status = "Installing..."; + vm.installState.status = localizationService.localize("packager_installStateInstalling"); vm.installState.progress = "33"; return packageResource.installFiles(pack); }, error) .then(function(pack) { - vm.installState.status = "Restarting, please wait..."; + vm.installState.status = localizationService.localize("packager_installStateRestarting"); vm.installState.progress = "66"; return packageResource.installData(pack); }, error) .then(function(pack) { - vm.installState.status = "All done, your browser will now refresh"; + vm.installState.status = localizationService.localize("packager_installStateComplete"); vm.installState.progress = "100"; return packageResource.cleanUp(pack); }, diff --git a/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.html b/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.html index a78966e492..973fa8c440 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.html +++ b/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.html @@ -153,7 +153,12 @@
- + +
+ The package and version chosen is already installed +
@@ -310,6 +315,7 @@ I accept terms of use
- -
+ +

{{vm.installState.status}}

diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index d9c4bc912e..8012081a1c 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -783,6 +783,13 @@ To manage your website, simply open the Umbraco back office and start adding con View package website The package and version chosen is already installed This package cannot be installed, it requires a minimum Umbraco version of %0% + Uninstalling... + Downloading... + Importing... + Installing... + Restarting, please wait... + All done, your browser will now refresh, please wait... + Paste with full formatting (Not recommended) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml index 8c3f7f8d70..8416efa423 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -780,6 +780,14 @@ To manage your website, simply open the Umbraco back office and start adding con Package version Package version history View package website + The package and version chosen is already installed + This package cannot be installed, it requires a minimum Umbraco version of %0% + Uninstalling... + Downloading... + Importing... + Installing... + Restarting, please wait... + All done, your browser will now refresh, please wait... Paste with full formatting (Not recommended) diff --git a/src/Umbraco.Web/Editors/PackageInstallController.cs b/src/Umbraco.Web/Editors/PackageInstallController.cs index 726584988b..696c8c8e16 100644 --- a/src/Umbraco.Web/Editors/PackageInstallController.cs +++ b/src/Umbraco.Web/Editors/PackageInstallController.cs @@ -48,6 +48,21 @@ namespace Umbraco.Web.Editors [UmbracoApplicationAuthorize(Core.Constants.Applications.Developer)] public class PackageInstallController : UmbracoAuthorizedJsonController { + /// + /// This checks if this package & version is alraedy installed + /// + /// + /// + /// + [HttpPost] + public IHttpActionResult ValidateInstalled(string name, string version) + { + var validate = ValidateInstalledInternal(name, version); + if (validate == false) + return BadRequest(); + return Ok(); + } + [HttpPost] public IHttpActionResult Uninstall(int packageId) { @@ -318,6 +333,34 @@ namespace Umbraco.Web.Editors } } + private bool ValidateInstalledInternal(string name, string version) + { + var allInstalled = InstalledPackage.GetAllInstalledPackages(); + var found = allInstalled.FirstOrDefault(x => + { + if (x.Data.Name != name) return false; + //match the exact version + if (x.Data.Version == version) + { + return true; + } + //now try to compare the versions + Version installed; + Version selected; + if (Version.TryParse(x.Data.Version, out installed) && Version.TryParse(version, out selected)) + { + if (installed >= selected) return true; + } + return false; + }); + if (found != null) + { + //this package is already installed + return false; + } + return true; + } + [HttpPost] [FileUploadCleanupFilter(false)] public async Task UploadLocalPackage() @@ -372,28 +415,11 @@ namespace Umbraco.Web.Editors //Populate the model from the metadata in the package file (zip file) PopulateFromPackageData(model); - var allInstalled = InstalledPackage.GetAllInstalledPackages(); - var found = allInstalled.FirstOrDefault(x => - { - if (x.Data.Name != model.Name) return false; - //match the exact version - if (x.Data.Version == model.Version) - { - return true; - } - //now try to compare the versions - Version installed; - Version selected; - if (Version.TryParse(x.Data.Version, out installed) && Version.TryParse(model.Version, out selected)) - { - if (installed >= selected) return true; - } - return false; - }); - if (found != null) + var validate = ValidateInstalledInternal(model.Name, model.Version); + + if (validate == false) { //this package is already installed - throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse( Services.TextService.Localize("packager/packageAlreadyInstalled"))); } @@ -442,6 +468,15 @@ namespace Umbraco.Web.Editors //Populate the model from the metadata in the package file (zip file) PopulateFromPackageData(model); + var validate = ValidateInstalledInternal(model.Name, model.Version); + + if (validate == false) + { + //this package is already installed + throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse( + Services.TextService.Localize("packager/packageAlreadyInstalled"))); + } + return model; } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.cs index 99b054affb..bdcbfaa775 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.cs @@ -26,7 +26,8 @@ namespace umbraco.presentation.developer.packages /// /// Summary description for packager. /// - public partial class Installer : UmbracoEnsuredPage + [Obsolete("This should not be used and will be removed in v8, this is kept here only for backwards compat reasons, this page should never be rendered/used")] + public class Installer : UmbracoEnsuredPage { public Installer() {