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
This commit is contained in:
Shannon
2016-07-11 14:34:02 +02:00
parent da92eef733
commit a86a5d3db2
10 changed files with 137 additions and 52 deletions

View File

@@ -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(

View File

@@ -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)

View File

@@ -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);
},

View File

@@ -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

View File

@@ -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);
},

View File

@@ -153,7 +153,12 @@
<div class="umb-package-details__sidebar">
<div class="umb-package-details__section">
<button class="umb-era-button -blue -full-width" ng-click="vm.downloadPackage(vm.package)">Install package</button>
<button class="umb-era-button -blue -full-width"
ng-if="vm.package.isValid === true"
ng-click="vm.downloadPackage(vm.package)">Install package</button>
<div class="umb-info-local-item text-error" ng-if="vm.package.isValid === false">
<localize key="packager_packageAlreadyInstalled">The package and version chosen is already installed</localize>
</div>
</div>
<div class="umb-package-details__section">
@@ -310,6 +315,7 @@
<strong class="label-text">I accept <a href="{{ vm.localPackage.licenseUrl }}">terms of use</a></strong>
</label>
<button type="button"
ng-if="vm.installState.type !== 'error'"
ng-class="{'-inactive' : localPackageForm.$invalid}"
ng-disabled="localPackageForm.$invalid"
class="umb-era-button -blue flex-inline mt3"
@@ -328,8 +334,8 @@
<div class="umb-info-local-item text-error" ng-if="!vm.localPackage.isCompatible">
This package cannot be installed, it requires a minimum Umbraco version of {{vm.localPackage.umbracoVersion}}
</div>
<div class="umb-info-local-item text-info">
<div class="umb-info-local-item text-info" ng-class="{'text-info' : vm.installState.type !== 'error'}">
<p>{{vm.installState.status}}</p>
</div>

View File

@@ -783,6 +783,13 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="viewPackageWebsite">View package website</key>
<key alias="packageAlreadyInstalled">The package and version chosen is already installed</key>
<key alias="targetVersionMismatch">This package cannot be installed, it requires a minimum Umbraco version of %0%</key>
<key alias="installStateUninstalling">Uninstalling...</key>
<key alias="installStateDownloading">Downloading...</key>
<key alias="installStateImporting">Importing...</key>
<key alias="installStateInstalling">Installing...</key>
<key alias="installStateRestarting">Restarting, please wait...</key>
<key alias="installStateComplete">All done, your browser will now refresh, please wait...</key>
</area>
<area alias="paste">
<key alias="doNothing">Paste with full formatting (Not recommended)</key>

View File

@@ -780,6 +780,14 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="packageVersion">Package version</key>
<key alias="packageVersionHistory">Package version history</key>
<key alias="viewPackageWebsite">View package website</key>
<key alias="packageAlreadyInstalled">The package and version chosen is already installed</key>
<key alias="targetVersionMismatch">This package cannot be installed, it requires a minimum Umbraco version of %0%</key>
<key alias="installStateUninstalling">Uninstalling...</key>
<key alias="installStateDownloading">Downloading...</key>
<key alias="installStateImporting">Importing...</key>
<key alias="installStateInstalling">Installing...</key>
<key alias="installStateRestarting">Restarting, please wait...</key>
<key alias="installStateComplete">All done, your browser will now refresh, please wait...</key>
</area>
<area alias="paste">
<key alias="doNothing">Paste with full formatting (Not recommended)</key>

View File

@@ -48,6 +48,21 @@ namespace Umbraco.Web.Editors
[UmbracoApplicationAuthorize(Core.Constants.Applications.Developer)]
public class PackageInstallController : UmbracoAuthorizedJsonController
{
/// <summary>
/// This checks if this package & version is alraedy installed
/// </summary>
/// <param name="name"></param>
/// <param name="version"></param>
/// <returns></returns>
[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<LocalPackageInstallModel> 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;
}

View File

@@ -26,7 +26,8 @@ namespace umbraco.presentation.developer.packages
/// <summary>
/// Summary description for packager.
/// </summary>
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()
{