From cb7c1f9bbda57f447e1ea6b4c3c274c34dc12585 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 4 Dec 2018 11:18:12 +0100 Subject: [PATCH 01/93] use table for installed packages --- .../src/views/packages/views/installed.html | 50 ++++++++++--------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/views/installed.html b/src/Umbraco.Web.UI.Client/src/views/packages/views/installed.html index a015748078..3c443e0bac 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/views/installed.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/views/installed.html @@ -7,29 +7,33 @@
Installed packages
-
- -
- -
- - -
- -
-
{{ installedPackage.name }}
-
- {{ installedPackage.version }} | {{ installedPackage.url }}| {{ installedPackage.author }} -
-
- -
- Uninstall -
- -
- -
+ + + + + + + +
+
+ + +
+
+
{{ installedPackage.name }}
+
+ {{ installedPackage.version }} | {{ installedPackage.url }}| {{ installedPackage.author }} +
+
+
+ + +
From 04e8fc7fa3e1c5ff0925710fd1c1c334b4348782 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 4 Dec 2018 11:18:32 +0100 Subject: [PATCH 02/93] fix missing form from install local package --- .../src/views/packages/views/install-local.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.html b/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.html index f7843fb0fc..7f22b7ace8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.html @@ -4,7 +4,7 @@
-
+
Install a local package by selecting it from your machine. Only install packages from sources you know and trust.

- +
From bd455aaede8a6a354cb7d3fc4f20a224fd21c898 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 4 Dec 2018 11:19:08 +0100 Subject: [PATCH 03/93] fixing up styles to better fit new UI --- .../components/umb-package-local-install.less | 5 +- .../src/less/components/umb-packages.less | 67 +---- .../src/views/packages/views/repo.html | 270 +++++++++--------- 3 files changed, 148 insertions(+), 194 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-package-local-install.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-package-local-install.less index 75f58f983b..99759fcee7 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-package-local-install.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-package-local-install.less @@ -15,7 +15,7 @@ height: 300px; border: 2px dashed @gray-8; border-radius: 3px; - background: @gray-10; + background: @white; display: flex; flex-direction: column; justify-content: center; @@ -74,7 +74,6 @@ // Info state .umb-info-local-items { - border: 2px solid @gray-8; border-radius: 3px; background: @gray-10; display: flex; @@ -84,6 +83,8 @@ margin: 0 20px; width: 100%; max-width: 540px; + background: @white; + box-shadow: 0 1px 1px 0 rgba(0,0,0,.16); } .umb-info-local-items a { diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-packages.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-packages.less index e2cfb0bded..a517605c4a 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-packages.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-packages.less @@ -23,9 +23,7 @@ .umb-packages-search { width: 100%; - background: @gray-10; border-radius: 3px; - padding: 30px; box-sizing: border-box; } @@ -49,60 +47,14 @@ } .umb-packages { - margin: 0 -10px; - display: flex; - flex-wrap: wrap; + display: grid; + grid-gap: 20px; + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); } // Cards .umb-package { - padding: 10px; box-sizing: border-box; - flex: 0 0 100%; - max-width: 100%; -} - -@media (min-width: 768px) { - .umb-package { - flex: 0 0 50%; - max-width: 50%; - } -} - -@media (min-width: 1200px) { - .umb-package { - flex: 0 0 33.33%; - max-width: 33.33%; - } -} - -@media (min-width: 1400px) { - .umb-package { - flex: 0 0 25%; - max-width: 25%; - } -} - -@media (min-width: 1700px) { - .umb-package { - flex: 0 0 20%; - max-width: 20%; - } -} - - -@media (min-width: 1900px) { - .umb-package { - flex: 0 0 16.66%; - max-width: 16.66%; - } -} - -@media (min-width: 2200px) { - .umb-package { - flex: 0 0 14.28%; - max-width: 14.28%; - } } .umb-package-link { @@ -114,10 +66,11 @@ box-sizing: border-box; height: 100%; width: 100%; - border: 1px solid @gray-9; border-radius: 3px; text-decoration: none !important; transition: border-color 100ms ease; + background-color: @white; + box-shadow: 0 1px 1px 0 rgba(0,0,0,0.16); &:hover { border-color: @turquoise; @@ -151,15 +104,8 @@ // Info .umb-package-info { - padding-right: 15px; - padding-bottom: 15px; - padding-left: 15px; - padding-top: 15px; + padding: 15px; text-align: center; - background: @gray-10; - border-bottom-left-radius: 3px; - border-bottom-right-radius: 3px; - border-top: 1px solid @gray-9; } @@ -251,6 +197,7 @@ border-bottom: 1px solid @gray-8; border-right: 1px solid @gray-8; padding: 10px 0; + background: @white; } diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/views/repo.html b/src/Umbraco.Web.UI.Client/src/views/packages/views/repo.html index 7b4b960c2c..fc272c86ff 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/views/repo.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/views/repo.html @@ -27,7 +27,7 @@

Popular

-
+
@@ -61,7 +61,7 @@

New Releases

Results for '{{ vm.searchQuery }}'

-
+
@@ -129,147 +129,153 @@
-
{{ vm.package.name }}
+ + +
{{ vm.package.name }}
+
+
+ + -
- - - - - + +
-
- - - - - - - -
- -
-
- -
- + + - -
+ action="vm.downloadPackage(vm.package)" + label-key="packager_packageInstall"> + + + + + + + +
+ +
+ + +
+ +
+
{{ vm.package.ownerInfo.owner }}
+
+ {{ vm.package.ownerInfo.owner }} has {{ vm.package.ownerInfo.karma }} karma points +
+
+
+
+
+ + + +
Information
-
{{ vm.package.ownerInfo.owner }}
-
- {{ vm.package.ownerInfo.owner }} has {{ vm.package.ownerInfo.karma }} karma points + +
+
Owner:
+
{{vm.package.ownerInfo.owner}}
+
+ +
+
Contributors:
+
+ {{ contributor }} +
+
+ +
+
Created:
+
{{vm.package.created | date:'yyyy-MM-dd HH:mm:ss'}}
+
+ +
+
Current version:
+
{{vm.package.latestVersion}}
+
+ +
+
.NET Version:
+
{{vm.package.information.netVersion}}
+
+ +
+
License:
+
{{vm.package.licenseName}}
+
+ +
+
Downloads:
+
{{vm.package.downloads}}
+
+ +
+
Likes:
+
{{vm.package.likes}}
+
+ +
+ + + + + +
Compatibility
+
This package is compatible with the following versions of Umbraco, as reported by community members. Full compatability cannot be gauranteed for versions reported below 100%
+
+
+ {{compatibility.version}} + ({{compatibility.percentage}}%) +
+ + +
+
+
+ + + +
External sources
+ -
-
- -
-
Information
-
- -
-
Owner:
-
{{vm.package.ownerInfo.owner}}
-
- -
-
Contributors:
-
- {{ contributor }} -
-
- -
-
Created:
-
{{vm.package.created | date:'yyyy-MM-dd HH:mm:ss'}}
-
- -
-
Current version:
-
{{vm.package.latestVersion}}
-
- -
-
.NET Version:
-
{{vm.package.information.netVersion}}
-
- -
-
License:
-
{{vm.package.licenseName}}
-
- -
-
Downloads:
-
{{vm.package.downloads}}
-
- -
-
Likes:
-
{{vm.package.likes}}
-
- -
-
- -
-
Compatibility
-
This package is compatible with the following versions of Umbraco, as reported by community members. Full compatability cannot be gauranteed for versions reported below 100%
-
-
- {{compatibility.version}} - ({{compatibility.percentage}}%) -
- - - - -
-
- -
-
External sources
- - -
+ +
From a4bdcf99b19e5a85d3f20661bbd5db3950d92a88 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 5 Dec 2018 10:36:20 +0100 Subject: [PATCH 04/93] wip create package editor --- .../src/common/services/overlay.service.js | 5 + .../src/views/packages/edit.controller.js | 154 ++++++++++++ .../src/views/packages/edit.html | 227 ++++++++++++++++++ .../src/views/packages/overview.controller.js | 6 + .../packages/views/created.controller.js | 84 +++++++ .../src/views/packages/views/created.html | 45 ++++ 6 files changed, 521 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/packages/edit.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/packages/views/created.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/packages/views/created.html diff --git a/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js b/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js index 6c50e58490..6de0b4170b 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js @@ -27,6 +27,11 @@ overlay.position = "center"; } + // use a default empty view if nothing is set + if(!overlay.view) { + overlay.view = "views/common/overlays/default/default.html"; + } + // option to disable backdrop clicks if(overlay.disableBackdropClick) { backdropOptions.disableEventsOnClick = true; diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js new file mode 100644 index 0000000000..a031baa26d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js @@ -0,0 +1,154 @@ +(function () { + "use strict"; + + function EditController($location) { + + const vm = this; + + vm.showBackButton = true; + + // open all expansion panels + vm.propertiesOpen = true; + vm.contentOpen = true; + vm.filesOpen = true; + vm.actionsOpen = true; + + vm.back = back; + vm.createPackage = createPackage; + vm.save = save; + + function onInit() { + // load package + + vm.package = { + "name": "My package" + }; + + vm.documentTypes = [ + { + "name": "Home" + }, + { + "name": "Blog" + }, + { + "name": "Blog Post" + }, + { + "name": "News" + }, + { + "name": "News Item" + }, + { + "name": "Contact" + }, + { + "name": "About" + } + ]; + + vm.templates = [ + { + "name": "Home" + }, + { + "name": "Blog" + }, + { + "name": "Blog Post" + }, + { + "name": "News" + }, + { + "name": "News Item" + }, + { + "name": "Contact" + }, + { + "name": "About" + } + ]; + + vm.stylesheets = [ + { + "name": "styles.css" + }, + { + "name": "carousel.css" + }, + { + "name": "fonts.css" + } + ]; + + vm.macros = [ + { + "name": "Macro 1" + }, + { + "name": "Macro 2" + }, + { + "name": "Macro 3" + } + ]; + + vm.languages = [ + { + "name": "English (United States)" + }, + { + "name": "Danish" + }, + { + "name": "Spanish" + } + ]; + + vm.dictionaryItems = [ + { + "name": "Item 1" + }, + { + "name": "Item 2" + }, + { + "name": "Item 3" + } + ]; + + vm.dataTypes = [ + { + "name": "Datatype 1" + }, + { + "name": "Datatype 2" + }, + { + "name": "Datatype 3" + } + ]; + } + + function back() { + $location.path("packages/packages/overview"); + } + + function createPackage() { + console.log("create package"); + } + + function save() { + console.log("save package"); + } + + onInit(); + + } + + angular.module("umbraco").controller("Umbraco.Editors.Packages.EditController", EditController); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html new file mode 100644 index 0000000000..68e2a30b99 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html @@ -0,0 +1,227 @@ +
+ +
+ + + + + + + + + + +
+ +
+
Package Properties
+   +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ +
+
Package Content
+   +
+ +
+ + + + +
+ +
+
+ + +
+ +
+
+ + +
+ +
+
+ + +
+ +
+
+ + +
+ +
+
+ + +
+ +
+
+ + +
+ +
+
+ +
+ +
+ +
+ +
+
Package Files
+   +
+ +
+ + + + + + + +
+ +
+ +
+
+
Package Actions
+   +
+
+ +
+ Documentation +
+ +
+
+
+
+
+ +
+ + + + + + + + + + + + +
+ +
+ +
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/overview.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/overview.controller.js index 42fcddaa56..43a9d0473e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/overview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/overview.controller.js @@ -44,6 +44,12 @@ "view": "views/packages/views/install-local.html", "active": installPackageUri === "local", "alias": "umbInstallLocal" + }, + { + "name": "Created", + "icon": "icon-add", + "view": "views/packages/views/created.html", + "alias": "umbCreatedPackages" } ]; diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/views/created.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/views/created.controller.js new file mode 100644 index 0000000000..8c92f3553c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/packages/views/created.controller.js @@ -0,0 +1,84 @@ +(function () { + "use strict"; + + function CreatedController($timeout, $location, localizationService, overlayService) { + + const vm = this; + + vm.deleteCreatedPackage = deleteCreatedPackage; + vm.goToPackage = goToPackage; + vm.createPackage = createPackage; + + function onInit() { + + vm.createdPackages = []; + + //load created packages + $timeout(function(){ + vm.createdPackages = [ + { + "author": "Test", + "files": [], + "iconUrl": "", + "id": 1, + "license": "MIT License", + "licenseUrl": "http://opensource.org/licenses/MIT", + "name": "Test v8", + "url": "https://test.com", + "version": "0.0.0" + }, + { + "author": "Test", + "files": [], + "iconUrl": "", + "id": 2, + "license": "MIT License", + "licenseUrl": "http://opensource.org/licenses/MIT", + "name": "Another Test v8", + "url": "https://test.com", + "version": "0.0.0" + } + ]; + }, 1000); + + } + + function deleteCreatedPackage(createdPackage) { + + const dialog = { + submitButtonLabelKey: "contentTypeEditor_yesDelete", + submit: function (model) { + performDelete(createdPackage); + overlayService.close(); + }, + close: function () { + overlayService.close(); + } + }; + + localizationService.localize("general_delete").then(value => { + dialog.title = value; + overlayService.open(dialog); + }); + + } + + function performDelete(createdPackage) { + console.log("perform delete"); + } + + function goToPackage(createdPackage) { + $location.path("packages/packages/edit/" + createdPackage.id); + } + + function createPackage() { + console.log("create package"); + } + + onInit(); + + } + + angular.module("umbraco").controller("Umbraco.Editors.Packages.CreatedController", CreatedController); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/views/created.html b/src/Umbraco.Web.UI.Client/src/views/packages/views/created.html new file mode 100644 index 0000000000..52f0cef9fd --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/packages/views/created.html @@ -0,0 +1,45 @@ +
+ + + + + + + + + + + + + + + + + +
+
+ + +
+
+
{{ createdPackage.name }}
+
+ {{ createdPackage.version }} | {{ createdPackage.url }}| {{ createdPackage.author }} +
+
+
+ + +
+ +
\ No newline at end of file From bd505a857446dacb7b7f5c9ad717bf70c5684aee Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 5 Dec 2018 13:37:01 +0100 Subject: [PATCH 05/93] get doctypes, datatypes, languages etc from the server and bind selection to model + add content picker --- .../src/views/packages/edit.controller.js | 151 ++++++------------ .../src/views/packages/edit.html | 66 ++++++-- 2 files changed, 102 insertions(+), 115 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js index a031baa26d..a161040c24 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function EditController($location) { + function EditController($location, contentTypeResource, templateResource, stylesheetResource, languageResource, dictionaryResource, dataTypeResource, editorService) { const vm = this; @@ -16,6 +16,8 @@ vm.back = back; vm.createPackage = createPackage; vm.save = save; + vm.removeContentItem = removeContentItem; + vm.openContentPicker = openContentPicker; function onInit() { // load package @@ -24,113 +26,39 @@ "name": "My package" }; - vm.documentTypes = [ - { - "name": "Home" - }, - { - "name": "Blog" - }, - { - "name": "Blog Post" - }, - { - "name": "News" - }, - { - "name": "News Item" - }, - { - "name": "Contact" - }, - { - "name": "About" - } - ]; + // get all doc types + contentTypeResource.getAll().then(documentTypes => { + vm.documentTypes = documentTypes; + }); - vm.templates = [ - { - "name": "Home" - }, - { - "name": "Blog" - }, - { - "name": "Blog Post" - }, - { - "name": "News" - }, - { - "name": "News Item" - }, - { - "name": "Contact" - }, - { - "name": "About" - } - ]; + // get all templates + templateResource.getAll().then(templates => { + vm.templates = templates; + }); - vm.stylesheets = [ - { - "name": "styles.css" - }, - { - "name": "carousel.css" - }, - { - "name": "fonts.css" - } - ]; + // get all stylesheets + stylesheetResource.getAll().then(stylesheets => { + vm.stylesheets = stylesheets; + }); - vm.macros = [ - { - "name": "Macro 1" - }, - { - "name": "Macro 2" - }, - { - "name": "Macro 3" - } - ]; + // TODO: implement macros + vm.macros = []; + + // get all languages + languageResource.getAll().then(languages => { + vm.languages = languages; + }); - vm.languages = [ - { - "name": "English (United States)" - }, - { - "name": "Danish" - }, - { - "name": "Spanish" - } - ]; + // get all dictionary items + dictionaryResource.getList().then(dictionaryItems => { + vm.dictionaryItems = dictionaryItems; + }); - vm.dictionaryItems = [ - { - "name": "Item 1" - }, - { - "name": "Item 2" - }, - { - "name": "Item 3" - } - ]; + // get all data types items + dataTypeResource.getAll().then(dataTypes => { + vm.dataTypes = dataTypes; + }); - vm.dataTypes = [ - { - "name": "Datatype 1" - }, - { - "name": "Datatype 2" - }, - { - "name": "Datatype 3" - } - ]; } function back() { @@ -145,6 +73,25 @@ console.log("save package"); } + function removeContentItem() { + vm.package.contentItem = null; + } + + function openContentPicker() { + const contentPicker = { + submit: function(model) { + if(model.selection && model.selection.length > 0) { + vm.package.contentItem = model.selection[0]; + } + editorService.close(); + }, + close: function() { + editorService.close(); + } + }; + editorService.contentPicker(contentPicker); + } + onInit(); } diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html index 68e2a30b99..1888c5d9c1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html @@ -78,13 +78,43 @@
- + + + + + + + + Add + + + +
@@ -93,7 +123,10 @@
@@ -102,25 +135,26 @@
-
- -
+ NOT IMPLEMENTED
@@ -129,7 +163,10 @@
@@ -138,7 +175,10 @@
From e599685684b86f2e2b2b75f001e727b485b44a5f Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 5 Dec 2018 14:20:34 +0100 Subject: [PATCH 06/93] wip file pickers ui --- .../src/views/packages/edit.controller.js | 17 +++++++++++++++++ .../src/views/packages/edit.html | 17 +++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js index a161040c24..c8d6af5d6b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js @@ -18,6 +18,7 @@ vm.save = save; vm.removeContentItem = removeContentItem; vm.openContentPicker = openContentPicker; + vm.openFilePicker = openFilePicker; function onInit() { // load package @@ -92,6 +93,22 @@ editorService.contentPicker(contentPicker); } + function openFilePicker() { + const filePicker = { + treeAlias: "files", + section:"settings", + entityType: "file", + submit: function(model) { + console.log(model.selection); + editorService.close(); + }, + close: function() { + editorService.close(); + } + }; + editorService.contentPicker(filePicker); + } + onInit(); } diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html index 1888c5d9c1..a84d21d709 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html @@ -202,12 +202,25 @@ - + + Add + + - + + + + Add +
From d77796bcacfe4ce2e014b3929cd8d5b50f458454 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 5 Dec 2018 15:06:50 +0100 Subject: [PATCH 07/93] show selected files and control --- .../src/views/packages/edit.controller.js | 38 +++++++++++++++++++ .../src/views/packages/edit.html | 24 +++++++++++- 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js index c8d6af5d6b..dfc0350630 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js @@ -19,6 +19,9 @@ vm.removeContentItem = removeContentItem; vm.openContentPicker = openContentPicker; vm.openFilePicker = openFilePicker; + vm.removeFile = removeFile; + vm.openControlPicker = openControlPicker; + vm.removeControl = removeControl; function onInit() { // load package @@ -98,8 +101,17 @@ treeAlias: "files", section:"settings", entityType: "file", + multiPicker: true, submit: function(model) { console.log(model.selection); + + if(model && model.selection) { + vm.package.files = vm.package.files ? vm.package.files : []; + model.selection.forEach(selected => { + vm.package.files.push(selected); + }); + } + editorService.close(); }, close: function() { @@ -109,6 +121,32 @@ editorService.contentPicker(filePicker); } + function removeFile(index) { + vm.package.files.splice(index, 1); + } + + function openControlPicker() { + const controlPicker = { + treeAlias: "files", + section:"settings", + entityType: "file", + submit: function(model) { + if(model.selection && model.selection.length > 0) { + vm.package.control = model.selection[0]; + } + editorService.close(); + }, + close: function() { + editorService.close(); + } + }; + editorService.contentPicker(controlPicker); + } + + function removeControl() { + vm.package.control = null; + } + onInit(); } diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html index a84d21d709..d6309d812a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html @@ -202,25 +202,47 @@ + + + + Add + + + Add +
From 00428ec2de547c6bd1abadcb67f4d32f3e31ed2f Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Wed, 5 Dec 2018 14:28:07 +0000 Subject: [PATCH 08/93] Adds in new hidden aka initialized=false tree (so that it can be displayed only as a dialog/infinite editor) to display files & folders from root of the site for the package creator UI --- .../src/views/packages/edit.controller.js | 7 +++--- src/Umbraco.Web.UI/config/trees.config | 2 ++ src/Umbraco.Web/Trees/FilesTreeController.cs | 23 +++++++++++++++++++ src/Umbraco.Web/Umbraco.Web.csproj | 1 + 4 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 src/Umbraco.Web/Trees/FilesTreeController.cs diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js index c8d6af5d6b..ffdeee88bd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js @@ -44,7 +44,7 @@ // TODO: implement macros vm.macros = []; - + // get all languages languageResource.getAll().then(languages => { vm.languages = languages; @@ -95,9 +95,10 @@ function openFilePicker() { const filePicker = { + section: "settings", treeAlias: "files", - section:"settings", entityType: "file", + onlyInitialized: false, submit: function(model) { console.log(model.selection); editorService.close(); @@ -106,7 +107,7 @@ editorService.close(); } }; - editorService.contentPicker(filePicker); + editorService.treePicker(filePicker); } onInit(); diff --git a/src/Umbraco.Web.UI/config/trees.config b/src/Umbraco.Web.UI/config/trees.config index 2b99f8751f..da6f75300a 100644 --- a/src/Umbraco.Web.UI/config/trees.config +++ b/src/Umbraco.Web.UI/config/trees.config @@ -20,6 +20,8 @@ + + diff --git a/src/Umbraco.Web/Trees/FilesTreeController.cs b/src/Umbraco.Web/Trees/FilesTreeController.cs new file mode 100644 index 0000000000..a435d3a668 --- /dev/null +++ b/src/Umbraco.Web/Trees/FilesTreeController.cs @@ -0,0 +1,23 @@ +using Umbraco.Core; +using Umbraco.Core.IO; +using Umbraco.Web.Models.Trees; + +namespace Umbraco.Web.Trees +{ + [CoreTree(TreeGroup = Constants.Trees.Groups.Templating)] + [Tree(Constants.Applications.Settings, "files", "Files", "icon-folder", "icon-folder", sortOrder: 13, initialize: false)] + public class FilesTreeController : FileSystemTreeController + { + protected override IFileSystem FileSystem => new PhysicalFileSystem("~/"); // fixme inject + + protected override string[] Extensions => new string[] { }; + + protected override string FileIcon => "icon-script"; + + protected override void OnRenderFolderNode(ref TreeNode treeNode) + { + //TODO: This isn't the best way to ensure a noop process for clicking a node but it works for now. + treeNode.AdditionalData["jsClickCallback"] = "javascript:void(0);"; + } + } +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 32134e7a45..717c2f508e 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -183,6 +183,7 @@ + From 749a25aabbb25fd497759a92c05228d7891a99ae Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 5 Dec 2018 15:41:55 +0100 Subject: [PATCH 09/93] user overlayService for ysods --- .../src/common/services/events.service.js | 1 - .../common/services/umbrequesthelper.service.js | 16 +++++----------- .../src/controllers/main.controller.js | 8 -------- src/Umbraco.Web.UI/Umbraco/Views/Default.cshtml | 6 ------ 4 files changed, 5 insertions(+), 26 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/events.service.js b/src/Umbraco.Web.UI.Client/src/common/services/events.service.js index 6bab8fda81..51f63e6787 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/events.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/events.service.js @@ -6,7 +6,6 @@ app.ready app.authenticated app.notAuthenticated - app.ysod app.reInitialize app.userRefresh app.navigationReady diff --git a/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js index fcb5585d5d..0834799be4 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js @@ -3,7 +3,7 @@ * @name umbraco.services.umbRequestHelper * @description A helper object used for sending requests to the server **/ -function umbRequestHelper($http, $q, notificationsService, eventsService, formHelper) { +function umbRequestHelper($http, $q, notificationsService, eventsService, formHelper, overlayService) { return { @@ -176,11 +176,8 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe //show a ysod dialog if (Umbraco.Sys.ServerVariables["isDebuggingEnabled"] === true) { - eventsService.emit('app.ysod', - { - errorMsg: 'An error occured', - data: response.data - }); + const error = { errorMsg: 'An error occured', data: response.data }; + overlayService.ysod(error); } else { //show a simple error notification @@ -290,11 +287,8 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe } else if (Umbraco.Sys.ServerVariables["isDebuggingEnabled"] === true) { //show a ysod dialog - eventsService.emit('app.ysod', - { - errorMsg: 'An error occured', - data: response.data - }); + const error = { errorMsg: 'An error occured', data: response.data }; + overlayService.ysod(error); } else { //show a simple error notification diff --git a/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js b/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js index 30a7e2ac7d..c2b2ba26d7 100644 --- a/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js +++ b/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js @@ -103,14 +103,6 @@ function MainController($scope, $location, appState, treeService, notificationsS })); - evts.push(eventsService.on("app.ysod", function (name, error) { - $scope.ysodOverlay = { - view: "ysod", - error: error, - show: true - }; - })); - // events for search evts.push(eventsService.on("appState.searchState.changed", function (e, args) { if (args.key === "show") { diff --git a/src/Umbraco.Web.UI/Umbraco/Views/Default.cshtml b/src/Umbraco.Web.UI/Umbraco/Views/Default.cshtml index 4659674c59..2ee71a6555 100644 --- a/src/Umbraco.Web.UI/Umbraco/Views/Default.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/Views/Default.cshtml @@ -106,12 +106,6 @@ parent-scope="overlay.parentScope"> - - - From 74d9945e09e49a5ee7fc9755cf955a283ce3a156 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 5 Dec 2018 15:42:44 +0100 Subject: [PATCH 10/93] Add title to files and control pickers --- src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js index 8b1878d6fc..ff587dd540 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js @@ -98,6 +98,7 @@ function openFilePicker() { const filePicker = { + title: "Select files", section: "settings", treeAlias: "files", entityType: "file", @@ -128,6 +129,7 @@ function openControlPicker() { const controlPicker = { + title: "Select control", treeAlias: "files", section:"settings", entityType: "file", From 8425c3124d9771d0b8b48b4acaaa9a132f592feb Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Wed, 5 Dec 2018 15:15:45 +0000 Subject: [PATCH 11/93] Modify FileSystemTree Controller - that if * is used for file extension then we will return all files :) --- src/Umbraco.Web/Trees/FileSystemTreeController.cs | 4 ++++ src/Umbraco.Web/Trees/FilesTreeController.cs | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/Trees/FileSystemTreeController.cs b/src/Umbraco.Web/Trees/FileSystemTreeController.cs index 9babb656fe..bf301ebe60 100644 --- a/src/Umbraco.Web/Trees/FileSystemTreeController.cs +++ b/src/Umbraco.Web/Trees/FileSystemTreeController.cs @@ -57,6 +57,10 @@ namespace Umbraco.Web.Trees var files = FileSystem.GetFiles(path).Where(x => { var extension = Path.GetExtension(x); + + if (Extensions.Contains("*")) + return true; + return extension != null && Extensions.Contains(extension.Trim('.'), StringComparer.InvariantCultureIgnoreCase); }); diff --git a/src/Umbraco.Web/Trees/FilesTreeController.cs b/src/Umbraco.Web/Trees/FilesTreeController.cs index a435d3a668..947522747d 100644 --- a/src/Umbraco.Web/Trees/FilesTreeController.cs +++ b/src/Umbraco.Web/Trees/FilesTreeController.cs @@ -10,9 +10,11 @@ namespace Umbraco.Web.Trees { protected override IFileSystem FileSystem => new PhysicalFileSystem("~/"); // fixme inject - protected override string[] Extensions => new string[] { }; + private static readonly string[] ExtensionsStatic = { "*" }; - protected override string FileIcon => "icon-script"; + protected override string[] Extensions => ExtensionsStatic; + + protected override string FileIcon => "icon-document"; protected override void OnRenderFolderNode(ref TreeNode treeNode) { From 849cc2d383cefa9cbc08551e2e0a7f71b5ddaffc Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 6 Dec 2018 10:20:48 +0100 Subject: [PATCH 12/93] remove tree sync for packages and users as we don't have a tree --- .../src/views/packages/overview.controller.js | 3 --- .../src/views/users/overview.controller.js | 4 ---- 2 files changed, 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/overview.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/overview.controller.js index 43a9d0473e..418861c636 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/overview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/overview.controller.js @@ -53,9 +53,6 @@ } ]; - $timeout(function () { - navigationService.syncTree({ tree: "packages", path: "-1" }); - }); } } diff --git a/src/Umbraco.Web.UI.Client/src/views/users/overview.controller.js b/src/Umbraco.Web.UI.Client/src/views/users/overview.controller.js index 4d25419175..49e8007d8c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/overview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/users/overview.controller.js @@ -25,10 +25,6 @@ setPageName(); - $timeout(function () { - navigationService.syncTree({ tree: "users", path: "-1" }); - }); - } function loadNavigation() { From 3546de32077f9d62c923f4737d9cfdbee5e228c8 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 6 Dec 2018 09:32:04 +0000 Subject: [PATCH 13/93] Update packages tree so that its a full screen app/section like users --- src/Umbraco.Web.UI/config/trees.config | 2 +- .../Trees/PackagesTreeController.cs | 86 +++---------------- src/Umbraco.Web/Trees/UserTreeController.cs | 2 - 3 files changed, 13 insertions(+), 77 deletions(-) diff --git a/src/Umbraco.Web.UI/config/trees.config b/src/Umbraco.Web.UI/config/trees.config index da6f75300a..dd9fac783f 100644 --- a/src/Umbraco.Web.UI/config/trees.config +++ b/src/Umbraco.Web.UI/config/trees.config @@ -23,7 +23,7 @@ - + diff --git a/src/Umbraco.Web/Trees/PackagesTreeController.cs b/src/Umbraco.Web/Trees/PackagesTreeController.cs index 8158b47985..b8acb98daa 100644 --- a/src/Umbraco.Web/Trees/PackagesTreeController.cs +++ b/src/Umbraco.Web/Trees/PackagesTreeController.cs @@ -1,21 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http.Formatting; +using System.Net.Http.Formatting; using Umbraco.Web.Models.Trees; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; -using umbraco; -using umbraco.cms.businesslogic.packager; -using Umbraco.Core.Services; -using Umbraco.Web.Actions; using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Trees { [UmbracoTreeAuthorize(Constants.Trees.Packages)] - [Tree(Constants.Applications.Packages, Constants.Trees.Packages, null, sortOrder: 0)] + [Tree(Constants.Applications.Packages, Constants.Trees.Packages, null, sortOrder: 0, isSingleNodeTree: true)] [PluginController("UmbracoTrees")] [CoreTree] public class PackagesTreeController : TreeController @@ -27,80 +20,25 @@ namespace Umbraco.Web.Trees protected override TreeNode CreateRootNode(FormDataCollection queryStrings) { var root = base.CreateRootNode(queryStrings); - - root.RoutePath = $"{Constants.Applications.Packages}/{Constants.Trees.Packages}/overview"; - + + //this will load in a custom UI instead of the dashboard for the root node + root.RoutePath = string.Format("{0}/{1}/{2}", Constants.Applications.Packages, Constants.Trees.Packages, "overview"); root.Icon = "icon-box"; - + + root.HasChildren = false; return root; } + protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings) { - var nodes = new TreeNodeCollection(); - - var createdPackages = CreatedPackage.GetAllCreatedPackages(); - - if (id == "created") - { - nodes.AddRange( - createdPackages - .OrderBy(entity => entity.Data.Name) - .Select(dt => - { - var node = CreateTreeNode(dt.Data.Id.ToString(), id, queryStrings, dt.Data.Name, "icon-inbox", false, - $"/{queryStrings.GetValue("application")}/framed/{Uri.EscapeDataString("developer/Packages/EditPackage.aspx?id=" + dt.Data.Id)}"); - return node; - })); - } - else - { - //must be root - var node = CreateTreeNode( - "created", - id, - queryStrings, - Services.TextService.Localize("treeHeaders/createdPackages"), - "icon-folder", - createdPackages.Count > 0, - string.Empty); - - - - //TODO: This isn't the best way to ensure a noop process for clicking a node but it works for now. - node.AdditionalData["jsClickCallback"] = "javascript:void(0);"; - - nodes.Add(node); - } - - - - return nodes; + //full screen app without tree nodes + return TreeNodeCollection.Empty; } protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings) { - var menu = new MenuItemCollection(); - - // Root actions - if (id == "-1") - { - menu.Items.Add(Services.TextService, opensDialog: true) - .ConvertLegacyMenuItem(null, Constants.Trees.Packages, queryStrings.GetValue("application")); - } - else if (id == "created") - { - menu.Items.Add(Services.TextService, opensDialog: true) - .ConvertLegacyMenuItem(null, Constants.Trees.Packages, queryStrings.GetValue("application")); - - menu.Items.Add(new RefreshNode(Services.TextService, true)); - } - else - { - //it's a package node - menu.Items.Add(Services.TextService, opensDialog: true); - } - - return menu; + //doesn't have a menu, this is a full screen app without tree nodes + return MenuItemCollection.Empty; } } } diff --git a/src/Umbraco.Web/Trees/UserTreeController.cs b/src/Umbraco.Web/Trees/UserTreeController.cs index f029a929de..57f5913fbb 100644 --- a/src/Umbraco.Web/Trees/UserTreeController.cs +++ b/src/Umbraco.Web/Trees/UserTreeController.cs @@ -1,6 +1,4 @@ using System.Net.Http.Formatting; -using Umbraco.Core; -using Umbraco.Core.Services; using Umbraco.Web.Models.Trees; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; From 17d489bb54dcbdd755dc15376662ae3b2872f02c Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 6 Dec 2018 09:50:05 +0000 Subject: [PATCH 14/93] Adds new API/Editor endpoint to deal with created packages - get methods --- src/Umbraco.Web/Editors/PackageController.cs | 49 ++++++++++++++++++++ src/Umbraco.Web/Umbraco.Web.csproj | 1 + 2 files changed, 50 insertions(+) create mode 100644 src/Umbraco.Web/Editors/PackageController.cs diff --git a/src/Umbraco.Web/Editors/PackageController.cs b/src/Umbraco.Web/Editors/PackageController.cs new file mode 100644 index 0000000000..e6d84e8288 --- /dev/null +++ b/src/Umbraco.Web/Editors/PackageController.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web.Http; +using umbraco.cms.businesslogic.packager; +using Umbraco.Web.Mvc; +using Umbraco.Web.WebApi.Filters; + +namespace Umbraco.Web.Editors +{ + /// + /// A controller used for installing packages and managing all of the data in the packages section in the back office + /// + [PluginController("UmbracoApi")] + [UmbracoApplicationAuthorize(Core.Constants.Applications.Packages)] + public class PackageController : UmbracoAuthorizedJsonController + { + [HttpGet] + public List GetCreatedPackages() + { + //TODO: Packager stuff still lives in business logic - YUK + //TODO: Could be too much data down the wire + return CreatedPackage.GetAllCreatedPackages(); + + /* + * "author": "Test", + "files": [], + "iconUrl": "", + "id": 1, + "license": "MIT License", + "licenseUrl": "http://opensource.org/licenses/MIT", + "name": "Test v8", + "url": "https://test.com", + "version": "0.0.0" + */ + + + } + + [HttpGet] + public CreatedPackage GetCreatedPackageById(int id) + { + return CreatedPackage.GetById(id); + } + + } +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 717c2f508e..5b396c9486 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -111,6 +111,7 @@ + From 9df8c71bf4a06e944738ff4998e64727e60ecb78 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 6 Dec 2018 09:54:21 +0000 Subject: [PATCH 15/93] Adds in Delete method --- src/Umbraco.Web/Editors/PackageController.cs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web/Editors/PackageController.cs b/src/Umbraco.Web/Editors/PackageController.cs index e6d84e8288..6d4655f0be 100644 --- a/src/Umbraco.Web/Editors/PackageController.cs +++ b/src/Umbraco.Web/Editors/PackageController.cs @@ -1,8 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Collections.Generic; +using System.Net; +using System.Net.Http; using System.Web.Http; using umbraco.cms.businesslogic.packager; using Umbraco.Web.Mvc; @@ -45,5 +43,15 @@ namespace Umbraco.Web.Editors return CreatedPackage.GetById(id); } + [HttpDelete] + public HttpResponseMessage DeleteCreatedPackageById(int id) + { + var package = CreatedPackage.GetById(id); + package.Delete(); + + //204 No Content + return new HttpResponseMessage(HttpStatusCode.NoContent); + } + } } From cd8bb8ed99cdcee645e4c23061cf12592407b2b7 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 6 Dec 2018 10:07:53 +0000 Subject: [PATCH 16/93] Adds the package api controller to Umbraco.Sys JS object --- src/Umbraco.Web/Editors/BackOfficeServerVariables.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs index 23d8e2cc4e..472728e087 100644 --- a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs +++ b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs @@ -208,6 +208,10 @@ namespace Umbraco.Web.Editors "packageInstallApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( controller => controller.Fetch(string.Empty)) }, + { + "packageApiBaseUrl ", _urlHelper.GetUmbracoApiServiceBaseUrl( + controller => controller.GetCreatedPackages()) + }, { "relationApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( controller => controller.GetById(0)) From de86dace707cbab5c8b32c45cb23e59f54c2695b Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 6 Dec 2018 10:18:31 +0000 Subject: [PATCH 17/93] Adds a Create/Publish method - will need to figure out validation --- src/Umbraco.Web/Editors/PackageController.cs | 34 +++++++++++--------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.Web/Editors/PackageController.cs b/src/Umbraco.Web/Editors/PackageController.cs index 6d4655f0be..d192ee21f3 100644 --- a/src/Umbraco.Web/Editors/PackageController.cs +++ b/src/Umbraco.Web/Editors/PackageController.cs @@ -8,6 +8,8 @@ using Umbraco.Web.WebApi.Filters; namespace Umbraco.Web.Editors { + //TODO: Packager stuff still lives in business logic - YUK + /// /// A controller used for installing packages and managing all of the data in the packages section in the back office /// @@ -18,23 +20,8 @@ namespace Umbraco.Web.Editors [HttpGet] public List GetCreatedPackages() { - //TODO: Packager stuff still lives in business logic - YUK //TODO: Could be too much data down the wire return CreatedPackage.GetAllCreatedPackages(); - - /* - * "author": "Test", - "files": [], - "iconUrl": "", - "id": 1, - "license": "MIT License", - "licenseUrl": "http://opensource.org/licenses/MIT", - "name": "Test v8", - "url": "https://test.com", - "version": "0.0.0" - */ - - } [HttpGet] @@ -43,6 +30,23 @@ namespace Umbraco.Web.Editors return CreatedPackage.GetById(id); } + [HttpPost] + public CreatedPackage PostCreatePackage(PackageInstance model) + { + //TODO Validation on the model?! + var newPackage = new CreatedPackage + { + Data = model + }; + + //Save then publish + newPackage.Save(); + newPackage.Publish(); + + //We should have packagepath populated now + return newPackage; + } + [HttpDelete] public HttpResponseMessage DeleteCreatedPackageById(int id) { From 68006f78ee6ebf184a80d1719630a151aa344517 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 6 Dec 2018 11:33:03 +0100 Subject: [PATCH 18/93] wire up end point for get created packages --- .../src/common/resources/package.resource.js | 18 ++++++++++ .../src/views/packages/edit.controller.js | 21 ++++++++--- .../packages/views/created.controller.js | 35 ++++--------------- .../src/views/packages/views/created.html | 6 ++++ 4 files changed, 46 insertions(+), 34 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 8fd6836884..522423f5b2 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 @@ -151,7 +151,25 @@ function packageResource($q, $http, umbDataFormatter, umbRequestHelper) { "packageInstallApiBaseUrl", "CleanUp"), umbPackage), 'Failed to install package. Error during the step "CleanUp" '); + }, + + /** + * @ngdoc method + * @name umbraco.resources.packageInstallResource#getCreated + * @methodOf umbraco.resources.packageInstallResource + * + * @description + * Gets a list of created packages + */ + getAllCreated: function() { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "packageApiBaseUrl", + "GetCreated")), + 'Failed to get created packages'); } + }; } diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js index ff587dd540..af17c44b60 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function EditController($location, contentTypeResource, templateResource, stylesheetResource, languageResource, dictionaryResource, dataTypeResource, editorService) { + function EditController($location, $routeParams, contentTypeResource, templateResource, stylesheetResource, languageResource, dictionaryResource, dataTypeResource, editorService) { const vm = this; @@ -24,11 +24,22 @@ vm.removeControl = removeControl; function onInit() { - // load package - vm.package = { - "name": "My package" - }; + const packageId = $routeParams.id; + const create = $routeParams.create; + + if(create) { + //pre populate package with some values + vm.package = { + "version": "1.0.0", + "license": "MIT License", + "licenseUrl": "http://opensource.org/licenses/MIT" + }; + + } else { + // load package + + } // get all doc types contentTypeResource.getAll().then(documentTypes => { diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/views/created.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/views/created.controller.js index 8c92f3553c..2a10a9e8fe 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/views/created.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/views/created.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function CreatedController($timeout, $location, localizationService, overlayService) { + function CreatedController($timeout, $location, packageResource, localizationService, overlayService) { const vm = this; @@ -13,33 +13,9 @@ vm.createdPackages = []; - //load created packages - $timeout(function(){ - vm.createdPackages = [ - { - "author": "Test", - "files": [], - "iconUrl": "", - "id": 1, - "license": "MIT License", - "licenseUrl": "http://opensource.org/licenses/MIT", - "name": "Test v8", - "url": "https://test.com", - "version": "0.0.0" - }, - { - "author": "Test", - "files": [], - "iconUrl": "", - "id": 2, - "license": "MIT License", - "licenseUrl": "http://opensource.org/licenses/MIT", - "name": "Another Test v8", - "url": "https://test.com", - "version": "0.0.0" - } - ]; - }, 1000); + packageResource.getAllCreated().then(createdPackages => { + vm.createdPackages = createdPackages; + }, angular.noop); } @@ -72,7 +48,8 @@ } function createPackage() { - console.log("create package"); + $location.search('create', null); + $location.path("packages/packages/edit/-1").search("create", "true"); } onInit(); diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/views/created.html b/src/Umbraco.Web.UI.Client/src/views/packages/views/created.html index 52f0cef9fd..53fd78b3c6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/views/created.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/views/created.html @@ -42,4 +42,10 @@ + + No packages have been created yet + +
\ No newline at end of file From 17a005c1635f1fc9afe698c3b312a2945ed3a788 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 6 Dec 2018 10:42:05 +0000 Subject: [PATCH 19/93] Fix up Umbraco.Sys object URL to API endpoint --- src/Umbraco.Web/Editors/BackOfficeServerVariables.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs index 472728e087..86c232c449 100644 --- a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs +++ b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs @@ -209,7 +209,7 @@ namespace Umbraco.Web.Editors controller => controller.Fetch(string.Empty)) }, { - "packageApiBaseUrl ", _urlHelper.GetUmbracoApiServiceBaseUrl( + "packageApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( controller => controller.GetCreatedPackages()) }, { From 452f97de625bddda1194511e17bb3e9783c3e98e Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 6 Dec 2018 11:53:26 +0100 Subject: [PATCH 20/93] wire up get created package by id --- .../src/common/resources/package.resource.js | 20 ++++++++++++++++++- .../src/views/packages/edit.controller.js | 7 ++++--- 2 files changed, 23 insertions(+), 4 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 522423f5b2..6023a16d5b 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 @@ -166,8 +166,26 @@ function packageResource($q, $http, umbDataFormatter, umbRequestHelper) { $http.get( umbRequestHelper.getApiUrl( "packageApiBaseUrl", - "GetCreated")), + "GetCreatedPackages")), 'Failed to get created packages'); + }, + + /** + * @ngdoc method + * @name umbraco.resources.packageInstallResource#getCreatedById + * @methodOf umbraco.resources.packageInstallResource + * + * @description + * Gets a created package by id + */ + getCreatedById: function(id) { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "packageApiBaseUrl", + "GetCreatedPackageById", + [{ id: id }])), + 'Failed to get package'); } }; diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js index af17c44b60..a3408e1360 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function EditController($location, $routeParams, contentTypeResource, templateResource, stylesheetResource, languageResource, dictionaryResource, dataTypeResource, editorService) { + function EditController($location, $routeParams, packageResource, contentTypeResource, templateResource, stylesheetResource, languageResource, dictionaryResource, dataTypeResource, editorService) { const vm = this; @@ -35,10 +35,11 @@ "license": "MIT License", "licenseUrl": "http://opensource.org/licenses/MIT" }; - } else { // load package - + packageResource.getCreatedById(packageId).then(createdPackage => { + vm.package = createdPackage; + }, angular.noop); } // get all doc types From c7e6cf44ed019e4e99c493c786e02e7b4c079301 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 6 Dec 2018 11:21:01 +0000 Subject: [PATCH 21/93] Anotate model so its camelCase friendly for API responses for Mads --- .../PackageInstance/PackageInstance.cs | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackageInstance.cs b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackageInstance.cs index 8f7cb56320..63bd568b73 100644 --- a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackageInstance.cs +++ b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackageInstance.cs @@ -1,70 +1,97 @@ using System; - -using System.Xml; -using System.Xml.XPath; using System.Collections.Generic; +using System.Runtime.Serialization; namespace umbraco.cms.businesslogic.packager { + [DataContract(Name = "packageInstance")] public class PackageInstance { + [DataMember(Name = "id")] public int Id { get; set; } + [DataMember(Name = "repositoryGuid")] public string RepositoryGuid { get; set; } + [DataMember(Name = "packageGuid")] public string PackageGuid { get; set; } + [DataMember(Name = "hasUpdate")] public bool HasUpdate { get; set; } + [DataMember(Name = "name")] public string Name { get; set; } + [DataMember(Name = "url")] public string Url { get; set; } + [DataMember(Name = "folder")] public string Folder { get; set; } + [DataMember(Name = "packagePath")] public string PackagePath { get; set; } + [DataMember(Name = "version")] public string Version { get; set; } /// /// The minimum umbraco version that this package requires /// + [DataMember(Name = "umbracoVersion")] public Version UmbracoVersion { get; set; } + [DataMember(Name = "author")] public string Author { get; set; } + [DataMember(Name = "authorUrl")] public string AuthorUrl { get; set; } + [DataMember(Name = "license")] public string License { get; set; } + [DataMember(Name = "licenseUrl")] public string LicenseUrl { get; set; } + [DataMember(Name = "readme")] public string Readme { get; set; } + [DataMember(Name = "contentLoadChildNodes")] public bool ContentLoadChildNodes { get; set; } + [DataMember(Name = "contentNodeId")] public string ContentNodeId { get; set; } + [DataMember(Name = "macros")] public List Macros { get; set; } + [DataMember(Name = "languages")] public List Languages { get; set; } + [DataMember(Name = "dictionaryItems")] public List DictionaryItems { get; set; } + [DataMember(Name = "templates")] public List Templates { get; set; } + [DataMember(Name = "documenttypes")] public List Documenttypes { get; set; } + [DataMember(Name = "stylesheets")] public List Stylesheets { get; set; } + [DataMember(Name = "files")] public List Files { get; set; } + [DataMember(Name = "loadControl")] public string LoadControl { get; set; } + [DataMember(Name = "actions")] public string Actions { get; set; } + [DataMember(Name = "dataTypes")] public List DataTypes { get; set; } + [DataMember(Name = "iconUrl")] public string IconUrl { get; set; } public PackageInstance() From 465229fb39f4f49deb56f5eaec9822fcfb8f1553 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 6 Dec 2018 11:21:28 +0000 Subject: [PATCH 22/93] Return PackageInstance as opposed to CreatedPackage in API responses --- src/Umbraco.Web/Editors/PackageController.cs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web/Editors/PackageController.cs b/src/Umbraco.Web/Editors/PackageController.cs index d192ee21f3..652a7c29f8 100644 --- a/src/Umbraco.Web/Editors/PackageController.cs +++ b/src/Umbraco.Web/Editors/PackageController.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; @@ -18,20 +19,21 @@ namespace Umbraco.Web.Editors public class PackageController : UmbracoAuthorizedJsonController { [HttpGet] - public List GetCreatedPackages() + public List GetCreatedPackages() { //TODO: Could be too much data down the wire - return CreatedPackage.GetAllCreatedPackages(); + return CreatedPackage.GetAllCreatedPackages().Select(x => x.Data).ToList(); } [HttpGet] - public CreatedPackage GetCreatedPackageById(int id) + public PackageInstance GetCreatedPackageById(int id) { - return CreatedPackage.GetById(id); + //TODO throw an error if cant find by ID + return CreatedPackage.GetById(id).Data; } [HttpPost] - public CreatedPackage PostCreatePackage(PackageInstance model) + public PackageInstance PostCreatePackage(PackageInstance model) { //TODO Validation on the model?! var newPackage = new CreatedPackage @@ -42,14 +44,15 @@ namespace Umbraco.Web.Editors //Save then publish newPackage.Save(); newPackage.Publish(); - + //We should have packagepath populated now - return newPackage; + return newPackage.Data; } [HttpDelete] public HttpResponseMessage DeleteCreatedPackageById(int id) { + //TODO: Validation ensure can find it by ID var package = CreatedPackage.GetById(id); package.Delete(); From 3c399ade16d9d4481d8671bee094e8baf20a7273 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 6 Dec 2018 12:50:08 +0100 Subject: [PATCH 23/93] last textarea should be actions --- src/Umbraco.Web.UI.Client/src/views/packages/edit.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html index d6309d812a..a6ef4093a7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html @@ -255,13 +255,13 @@
From 7df08696493461a0f12b33ee4c373e961d507bbb Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 6 Dec 2018 13:13:55 +0100 Subject: [PATCH 24/93] wire up delete --- .../packages/views/created.controller.js | 23 ++++++++++++++----- .../src/views/packages/views/created.html | 2 +- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/views/created.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/views/created.controller.js index 2a10a9e8fe..9d06ea1092 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/views/created.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/views/created.controller.js @@ -19,28 +19,39 @@ } - function deleteCreatedPackage(createdPackage) { + function deleteCreatedPackage(event, index, createdPackage) { + + event.stopPropagation(); + event.preventDefault(); const dialog = { submitButtonLabelKey: "contentTypeEditor_yesDelete", submit: function (model) { - performDelete(createdPackage); + performDelete(index, createdPackage); overlayService.close(); }, close: function () { overlayService.close(); } }; + + const keys = [ + "general_delete", + "defaultdialogs_confirmdelete" + ]; - localizationService.localize("general_delete").then(value => { - dialog.title = value; + localizationService.localizeMany(keys).then(values => { + dialog.title = values[0]; + dialog.content = values[1]; overlayService.open(dialog); }); } - function performDelete(createdPackage) { - console.log("perform delete"); + function performDelete(index, createdPackage) { + packageResource.deleteCreatedPackage(createdPackage.id).then(()=> { + vm.createdPackages.splice(index, 1); + }, angular.noop); } function goToPackage(createdPackage) { diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/views/created.html b/src/Umbraco.Web.UI.Client/src/views/packages/views/created.html index 53fd78b3c6..632d2e2b3c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/views/created.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/views/created.html @@ -35,7 +35,7 @@ size="xxs" label-key="general_delete" add-ellipsis="true" - action="vm.deleteCreatedPackage(createdPackage)"> + action="vm.deleteCreatedPackage($event, $index, createdPackage)"> From 9409b46ef41774b1107103bc5b56360bbb8ec9a4 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 6 Dec 2018 13:57:21 +0100 Subject: [PATCH 25/93] fix back buttons --- .../src/views/packages/overview.controller.js | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/overview.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/overview.controller.js index 418861c636..4ca0015aa5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/overview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/overview.controller.js @@ -10,6 +10,8 @@ // we will refresh and then navigate there. var installPackageUri = localStorageService.get("packageInstallUri"); + const packageUri = $location.search().subview; + if (installPackageUri) { localStorageService.remove("packageInstallUri"); } @@ -28,28 +30,41 @@ "name": "Packages", "icon": "icon-cloud", "view": "views/packages/views/repo.html", - "active": !installPackageUri || installPackageUri === "navigation", - "alias": "umbPackages" + "active": !packageUri || installPackageUri === "navigation" || packageUri === "navigation", + "alias": "umbPackages", + "action": function() { + $location.search("subview", "navigation"); + } }, { "name": "Installed", "icon": "icon-box", "view": "views/packages/views/installed.html", - "active": installPackageUri === "installed", - "alias": "umbInstalled" + "active": installPackageUri === "installed" || packageUri === "installed", + "alias": "umbInstalled", + "action": function() { + $location.search("subview", "installed"); + } }, { "name": "Install local", "icon": "icon-add", "view": "views/packages/views/install-local.html", - "active": installPackageUri === "local", - "alias": "umbInstallLocal" + "active": installPackageUri === "local" || packageUri === "local", + "alias": "umbInstallLocal", + "action": function() { + $location.search("subview", "local"); + } }, { "name": "Created", "icon": "icon-add", "view": "views/packages/views/created.html", - "alias": "umbCreatedPackages" + "active": packageUri === "created", + "alias": "umbCreatedPackages", + "action": function() { + $location.search("subview", "created"); + } } ]; From 34dc81058a813115c5d74f07b4c5ae599d88b30c Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 6 Dec 2018 13:09:34 +0000 Subject: [PATCH 26/93] Move the Delete method over into our new controller --- src/Umbraco.Web/Editors/PackageController.cs | 19 ++++++++++++------ .../Editors/PackageInstallController.cs | 20 +------------------ .../PackageInstance/PackageInstance.cs | 2 +- 3 files changed, 15 insertions(+), 26 deletions(-) diff --git a/src/Umbraco.Web/Editors/PackageController.cs b/src/Umbraco.Web/Editors/PackageController.cs index 652a7c29f8..c2a78da1be 100644 --- a/src/Umbraco.Web/Editors/PackageController.cs +++ b/src/Umbraco.Web/Editors/PackageController.cs @@ -49,16 +49,23 @@ namespace Umbraco.Web.Editors return newPackage.Data; } + /// + /// Deletes a created package + /// + /// + /// + [HttpPost] [HttpDelete] - public HttpResponseMessage DeleteCreatedPackageById(int id) + public IHttpActionResult DeleteCreatedPackage(int packageId) { - //TODO: Validation ensure can find it by ID - var package = CreatedPackage.GetById(id); + var package = CreatedPackage.GetById(packageId); + if (package == null) + return NotFound(); + package.Delete(); - //204 No Content - return new HttpResponseMessage(HttpStatusCode.NoContent); + return Ok(); } - + } } diff --git a/src/Umbraco.Web/Editors/PackageInstallController.cs b/src/Umbraco.Web/Editors/PackageInstallController.cs index 94465feab8..7224ddfb98 100644 --- a/src/Umbraco.Web/Editors/PackageInstallController.cs +++ b/src/Umbraco.Web/Editors/PackageInstallController.cs @@ -289,25 +289,7 @@ namespace Umbraco.Web.Editors }) .ToList(); } - - /// - /// Deletes a created package - /// - /// - /// - [HttpPost] - [HttpDelete] - public IHttpActionResult DeleteCreatedPackage(int packageId) - { - var package = CreatedPackage.GetById(packageId); - if (package == null) - return NotFound(); - - package.Delete(); - - return Ok(); - } - + private void PopulateFromPackageData(LocalPackageInstallModel model) { var ins = new global::umbraco.cms.businesslogic.packager.Installer(Security.CurrentUser.Id); diff --git a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackageInstance.cs b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackageInstance.cs index 63bd568b73..68166c9dd5 100644 --- a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackageInstance.cs +++ b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackageInstance.cs @@ -39,7 +39,7 @@ namespace umbraco.cms.businesslogic.packager /// [DataMember(Name = "umbracoVersion")] public Version UmbracoVersion { get; set; } - + [DataMember(Name = "author")] public string Author { get; set; } From e566e240b13d427d93cb493bcc911e146eb4c61f Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 6 Dec 2018 14:49:02 +0100 Subject: [PATCH 27/93] prefill umbraco version number --- .../src/views/packages/edit.controller.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js index a3408e1360..f9eb6f8c84 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js @@ -31,9 +31,10 @@ if(create) { //pre populate package with some values vm.package = { - "version": "1.0.0", + "version": "0.0.1", "license": "MIT License", - "licenseUrl": "http://opensource.org/licenses/MIT" + "licenseUrl": "http://opensource.org/licenses/MIT", + "umbracoVersion": Umbraco.Sys.ServerVariables.application.version }; } else { // load package From 6f5155b0f39c53c55c093a2f92364b53cbad0117 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 6 Dec 2018 13:54:38 +0000 Subject: [PATCH 28/93] Adds in new attribute to override the specific PackageController JSON Serializer to use the NewtonSoft built-in VersionConvertor --- src/Umbraco.Web/Editors/PackageController.cs | 3 +++ src/Umbraco.Web/Umbraco.Web.csproj | 1 + .../WebApi/SerializeVersionAttribute.cs | 15 +++++++++++++++ 3 files changed, 19 insertions(+) create mode 100644 src/Umbraco.Web/WebApi/SerializeVersionAttribute.cs diff --git a/src/Umbraco.Web/Editors/PackageController.cs b/src/Umbraco.Web/Editors/PackageController.cs index c2a78da1be..baacea53d8 100644 --- a/src/Umbraco.Web/Editors/PackageController.cs +++ b/src/Umbraco.Web/Editors/PackageController.cs @@ -2,9 +2,11 @@ using System.Linq; using System.Net; using System.Net.Http; +using System.Net.Http.Formatting; using System.Web.Http; using umbraco.cms.businesslogic.packager; using Umbraco.Web.Mvc; +using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; namespace Umbraco.Web.Editors @@ -15,6 +17,7 @@ namespace Umbraco.Web.Editors /// A controller used for installing packages and managing all of the data in the packages section in the back office /// [PluginController("UmbracoApi")] + [SerializeVersion] [UmbracoApplicationAuthorize(Core.Constants.Applications.Packages)] public class PackageController : UmbracoAuthorizedJsonController { diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 5b396c9486..e32d808be9 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -190,6 +190,7 @@ + diff --git a/src/Umbraco.Web/WebApi/SerializeVersionAttribute.cs b/src/Umbraco.Web/WebApi/SerializeVersionAttribute.cs new file mode 100644 index 0000000000..ea285434b3 --- /dev/null +++ b/src/Umbraco.Web/WebApi/SerializeVersionAttribute.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json.Converters; +using System; +using System.Web.Http.Controllers; + +namespace Umbraco.Web.WebApi +{ + internal class SerializeVersionAttribute : Attribute, IControllerConfiguration + { + public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor) + { + var formatter = controllerSettings.Formatters.JsonFormatter; + formatter.SerializerSettings.Converters.Add(new VersionConverter()); + } + } +} From 57f462b2046eed5bec1cf531f12ed8cd7964aa98 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 6 Dec 2018 15:26:12 +0100 Subject: [PATCH 29/93] wire up create package --- .../src/common/resources/package.resource.js | 18 +++++++++++++++++- .../src/views/packages/edit.controller.js | 10 +++++++++- .../src/views/packages/edit.html | 16 ++++------------ 3 files changed, 30 insertions(+), 14 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 6023a16d5b..28a9f3b9ac 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 @@ -186,8 +186,24 @@ function packageResource($q, $http, umbDataFormatter, umbRequestHelper) { "GetCreatedPackageById", [{ id: id }])), 'Failed to get package'); + }, + + /** + * @ngdoc method + * @name umbraco.resources.packageInstallResource#createPackage + * @methodOf umbraco.resources.packageInstallResource + * + * @description + * Creates a new package + */ + createPackage: function (umbPackage) { + return umbRequestHelper.resourcePromise( + $http.post( + umbRequestHelper.getApiUrl( + "packageApiBaseUrl", + "PostCreatePackage"), umbPackage), + 'Failed to create package'); } - }; } diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js index f9eb6f8c84..d5571ba78c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js @@ -83,7 +83,15 @@ } function createPackage() { - console.log("create package"); + + vm.createPackageButtonState = "busy"; + + packageResource.createPackage(vm.package).then((updatedPackage) => { + vm.package = updatedPackage; + vm.createPackageButtonState = "success"; + }, function(error){ + vm.createPackageButtonState = "error"; + }); } function save() { diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html index a6ef4093a7..cc48dddf6f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html @@ -47,7 +47,7 @@ - + @@ -268,25 +268,17 @@
+
{{vm.package | json}}
+ - - From 2013fd1619c56be965a0008f8ff5d932e15237a0 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 6 Dec 2018 14:41:53 +0000 Subject: [PATCH 30/93] Try the MakeNew method on CreatedPackage() --- src/Umbraco.Web/Editors/PackageController.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web/Editors/PackageController.cs b/src/Umbraco.Web/Editors/PackageController.cs index baacea53d8..3713674f84 100644 --- a/src/Umbraco.Web/Editors/PackageController.cs +++ b/src/Umbraco.Web/Editors/PackageController.cs @@ -38,11 +38,11 @@ namespace Umbraco.Web.Editors [HttpPost] public PackageInstance PostCreatePackage(PackageInstance model) { + + var newPackage = CreatedPackage.MakeNew(model.Name); + newPackage.Data = model; + //TODO Validation on the model?! - var newPackage = new CreatedPackage - { - Data = model - }; //Save then publish newPackage.Save(); From 7f380347dcc0a6047c59827c44f73e3f6e85580b Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 6 Dec 2018 15:35:40 +0000 Subject: [PATCH 31/93] Some WIP binding updates in the view - will mostly need to bind an array of integers back to items --- .../src/views/packages/edit.html | 92 ++++++++++--------- 1 file changed, 49 insertions(+), 43 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html index cc48dddf6f..9ba5421683 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html @@ -3,7 +3,7 @@
- + - + @@ -23,7 +23,7 @@
Package Properties
 
- +
@@ -39,7 +39,7 @@ - + @@ -67,22 +67,24 @@
- +
- +
Package Content
 
- +
+ + +
{{vm.documentTypes | json }} 
+
{{vm.templates | json }} 
- + +
{{vm.stylesheets | json }}
- NOT IMPLEMENTED + NOT IMPLEMENTED IN V8 YET +
{{vm.languages | json }}
@@ -254,9 +260,9 @@  
-
Documentation @@ -275,7 +281,7 @@ - - + - +
\ No newline at end of file From 6566a43ef9a26be1901f73b64e05fa8a3608d1dc Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Fri, 7 Dec 2018 10:05:31 +0100 Subject: [PATCH 32/93] change end point for delete created package --- .../src/common/resources/package.resource.js | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 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 28a9f3b9ac..0b74729cf6 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 @@ -33,15 +33,6 @@ function packageResource($q, $http, umbDataFormatter, umbRequestHelper) { 'Failed to validate package ' + name); }, - deleteCreatedPackage: function (packageId) { - return umbRequestHelper.resourcePromise( - $http.post( - umbRequestHelper.getApiUrl( - "packageInstallApiBaseUrl", - "DeleteCreatedPackage", { packageId: packageId })), - 'Failed to delete package ' + packageId); - }, - uninstall: function(packageId) { return umbRequestHelper.resourcePromise( $http.post( @@ -203,6 +194,23 @@ function packageResource($q, $http, umbDataFormatter, umbRequestHelper) { "packageApiBaseUrl", "PostCreatePackage"), umbPackage), 'Failed to create package'); + }, + + /** + * @ngdoc method + * @name umbraco.resources.packageInstallResource#deleteCreatedPackage + * @methodOf umbraco.resources.packageInstallResource + * + * @description + * Detes a created package + */ + deleteCreatedPackage: function (packageId) { + return umbRequestHelper.resourcePromise( + $http.post( + umbRequestHelper.getApiUrl( + "packageApiBaseUrl", + "DeleteCreatedPackage", { packageId: packageId })), + 'Failed to delete package ' + packageId); } }; } From fbf29f9f4718f1233939f69dc439962e694505bc Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Fri, 7 Dec 2018 10:13:55 +0100 Subject: [PATCH 33/93] bind stylesheets correctly --- src/Umbraco.Web.UI.Client/src/views/packages/edit.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html index 9ba5421683..796edff588 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html @@ -143,7 +143,7 @@ + checklist-value="stylesheet.name" /> {{stylesheet.path}}
From a9aba6731dbe41de27475c748b4287ba860efed5 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Fri, 7 Dec 2018 09:29:44 +0000 Subject: [PATCH 34/93] Update to CreatePackage API endpoint - the ID & Guid was getting overwritten by mistake --- src/Umbraco.Web/Editors/PackageController.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/Editors/PackageController.cs b/src/Umbraco.Web/Editors/PackageController.cs index 3713674f84..a2505a1287 100644 --- a/src/Umbraco.Web/Editors/PackageController.cs +++ b/src/Umbraco.Web/Editors/PackageController.cs @@ -38,10 +38,16 @@ namespace Umbraco.Web.Editors [HttpPost] public PackageInstance PostCreatePackage(PackageInstance model) { - var newPackage = CreatedPackage.MakeNew(model.Name); - newPackage.Data = model; + var packageId = newPackage.Data.Id; + var packageGuid = newPackage.Data.PackageGuid; + //Need to reset the package ID - as the posted model the package ID is always 0 + //MakeNew will init create the XML & update the file and give us an ID to use + newPackage.Data = model; + newPackage.Data.Id = packageId; + newPackage.Data.PackageGuid = packageGuid; + //TODO Validation on the model?! //Save then publish From 5d3432db511af114b05b8dc6ea2dc75f0d62b673 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Fri, 7 Dec 2018 11:15:08 +0100 Subject: [PATCH 35/93] get render model for content item --- .../src/views/packages/edit.controller.js | 16 +++++++++++++--- .../src/views/packages/edit.html | 6 ++---- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js index d5571ba78c..6520a2ecf8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function EditController($location, $routeParams, packageResource, contentTypeResource, templateResource, stylesheetResource, languageResource, dictionaryResource, dataTypeResource, editorService) { + function EditController($location, $routeParams, entityResource, packageResource, contentTypeResource, templateResource, stylesheetResource, languageResource, dictionaryResource, dataTypeResource, editorService) { const vm = this; @@ -40,6 +40,15 @@ // load package packageResource.getCreatedById(packageId).then(createdPackage => { vm.package = createdPackage; + + // get render model for content node + if(vm.package.contentNodeId) { + entityResource.getById(vm.package.contentNodeId, "Document") + .then((entity) => { + vm.contentNodeDisplayModel = entity; + }); + } + }, angular.noop); } @@ -99,14 +108,15 @@ } function removeContentItem() { - vm.package.contentItem = null; + vm.package.contentNodeId = null; } function openContentPicker() { const contentPicker = { submit: function(model) { if(model.selection && model.selection.length > 0) { - vm.package.contentItem = model.selection[0]; + vm.package.contentNodeId = model.selection[0].id.toString(); + vm.contentNodeDisplayModel = model.selection[0]; } editorService.close(); }, diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html index 796edff588..77743a1116 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html @@ -81,12 +81,10 @@ - - Date: Fri, 7 Dec 2018 10:22:33 +0000 Subject: [PATCH 36/93] Adds in validation to the POST / Create package to ensure name, author is set etc... --- src/Umbraco.Web/Editors/PackageController.cs | 16 +++++++++++----- .../Packager/PackageInstance/PackageInstance.cs | 8 ++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web/Editors/PackageController.cs b/src/Umbraco.Web/Editors/PackageController.cs index a2505a1287..44efa44c21 100644 --- a/src/Umbraco.Web/Editors/PackageController.cs +++ b/src/Umbraco.Web/Editors/PackageController.cs @@ -24,20 +24,28 @@ namespace Umbraco.Web.Editors [HttpGet] public List GetCreatedPackages() { - //TODO: Could be too much data down the wire return CreatedPackage.GetAllCreatedPackages().Select(x => x.Data).ToList(); } [HttpGet] public PackageInstance GetCreatedPackageById(int id) { - //TODO throw an error if cant find by ID - return CreatedPackage.GetById(id).Data; + var package = CreatedPackage.GetById(id); + if (package == null) + throw new HttpResponseException(HttpStatusCode.NotFound); + + return package.Data; } [HttpPost] public PackageInstance PostCreatePackage(PackageInstance model) { + if (ModelState.IsValid == false) + { + //Throw/bubble up errors + throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); + } + var newPackage = CreatedPackage.MakeNew(model.Name); var packageId = newPackage.Data.Id; var packageGuid = newPackage.Data.PackageGuid; @@ -48,8 +56,6 @@ namespace Umbraco.Web.Editors newPackage.Data.Id = packageId; newPackage.Data.PackageGuid = packageGuid; - //TODO Validation on the model?! - //Save then publish newPackage.Save(); newPackage.Publish(); diff --git a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackageInstance.cs b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackageInstance.cs index 68166c9dd5..615d726dbc 100644 --- a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackageInstance.cs +++ b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackageInstance.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Runtime.Serialization; namespace umbraco.cms.businesslogic.packager @@ -20,9 +21,12 @@ namespace umbraco.cms.businesslogic.packager public bool HasUpdate { get; set; } [DataMember(Name = "name")] + [Required] public string Name { get; set; } [DataMember(Name = "url")] + [Required] + [Url] public string Url { get; set; } [DataMember(Name = "folder")] @@ -32,6 +36,7 @@ namespace umbraco.cms.businesslogic.packager public string PackagePath { get; set; } [DataMember(Name = "version")] + [Required] public string Version { get; set; } /// @@ -41,9 +46,12 @@ namespace umbraco.cms.businesslogic.packager public Version UmbracoVersion { get; set; } [DataMember(Name = "author")] + [Required] public string Author { get; set; } [DataMember(Name = "authorUrl")] + [Required] + [Url] public string AuthorUrl { get; set; } [DataMember(Name = "license")] From 8252f4d95a34d824435c6012a24b050b2db69ac3 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Fri, 7 Dec 2018 11:31:33 +0100 Subject: [PATCH 37/93] clear create query string when using back button --- src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js index 6520a2ecf8..12a9ea0092 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js @@ -88,7 +88,7 @@ } function back() { - $location.path("packages/packages/overview"); + $location.path("packages/packages/overview").search('create', null);; } function createPackage() { From b35a8b406b8a6d3a2cd9aee1b393ce1dd3e29bdb Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Fri, 7 Dec 2018 10:37:33 +0000 Subject: [PATCH 38/93] Fix API Controller for Package create to support new entry & update existing entry --- src/Umbraco.Web/Editors/PackageController.cs | 33 +++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Web/Editors/PackageController.cs b/src/Umbraco.Web/Editors/PackageController.cs index 44efa44c21..8b9e15f9d1 100644 --- a/src/Umbraco.Web/Editors/PackageController.cs +++ b/src/Umbraco.Web/Editors/PackageController.cs @@ -45,23 +45,34 @@ namespace Umbraco.Web.Editors //Throw/bubble up errors throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); } + + var package = new CreatedPackage + { + Data = model + }; - var newPackage = CreatedPackage.MakeNew(model.Name); - var packageId = newPackage.Data.Id; - var packageGuid = newPackage.Data.PackageGuid; + //If ID is 0 & PackageGuid is null - its brand new + if(model.Id == 0 && model.PackageGuid == null) + { + //Brand new + package = CreatedPackage.MakeNew(model.Name); - //Need to reset the package ID - as the posted model the package ID is always 0 - //MakeNew will init create the XML & update the file and give us an ID to use - newPackage.Data = model; - newPackage.Data.Id = packageId; - newPackage.Data.PackageGuid = packageGuid; + var packageId = package.Data.Id; + var packageGuid = package.Data.PackageGuid; + + //Need to reset the package ID - as the posted model the package ID is always 0 + //MakeNew will init create the XML & update the file and give us an ID to use + package.Data = model; + package.Data.Id = packageId; + package.Data.PackageGuid = packageGuid; + } //Save then publish - newPackage.Save(); - newPackage.Publish(); + package.Save(); + package.Publish(); //We should have packagepath populated now - return newPackage.Data; + return package.Data; } /// From bb5a561932ef9fc4c2f916b5a8189217844fd571 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Fri, 7 Dec 2018 11:43:36 +0000 Subject: [PATCH 39/93] Wiring up server side validation in the UI --- .../src/views/packages/edit.controller.js | 24 +++++---- .../src/views/packages/edit.html | 50 ++++++++++++++----- 2 files changed, 51 insertions(+), 23 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js index 12a9ea0092..494c26e8e8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function EditController($location, $routeParams, entityResource, packageResource, contentTypeResource, templateResource, stylesheetResource, languageResource, dictionaryResource, dataTypeResource, editorService) { + function EditController($scope, $location, $routeParams, entityResource, packageResource, contentTypeResource, templateResource, stylesheetResource, languageResource, dictionaryResource, dataTypeResource, editorService, formHelper) { const vm = this; @@ -91,16 +91,20 @@ $location.path("packages/packages/overview").search('create', null);; } - function createPackage() { + function createPackage(editPackageForm) { - vm.createPackageButtonState = "busy"; + if (formHelper.submitForm({ formCtrl: editPackageForm, scope: $scope })) { - packageResource.createPackage(vm.package).then((updatedPackage) => { - vm.package = updatedPackage; - vm.createPackageButtonState = "success"; - }, function(error){ - vm.createPackageButtonState = "error"; - }); + vm.createPackageButtonState = "busy"; + + packageResource.createPackage(vm.package).then((updatedPackage) => { + vm.package = updatedPackage; + vm.createPackageButtonState = "success"; + }, function(err){ + formHelper.handleError(err); + vm.createPackageButtonState = "error"; + }); + } } function save() { @@ -144,7 +148,7 @@ vm.package.files.push(selected); }); } - + editorService.close(); }, close: function() { diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html index 77743a1116..5f63d74fe7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html @@ -1,6 +1,6 @@ -
+
-
+ @@ -27,43 +27,67 @@
- + + + + Required + {{editPackageForm.url.errorMsg}} + - + + + + Required + {{editPackageForm.version.errorMsg}} + - + - + - + + + + {{editPackageForm.umbracoVersion.errorMsg}} + - + + + + Required + {{editPackageForm.author.errorMsg}} + - + + + + Required + {{editPackageForm.authorUrl.errorMsg}} + - + - + - +
@@ -281,7 +305,7 @@ Date: Fri, 7 Dec 2018 13:10:50 +0100 Subject: [PATCH 40/93] fix binding of documenttypes and templates --- .../src/views/packages/edit.controller.js | 10 ++++++++++ src/Umbraco.Web.UI.Client/src/views/packages/edit.html | 4 +--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js index 494c26e8e8..aa23b0209f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js @@ -54,11 +54,21 @@ // get all doc types contentTypeResource.getAll().then(documentTypes => { + // a package stores the id as a string so we + // need to convert all ids to string for comparison + documentTypes.forEach(documentType => { + documentType.id = documentType.id.toString(); + }); vm.documentTypes = documentTypes; }); // get all templates templateResource.getAll().then(templates => { + // a package stores the id as a string so we + // need to convert all ids to string for comparison + templates.forEach(template => { + template.id = template.id.toString(); + }); vm.templates = templates; }); diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html index 5f63d74fe7..4fc3ea74d1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html @@ -132,13 +132,12 @@ -
{{vm.documentTypes | json }} 
@@ -146,7 +145,6 @@ -
{{vm.templates | json }} 
PropertyGroup + + //TODO: Dictionary? } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs index ce8b5835e2..f2392c9c3d 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs @@ -8,30 +8,29 @@ using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Security; using Umbraco.Core.Services; +using Umbraco.Core.Services.Implement; using Umbraco.Core.Xml.XPath; using Umbraco.Web.PublishedCache.NuCache.Navigable; namespace Umbraco.Web.PublishedCache.NuCache { - class MemberCache : IPublishedMemberCache, INavigableData + internal class MemberCache : IPublishedMemberCache, INavigableData { private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; public readonly IVariationContextAccessor VariationContextAccessor; + private readonly IEntityXmlSerializer _entitySerializer; private readonly ICacheProvider _snapshotCache; private readonly IMemberService _memberService; - private readonly IDataTypeService _dataTypeService; - private readonly ILocalizationService _localizationService; private readonly PublishedContentTypeCache _contentTypeCache; private readonly bool _previewDefault; - public MemberCache(bool previewDefault, ICacheProvider snapshotCache, IMemberService memberService, IDataTypeService dataTypeService, ILocalizationService localizationService, PublishedContentTypeCache contentTypeCache, IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor) + public MemberCache(bool previewDefault, ICacheProvider snapshotCache, IMemberService memberService, PublishedContentTypeCache contentTypeCache, IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, IEntityXmlSerializer entitySerializer) { _snapshotCache = snapshotCache; _publishedSnapshotAccessor = publishedSnapshotAccessor; VariationContextAccessor = variationContextAccessor; + _entitySerializer = entitySerializer; _memberService = memberService; - _dataTypeService = dataTypeService; - _localizationService = localizationService; _previewDefault = previewDefault; _contentTypeCache = contentTypeCache; } @@ -141,7 +140,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var result = _memberService.GetById(id); if (result == null) return null; - var s = EntityXmlSerializer.Serialize(_dataTypeService, _localizationService, result); + var s = _entitySerializer.Serialize(result); var n = s.GetXmlNode(); return n.CreateNavigator(); } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index 3aa33f0e2f..4ebddee6dc 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -43,6 +43,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly IMemberRepository _memberRepository; private readonly IGlobalSettings _globalSettings; private readonly ISiteDomainHelper _siteDomainHelper; + private readonly IEntityXmlSerializer _entitySerializer; private readonly IDefaultCultureAccessor _defaultCultureAccessor; // volatile because we read it with no lock @@ -85,7 +86,8 @@ namespace Umbraco.Web.PublishedCache.NuCache ILogger logger, IScopeProvider scopeProvider, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, IDefaultCultureAccessor defaultCultureAccessor, - IDataSource dataSource, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper) + IDataSource dataSource, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper, + IEntityXmlSerializer entitySerializer) : base(publishedSnapshotAccessor, variationContextAccessor) { //if (Interlocked.Increment(ref _singletonCheck) > 1) @@ -102,6 +104,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _defaultCultureAccessor = defaultCultureAccessor; _globalSettings = globalSettings; _siteDomainHelper = siteDomainHelper; + _entitySerializer = entitySerializer; // we always want to handle repository events, configured or not // assuming no repository event will trigger before the whole db is ready @@ -1011,7 +1014,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { ContentCache = new ContentCache(previewDefault, contentSnap, snapshotCache, elementsCache, domainHelper, _globalSettings, _serviceContext.LocalizationService), MediaCache = new MediaCache(previewDefault, mediaSnap, snapshotCache, elementsCache), - MemberCache = new MemberCache(previewDefault, snapshotCache, _serviceContext.MemberService, _serviceContext.DataTypeService, _serviceContext.LocalizationService, memberTypeCache, PublishedSnapshotAccessor, VariationContextAccessor), + MemberCache = new MemberCache(previewDefault, snapshotCache, _serviceContext.MemberService, memberTypeCache, PublishedSnapshotAccessor, VariationContextAccessor, _entitySerializer), DomainCache = domainCache, SnapshotCache = snapshotCache, ElementsCache = elementsCache diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs index 229a981510..7ec935139e 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs @@ -17,6 +17,7 @@ using Umbraco.Core.Xml; using Umbraco.Examine; using Umbraco.Core.Cache; using Umbraco.Core.Services; +using Umbraco.Core.Services.Implement; using Umbraco.Web.Composing; namespace Umbraco.Web.PublishedCache.XmlPublishedCache @@ -37,14 +38,14 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache // method GetExamineManagerSafe(). // private readonly ISearcher _searchProvider; - private readonly IIndex _indexProvider; private readonly XmlStore _xmlStore; private readonly PublishedContentTypeCache _contentTypeCache; + private readonly IEntityXmlSerializer _entitySerializer; // must be specified by the ctor private readonly ICacheProvider _cacheProvider; - public PublishedMediaCache(XmlStore xmlStore, IMediaService mediaService, IUserService userService, ICacheProvider cacheProvider, PublishedContentTypeCache contentTypeCache) + public PublishedMediaCache(XmlStore xmlStore, IMediaService mediaService, IUserService userService, ICacheProvider cacheProvider, PublishedContentTypeCache contentTypeCache, IEntityXmlSerializer entitySerializer) : base(false) { _mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); @@ -53,6 +54,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache _cacheProvider = cacheProvider; _xmlStore = xmlStore; _contentTypeCache = contentTypeCache; + _entitySerializer = entitySerializer; } /// @@ -61,18 +63,18 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache /// /// /// - /// /// /// - internal PublishedMediaCache(IMediaService mediaService, IUserService userService, ISearcher searchProvider, BaseIndexProvider indexProvider, ICacheProvider cacheProvider, PublishedContentTypeCache contentTypeCache) + /// + internal PublishedMediaCache(IMediaService mediaService, IUserService userService, ISearcher searchProvider, ICacheProvider cacheProvider, PublishedContentTypeCache contentTypeCache, IEntityXmlSerializer entitySerializer) : base(false) { _mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); _userService = userService ?? throw new ArgumentNullException(nameof(userService)); _searchProvider = searchProvider ?? throw new ArgumentNullException(nameof(searchProvider)); - _indexProvider = indexProvider ?? throw new ArgumentNullException(nameof(indexProvider)); _cacheProvider = cacheProvider; _contentTypeCache = contentTypeCache; + _entitySerializer = entitySerializer; } static PublishedMediaCache() @@ -555,14 +557,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache return Enumerable.Empty(); } - var serialized = EntityXmlSerializer.Serialize( - Current.Services.MediaService, - Current.Services.DataTypeService, - Current.Services.UserService, - Current.Services.LocalizationService, - Current.UrlSegmentProviders, - media, - true); + var serialized = _entitySerializer.Serialize(media, true); var mediaIterator = serialized.CreateNavigator().Select("/"); diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs index 78585ba2e2..e286e9d95c 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs @@ -34,6 +34,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache private readonly IGlobalSettings _globalSettings; private readonly IDefaultCultureAccessor _defaultCultureAccessor; private readonly ISiteDomainHelper _siteDomainHelper; + private readonly IEntityXmlSerializer _entitySerializer; #region Constructors @@ -42,20 +43,20 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache IPublishedContentTypeFactory publishedContentTypeFactory, IScopeProvider scopeProvider, ICacheProvider requestCache, - IEnumerable segmentProviders, IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, IDefaultCultureAccessor defaultCultureAccessor, ILogger logger, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper, + IEntityXmlSerializer entitySerializer, MainDom mainDom, bool testing = false, bool enableRepositoryEvents = true) - : this(serviceContext, publishedContentTypeFactory, scopeProvider, requestCache, segmentProviders, + : this(serviceContext, publishedContentTypeFactory, scopeProvider, requestCache, publishedSnapshotAccessor, variationContextAccessor, documentRepository, mediaRepository, memberRepository, defaultCultureAccessor, - logger, globalSettings, siteDomainHelper, null, mainDom, testing, enableRepositoryEvents) + logger, globalSettings, siteDomainHelper, entitySerializer, null, mainDom, testing, enableRepositoryEvents) { } // used in some tests @@ -69,27 +70,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache ILogger logger, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper, - PublishedContentTypeCache contentTypeCache, - MainDom mainDom, - bool testing, bool enableRepositoryEvents) - : this(serviceContext, publishedContentTypeFactory, scopeProvider, requestCache, Enumerable.Empty(), - publishedSnapshotAccessor, variationContextAccessor, - documentRepository, mediaRepository, memberRepository, - defaultCultureAccessor, - logger, globalSettings, siteDomainHelper, contentTypeCache, mainDom, testing, enableRepositoryEvents) - { } - - private PublishedSnapshotService(ServiceContext serviceContext, - IPublishedContentTypeFactory publishedContentTypeFactory, - IScopeProvider scopeProvider, - ICacheProvider requestCache, - IEnumerable segmentProviders, - IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, - IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, - IDefaultCultureAccessor defaultCultureAccessor, - ILogger logger, - IGlobalSettings globalSettings, - ISiteDomainHelper siteDomainHelper, + IEntityXmlSerializer entitySerializer, PublishedContentTypeCache contentTypeCache, MainDom mainDom, bool testing, bool enableRepositoryEvents) @@ -100,9 +81,9 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache _contentTypeCache = contentTypeCache ?? new PublishedContentTypeCache(serviceContext.ContentTypeService, serviceContext.MediaTypeService, serviceContext.MemberTypeService, publishedContentTypeFactory, logger); - _xmlStore = new XmlStore(serviceContext, scopeProvider, _routesCache, - _contentTypeCache, segmentProviders, publishedSnapshotAccessor, mainDom, testing, enableRepositoryEvents, - documentRepository, mediaRepository, memberRepository, globalSettings); + _xmlStore = new XmlStore(serviceContext.ContentTypeService, serviceContext.ContentService, scopeProvider, _routesCache, + _contentTypeCache, publishedSnapshotAccessor, mainDom, testing, enableRepositoryEvents, + documentRepository, mediaRepository, memberRepository, globalSettings, entitySerializer); _domainService = serviceContext.DomainService; _memberService = serviceContext.MemberService; @@ -113,6 +94,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache _requestCache = requestCache; _globalSettings = globalSettings; _siteDomainHelper = siteDomainHelper; + _entitySerializer = entitySerializer; } public override void Dispose() @@ -157,7 +139,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache return new PublishedSnapshot( new PublishedContentCache(_xmlStore, domainCache, _requestCache, _globalSettings, _siteDomainHelper, _contentTypeCache, _routesCache, previewToken), - new PublishedMediaCache(_xmlStore, _mediaService, _userService, _requestCache, _contentTypeCache), + new PublishedMediaCache(_xmlStore, _mediaService, _userService, _requestCache, _contentTypeCache, _entitySerializer), new PublishedMemberCache(_xmlStore, _requestCache, _memberService, _contentTypeCache), domainCache); } diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs index fef5039579..614515e433 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs @@ -44,15 +44,16 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache private readonly IMediaRepository _mediaRepository; private readonly IMemberRepository _memberRepository; private readonly IGlobalSettings _globalSettings; + private readonly IEntityXmlSerializer _entitySerializer; private XmlStoreFilePersister _persisterTask; private volatile bool _released; private bool _withRepositoryEvents; private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; private readonly PublishedContentTypeCache _contentTypeCache; - private readonly IEnumerable _segmentProviders; private readonly RoutesCache _routesCache; - private readonly ServiceContext _serviceContext; // fixme WHY + private readonly IContentTypeService _contentTypeService; + private readonly IContentService _contentService; private readonly IScopeProvider _scopeProvider; #region Constructors @@ -61,22 +62,23 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache /// Initializes a new instance of the class. /// /// The default constructor will boot the cache, load data from file or database, /// wire events in order to manage changes, etc. - public XmlStore(ServiceContext serviceContext, IScopeProvider scopeProvider, RoutesCache routesCache, PublishedContentTypeCache contentTypeCache, - IEnumerable segmentProviders, IPublishedSnapshotAccessor publishedSnapshotAccessor, MainDom mainDom, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, IGlobalSettings globalSettings) - : this(serviceContext, scopeProvider, routesCache, contentTypeCache, segmentProviders, publishedSnapshotAccessor, mainDom, false, false, documentRepository, mediaRepository, memberRepository, globalSettings) + public XmlStore(IContentTypeService contentTypeService, IContentService contentService, IScopeProvider scopeProvider, RoutesCache routesCache, PublishedContentTypeCache contentTypeCache, + IPublishedSnapshotAccessor publishedSnapshotAccessor, MainDom mainDom, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, IGlobalSettings globalSettings, IEntityXmlSerializer entitySerializer) + : this(contentTypeService, contentService, scopeProvider, routesCache, contentTypeCache, publishedSnapshotAccessor, mainDom, false, false, documentRepository, mediaRepository, memberRepository, globalSettings, entitySerializer) { } // internal for unit tests // no file nor db, no config check // fixme - er, we DO have a DB? - internal XmlStore(ServiceContext serviceContext, IScopeProvider scopeProvider, RoutesCache routesCache, PublishedContentTypeCache contentTypeCache, - IEnumerable segmentProviders, IPublishedSnapshotAccessor publishedSnapshotAccessor, MainDom mainDom, - bool testing, bool enableRepositoryEvents, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, IGlobalSettings globalSettings) + internal XmlStore(IContentTypeService contentTypeService, IContentService contentService, IScopeProvider scopeProvider, RoutesCache routesCache, PublishedContentTypeCache contentTypeCache, + IPublishedSnapshotAccessor publishedSnapshotAccessor, MainDom mainDom, + bool testing, bool enableRepositoryEvents, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, IGlobalSettings globalSettings, IEntityXmlSerializer entitySerializer) { if (testing == false) EnsureConfigurationIsValid(); - _serviceContext = serviceContext; + _contentTypeService = contentTypeService; + _contentService = contentService; _scopeProvider = scopeProvider; _routesCache = routesCache; _contentTypeCache = contentTypeCache; @@ -85,8 +87,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache _mediaRepository = mediaRepository; _memberRepository = memberRepository; _globalSettings = globalSettings; + _entitySerializer = entitySerializer; _xmlFileName = IOHelper.MapPath(SystemFiles.GetContentCacheXml(_globalSettings)); - _segmentProviders = segmentProviders; if (testing) { @@ -399,7 +401,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache try { var dtdInner = new StringBuilder(); - var contentTypes = _serviceContext.ContentTypeService.GetAll(); + var contentTypes = _contentTypeService.GetAll(); // though aliases should be safe and non null already? var aliases = contentTypes.Select(x => x.Alias.ToSafeAlias()).WhereNotNull(); foreach (var alias in aliases) @@ -556,7 +558,7 @@ AND (umbracoNode.id=@id)"; public XmlDocument GetPreviewXml(int contentId, bool includeSubs) { - var content = _serviceContext.ContentService.GetById(contentId); + var content = _contentService.GetById(contentId); var doc = (XmlDocument)Xml.Clone(); if (content == null) return doc; @@ -1065,7 +1067,7 @@ ORDER BY umbracoNode.level, umbracoNode.sortOrder"; continue; } - var content = _serviceContext.ContentService.GetById(payload.Id); + var content = _contentService.GetById(payload.Id); var current = safeXml.Xml.GetElementById(payload.Id.ToInvariantString()); if (content == null || content.Published == false || content.Trashed) @@ -1536,7 +1538,7 @@ ORDER BY umbracoNode.level, umbracoNode.sortOrder"; var entity = args.Entity; // serialize edit values for preview - var editXml = EntityXmlSerializer.Serialize(_serviceContext.ContentService, _serviceContext.DataTypeService, _serviceContext.UserService, _serviceContext.LocalizationService, _segmentProviders, entity, false).ToDataString(); + var editXml = _entitySerializer.Serialize(entity, false).ToDataString(); // change below to write only one row - not one per version var dto1 = new PreviewXmlDto @@ -1565,7 +1567,7 @@ ORDER BY umbracoNode.level, umbracoNode.sortOrder"; return; // serialize published values for content cache - var publishedXml = EntityXmlSerializer.Serialize(_serviceContext.ContentService, _serviceContext.DataTypeService, _serviceContext.UserService, _serviceContext.LocalizationService, _segmentProviders, entity, true).ToDataString(); + var publishedXml = _entitySerializer.Serialize(entity, true).ToDataString(); var dto2 = new ContentXmlDto { NodeId = entity.Id, Xml = publishedXml }; OnRepositoryRefreshed(db, dto2); @@ -1581,7 +1583,7 @@ ORDER BY umbracoNode.level, umbracoNode.sortOrder"; if (entity.Trashed) db.Execute("DELETE FROM cmsContentXml WHERE nodeId=@id", new { id = entity.Id }); - var xml = EntityXmlSerializer.Serialize(_serviceContext.MediaService, _serviceContext.DataTypeService, _serviceContext.UserService, _serviceContext.LocalizationService, _segmentProviders, entity).ToDataString(); + var xml = _entitySerializer.Serialize(entity).ToDataString(); var dto1 = new ContentXmlDto { NodeId = entity.Id, Xml = xml }; OnRepositoryRefreshed(db, dto1); @@ -1592,7 +1594,7 @@ ORDER BY umbracoNode.level, umbracoNode.sortOrder"; var db = args.Scope.Database; var entity = args.Entity; - var xml = EntityXmlSerializer.Serialize(_serviceContext.DataTypeService, _serviceContext.LocalizationService, entity).ToDataString(); + var xml = _entitySerializer.Serialize(entity).ToDataString(); var dto1 = new ContentXmlDto { NodeId = entity.Id, Xml = xml }; OnRepositoryRefreshed(db, dto1); @@ -1749,7 +1751,7 @@ WHERE cmsContentXml.nodeId IN ( var descendants = _documentRepository.GetPage(query, pageIndex++, groupSize, out total, null, Ordering.By("Path")); const bool published = true; // contentXml contains published content! var items = descendants.Select(c => new ContentXmlDto { NodeId = c.Id, Xml = - EntityXmlSerializer.Serialize(_serviceContext.ContentService, _serviceContext.DataTypeService, _serviceContext.UserService, _serviceContext.LocalizationService, _segmentProviders, c, published).ToDataString() }).ToArray(); + _entitySerializer.Serialize(c, published).ToDataString() }).ToArray(); db.BulkInsertRecords(items); processed += items.Length; } while (processed < total); @@ -1824,7 +1826,7 @@ WHERE cmsPreviewXml.nodeId IN ( var items = descendants.Select(c => new PreviewXmlDto { NodeId = c.Id, - Xml = EntityXmlSerializer.Serialize(_serviceContext.ContentService, _serviceContext.DataTypeService, _serviceContext.UserService, _serviceContext.LocalizationService, _segmentProviders, c, published).ToDataString() + Xml = _entitySerializer.Serialize(c, published).ToDataString() }).ToArray(); db.BulkInsertRecords(items); processed += items.Length; @@ -1894,7 +1896,7 @@ WHERE cmsContentXml.nodeId IN ( { var descendants = _mediaRepository.GetPage(query, pageIndex++, groupSize, out total, null, Ordering.By("Path")); var items = descendants.Select(m => new ContentXmlDto { NodeId = m.Id, Xml = - EntityXmlSerializer.Serialize(_serviceContext.MediaService, _serviceContext.DataTypeService, _serviceContext.UserService, _serviceContext.LocalizationService, _segmentProviders, m).ToDataString() }).ToArray(); + _entitySerializer.Serialize(m).ToDataString() }).ToArray(); db.BulkInsertRecords(items); processed += items.Length; } while (processed < total); @@ -1962,7 +1964,7 @@ WHERE cmsContentXml.nodeId IN ( do { var descendants = _memberRepository.GetPage(query, pageIndex++, groupSize, out total, null, Ordering.By("Path")); - var items = descendants.Select(m => new ContentXmlDto { NodeId = m.Id, Xml = EntityXmlSerializer.Serialize(_serviceContext.DataTypeService, _serviceContext.LocalizationService, m).ToDataString() }).ToArray(); + var items = descendants.Select(m => new ContentXmlDto { NodeId = m.Id, Xml = _entitySerializer.Serialize(m).ToDataString() }).ToArray(); db.BulkInsertRecords(items); processed += items.Length; } while (processed < total); diff --git a/src/Umbraco.Web/Services/SectionService.cs b/src/Umbraco.Web/Services/SectionService.cs index 5d013d7e79..6337db67f9 100644 --- a/src/Umbraco.Web/Services/SectionService.cs +++ b/src/Umbraco.Web/Services/SectionService.cs @@ -298,7 +298,7 @@ namespace Umbraco.Web.Services //we need to interrogate the attributes for the data. Would be better to have a base class that contains //metadata populated by the attribute. Oh well i guess. var attrs = types.Select(x => x.GetCustomAttributes(false).Single()); - return Enumerable.ToArray
(attrs.Select(x => new Section(x.Name, x.Alias, x.SortOrder))); + return attrs.Select(x => new Section(x.Name, x.Alias, x.SortOrder)).ToArray(); }); } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 36f0a835b1..fdf7f48201 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -1144,9 +1144,6 @@ - - - @@ -1231,7 +1228,6 @@ Code - FeedProxy.aspx diff --git a/src/Umbraco.Web/_Legacy/Packager/Installer.cs b/src/Umbraco.Web/_Legacy/Packager/Installer.cs index 15ea222bda..9d7fe77e7b 100644 --- a/src/Umbraco.Web/_Legacy/Packager/Installer.cs +++ b/src/Umbraco.Web/_Legacy/Packager/Installer.cs @@ -14,18 +14,18 @@ using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Packaging; +using Umbraco.Core.Packaging; using Umbraco.Core.Services.Implement; using Umbraco.Core.Xml; using Umbraco.Web._Legacy.Packager.PackageInstance; using File = System.IO.File; -using PackageAction = Umbraco.Web._Legacy.Packager.PackageInstance.PackageAction; namespace Umbraco.Web._Legacy.Packager { /// /// The packager is a component which enables sharing of both data and functionality components between different umbraco installations. /// - /// The output is a .umb (a zip compressed file) which contains the exported documents/medias/macroes/documenttypes (etc.) + /// The output is a .umb (a zip compressed file) which contains the exported documents/medias/macroes/documentTypes (etc.) /// in a Xml document, along with the physical files used (images/usercontrols/xsl documents etc.) /// /// Partly implemented, import of packages is done, the export is *under construction*. @@ -420,7 +420,7 @@ namespace Umbraco.Web._Legacy.Packager var contentTypes = packagingService.ImportContentTypes(docTypeElement, currentUser.Id); foreach (var contentType in contentTypes) { - insPack.Data.Documenttypes.Add(contentType.Id.ToString(CultureInfo.InvariantCulture)); + insPack.Data.DocumentTypes.Add(contentType.Id.ToString(CultureInfo.InvariantCulture)); //saveNeeded = true; } } @@ -510,7 +510,7 @@ namespace Umbraco.Web._Legacy.Packager if (alias.IsNullOrWhiteSpace() == false) { - PackageAction.RunPackageAction(insPack.Data.Name, alias, n); + Current.PackageActionRunner.RunPackageAction(insPack.Data.Name, alias, n); } } } diff --git a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/CreatedPackage.cs b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/CreatedPackage.cs index 3bfcdd3f86..deff3b2ffd 100644 --- a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/CreatedPackage.cs +++ b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/CreatedPackage.cs @@ -7,11 +7,13 @@ using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Models; using Umbraco.Core.Services; +using Umbraco.Core.Services.Implement; using File = System.IO.File; namespace Umbraco.Web._Legacy.Packager.PackageInstance { + //TODO: Fix this class , service + model + internal? public class CreatedPackage { @@ -22,14 +24,13 @@ namespace Umbraco.Web._Legacy.Packager.PackageInstance return pack; } - public static CreatedPackage MakeNew(string name) + public static CreatedPackage MakeNew(string name, Core.Models.Packaging.PackageDefinition packageData = null) { var pack = new CreatedPackage { - Data = data.MakeNew(name, IOHelper.MapPath(Settings.CreatedPackagesSettings)) + Data = packageData ?? data.MakeNew(name, IOHelper.MapPath(Settings.CreatedPackagesSettings)) }; - return pack; } @@ -43,7 +44,7 @@ namespace Umbraco.Web._Legacy.Packager.PackageInstance data.Delete(this.Data.Id, IOHelper.MapPath(Settings.CreatedPackagesSettings)); } - public PackageInstance Data { get; set; } + public Core.Models.Packaging.PackageDefinition Data { get; set; } public static List GetAllCreatedPackages() { @@ -81,7 +82,7 @@ namespace Umbraco.Web._Legacy.Packager.PackageInstance } - public void Publish() + public void Publish(IEntityXmlSerializer serializer) { var package = this; @@ -118,7 +119,7 @@ namespace Umbraco.Web._Legacy.Packager.PackageInstance //var umbDocument = new Document(contentNodeId); //var x = umbDocument.ToXml(_packageManifest, pack.ContentLoadChildNodes); var udoc = Current.Services.ContentService.GetById(contentNodeId); - var xe = pack.ContentLoadChildNodes ? udoc.ToDeepXml(Current.Services.PackagingService) : udoc.ToXml(Current.Services.PackagingService); + var xe = pack.ContentLoadChildNodes ? udoc.ToDeepXml(serializer) : udoc.ToXml(serializer); var x = xe.GetXmlNode(_packageManifest); documentSet.AppendChild(x); @@ -188,7 +189,7 @@ namespace Umbraco.Web._Legacy.Packager.PackageInstance //Document types.. var dtl = new List(); var docTypes = _packageManifest.CreateElement("DocumentTypes"); - foreach (var dtId in pack.Documenttypes) + foreach (var dtId in pack.DocumentTypes) { if (int.TryParse(dtId, out outInt)) { @@ -200,11 +201,9 @@ namespace Umbraco.Web._Legacy.Packager.PackageInstance } } - var exporter = new EntityXmlSerializer(); - foreach (var d in dtl) { - var xml = exporter.Serialize(Current.Services.DataTypeService, Current.Services.ContentTypeService, d); + var xml = serializer.Serialize(d); var xNode = xml.GetXmlNode(); var n = (XmlElement) _packageManifest.ImportNode(xNode, true); docTypes.AppendChild(n); @@ -220,7 +219,6 @@ namespace Umbraco.Web._Legacy.Packager.PackageInstance { var t = Current.Services.FileService.GetTemplate(outInt); - var serializer = new EntityXmlSerializer(); var serialized = serializer.Serialize(t); var n = serialized.GetXmlNode(_packageManifest); @@ -241,16 +239,16 @@ namespace Umbraco.Web._Legacy.Packager.PackageInstance } AppendElement(stylesheets); - //Macros - var macros = _packageManifest.CreateElement("Macros"); - foreach (var macroId in pack.Macros) - { - if (int.TryParse(macroId, out outInt)) - { - macros.AppendChild(PackagerUtility.Macro(int.Parse(macroId), true, localPath, _packageManifest)); - } - } - AppendElement(macros); + ////Macros + //var macros = _packageManifest.CreateElement("Macros"); + //foreach (var macroId in pack.Macros) + //{ + // if (int.TryParse(macroId, out outInt)) + // { + // macros.AppendChild(PackagerUtility.Macro(int.Parse(macroId), true, localPath, _packageManifest)); + // } + //} + //AppendElement(macros); //Dictionary Items var dictionaryItems = _packageManifest.CreateElement("DictionaryItems"); @@ -259,8 +257,7 @@ namespace Umbraco.Web._Legacy.Packager.PackageInstance if (int.TryParse(dictionaryId, out outInt)) { var di = Current.Services.LocalizationService.GetDictionaryItemById(outInt); - var entitySerializer = new EntityXmlSerializer(); - var xmlNode = entitySerializer.Serialize(di).GetXmlNode(_packageManifest); + var xmlNode = serializer.Serialize(di, false).GetXmlNode(_packageManifest); dictionaryItems.AppendChild(xmlNode); } } @@ -274,7 +271,6 @@ namespace Umbraco.Web._Legacy.Packager.PackageInstance { var lang = Current.Services.LocalizationService.GetLanguageById(outInt); - var serializer = new EntityXmlSerializer(); var xml = serializer.Serialize(lang); var n = xml.GetXmlNode(_packageManifest); @@ -328,7 +324,10 @@ namespace Umbraco.Web._Legacy.Packager.PackageInstance AppendElement(actions); } } - catch { } + catch + { + //TODO: Log!? + } } var manifestFileName = localPath + "/package.xml"; diff --git a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/IPackageInstance.cs b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/IPackageInstance.cs deleted file mode 100644 index b920c85a9f..0000000000 --- a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/IPackageInstance.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace Umbraco.Web._Legacy.Packager.PackageInstance{ - public interface IPackageInstance { - string Actions { get; set; } - string Author { get; set; } - string AuthorUrl { get; set; } - bool ContentLoadChildNodes { get; set; } - string ContentNodeId { get; set; } - System.Collections.Generic.List Documenttypes { get; set; } - System.Collections.Generic.List Files { get; set; } - string Folder { get; set; } - bool HasUpdate { get; set; } - int Id { get; set; } - string License { get; set; } - string LicenseUrl { get; set; } - string LoadControl { get; set; } - System.Collections.Generic.List Macros { get; set; } - string Name { get; set; } - string PackageGuid { get; set; } - string PackagePath { get; set; } - string Readme { get; set; } - string RepositoryGuid { get; set; } - System.Collections.Generic.List Stylesheets { get; set; } - System.Collections.Generic.List Templates { get; set; } - string Url { get; set; } - string Version { get; set; } - } -} diff --git a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/InstalledPackage.cs b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/InstalledPackage.cs index 5842a456e5..8c106f142d 100644 --- a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/InstalledPackage.cs +++ b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/InstalledPackage.cs @@ -53,7 +53,7 @@ namespace Umbraco.Web._Legacy.Packager.PackageInstance List val = new List(); - foreach (PackageInstance pack in data.GetAllPackages(IOHelper.MapPath(Settings.InstalledPackagesSettings))) + foreach (Core.Models.Packaging.PackageDefinition pack in data.GetAllPackages(IOHelper.MapPath(Settings.InstalledPackagesSettings))) { InstalledPackage insPackage = new InstalledPackage(); insPackage.Data = pack; @@ -63,8 +63,8 @@ namespace Umbraco.Web._Legacy.Packager.PackageInstance return val; } - private PackageInstance m_data; - public PackageInstance Data + private Core.Models.Packaging.PackageDefinition m_data; + public Core.Models.Packaging.PackageDefinition Data { get { return m_data; } set { m_data = value; } @@ -156,7 +156,7 @@ namespace Umbraco.Web._Legacy.Packager.PackageInstance { var macros = TryGetIntegerIds(Data.Macros).Select(macroService.GetById).ToList(); var templates = TryGetIntegerIds(Data.Templates).Select(fileService.GetTemplate).ToList(); - var contentTypes = TryGetIntegerIds(Data.Documenttypes).Select(contentTypeService.Get).ToList(); // fixme - media types? + var contentTypes = TryGetIntegerIds(Data.DocumentTypes).Select(contentTypeService.Get).ToList(); // fixme - media types? var dataTypes = TryGetIntegerIds(Data.DataTypes).Select(dataTypeService.GetDataType).ToList(); var dictionaryItems = TryGetIntegerIds(Data.DictionaryItems).Select(localizationService.GetDictionaryItemById).ToList(); var languages = TryGetIntegerIds(Data.Languages).Select(localizationService.GetLanguageById).ToList(); diff --git a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackageInstance.cs b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackageInstance.cs deleted file mode 100644 index 8ec3c21bdb..0000000000 --- a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackageInstance.cs +++ /dev/null @@ -1,132 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Runtime.Serialization; - -namespace Umbraco.Web._Legacy.Packager.PackageInstance -{ - [DataContract(Name = "packageInstance")] - public class PackageInstance - { - [DataMember(Name = "id")] - public int Id { get; set; } - - [DataMember(Name = "repositoryGuid")] - public string RepositoryGuid { get; set; } - - [DataMember(Name = "packageGuid")] - public string PackageGuid { get; set; } - - [DataMember(Name = "hasUpdate")] - public bool HasUpdate { get; set; } - - [DataMember(Name = "name")] - [Required] - public string Name { get; set; } - - [DataMember(Name = "url")] - [Required] - [Url] - public string Url { get; set; } - - [DataMember(Name = "folder")] - public string Folder { get; set; } - - [DataMember(Name = "packagePath")] - public string PackagePath { get; set; } - - [DataMember(Name = "version")] - [Required] - public string Version { get; set; } - - /// - /// The minimum umbraco version that this package requires - /// - [DataMember(Name = "umbracoVersion")] - public Version UmbracoVersion { get; set; } - - [DataMember(Name = "author")] - [Required] - public string Author { get; set; } - - [DataMember(Name = "authorUrl")] - [Required] - [Url] - public string AuthorUrl { get; set; } - - [DataMember(Name = "license")] - public string License { get; set; } - - [DataMember(Name = "licenseUrl")] - public string LicenseUrl { get; set; } - - [DataMember(Name = "readme")] - public string Readme { get; set; } - - [DataMember(Name = "contentLoadChildNodes")] - public bool ContentLoadChildNodes { get; set; } - - [DataMember(Name = "contentNodeId")] - public string ContentNodeId { get; set; } - - [DataMember(Name = "macros")] - public List Macros { get; set; } - - [DataMember(Name = "languages")] - public List Languages { get; set; } - - [DataMember(Name = "dictionaryItems")] - public List DictionaryItems { get; set; } - - [DataMember(Name = "templates")] - public List Templates { get; set; } - - [DataMember(Name = "documenttypes")] - public List Documenttypes { get; set; } - - [DataMember(Name = "stylesheets")] - public List Stylesheets { get; set; } - - [DataMember(Name = "files")] - public List Files { get; set; } - - [DataMember(Name = "loadControl")] - public string LoadControl { get; set; } - - [DataMember(Name = "actions")] - public string Actions { get; set; } - - [DataMember(Name = "dataTypes")] - public List DataTypes { get; set; } - - [DataMember(Name = "iconUrl")] - public string IconUrl { get; set; } - - public PackageInstance() - { - Name = string.Empty; - Url = string.Empty; - Folder = string.Empty; - PackagePath = string.Empty; - Version = string.Empty; - UmbracoVersion = null; - Author = string.Empty; - AuthorUrl = string.Empty; - License = string.Empty; - LicenseUrl = string.Empty; - Readme = string.Empty; - ContentNodeId = string.Empty; - IconUrl = string.Empty; - Macros = new List(); - Languages = new List(); - DictionaryItems = new List(); - Templates = new List(); - Documenttypes = new List(); - Stylesheets = new List(); - Files = new List(); - LoadControl = string.Empty; - DataTypes = new List(); - ContentLoadChildNodes = false; - } - } -} diff --git a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackagerUtility.cs b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackagerUtility.cs index d972977bad..af32e8c80a 100644 --- a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackagerUtility.cs +++ b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackagerUtility.cs @@ -7,6 +7,7 @@ using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Services; +using Umbraco.Core.Services.Implement; namespace Umbraco.Web._Legacy.Packager.PackageInstance { @@ -22,7 +23,7 @@ namespace Umbraco.Web._Legacy.Packager.PackageInstance /// The packinstance. /// The xml document. /// - public static XmlNode PackageInfo(PackageInstance pack, XmlDocument doc) + public static XmlNode PackageInfo(Core.Models.Packaging.PackageDefinition pack, XmlDocument doc) { XmlNode info = doc.CreateElement("info"); @@ -60,31 +61,6 @@ namespace Umbraco.Web._Legacy.Packager.PackageInstance return info; } - - /// - /// Converts an umbraco template to a package xml node - /// - /// The template id. - /// The xml doc. - /// - public static XmlNode Template(int templateId, XmlDocument doc) - { - var tmpl = Current.Services.FileService.GetTemplate(templateId); - //Template tmpl = new Template(templateId); - - XmlNode template = doc.CreateElement("Template"); - template.AppendChild(CreateNode("Name", tmpl.Name, doc)); - template.AppendChild(CreateNode("Alias", tmpl.Alias, doc)); - - //if (tmpl.MasterTemplate != 0) - if (string.IsNullOrWhiteSpace(tmpl.MasterTemplateAlias) == false) - template.AppendChild(CreateNode("Master", tmpl.MasterTemplateAlias, doc)); - - template.AppendChild(CreateNode("Design", "", doc)); - - return template; - } - /// /// Converts a umbraco stylesheet to a package xml node /// @@ -118,29 +94,6 @@ namespace Umbraco.Web._Legacy.Packager.PackageInstance return stylesheet; } - /// - /// Converts a macro to a package xml node - /// - /// The macro id. - /// if set to true [append file]. - /// The package directory. - /// The doc. - /// - public static XmlNode Macro(int macroId, bool appendFile, string packageDirectory, XmlDocument doc) - { - var mcr = Current.Services.MacroService.GetById(macroId); - - if (appendFile) - { - if (!string.IsNullOrEmpty(mcr.MacroSource)) - AppendFileToManifest(mcr.MacroSource, packageDirectory, doc); - } - - var serializer = new EntityXmlSerializer(); - var xml = serializer.Serialize(mcr); - return xml.GetXmlNode(doc); - } - /// /// Appends a file to package manifest and copies the file to the correct folder. diff --git a/src/Umbraco.Web/_Legacy/Packager/Settings.cs b/src/Umbraco.Web/_Legacy/Packager/Settings.cs index 57ab4b9ea4..92b0ae030a 100644 --- a/src/Umbraco.Web/_Legacy/Packager/Settings.cs +++ b/src/Umbraco.Web/_Legacy/Packager/Settings.cs @@ -6,69 +6,11 @@ namespace Umbraco.Web._Legacy.Packager { public class Settings { + public static string InstalledPackagesSettings => SystemDirectories.Packages + IOHelper.DirSepChar + "installedPackages.config"; - public static string PackagerRoot - { - get { return SystemDirectories.Packages; } - } - - public static string PackagesStorage - { - get { return SystemDirectories.Packages + IOHelper.DirSepChar + "created"; } - } - - public static string InstalledPackagesStorage - { - get { return SystemDirectories.Packages + IOHelper.DirSepChar + "installed"; } - } - - public static string InstalledPackagesSettings - { - get { return SystemDirectories.Packages + IOHelper.DirSepChar + "installed" + IOHelper.DirSepChar + "installedPackages.config"; } - } - - public static string CreatedPackagesSettings - { - get { return SystemDirectories.Packages + IOHelper.DirSepChar + "created" + IOHelper.DirSepChar + "createdPackages.config"; } - } - - public static string PackageFileExtension - { - get { return "zip"; } - } - - public static bool HasFileAccess(ref Exception exp) - { - bool hasAccess = false; - StreamWriter sw1 = null; - StreamWriter sw2 = null; - try - { - sw1 = System.IO.File.AppendText(IOHelper.MapPath(InstalledPackagesSettings)); - sw1.Close(); - - sw2 = System.IO.File.AppendText(IOHelper.MapPath(CreatedPackagesSettings)); - sw1.Close(); - - System.IO.Directory.CreateDirectory(IOHelper.MapPath(PackagesStorage) + IOHelper.DirSepChar + "__testFolder__"); - System.IO.Directory.CreateDirectory(IOHelper.MapPath(InstalledPackagesStorage) + IOHelper.DirSepChar + "__testFolder__"); - - System.IO.Directory.Delete(IOHelper.MapPath(PackagesStorage) + IOHelper.DirSepChar + "__testFolder__", true); - System.IO.Directory.Delete(IOHelper.MapPath(InstalledPackagesStorage) + IOHelper.DirSepChar + "__testFolder__", true); - - hasAccess = true; - } - finally - { - if (sw1 != null) - sw1.Close(); - if (sw2 != null) - sw2.Close(); - } - - return hasAccess; - } + public static string CreatedPackagesSettings => SystemDirectories.Packages + IOHelper.DirSepChar + "createdPackages.config"; + public static string PackageFileExtension => "zip"; } diff --git a/src/Umbraco.Web/_Legacy/Packager/data.cs b/src/Umbraco.Web/_Legacy/Packager/data.cs index 962b7d5ed4..1882c151d2 100644 --- a/src/Umbraco.Web/_Legacy/Packager/data.cs +++ b/src/Umbraco.Web/_Legacy/Packager/data.cs @@ -23,20 +23,12 @@ namespace Umbraco.Web._Legacy.Packager //do some error checking and create the folders/files if they don't exist if (!File.Exists(dataSource)) { - if (!Directory.Exists(IOHelper.MapPath(Settings.PackagerRoot))) + if (!Directory.Exists(IOHelper.MapPath(SystemDirectories.Packages))) { - Directory.CreateDirectory(IOHelper.MapPath(Settings.PackagerRoot)); - } - if (!Directory.Exists(IOHelper.MapPath(Settings.PackagesStorage))) - { - Directory.CreateDirectory(IOHelper.MapPath(Settings.PackagesStorage)); - } - if (!Directory.Exists(IOHelper.MapPath(Settings.InstalledPackagesStorage))) - { - Directory.CreateDirectory(IOHelper.MapPath(Settings.InstalledPackagesStorage)); + Directory.CreateDirectory(IOHelper.MapPath(SystemDirectories.Packages)); } - using (StreamWriter sw = File.CreateText(dataSource)) + using (var sw = File.CreateText(dataSource)) { sw.Write($@"{Environment.NewLine}{Environment.NewLine}"); sw.Flush(); @@ -85,7 +77,7 @@ namespace Umbraco.Web._Legacy.Packager return Source.SelectSingleNode("/packages/package [@packageGuid = '" + guid + "']"); } - public static PackageInstance.PackageInstance MakeNew(string Name, string dataSource) + public static Core.Models.Packaging.PackageDefinition MakeNew(string name, string dataSource) { Reload(dataSource); @@ -101,7 +93,7 @@ namespace Umbraco.Web._Legacy.Packager instance.Attributes.Append(XmlHelper.AddAttribute(Source, "id", maxId.ToString())); instance.Attributes.Append(XmlHelper.AddAttribute(Source, "version", "")); instance.Attributes.Append(XmlHelper.AddAttribute(Source, "url", "")); - instance.Attributes.Append(XmlHelper.AddAttribute(Source, "name", Name)); + instance.Attributes.Append(XmlHelper.AddAttribute(Source, "name", name)); instance.Attributes.Append(XmlHelper.AddAttribute(Source, "folder", Guid.NewGuid().ToString())); instance.Attributes.Append(XmlHelper.AddAttribute(Source, "packagepath", "")); instance.Attributes.Append(XmlHelper.AddAttribute(Source, "repositoryGuid", "")); @@ -137,7 +129,7 @@ namespace Umbraco.Web._Legacy.Packager instance.AppendChild(XmlHelper.AddTextNode(Source, "templates", "")); instance.AppendChild(XmlHelper.AddTextNode(Source, "stylesheets", "")); - instance.AppendChild(XmlHelper.AddTextNode(Source, "documenttypes", "")); + instance.AppendChild(XmlHelper.AddTextNode(Source, "documentTypes", "")); instance.AppendChild(XmlHelper.AddTextNode(Source, "macros", "")); instance.AppendChild(XmlHelper.AddTextNode(Source, "files", "")); instance.AppendChild(XmlHelper.AddTextNode(Source, "languages", "")); @@ -151,26 +143,26 @@ namespace Umbraco.Web._Legacy.Packager return retVal; } - public static PackageInstance.PackageInstance Package(int id, string datasource) + public static Core.Models.Packaging.PackageDefinition Package(int id, string datasource) { return ConvertXmlToPackage(GetFromId(id, datasource, true)); } - public static PackageInstance.PackageInstance Package(string guid, string datasource) + public static Core.Models.Packaging.PackageDefinition Package(string guid, string datasource) { XmlNode node = GetFromGuid(guid, datasource, true); if (node != null) return ConvertXmlToPackage(node); else - return new PackageInstance.PackageInstance(); + return new Core.Models.Packaging.PackageDefinition(); } - public static List GetAllPackages(string dataSource) + public static List GetAllPackages(string dataSource) { Reload(dataSource); XmlNodeList nList = data.Source.SelectNodes("packages/package"); - List retVal = new List(); + List retVal = new List(); for (int i = 0; i < nList.Count; i++) { @@ -187,9 +179,9 @@ namespace Umbraco.Web._Legacy.Packager return retVal; } - private static PackageInstance.PackageInstance ConvertXmlToPackage(XmlNode n) + private static Core.Models.Packaging.PackageDefinition ConvertXmlToPackage(XmlNode n) { - PackageInstance.PackageInstance retVal = new PackageInstance.PackageInstance(); + Core.Models.Packaging.PackageDefinition retVal = new Core.Models.Packaging.PackageDefinition(); if (n != null) { @@ -227,7 +219,7 @@ namespace Umbraco.Web._Legacy.Packager retVal.Macros = new List(SafeNodeValue(n.SelectSingleNode("macros")).Trim(',').Split(',')); retVal.Templates = new List(SafeNodeValue(n.SelectSingleNode("templates")).Trim(',').Split(',')); retVal.Stylesheets = new List(SafeNodeValue(n.SelectSingleNode("stylesheets")).Trim(',').Split(',')); - retVal.Documenttypes = new List(SafeNodeValue(n.SelectSingleNode("documenttypes")).Trim(',').Split(',')); + retVal.DocumentTypes = new List(SafeNodeValue(n.SelectSingleNode("documentTypes")).Trim(',').Split(',')); retVal.Languages = new List(SafeNodeValue(n.SelectSingleNode("languages")).Trim(',').Split(',')); retVal.DictionaryItems = new List(SafeNodeValue(n.SelectSingleNode("dictionaryitems")).Trim(',').Split(',')); retVal.DataTypes = new List(SafeNodeValue(n.SelectSingleNode("datatypes")).Trim(',').Split(',')); @@ -263,7 +255,7 @@ namespace Umbraco.Web._Legacy.Packager } - public static void Save(PackageInstance.PackageInstance package, string dataSource) + public static void Save(Core.Models.Packaging.PackageDefinition package, string dataSource) { Reload(dataSource); var xmlDef = GetFromId(package.Id, dataSource, false); @@ -311,7 +303,7 @@ namespace Umbraco.Web._Legacy.Packager XmlHelper.SetTextNode(Source, xmlDef, "macros", JoinList(package.Macros, ',')); XmlHelper.SetTextNode(Source, xmlDef, "templates", JoinList(package.Templates, ',')); XmlHelper.SetTextNode(Source, xmlDef, "stylesheets", JoinList(package.Stylesheets, ',')); - XmlHelper.SetTextNode(Source, xmlDef, "documenttypes", JoinList(package.Documenttypes, ',')); + XmlHelper.SetTextNode(Source, xmlDef, "documentTypes", JoinList(package.DocumentTypes, ',')); XmlHelper.SetTextNode(Source, xmlDef, "languages", JoinList(package.Languages, ',')); XmlHelper.SetTextNode(Source, xmlDef, "dictionaryitems", JoinList(package.DictionaryItems, ',')); XmlHelper.SetTextNode(Source, xmlDef, "datatypes", JoinList(package.DataTypes, ',')); diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/CreatedPackageTasks.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/CreatedPackageTasks.cs deleted file mode 100644 index 47980f2808..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/CreatedPackageTasks.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Umbraco.Core.Logging; -using Umbraco.Web.UI; -using Umbraco.Core; -using Umbraco.Web; -using Umbraco.Web.Composing; -using Umbraco.Web._Legacy.Packager.PackageInstance; -using Umbraco.Web._Legacy.UI; - -namespace Umbraco.Web -{ - public class CreatedPackageTasks : LegacyDialogTask - { - - public override bool PerformSave() - { - Current.Logger.Info("Xml save started"); - int id = CreatedPackage.MakeNew(Alias).Data.Id; - _returnUrl = string.Format("developer/packages/editPackage.aspx?id={0}", id); - return true; - } - - public override bool PerformDelete() - { - // we need to grab the id from the alias as the new tree needs to prefix the NodeID with "package_" - if (ParentID == 0) - { - ParentID = int.Parse(Alias.Substring("package_".Length)); - } - CreatedPackage.GetById(ParentID).Delete(); - return true; - } - - private string _returnUrl = ""; - - public override string ReturnUrl - { - get { return _returnUrl; } - } - - public override string AssignedApp - { - get { return Constants.Applications.Packages.ToString(); } - } - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.cs index 645aa088f2..3b863895a4 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.cs @@ -10,6 +10,8 @@ using System.Xml; using umbraco.controls; using Umbraco.Core; using Umbraco.Core.IO; +using Umbraco.Core.Models.Packaging; +using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.UI; using Umbraco.Web.UI.Pages; using Umbraco.Web._Legacy.Packager.PackageInstance; @@ -32,7 +34,7 @@ namespace umbraco.presentation.developer.packages public Umbraco.Web._Legacy.Controls.TabPage packageActions; protected ContentPicker cp; - private PackageInstance pack; + private PackageDefinition pack; private CreatedPackage createdPackage; protected void Page_Load(object sender, EventArgs e) @@ -102,7 +104,7 @@ namespace umbraco.presentation.developer.packages foreach (var dc in nContentTypes) { ListItem li = new ListItem(dc.Name, dc.Id.ToString()); - if (pack.Documenttypes.Contains(dc.Id.ToString())) + if (pack.DocumentTypes.Contains(dc.Id.ToString())) li.Selected = true; documentTypes.Items.Add(li); @@ -223,41 +225,41 @@ namespace umbraco.presentation.developer.packages e.IsValid = true; } - protected void saveOrPublish(object sender, CommandEventArgs e) - { + //protected void saveOrPublish(object sender, CommandEventArgs e) + //{ - if (!Page.IsValid) - { - this.ClientTools.ShowSpeechBubble(SpeechBubbleIcon.Error, "Saved failed.", "Some fields have not been filled-out correctly"); - } - else - { - if (e.CommandName == "save") - SavePackage(true); + // if (!Page.IsValid) + // { + // this.ClientTools.ShowSpeechBubble(SpeechBubbleIcon.Error, "Saved failed.", "Some fields have not been filled-out correctly"); + // } + // else + // { + // if (e.CommandName == "save") + // SavePackage(true); - if (e.CommandName == "publish") - { - SavePackage(false); - int packageID = int.Parse(Request.QueryString["id"]); - //string packFileName = cms.businesslogic.packager. Publish.publishPackage(packageID); + // if (e.CommandName == "publish") + // { + // SavePackage(false); + // int packageID = int.Parse(Request.QueryString["id"]); + // //string packFileName = cms.businesslogic.packager. Publish.publishPackage(packageID); - createdPackage.Publish(); + // createdPackage.Publish(); - if (!string.IsNullOrEmpty(pack.PackagePath)) - { + // if (!string.IsNullOrEmpty(pack.PackagePath)) + // { - packageUmbFile.Text = "   Download"; + // packageUmbFile.Text = "   Download"; - this.ClientTools.ShowSpeechBubble(SpeechBubbleIcon.Success, "Package saved and published", ""); - } - else - { - this.ClientTools.ShowSpeechBubble(SpeechBubbleIcon.Error, "Save failed", "check your umbraco log."); - } - } - } - } + // this.ClientTools.ShowSpeechBubble(SpeechBubbleIcon.Success, "Package saved and published", ""); + // } + // else + // { + // this.ClientTools.ShowSpeechBubble(SpeechBubbleIcon.Error, "Save failed", "check your umbraco log."); + // } + // } + // } + //} private void SavePackage(bool showNotification) @@ -300,7 +302,7 @@ namespace umbraco.presentation.developer.packages if (li.Selected) tmpDoctypes += li.Value + ","; } - pack.Documenttypes = new List(tmpDoctypes.Trim(',').Split(',')); + pack.DocumentTypes = new List(tmpDoctypes.Trim(',').Split(',')); string tmpMacros = ""; @@ -361,7 +363,7 @@ namespace umbraco.presentation.developer.packages if (newPath.Trim() != "") { CreatedPackage createdPackage = CreatedPackage.GetById(int.Parse(Request.QueryString["id"])); - PackageInstance pack = createdPackage.Data; + PackageDefinition pack = createdPackage.Data; pack.Files.Add(newPath); @@ -388,7 +390,7 @@ namespace umbraco.presentation.developer.packages } CreatedPackage createdPackage = CreatedPackage.GetById(int.Parse(Request.QueryString["id"])); - PackageInstance pack = createdPackage.Data; + PackageDefinition pack = createdPackage.Data; pack.Files = new List(tmpFilePathString.Trim('�').Split('�')); pack.Files.TrimExcess(); From b8d2309b9c1fae3e21a21d86744d27a10a997056 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 10 Jan 2019 17:18:47 +1100 Subject: [PATCH 48/93] Gets the IPackageBuilder working and tested, now the controller can create and update packages, validation is also working, need to do some more tests for adding other entities , removes more of the old packaging code. --- .../Composing/Composers/ServicesComposer.cs | 7 +- src/Umbraco.Core/IO/SystemDirectories.cs | 4 +- .../Models/Packaging/PackageDefinition.cs | 36 +- src/Umbraco.Core/Packaging/IPackageBuilder.cs | 30 + .../Packaging/IPackageCreation.cs | 22 - .../{PackageCreation.cs => PackageBuilder.cs} | 190 ++++-- .../Packaging}/RequirementsType.cs | 2 +- .../Services/IPackagingService.cs | 20 +- .../Services/Implement/PackagingService.cs | 16 +- src/Umbraco.Core/Umbraco.Core.csproj | 5 +- .../Composing/TypeFinderTests.cs | 2 +- .../Services/PackagingServiceTests.cs | 3 +- src/Umbraco.Tests/TestHelpers/TestObjects.cs | 2 +- src/Umbraco.Tests/Umbraco.Tests.csproj | 3 + .../src/common/resources/package.resource.js | 17 +- .../src/common/services/formhelper.service.js | 2 + .../services/umbrequesthelper.service.js | 2 + .../content/content.delete.controller.js | 1 + .../views/media/media.delete.controller.js | 1 + .../src/views/packages/edit.controller.js | 58 +- .../src/views/packages/edit.html | 17 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 10 - .../Umbraco/config/create/UI.Release.xml | 28 - .../Umbraco/config/create/UI.xml | 28 - .../Packages/DirectoryBrowser.aspx.cs | 140 ---- .../DirectoryBrowser.aspx.designer.cs | 42 -- .../developer/Packages/directoryBrowser.aspx | 26 - .../developer/Packages/editPackage.aspx | 232 ------- .../Umbraco/developer/Packages/installer.aspx | 10 - .../developer/Packages/editPackage.aspx | 232 ------- src/Umbraco.Web/Editors/PackageController.cs | 55 +- .../Editors/PackageInstallController.cs | 3 +- .../Controllers/InstallPackageController.cs | 2 +- .../InstallSteps/StarterKitDownloadStep.cs | 2 +- src/Umbraco.Web/Umbraco.Web.csproj | 13 - src/Umbraco.Web/_Legacy/Packager/Installer.cs | 17 +- .../PackageInstance/CreatedPackage.cs | 387 ----------- .../PackageInstance/InstalledPackage.cs | 2 +- .../PackageInstance/PackagerUtility.cs | 279 -------- src/Umbraco.Web/_Legacy/Packager/Settings.cs | 4 - src/Umbraco.Web/_Legacy/Packager/data.cs | 18 +- .../developer/Packages/editPackage.aspx | 232 ------- .../developer/Packages/editPackage.aspx.cs | 453 ------------- .../Packages/editPackage.aspx.designer.cs | 609 ------------------ 44 files changed, 333 insertions(+), 2931 deletions(-) create mode 100644 src/Umbraco.Core/Packaging/IPackageBuilder.cs delete mode 100644 src/Umbraco.Core/Packaging/IPackageCreation.cs rename src/Umbraco.Core/Packaging/{PackageCreation.cs => PackageBuilder.cs} (72%) rename src/{Umbraco.Web/_Legacy/Packager => Umbraco.Core/Packaging}/RequirementsType.cs (65%) delete mode 100644 src/Umbraco.Web.UI/Umbraco/developer/Packages/DirectoryBrowser.aspx.cs delete mode 100644 src/Umbraco.Web.UI/Umbraco/developer/Packages/DirectoryBrowser.aspx.designer.cs delete mode 100644 src/Umbraco.Web.UI/Umbraco/developer/Packages/directoryBrowser.aspx delete mode 100644 src/Umbraco.Web.UI/Umbraco/developer/Packages/editPackage.aspx delete mode 100644 src/Umbraco.Web.UI/Umbraco/developer/Packages/installer.aspx delete mode 100644 src/Umbraco.Web.UI/umbraco/developer/Packages/editPackage.aspx delete mode 100644 src/Umbraco.Web/_Legacy/Packager/PackageInstance/CreatedPackage.cs delete mode 100644 src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackagerUtility.cs delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.cs delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.designer.cs diff --git a/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs b/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs index 6133d63377..6395a2c6db 100644 --- a/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs +++ b/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs @@ -60,8 +60,11 @@ namespace Umbraco.Core.Composing.Composers composition.RegisterUnique(); composition.RegisterUnique(); - composition.RegisterUnique(); - + composition.RegisterUnique(factory => + new PackageBuilder( //we are using a factory because there are optional ctor args + factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), + factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), + factory.GetInstance(), factory.GetInstance())); //TODO: These are replaced in the web project - we need to declare them so that // something is wired up, just not sure this is very nice but will work for now. diff --git a/src/Umbraco.Core/IO/SystemDirectories.cs b/src/Umbraco.Core/IO/SystemDirectories.cs index 183d48e3d9..94aa7b16cc 100644 --- a/src/Umbraco.Core/IO/SystemDirectories.cs +++ b/src/Umbraco.Core/IO/SystemDirectories.cs @@ -43,9 +43,9 @@ namespace Umbraco.Core.IO [Obsolete("Only used by legacy load balancing which is obsolete and should be removed")] public static string WebServices => IOHelper.ReturnPath("umbracoWebservicesPath", Umbraco.EnsureEndsWith("/") + "webservices"); - public static string Packages => Data + IOHelper.DirSepChar + "packages"; + public static string Packages => Data + "/packages"; - public static string Preview => Data + IOHelper.DirSepChar + "preview"; + public static string Preview => Data + "/preview"; private static string _root; diff --git a/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs b/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs index 783b11235f..16934dc4c4 100644 --- a/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs +++ b/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs @@ -11,15 +11,8 @@ namespace Umbraco.Core.Models.Packaging [DataMember(Name = "id")] public int Id { get; set; } - //TODO: I don't see why this is necessary - [DataMember(Name = "repositoryGuid")] - public string RepositoryGuid { get; set; } - [DataMember(Name = "packageGuid")] - public string PackageGuid { get; set; } - - [DataMember(Name = "hasUpdate")] - public bool HasUpdate { get; set; } + public Guid PackageId { get; set; } [DataMember(Name = "name")] [Required] @@ -30,21 +23,24 @@ namespace Umbraco.Core.Models.Packaging [Url] public string Url { get; set; } = string.Empty; + /// + /// This is a generated GUID which is used to determine a temporary folder name for processing the package + /// [DataMember(Name = "folder")] - public string Folder { get; set; } = string.Empty; + public Guid FolderId { get; set; } [DataMember(Name = "packagePath")] public string PackagePath { get; set; } = string.Empty; [DataMember(Name = "version")] [Required] - public string Version { get; set; } = string.Empty; + public string Version { get; set; } = "1.0.0"; /// /// The minimum umbraco version that this package requires /// [DataMember(Name = "umbracoVersion")] - public Version UmbracoVersion { get; set; } + public Version UmbracoVersion { get; set; } = Configuration.UmbracoVersion.Current; [DataMember(Name = "author")] [Required] @@ -65,31 +61,31 @@ namespace Umbraco.Core.Models.Packaging public string Readme { get; set; } = string.Empty; [DataMember(Name = "contentLoadChildNodes")] - public bool ContentLoadChildNodes { get; set; } = false; + public bool ContentLoadChildNodes { get; set; } [DataMember(Name = "contentNodeId")] public string ContentNodeId { get; set; } = string.Empty; [DataMember(Name = "macros")] - public List Macros { get; set; } = new List(); + public IList Macros { get; set; } = new List(); [DataMember(Name = "languages")] - public List Languages { get; set; } = new List(); + public IList Languages { get; set; } = new List(); [DataMember(Name = "dictionaryItems")] - public List DictionaryItems { get; set; } = new List(); + public IList DictionaryItems { get; set; } = new List(); [DataMember(Name = "templates")] - public List Templates { get; set; } = new List(); + public IList Templates { get; set; } = new List(); [DataMember(Name = "documentTypes")] - public List DocumentTypes { get; set; } = new List(); + public IList DocumentTypes { get; set; } = new List(); [DataMember(Name = "stylesheets")] - public List Stylesheets { get; set; } = new List(); + public IList Stylesheets { get; set; } = new List(); [DataMember(Name = "files")] - public List Files { get; set; } = new List(); + public IList Files { get; set; } = new List(); //TODO: Change this to angular view [DataMember(Name = "loadControl")] @@ -99,7 +95,7 @@ namespace Umbraco.Core.Models.Packaging public string Actions { get; set; } [DataMember(Name = "dataTypes")] - public List DataTypes { get; set; } = new List(); + public IList DataTypes { get; set; } = new List(); [DataMember(Name = "iconUrl")] public string IconUrl { get; set; } = string.Empty; diff --git a/src/Umbraco.Core/Packaging/IPackageBuilder.cs b/src/Umbraco.Core/Packaging/IPackageBuilder.cs new file mode 100644 index 0000000000..5a7449545f --- /dev/null +++ b/src/Umbraco.Core/Packaging/IPackageBuilder.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Models.Packaging; + +namespace Umbraco.Core.Packaging +{ + /// + /// Creates packages + /// + public interface IPackageBuilder + { + IEnumerable GetAll(); + PackageDefinition GetById(int id); + void Delete(int id); + + /// + /// Persists a package definition to storage + /// + /// + /// true if creating/updating the package was successful, otherwise false + /// + bool SavePackage(PackageDefinition definition); + + /// + /// Creates the package file and returns it's physical path + /// + /// + string ExportPackage(PackageDefinition definition); + } +} diff --git a/src/Umbraco.Core/Packaging/IPackageCreation.cs b/src/Umbraco.Core/Packaging/IPackageCreation.cs deleted file mode 100644 index 35397299a7..0000000000 --- a/src/Umbraco.Core/Packaging/IPackageCreation.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Umbraco.Core.Models.Packaging; - -namespace Umbraco.Core.Packaging -{ - /// - /// Creates packages - /// - public interface IPackageCreation - { - /// - /// Persists a package definition to storage - /// - /// - void SavePackageDefinition(PackageDefinition definition); - - /// - /// Creates the package file and returns it's physical path - /// - /// - string ExportPackageDefinition(PackageDefinition definition); - } -} diff --git a/src/Umbraco.Core/Packaging/PackageCreation.cs b/src/Umbraco.Core/Packaging/PackageBuilder.cs similarity index 72% rename from src/Umbraco.Core/Packaging/PackageCreation.cs rename to src/Umbraco.Core/Packaging/PackageBuilder.cs index 615b844101..cc77f9bc78 100644 --- a/src/Umbraco.Core/Packaging/PackageCreation.cs +++ b/src/Umbraco.Core/Packaging/PackageBuilder.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.IO; using System.IO.Compression; using System.Linq; @@ -14,7 +15,7 @@ using File = System.IO.File; namespace Umbraco.Core.Packaging { - internal class PackageCreation : IPackageCreation + internal class PackageBuilder : IPackageBuilder { private readonly IContentService _contentService; private readonly IContentTypeService _contentTypeService; @@ -24,11 +25,15 @@ namespace Umbraco.Core.Packaging private readonly ILocalizationService _languageService; private readonly IEntityXmlSerializer _serializer; private readonly ILogger _logger; - - public PackageCreation(IContentService contentService, IContentTypeService contentTypeService, + private readonly string _mediaFolderPath; + private readonly string _packagesFolderPath; + private readonly string _tempFolderPath; + + public PackageBuilder(IContentService contentService, IContentTypeService contentTypeService, IDataTypeService dataTypeService, IFileService fileService, IMacroService macroService, ILocalizationService languageService, - IEntityXmlSerializer serializer, ILogger logger) + IEntityXmlSerializer serializer, ILogger logger, + string tempFolderPath = null, string packagesFolderPath = null, string mediaFolderPath = null) { _contentService = contentService; _contentTypeService = contentTypeService; @@ -38,49 +43,87 @@ namespace Umbraco.Core.Packaging _languageService = languageService; _serializer = serializer; _logger = logger; + + _tempFolderPath = tempFolderPath ?? SystemDirectories.Data + "/TEMP/PackageFiles"; + _packagesFolderPath = packagesFolderPath ?? SystemDirectories.Packages; + _mediaFolderPath = mediaFolderPath ?? SystemDirectories.Media + "/created-packages"; } - public static string CreatedPackagesFile => SystemDirectories.Packages + IOHelper.DirSepChar + "createdPackages.config"; + private string CreatedPackagesFile => _packagesFolderPath.EnsureEndsWith('/') + "createdPackages.config"; - public void SavePackageDefinition(PackageDefinition definition) + public IEnumerable GetAll() + { + var packagesXml = EnsureStorage(out _); + foreach (var packageXml in packagesXml.Root.Elements("package")) + yield return XmlToPackageDefinition(packageXml); + } + + public PackageDefinition GetById(int id) + { + var packagesXml = EnsureStorage(out _); + var packageXml = packagesXml.Root.Elements("package").FirstOrDefault(x => x.AttributeValue("id") == id); + return packageXml == null ? null : XmlToPackageDefinition(packageXml); + } + + public void Delete(int id) + { + var packagesXml = EnsureStorage(out var packagesFile); + var packageXml = packagesXml.Root.Elements("package").FirstOrDefault(x => x.AttributeValue("id") == id); + if (packageXml == null) return; + + packageXml.Remove(); + + packagesXml.Save(packagesFile); + } + + public bool SavePackage(PackageDefinition definition) { if (definition == null) throw new ArgumentNullException(nameof(definition)); var packagesXml = EnsureStorage(out var packagesFile); + //ensure it's valid + ValidatePackage(definition); + if (definition.Id == default) { //need to gen an id and persist // Find max id - var maxId = packagesXml.Root.Elements("package").Max(x => x.AttributeValue("id")); + var maxId = packagesXml.Root.Elements("package").Max(x => x.AttributeValue("id")) ?? 0; var newId = maxId + 1; definition.Id = newId; - definition.PackageGuid = Guid.NewGuid().ToString(); - definition.Folder = Guid.NewGuid().ToString(); + definition.PackageId = Guid.NewGuid(); + definition.FolderId = Guid.NewGuid(); var packageXml = PackageDefinitionToXml(definition); - packagesXml.Add(packageXml); + packagesXml.Root.Add(packageXml); } else { //existing var packageXml = packagesXml.Root.Elements("package").FirstOrDefault(x => x.AttributeValue("id") == definition.Id); if (packageXml == null) - throw new InvalidOperationException($"The package with id {definition.Id} was not found"); + return false; var updatedXml = PackageDefinitionToXml(definition); packageXml.ReplaceWith(updatedXml); } packagesXml.Save(packagesFile); + + return true; } - public string ExportPackageDefinition(PackageDefinition definition) + public string ExportPackage(PackageDefinition definition) { if (definition.Id == default) throw new ArgumentException("The package definition does not have an ID, it must be saved before being exported"); - if (definition.PackageGuid.IsNullOrWhiteSpace()) throw new ArgumentException("the package definition does not have a GUID, it must be saved before being exported"); + if (definition.PackageId == default) throw new ArgumentException("the package definition does not have a GUID, it must be saved before being exported"); + if (definition.FolderId == default) throw new ArgumentException("the package definition does not have a folder GUID, it must be saved before being exported"); + + //ensure it's valid + ValidatePackage(definition); //Create a folder for building this package - var temporaryPath = IOHelper.MapPath(SystemDirectories.Data + "/TEMP/PackageFiles/" + definition.Folder); + var temporaryPath = IOHelper.MapPath(_tempFolderPath.EnsureEndsWith('/') + definition.FolderId); if (Directory.Exists(temporaryPath) == false) Directory.CreateDirectory(temporaryPath); @@ -90,7 +133,7 @@ namespace Umbraco.Core.Packaging var packageManifest = CreatePackageManifest(out var manifestRoot, out var filesXml); //Info section - packageManifest.Add(GetPackageInfoXml(definition)); + manifestRoot.Add(GetPackageInfoXml(definition)); PackageDocumentsAndTags(definition, manifestRoot); PackageDocumentTypes(definition, manifestRoot); @@ -124,7 +167,7 @@ namespace Umbraco.Core.Packaging } catch (Exception e) { - _logger.Warn(e, "Could not add package actions to the package manifest, the xml did not parse"); + _logger.Warn(e, "Could not add package actions to the package manifest, the xml did not parse"); } } @@ -136,13 +179,17 @@ namespace Umbraco.Core.Packaging packageManifest.Save(manifestFileName); // check if there's a packages directory below media - var packagesDirectory = SystemDirectories.Media + "/created-packages"; - if (Directory.Exists(IOHelper.MapPath(packagesDirectory)) == false) - Directory.CreateDirectory(IOHelper.MapPath(packagesDirectory)); + + if (Directory.Exists(IOHelper.MapPath(_mediaFolderPath)) == false) + Directory.CreateDirectory(IOHelper.MapPath(_mediaFolderPath)); - var packPath = packagesDirectory + "/" + (definition.Name + "_" + definition.Version).Replace(' ', '_') + ".zip"; + var packPath = _mediaFolderPath.EnsureEndsWith('/') + (definition.Name + "_" + definition.Version).Replace(' ', '_') + ".zip"; ZipPackage(temporaryPath, IOHelper.MapPath(packPath)); + //we need to update the package path and save it + definition.PackagePath = packPath; + SavePackage(definition); + return packPath; } finally @@ -152,6 +199,16 @@ namespace Umbraco.Core.Packaging } } + private void ValidatePackage(PackageDefinition definition) + { + //ensure it's valid + var context = new ValidationContext(definition, serviceProvider: null, items: null); + var results = new List(); + var isValid = Validator.TryValidateObject(definition, context, results); + if (!isValid) + throw new InvalidOperationException("Validation failed, there is invalid data on the model: " + string.Join(", ", results.Select(x => x.ErrorMessage))); + } + private void PackageDataTypes(PackageDefinition definition, XContainer manifestRoot) { var dataTypes = new XElement("DataTypes"); @@ -342,6 +399,8 @@ namespace Umbraco.Core.Packaging /// The save path. private static void ZipPackage(string path, string savePath) { + if (File.Exists(savePath)) + File.Delete(savePath); ZipFile.CreateFromDirectory(path, savePath); } @@ -476,7 +535,7 @@ namespace Umbraco.Core.Packaging requirements.Add(new XElement("patch", definition.UmbracoVersion == null ? UmbracoVersion.SemanticVersion.Patch.ToInvariantString() : definition.UmbracoVersion.Build.ToInvariantString())); if (definition.UmbracoVersion != null) - requirements.Add(new XAttribute("type", "strict")); + requirements.Add(new XAttribute("type", RequirementsType.Strict.ToString())); package.Add(requirements); info.Add(package); @@ -496,13 +555,13 @@ namespace Umbraco.Core.Packaging { files = new XElement("files"); root = new XElement("umbPackage", files); - var packageManifest = new XDocument(); + var packageManifest = new XDocument(root); return packageManifest; } - private static XDocument EnsureStorage(out string packagesFile) + private XDocument EnsureStorage(out string packagesFile) { - var packagesFolder = IOHelper.MapPath(SystemDirectories.Packages); + var packagesFolder = IOHelper.MapPath(_packagesFolderPath); //ensure it exists Directory.CreateDirectory(packagesFolder); @@ -517,45 +576,80 @@ namespace Umbraco.Core.Packaging return packagesXml; } + private static PackageDefinition XmlToPackageDefinition(XElement xml) + { + if (xml == null) return null; + + var retVal = new PackageDefinition + { + Id = xml.AttributeValue("id"), + Name = xml.AttributeValue("name") ?? string.Empty, + FolderId = xml.AttributeValue("folder"), + PackagePath = xml.AttributeValue("packagePath") ?? string.Empty, + Version = xml.AttributeValue("version") ?? string.Empty, + Url = xml.AttributeValue("url") ?? string.Empty, + PackageId = xml.AttributeValue("packageGuid"), + IconUrl = xml.AttributeValue("iconUrl") ?? string.Empty, + UmbracoVersion = xml.AttributeValue("umbVersion"), + License = xml.Element("license")?.Value ?? string.Empty, + LicenseUrl = xml.Element("license")?.AttributeValue("url") ?? string.Empty, + Author = xml.Element("author")?.Value ?? string.Empty, + AuthorUrl = xml.Element("author")?.AttributeValue("url") ?? string.Empty, + Readme = xml.Element("readme")?.Value ?? string.Empty, + Actions = xml.Element("actions")?.ToString() ?? string.Empty, + ContentNodeId = xml.Element("content")?.AttributeValue("nodeId") ?? string.Empty, + ContentLoadChildNodes = xml.Element("content")?.AttributeValue("loadChildNodes") ?? false, + Macros = xml.Element("macros")?.Value.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), + Templates = xml.Element("templates")?.Value.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), + Stylesheets = xml.Element("stylesheets")?.Value.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), + DocumentTypes = xml.Element("documentTypes")?.Value.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), + Languages = xml.Element("languages")?.Value.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), + DictionaryItems = xml.Element("dictionaryitems")?.Value.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), + DataTypes = xml.Element("datatypes")?.Value.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), + Files = xml.Element("files")?.Elements("file").Select(x => x.Value).ToList() ?? new List(), + LoadControl = xml.Element("loadcontrol")?.Value ?? string.Empty + }; + + return retVal; + } + private static XElement PackageDefinitionToXml(PackageDefinition def) { var packageXml = new XElement("package", new XAttribute("id", def.Id), - new XAttribute("version", def.Version), - new XAttribute("url", def.Url), - new XAttribute("name", def.Name), - new XAttribute("folder", def.Folder), //fixme: What is this? - new XAttribute("packagepath", def.PackagePath), - new XAttribute("repositoryGuid", def.RepositoryGuid), - new XAttribute("iconUrl", def.IconUrl), + new XAttribute("version", def.Version ?? string.Empty), + new XAttribute("url", def.Url ?? string.Empty), + new XAttribute("name", def.Name ?? string.Empty), + new XAttribute("folder", def.FolderId), + new XAttribute("packagePath", def.PackagePath ?? string.Empty), + new XAttribute("iconUrl", def.IconUrl ?? string.Empty), new XAttribute("umbVersion", def.UmbracoVersion), - new XAttribute("packageGuid", def.PackageGuid), - new XAttribute("hasUpdate", def.HasUpdate), //fixme: What is this? + new XAttribute("packageGuid", def.PackageId), new XElement("license", - new XCData(def.License), - new XAttribute("url", def.LicenseUrl)), + new XCData(def.License ?? string.Empty), + new XAttribute("url", def.LicenseUrl ?? string.Empty)), new XElement("author", - new XCData(def.Author), - new XAttribute("url", def.AuthorUrl)), + new XCData(def.Author ?? string.Empty), + new XAttribute("url", def.AuthorUrl ?? string.Empty)), - new XElement("readme", def.Readme), - new XElement("actions", def.Actions), - new XElement("datatypes", string.Join(",", def.DataTypes)), + new XElement("readme", def.Readme ?? string.Empty), + new XElement("actions", def.Actions ?? string.Empty), + new XElement("datatypes", string.Join(",", def.DataTypes ?? Array.Empty())), new XElement("content", new XAttribute("nodeId", def.ContentNodeId), new XAttribute("loadChildNodes", def.ContentLoadChildNodes)), - new XElement("templates", string.Join(",", def.Templates)), - new XElement("stylesheets", string.Join(",", def.Stylesheets)), - new XElement("documentTypes", string.Join(",", def.DocumentTypes)), - new XElement("macros", string.Join(",", def.Macros)), - new XElement("files", string.Join(",", def.Files)), - new XElement("languages", string.Join(",", def.Languages)), - new XElement("dictionaryitems", string.Join(",", def.DictionaryItems)), - new XElement("loadcontrol", "")); //fixme: no more loadcontrol, needs to be an angular view + new XElement("templates", string.Join(",", def.Templates ?? Array.Empty())), + new XElement("stylesheets", string.Join(",", def.Stylesheets ?? Array.Empty())), + new XElement("documentTypes", string.Join(",", def.DocumentTypes ?? Array.Empty())), + new XElement("macros", string.Join(",", def.Macros ?? Array.Empty())), + new XElement("files", (def.Files ?? Array.Empty()).Where(x => !x.IsNullOrWhiteSpace()).Select(x => new XElement("file", x))), + new XElement("languages", string.Join(",", def.Languages ?? Array.Empty())), + new XElement("dictionaryitems", string.Join(",", def.DictionaryItems ?? Array.Empty())), + new XElement("loadcontrol", def.LoadControl ?? string.Empty)); //fixme: no more loadcontrol, needs to be an angular view return packageXml; } diff --git a/src/Umbraco.Web/_Legacy/Packager/RequirementsType.cs b/src/Umbraco.Core/Packaging/RequirementsType.cs similarity index 65% rename from src/Umbraco.Web/_Legacy/Packager/RequirementsType.cs rename to src/Umbraco.Core/Packaging/RequirementsType.cs index ca91626128..38cac482c2 100644 --- a/src/Umbraco.Web/_Legacy/Packager/RequirementsType.cs +++ b/src/Umbraco.Core/Packaging/RequirementsType.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Web._Legacy.Packager +namespace Umbraco.Core.Packaging { public enum RequirementsType { diff --git a/src/Umbraco.Core/Services/IPackagingService.cs b/src/Umbraco.Core/Services/IPackagingService.cs index 585ea36aa9..4d60f3dca4 100644 --- a/src/Umbraco.Core/Services/IPackagingService.cs +++ b/src/Umbraco.Core/Services/IPackagingService.cs @@ -9,14 +9,27 @@ namespace Umbraco.Core.Services { public interface IPackagingService : IService { - #region Package Creation + #region Package Building + + IEnumerable GetAll(); + PackageDefinition GetById(int id); + void Delete(int id); + /// /// Persists a package definition to storage /// /// - void SavePackageDefinition(PackageDefinition definition); + bool SavePackage(PackageDefinition definition); + + /// + /// Creates the package file and returns it's physical path + /// + /// + string ExportPackage(PackageDefinition definition); + #endregion + #region Importing /// /// Imports and saves package xml as /// @@ -88,7 +101,8 @@ namespace Umbraco.Core.Services /// Optional id of the User performing the operation. Default is zero (admin) /// Optional parameter indicating whether or not to raise events /// An enumrable list of generated Templates - IEnumerable ImportTemplates(XElement element, int userId = 0, bool raiseEvents = true); + IEnumerable ImportTemplates(XElement element, int userId = 0, bool raiseEvents = true); + #endregion /// /// This will fetch an Umbraco package file from the package repository and return the relative file path to the downloaded package file diff --git a/src/Umbraco.Core/Services/Implement/PackagingService.cs b/src/Umbraco.Core/Services/Implement/PackagingService.cs index 203a2cb2e8..17959f0983 100644 --- a/src/Umbraco.Core/Services/Implement/PackagingService.cs +++ b/src/Umbraco.Core/Services/Implement/PackagingService.cs @@ -46,7 +46,7 @@ namespace Umbraco.Core.Services.Implement private readonly IAuditRepository _auditRepository; private readonly IContentTypeRepository _contentTypeRepository; private readonly PropertyEditorCollection _propertyEditors; - private readonly IPackageCreation _packageCreation; + private readonly IPackageBuilder _packageBuilder; private static HttpClient _httpClient; public PackagingService( @@ -62,7 +62,7 @@ namespace Umbraco.Core.Services.Implement IAuditRepository auditRepository, IContentTypeRepository contentTypeRepository, PropertyEditorCollection propertyEditors, - IPackageCreation packageCreation) + IPackageBuilder packageBuilder) { _logger = logger; _contentService = contentService; @@ -76,7 +76,7 @@ namespace Umbraco.Core.Services.Implement _auditRepository = auditRepository; _contentTypeRepository = contentTypeRepository; _propertyEditors = propertyEditors; - _packageCreation = packageCreation; + _packageBuilder = packageBuilder; _importedContentTypes = new Dictionary(); } @@ -1402,7 +1402,15 @@ namespace Umbraco.Core.Services.Implement #region Package Building - public void SavePackageDefinition(PackageDefinition definition) => _packageCreation.SavePackageDefinition(definition); + public void Delete(int id) => _packageBuilder.Delete(id); + + public IEnumerable GetAll() => _packageBuilder.GetAll(); + + public PackageDefinition GetById(int id) => _packageBuilder.GetById(id); + + public bool SavePackage(PackageDefinition definition) => _packageBuilder.SavePackage(definition); + + public string ExportPackage(PackageDefinition definition) => _packageBuilder.ExportPackage(definition); #endregion diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 58ba136cc4..00179f562a 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -444,9 +444,10 @@ - + - + + diff --git a/src/Umbraco.Tests/Composing/TypeFinderTests.cs b/src/Umbraco.Tests/Composing/TypeFinderTests.cs index 2b9474310b..49c807b19f 100644 --- a/src/Umbraco.Tests/Composing/TypeFinderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeFinderTests.cs @@ -90,7 +90,7 @@ namespace Umbraco.Tests.Composing Assert.AreEqual(0, typesFound.Count()); // 0 classes in _assemblies are marked with [Tree] typesFound = TypeFinder.FindClassesWithAttribute(new[] { typeof (UmbracoContext).Assembly }); - Assert.AreEqual(21, typesFound.Count()); // + classes in Umbraco.Web are marked with [Tree] + Assert.AreEqual(22, typesFound.Count()); // + classes in Umbraco.Web are marked with [Tree] } private static IProfilingLogger GetTestProfilingLogger() diff --git a/src/Umbraco.Tests/Services/PackagingServiceTests.cs b/src/Umbraco.Tests/Services/PackagingServiceTests.cs index a87e7907b5..2caf3e08b3 100644 --- a/src/Umbraco.Tests/Services/PackagingServiceTests.cs +++ b/src/Umbraco.Tests/Services/PackagingServiceTests.cs @@ -1,5 +1,4 @@ -using System; -using System.IO; +using System.IO; using NUnit.Framework; using Umbraco.Core.IO; using Umbraco.Core.Models.Packaging; diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects.cs b/src/Umbraco.Tests/TestHelpers/TestObjects.cs index 120b0bcabb..c6602fc54e 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects.cs @@ -170,7 +170,7 @@ namespace Umbraco.Tests.TestHelpers var macroService = GetLazyService(factory, c => new MacroService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c))); var packagingService = GetLazyService(factory, c => new PackagingService( logger, contentService.Value, contentTypeService.Value, macroService.Value, dataTypeService.Value, fileService.Value, localizationService.Value, entityService.Value, scopeProvider, GetRepo(c), GetRepo(c), new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())), - new PackageCreation(contentService.Value, contentTypeService.Value, dataTypeService.Value, fileService.Value, macroService.Value, localizationService.Value, + new PackageBuilder(contentService.Value, contentTypeService.Value, dataTypeService.Value, fileService.Value, macroService.Value, localizationService.Value, new EntityXmlSerializer(contentService.Value, mediaService.Value, dataTypeService.Value, userService.Value, localizationService.Value, contentTypeService.Value, urlSegmentProviders), logger))); var relationService = GetLazyService(factory, c => new RelationService(scopeProvider, logger, eventMessagesFactory, entityService.Value, GetRepo(c), GetRepo(c))); var treeService = GetLazyService(factory, c => new ApplicationTreeService(logger, cache, typeLoader)); diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 12b830407e..3e619c6f00 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -62,6 +62,8 @@ + + @@ -140,6 +142,7 @@ + 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 0b74729cf6..ce2a557390 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 @@ -179,20 +179,29 @@ function packageResource($q, $http, umbDataFormatter, umbRequestHelper) { 'Failed to get package'); }, + getEmpty: function () { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "packageApiBaseUrl", + "getEmpty")), + 'Failed to get scaffold'); + }, + /** * @ngdoc method - * @name umbraco.resources.packageInstallResource#createPackage + * @name umbraco.resources.packageInstallResource#savePackage * @methodOf umbraco.resources.packageInstallResource * * @description - * Creates a new package + * Creates or updates a package */ - createPackage: function (umbPackage) { + savePackage: function (umbPackage) { return umbRequestHelper.resourcePromise( $http.post( umbRequestHelper.getApiUrl( "packageApiBaseUrl", - "PostCreatePackage"), umbPackage), + "PostSavePackage"), umbPackage), 'Failed to create package'); }, diff --git a/src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js index f3b64e0c28..b21909f573 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js @@ -119,6 +119,8 @@ function formHelper(angularHelper, serverValidationManager, notificationsService serverValidationManager.notifyAndClearAllSubscriptions(); } else { + + //TODO: All YSOD handling should be done with an interceptor overlayService.ysod(err); } } diff --git a/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js index 0834799be4..fb1a1b8d5e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js @@ -177,6 +177,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe //show a ysod dialog if (Umbraco.Sys.ServerVariables["isDebuggingEnabled"] === true) { const error = { errorMsg: 'An error occured', data: response.data }; + //TODO: All YSOD handling should be done with an interceptor overlayService.ysod(error); } else { @@ -288,6 +289,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe else if (Umbraco.Sys.ServerVariables["isDebuggingEnabled"] === true) { //show a ysod dialog const error = { errorMsg: 'An error occured', data: response.data }; + //TODO: All YSOD handling should be done with an interceptor overlayService.ysod(error); } else { diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.delete.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.delete.controller.js index 92e02d0d14..e594bae2f4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.delete.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.delete.controller.js @@ -65,6 +65,7 @@ function ContentDeleteController($scope, $timeout, contentResource, treeService, //check if response is ysod if (err.status && err.status >= 500) { + //TODO: All YSOD handling should be done with an interceptor overlayService.ysod(err); } }); diff --git a/src/Umbraco.Web.UI.Client/src/views/media/media.delete.controller.js b/src/Umbraco.Web.UI.Client/src/views/media/media.delete.controller.js index e5e95e94df..70b5120ebe 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/media.delete.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/media/media.delete.controller.js @@ -59,6 +59,7 @@ function MediaDeleteController($scope, mediaResource, treeService, navigationSer //check if response is ysod if (err.status && err.status >= 500) { + //TODO: All YSOD handling should be done with an interceptor overlayService.ysod(err); } diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js index f30c14816b..5f3832f79b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function EditController($scope, $location, $routeParams, entityResource, packageResource, contentTypeResource, templateResource, stylesheetResource, languageResource, dictionaryResource, dataTypeResource, editorService, formHelper) { + function EditController($scope, $location, $routeParams, entityResource, stylesheetResource, languageResource, packageResource, dictionaryResource, editorService, formHelper) { const vm = this; @@ -12,10 +12,9 @@ vm.contentOpen = true; vm.filesOpen = true; vm.actionsOpen = true; - + vm.loading = true; vm.back = back; - vm.createPackage = createPackage; - vm.save = save; + vm.createOrUpdatePackage = createOrUpdatePackage; vm.removeContentItem = removeContentItem; vm.openContentPicker = openContentPicker; vm.openFilePicker = openFilePicker; @@ -23,24 +22,23 @@ vm.openControlPicker = openControlPicker; vm.removeControl = removeControl; - function onInit() { + const packageId = $routeParams.id; + const create = $routeParams.create; - const packageId = $routeParams.id; - const create = $routeParams.create; + function onInit() { if(create) { //pre populate package with some values - vm.package = { - "version": "0.0.1", - "license": "MIT License", - "licenseUrl": "http://opensource.org/licenses/MIT", - "umbracoVersion": Umbraco.Sys.ServerVariables.application.version - }; + packageResource.getEmpty().then(scaffold => { + vm.package = scaffold; + vm.loading = false; + }); + vm.buttonLabel = "Create"; } else { // load package packageResource.getCreatedById(packageId).then(createdPackage => { vm.package = createdPackage; - + vm.loading = false; // get render model for content node if(vm.package.contentNodeId) { entityResource.getById(vm.package.contentNodeId, "Document") @@ -49,11 +47,12 @@ }); } - }, angular.noop); + }); + vm.buttonLabel = "Save"; } // get all doc types - contentTypeResource.getAll().then(documentTypes => { + entityResource.getAll("DocumentType").then(documentTypes => { // a package stores the id as a string so we // need to convert all ids to string for comparison documentTypes.forEach(documentType => { @@ -63,7 +62,7 @@ }); // get all templates - templateResource.getAll().then(templates => { + entityResource.getAll("Template").then(templates => { // a package stores the id as a string so we // need to convert all ids to string for comparison templates.forEach(template => { @@ -101,7 +100,7 @@ }); // get all data types items - dataTypeResource.getAll().then(dataTypes => { + entityResource.getAll("DataType").then(dataTypes => { // a package stores the id as a string so we // need to convert all ids to string for comparison dataTypes.forEach(dataType => { @@ -116,26 +115,31 @@ $location.path("packages/packages/overview").search('create', null);; } - function createPackage(editPackageForm) { + function createOrUpdatePackage(editPackageForm) { if (formHelper.submitForm({ formCtrl: editPackageForm, scope: $scope })) { - vm.createPackageButtonState = "busy"; + vm.buttonState = "busy"; + + packageResource.savePackage(vm.package).then((updatedPackage) => { - packageResource.createPackage(vm.package).then((updatedPackage) => { vm.package = updatedPackage; - vm.createPackageButtonState = "success"; + vm.buttonState = "success"; + + if (create) { + //if we are creating, then redirect to the correct url and reload + $location.path("packages/packages/overview/" + vm.package.id).search("subview", "created"); + //don't add a browser history for this + $location.replace(); + } + }, function(err){ formHelper.handleError(err); - vm.createPackageButtonState = "error"; + vm.buttonState = "error"; }); } } - function save() { - console.log("save package"); - } - function removeContentItem() { vm.package.contentNodeId = null; } diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html index bf33c24d4d..b1a417fd6f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html @@ -300,14 +300,15 @@ - + + + @@ -316,4 +317,4 @@ -
\ No newline at end of file +
diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 9969cea687..9105b1170f 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -157,13 +157,6 @@ editMacro.aspx - - directoryBrowser.aspx - ASPXCodeBehind - - - directoryBrowser.aspx - default.Master ASPXCodeBehind @@ -209,7 +202,6 @@ - @@ -307,8 +299,6 @@ - - Designer diff --git a/src/Umbraco.Web.UI/Umbraco/config/create/UI.Release.xml b/src/Umbraco.Web.UI/Umbraco/config/create/UI.Release.xml index 0ebdb5cd48..635174b9da 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/create/UI.Release.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/create/UI.Release.xml @@ -15,32 +15,4 @@ - -
Package
- /create/simple.ascx - - - -
- -
Package
- /create/simple.ascx - - - -
- -
Package
- /create/simple.ascx - - - -
- -
Package
- /create/simple.ascx - - - -
diff --git a/src/Umbraco.Web.UI/Umbraco/config/create/UI.xml b/src/Umbraco.Web.UI/Umbraco/config/create/UI.xml index d6be62ff88..c075a0b8b9 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/create/UI.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/create/UI.xml @@ -15,32 +15,4 @@ - -
Package
- /create/simple.ascx - - - -
- -
Package
- /create/simple.ascx - - - -
- -
Package
- /create/simple.ascx - - - -
- -
Package
- /create/simple.ascx - - - -
diff --git a/src/Umbraco.Web.UI/Umbraco/developer/Packages/DirectoryBrowser.aspx.cs b/src/Umbraco.Web.UI/Umbraco/developer/Packages/DirectoryBrowser.aspx.cs deleted file mode 100644 index 3e2edd1471..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/developer/Packages/DirectoryBrowser.aspx.cs +++ /dev/null @@ -1,140 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.Web; -using System.Web.UI; -using Umbraco.Core; -using Umbraco.Core.IO; -using Umbraco.Web.UI.Pages; - -namespace Umbraco.Web.UI.Umbraco.Developer.Packages -{ - public partial class DirectoryBrowser : UmbracoEnsuredPage - { - public DirectoryBrowser() - { - CurrentApp = Constants.Applications.Packages; - } - - string _lsScriptName; - string _lsWebPath; - protected string Target = ""; - private readonly Regex _xssElementIdClean = new Regex(@"^([a-zA-Z0-9-_:\.]+)"); - - private readonly StringBuilder _sb = new StringBuilder(); - - protected override void OnLoad(EventArgs e) - { - base.OnLoad(e); - - Response.Cache.SetExpires(DateTime.Now.AddSeconds(5)); - Response.Cache.SetCacheability(HttpCacheability.Public); - - //we need to clean this string: - //http://issues.umbraco.org/issue/U4-2027 - var target = Request.QueryString.Get("target"); - if (target.IsNullOrWhiteSpace()) - throw new InvalidOperationException("The target query string must be set to a valid html element id"); - var matched = _xssElementIdClean.Matches(target); - if (matched.Count == 0) - throw new InvalidOperationException("The target query string must be set to a valid html element id"); - - Target = matched[0].Value; - - try - { - - //Variables used in script - var sebChar = IOHelper.DirSepChar.ToString(); - - //Work on path and ensure no back tracking - string sSubDir = Request.QueryString.Get("path"); - if (string.IsNullOrEmpty(sSubDir)) { sSubDir = "/"; } - - sSubDir = sSubDir.Replace(IOHelper.DirSepChar.ToString(), ""); - sSubDir = sSubDir.Replace("//", "/"); - sSubDir = sSubDir.Replace("..", "./"); - sSubDir = sSubDir.Replace('/', IOHelper.DirSepChar); - - //Clean path for processing and collect path varitations - if (sSubDir.Substring(0, 1) != sebChar) { sSubDir = sebChar + sSubDir; } - if (sSubDir.Substring(sSubDir.Length - 1, 1) != "\\") { sSubDir = sSubDir + sebChar; } - - //Get name of the browser script file - _lsScriptName = Request.ServerVariables.Get("SCRIPT_NAME"); - var j = _lsScriptName.LastIndexOf("/"); - if (j > 0) { _lsScriptName = _lsScriptName.Substring(j + 1, _lsScriptName.Length - (j + 1)).ToLower(); } - - //Create navigation string and other path strings - GetNavLink("", "root"); - if (sSubDir != sebChar) - { - j = 0; int i = 0; - do - { - i = sSubDir.IndexOf(sebChar, j + 1); - _lsWebPath += sSubDir.Substring(j + 1, i - (j + 1)) + "/"; - GetNavLink(_lsWebPath, sSubDir.Substring(j + 1, i - (j + 1))); - j = i; - } while (i != sSubDir.Length - 1); - } - - //Output header - _sb.Append(""); - - //Output directorys - var oDirInfo = new DirectoryInfo(IOHelper.MapPath("~/" + sSubDir)); - var oDirs = oDirInfo.GetDirectories(); - foreach (var oDir in oDirs) - { - try - { - _sb.Append(""); - } - catch (Exception) - { - _sb.Append(""); - } - } - - //Ouput files - var oFiles = oDirInfo.GetFiles(); - foreach (var oFile in oFiles.Where(oFile => oFile.Name.ToLower() != _lsScriptName)) - { - decimal iLen = oFile.Length; - string sLen; - if (iLen >= 1048960) { iLen = iLen / 1048960; sLen = "mb"; } else { iLen = iLen / 1024; sLen = "kb"; } - sLen = Decimal.Round(iLen, 2).ToString() + sLen; - _sb.Append(""); - } - - //Output footer - _sb.Append("
" + oDir.Name + " (Include entire folder)
" + oDir.Name + " (Access Denied)
" + oFile.Name + "
"); - - } - catch (Exception ex) - { - RptErr(ex.Message); - } - } - - protected override void OnPreRender(EventArgs e) - { - base.OnPreRender(e); - Output.Controls.Add(new LiteralControl(_sb.ToString())); - } - - private void RptErr(string psMessage) - { - _sb.Append("
Script Reported Error:  " + psMessage + "

"); - } - - private string GetNavLink(string psHref, string psText) - { - return ("/" + psText + ""); - } - - } -} diff --git a/src/Umbraco.Web.UI/Umbraco/developer/Packages/DirectoryBrowser.aspx.designer.cs b/src/Umbraco.Web.UI/Umbraco/developer/Packages/DirectoryBrowser.aspx.designer.cs deleted file mode 100644 index 22bf0892b7..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/developer/Packages/DirectoryBrowser.aspx.designer.cs +++ /dev/null @@ -1,42 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Umbraco.Web.UI.Umbraco.Developer.Packages { - - - public partial class DirectoryBrowser { - - /// - /// CssInclude1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.CssInclude CssInclude1; - - /// - /// pane control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane pane; - - /// - /// Output control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder Output; - } -} diff --git a/src/Umbraco.Web.UI/Umbraco/developer/Packages/directoryBrowser.aspx b/src/Umbraco.Web.UI/Umbraco/developer/Packages/directoryBrowser.aspx deleted file mode 100644 index c6d2645ab3..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/developer/Packages/directoryBrowser.aspx +++ /dev/null @@ -1,26 +0,0 @@ -<%@ Page Language="C#" AutoEventWireup="True" MasterPageFile="../../masterpages/umbracoDialog.Master" CodeBehind="DirectoryBrowser.aspx.cs" Inherits="Umbraco.Web.UI.Umbraco.Developer.Packages.DirectoryBrowser" %> - -<%@ Register TagPrefix="cc1" Namespace="Umbraco.Web._Legacy.Controls" Assembly="Umbraco.Web" %> -<%@ Register TagPrefix="cdf" Namespace="ClientDependency.Core.Controls" Assembly="ClientDependency.Core" %> - - - - - - - - - - -
- - - -
-
diff --git a/src/Umbraco.Web.UI/Umbraco/developer/Packages/editPackage.aspx b/src/Umbraco.Web.UI/Umbraco/developer/Packages/editPackage.aspx deleted file mode 100644 index 956c17fe4a..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/developer/Packages/editPackage.aspx +++ /dev/null @@ -1,232 +0,0 @@ -<%@ Page Language="C#" ValidateRequest="false" AutoEventWireup="true" MasterPageFile="../../masterpages/umbracoPage.Master" - Title="Package and export content" CodeBehind="editPackage.aspx.cs" Inherits="umbraco.presentation.developer.packages._Default" %> - -<%@ Register TagPrefix="cc2" Namespace="Umbraco.Web._Legacy.Controls" Assembly="Umbraco.Web" %> - - - - - - - - - * - - - - * - - - - * - - - - - - - - - - - - - - * - Invalid version number (eg. 7.5.0) - - - - - - - * - - - - * - - - - - - - * - - - - * - - - - - - - - - - -
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Remember: .ascx files for your macros - will be added automaticly, but you will still need to add assemblies, - images and script files manually to the list below. -
-
- - - - - - - - - - - - - - - - - -
- Absolute path to file (ie: /bin/umbraco.bin) - -
- - - -
- - - - - - -
-
- - - - - - - - -
- Load control after installation (ex: /usercontrols/installer.ascx) -
- - - - -
-
- - - - - - - - - - - -
-

- Here you can add custom installer / uninstaller events to perform certain tasks - during installation and uninstallation. -
- All actions are formed as a xml node, containing data for the action to be performed. - Package actions documentation -

- -
- Actions: -
- -
-
- -
diff --git a/src/Umbraco.Web.UI/Umbraco/developer/Packages/installer.aspx b/src/Umbraco.Web.UI/Umbraco/developer/Packages/installer.aspx deleted file mode 100644 index 1beda45dae..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/developer/Packages/installer.aspx +++ /dev/null @@ -1,10 +0,0 @@ -<%@ Page Language="c#" MasterPageFile="../../masterpages/umbracoPage.Master" -AutoEventWireup="True" Inherits="umbraco.presentation.developer.packages.Installer" Trace="false" ValidateRequest="false" %> -<%@ Register TagPrefix="cc1" Namespace="Umbraco.Web._Legacy.Controls" Assembly="Umbraco.Web" %> - - - - - - - diff --git a/src/Umbraco.Web.UI/umbraco/developer/Packages/editPackage.aspx b/src/Umbraco.Web.UI/umbraco/developer/Packages/editPackage.aspx deleted file mode 100644 index 956c17fe4a..0000000000 --- a/src/Umbraco.Web.UI/umbraco/developer/Packages/editPackage.aspx +++ /dev/null @@ -1,232 +0,0 @@ -<%@ Page Language="C#" ValidateRequest="false" AutoEventWireup="true" MasterPageFile="../../masterpages/umbracoPage.Master" - Title="Package and export content" CodeBehind="editPackage.aspx.cs" Inherits="umbraco.presentation.developer.packages._Default" %> - -<%@ Register TagPrefix="cc2" Namespace="Umbraco.Web._Legacy.Controls" Assembly="Umbraco.Web" %> - - - - - - - - - * - - - - * - - - - * - - - - - - - - - - - - - - * - Invalid version number (eg. 7.5.0) - - - - - - - * - - - - * - - - - - - - * - - - - * - - - - - - - - - - -
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Remember: .ascx files for your macros - will be added automaticly, but you will still need to add assemblies, - images and script files manually to the list below. -
-
- - - - - - - - - - - - - - - - - -
- Absolute path to file (ie: /bin/umbraco.bin) - -
- - - -
- - - - - - -
-
- - - - - - - - -
- Load control after installation (ex: /usercontrols/installer.ascx) -
- - - - -
-
- - - - - - - - - - - -
-

- Here you can add custom installer / uninstaller events to perform certain tasks - during installation and uninstallation. -
- All actions are formed as a xml node, containing data for the action to be performed. - Package actions documentation -

- -
- Actions: -
- -
-
- -
diff --git a/src/Umbraco.Web/Editors/PackageController.cs b/src/Umbraco.Web/Editors/PackageController.cs index dfb8823a39..66a0349417 100644 --- a/src/Umbraco.Web/Editors/PackageController.cs +++ b/src/Umbraco.Web/Editors/PackageController.cs @@ -23,57 +23,42 @@ namespace Umbraco.Web.Editors [UmbracoApplicationAuthorize(Core.Constants.Applications.Packages)] public class PackageController : UmbracoAuthorizedJsonController { - public List GetCreatedPackages() + public IEnumerable GetCreatedPackages() { - return CreatedPackage.GetAllCreatedPackages().Select(x => x.Data).ToList(); + return Services.PackagingService.GetAll(); } public PackageDefinition GetCreatedPackageById(int id) { - var package = CreatedPackage.GetById(id); + var package = Services.PackagingService.GetById(id); if (package == null) throw new HttpResponseException(HttpStatusCode.NotFound); - return package.Data; + return package; } - public PackageDefinition PostUpdatePackage(PackageDefinition model) + public PackageDefinition GetEmpty() { - var package = CreatedPackage.GetById(model.Id); - if (package == null) - throw new HttpResponseException(HttpStatusCode.NotFound); - - if (ModelState.IsValid == false) - { - //Throw/bubble up errors - throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState)); - } - - package.Data = model; - - //We should have packagepath populated now - return package.Data; + return new PackageDefinition(); } - public PackageDefinition PostCreatePackage(PackageDefinition model) + /// + /// Creates or updates a package + /// + /// + /// + public PackageDefinition PostSavePackage(PackageDefinition model) { - //creating requires an empty model/package id - if (model.Id != 0 || model.PackageGuid != null) - throw new HttpResponseException(HttpStatusCode.NotFound); - if (ModelState.IsValid == false) - { - //Throw/bubble up errors throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState)); - } //save it - Services.PackagingService.SavePackageDefinition(model); + if (!Services.PackagingService.SavePackage(model)) + throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse("The package with id {definition.Id} was not found")); - //then publish to get the file - //package.Publish(); - //TODO: We need a link to the downloadable zip file, in packagepath ? - + Services.PackagingService.ExportPackage(model); + + //the packagePath will be on the model return model; } @@ -86,11 +71,7 @@ namespace Umbraco.Web.Editors [HttpDelete] public IHttpActionResult DeleteCreatedPackage(int packageId) { - var package = CreatedPackage.GetById(packageId); - if (package == null) - return NotFound(); - - package.Delete(); + Services.PackagingService.Delete(packageId); return Ok(); } diff --git a/src/Umbraco.Web/Editors/PackageInstallController.cs b/src/Umbraco.Web/Editors/PackageInstallController.cs index b58ed05bde..55e843aceb 100644 --- a/src/Umbraco.Web/Editors/PackageInstallController.cs +++ b/src/Umbraco.Web/Editors/PackageInstallController.cs @@ -510,7 +510,7 @@ namespace Umbraco.Web.Editors } model.TemporaryDirectoryPath = Path.Combine(SystemDirectories.Data, tempPath); - model.Id = ins.CreateManifest(IOHelper.MapPath(model.TemporaryDirectoryPath), model.PackageGuid.ToString(), model.RepositoryGuid.ToString()); + model.Id = ins.CreateManifest(IOHelper.MapPath(model.TemporaryDirectoryPath), model.PackageGuid, model.RepositoryGuid.ToString()); return model; } @@ -584,6 +584,7 @@ namespace Umbraco.Web.Editors var redirectUrl = ""; if (ins.Control.IsNullOrWhiteSpace() == false) { + //fixme: this needs to be replaced with an angular view the installer.aspx no longer exists. redirectUrl = string.Format("/developer/framed/{0}", Uri.EscapeDataString( string.Format("/umbraco/developer/Packages/installer.aspx?installing=custominstaller&dir={0}&pId={1}&customControl={2}&customUrl={3}", tempDir, model.Id, ins.Control, ins.Url))); diff --git a/src/Umbraco.Web/Install/Controllers/InstallPackageController.cs b/src/Umbraco.Web/Install/Controllers/InstallPackageController.cs index 1fd2ac27bb..55680084e5 100644 --- a/src/Umbraco.Web/Install/Controllers/InstallPackageController.cs +++ b/src/Umbraco.Web/Install/Controllers/InstallPackageController.cs @@ -60,7 +60,7 @@ namespace Umbraco.Web.Install.Controllers var tempFile = installer.Import(packageFile); installer.LoadConfig(tempFile); - var pId = installer.CreateManifest(tempFile, model.KitGuid.ToString(), RepoGuid); + var pId = installer.CreateManifest(tempFile, model.KitGuid, RepoGuid); return Json(new { success = true, diff --git a/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs b/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs index 8f9f9242d7..c79be96a93 100644 --- a/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs @@ -68,7 +68,7 @@ namespace Umbraco.Web.Install.InstallSteps var tempFile = installer.Import(packageFile); installer.LoadConfig(tempFile); - var pId = installer.CreateManifest(tempFile, kitGuid.ToString(), RepoGuid); + var pId = installer.CreateManifest(tempFile, kitGuid, RepoGuid); InstallPackageFiles(pId, tempFile); diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index fdf7f48201..b9a6c17ef7 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -1142,10 +1142,7 @@ - - - @@ -1236,13 +1233,6 @@ FeedProxy.aspx - - editPackage.aspx - ASPXCodeBehind - - - editPackage.aspx - @@ -1288,9 +1278,6 @@ - - ASPXCodeBehind - diff --git a/src/Umbraco.Web/_Legacy/Packager/Installer.cs b/src/Umbraco.Web/_Legacy/Packager/Installer.cs index 9d7fe77e7b..15a7839227 100644 --- a/src/Umbraco.Web/_Legacy/Packager/Installer.cs +++ b/src/Umbraco.Web/_Legacy/Packager/Installer.cs @@ -209,7 +209,7 @@ namespace Umbraco.Web._Legacy.Packager return Import(inputFile, true); } - public int CreateManifest(string tempDir, string guid, string repoGuid) + public int CreateManifest(string tempDir, Guid guid, string repoGuid) { //This is the new improved install rutine, which chops up the process into 3 steps, creating the manifest, moving files, and finally handling umb objects var packName = XmlHelper.GetNodeValue(Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/name")); @@ -243,8 +243,7 @@ namespace Umbraco.Web._Legacy.Packager insPack.Data.Url = packUrl; insPack.Data.IconUrl = iconUrl; - insPack.Data.PackageGuid = guid; //the package unique key. - insPack.Data.RepositoryGuid = repoGuid; //the repository unique key, if the package is a file install, the repository will not get logged. + insPack.Data.PackageId = guid; //the package unique key. insPack.Save(); return insPack.Data.Id; @@ -324,7 +323,7 @@ namespace Umbraco.Web._Legacy.Packager { Current.Services.AuditService.Add(AuditType.PackagerInstall, _currentUserId, - -1, "Package", string.Format("Package '{0}' installed. Package guid: {1}", insPack.Data.Name, insPack.Data.PackageGuid)); + -1, "Package", string.Format("Package '{0}' installed. Package guid: {1}", insPack.Data.Name, insPack.Data.PackageId)); } insPack.Save(); @@ -373,7 +372,8 @@ namespace Umbraco.Web._Legacy.Packager if (languageItemsElement != null) { var insertedLanguages = packagingService.ImportLanguages(languageItemsElement); - insPack.Data.Languages.AddRange(insertedLanguages.Select(l => l.Id.ToString(CultureInfo.InvariantCulture))); + foreach(var x in insertedLanguages.Select(l => l.Id.ToString(CultureInfo.InvariantCulture))) + insPack.Data.Languages.Add(x); } #endregion @@ -383,7 +383,8 @@ namespace Umbraco.Web._Legacy.Packager if (dictionaryItemsElement != null) { var insertedDictionaryItems = packagingService.ImportDictionaryItems(dictionaryItemsElement); - insPack.Data.DictionaryItems.AddRange(insertedDictionaryItems.Select(d => d.Id.ToString(CultureInfo.InvariantCulture))); + foreach (var x in insertedDictionaryItems.Select(d => d.Id.ToString(CultureInfo.InvariantCulture))) + insPack.Data.DictionaryItems.Add(x); } #endregion @@ -392,7 +393,9 @@ namespace Umbraco.Web._Legacy.Packager if (macroItemsElement != null) { var insertedMacros = packagingService.ImportMacros(macroItemsElement); - insPack.Data.Macros.AddRange(insertedMacros.Select(m => m.Id.ToString(CultureInfo.InvariantCulture))); + foreach (var x in insertedMacros.Select(m => m.Id.ToString(CultureInfo.InvariantCulture))) + insPack.Data.Macros.Add(x); + } #endregion diff --git a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/CreatedPackage.cs b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/CreatedPackage.cs deleted file mode 100644 index deff3b2ffd..0000000000 --- a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/CreatedPackage.cs +++ /dev/null @@ -1,387 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Xml; -using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Core.IO; -using Umbraco.Core.Models; -using Umbraco.Core.Services; -using Umbraco.Core.Services.Implement; -using File = System.IO.File; - - -namespace Umbraco.Web._Legacy.Packager.PackageInstance -{ - //TODO: Fix this class , service + model + internal? - public class CreatedPackage - { - - public static CreatedPackage GetById(int id) - { - var pack = new CreatedPackage(); - pack.Data = data.Package(id, IOHelper.MapPath(Settings.CreatedPackagesSettings)); - return pack; - } - - public static CreatedPackage MakeNew(string name, Core.Models.Packaging.PackageDefinition packageData = null) - { - var pack = new CreatedPackage - { - Data = packageData ?? data.MakeNew(name, IOHelper.MapPath(Settings.CreatedPackagesSettings)) - }; - - return pack; - } - - public void Save() - { - data.Save(this.Data, IOHelper.MapPath(Settings.CreatedPackagesSettings)); - } - - public void Delete() - { - data.Delete(this.Data.Id, IOHelper.MapPath(Settings.CreatedPackagesSettings)); - } - - public Core.Models.Packaging.PackageDefinition Data { get; set; } - - public static List GetAllCreatedPackages() - { - var val = new List(); - - foreach (var pack in data.GetAllPackages(IOHelper.MapPath(Settings.CreatedPackagesSettings))) - { - var crPack = new CreatedPackage(); - crPack.Data = pack; - val.Add(crPack); - } - - return val; - } - - private static XmlDocument _packageManifest; - private static void CreatePackageManifest() - { - _packageManifest = new XmlDocument(); - var xmldecl = _packageManifest.CreateXmlDeclaration("1.0", "UTF-8", "no"); - - _packageManifest.AppendChild(xmldecl); - - //root node - XmlNode umbPackage = _packageManifest.CreateElement("umbPackage"); - _packageManifest.AppendChild(umbPackage); - //Files node - umbPackage.AppendChild(_packageManifest.CreateElement("files")); - } - - private static void AppendElement(XmlNode node) - { - var root = _packageManifest.SelectSingleNode("/umbPackage"); - root.AppendChild(node); - } - - - public void Publish(IEntityXmlSerializer serializer) - { - - var package = this; - var pack = package.Data; - - var outInt = 0; - - //Path checking... - var localPath = IOHelper.MapPath(SystemDirectories.Media + "/" + pack.Folder); - - if (Directory.Exists(localPath) == false) - Directory.CreateDirectory(localPath); - - //Init package file... - CreatePackageManifest(); - //Info section.. - AppendElement(PackagerUtility.PackageInfo(pack, _packageManifest)); - - //Documents and tags... - var contentNodeId = 0; - if (string.IsNullOrEmpty(pack.ContentNodeId) == false && int.TryParse(pack.ContentNodeId, out contentNodeId)) - { - if (contentNodeId > 0) - { - //Create the Documents/DocumentSet node - XmlNode documents = _packageManifest.CreateElement("Documents"); - XmlNode documentSet = _packageManifest.CreateElement("DocumentSet"); - XmlAttribute importMode = _packageManifest.CreateAttribute("importMode", ""); - importMode.Value = "root"; - documentSet.Attributes.Append(importMode); - documents.AppendChild(documentSet); - - //load content from umbraco. - //var umbDocument = new Document(contentNodeId); - //var x = umbDocument.ToXml(_packageManifest, pack.ContentLoadChildNodes); - var udoc = Current.Services.ContentService.GetById(contentNodeId); - var xe = pack.ContentLoadChildNodes ? udoc.ToDeepXml(serializer) : udoc.ToXml(serializer); - var x = xe.GetXmlNode(_packageManifest); - documentSet.AppendChild(x); - - AppendElement(documents); - - ////Create the TagProperties node - this is used to store a definition for all - //// document properties that are tags, this ensures that we can re-import tags properly - //XmlNode tagProps = _packageManifest.CreateElement("TagProperties"); - - ////before we try to populate this, we'll do a quick lookup to see if any of the documents - //// being exported contain published tags. - //var allExportedIds = documents.SelectNodes("//@id").Cast() - // .Select(x => x.Value.TryConvertTo()) - // .Where(x => x.Success) - // .Select(x => x.Result) - // .ToArray(); - //var allContentTags = new List(); - //foreach (var exportedId in allExportedIds) - //{ - // allContentTags.AddRange( - // Current.Services.TagService.GetTagsForEntity(exportedId)); - //} - - ////This is pretty round-about but it works. Essentially we need to get the properties that are tagged - //// but to do that we need to lookup by a tag (string) - //var allTaggedEntities = new List(); - //foreach (var group in allContentTags.Select(x => x.Group).Distinct()) - //{ - // allTaggedEntities.AddRange( - // Current.Services.TagService.GetTaggedContentByTagGroup(group)); - //} - - ////Now, we have all property Ids/Aliases and their referenced document Ids and tags - //var allExportedTaggedEntities = allTaggedEntities.Where(x => allExportedIds.Contains(x.EntityId)) - // .DistinctBy(x => x.EntityId) - // .OrderBy(x => x.EntityId); - - //foreach (var taggedEntity in allExportedTaggedEntities) - //{ - // foreach (var taggedProperty in taggedEntity.TaggedProperties.Where(x => x.Tags.Any())) - // { - // XmlNode tagProp = _packageManifest.CreateElement("TagProperty"); - // var docId = _packageManifest.CreateAttribute("docId", ""); - // docId.Value = taggedEntity.EntityId.ToString(CultureInfo.InvariantCulture); - // tagProp.Attributes.Append(docId); - - // var propertyAlias = _packageManifest.CreateAttribute("propertyAlias", ""); - // propertyAlias.Value = taggedProperty.PropertyTypeAlias; - // tagProp.Attributes.Append(propertyAlias); - - // var group = _packageManifest.CreateAttribute("group", ""); - // group.Value = taggedProperty.Tags.First().Group; - // tagProp.Attributes.Append(group); - - // tagProp.AppendChild(_packageManifest.CreateCDataSection( - // JsonConvert.SerializeObject(taggedProperty.Tags.Select(x => x.Text).ToArray()))); - - // tagProps.AppendChild(tagProp); - // } - //} - - //AppendElement(tagProps); - - } - } - - //Document types.. - var dtl = new List(); - var docTypes = _packageManifest.CreateElement("DocumentTypes"); - foreach (var dtId in pack.DocumentTypes) - { - if (int.TryParse(dtId, out outInt)) - { - var docT = Current.Services.ContentTypeService.Get(outInt); - //DocumentType docT = new DocumentType(outInt); - - AddDocumentType(docT, ref dtl); - - } - } - - foreach (var d in dtl) - { - var xml = serializer.Serialize(d); - var xNode = xml.GetXmlNode(); - var n = (XmlElement) _packageManifest.ImportNode(xNode, true); - docTypes.AppendChild(n); - } - - AppendElement(docTypes); - - //Templates - var templates = _packageManifest.CreateElement("Templates"); - foreach (var templateId in pack.Templates) - { - if (int.TryParse(templateId, out outInt)) - { - var t = Current.Services.FileService.GetTemplate(outInt); - - var serialized = serializer.Serialize(t); - var n = serialized.GetXmlNode(_packageManifest); - - - templates.AppendChild(n); - } - } - AppendElement(templates); - - //Stylesheets - var stylesheets = _packageManifest.CreateElement("Stylesheets"); - foreach (var stylesheetName in pack.Stylesheets) - { - if (stylesheetName.IsNullOrWhiteSpace()) continue; - var stylesheetXmlNode = PackagerUtility.Stylesheet(stylesheetName, true, _packageManifest); - if (stylesheetXmlNode != null) - stylesheets.AppendChild(stylesheetXmlNode); - } - AppendElement(stylesheets); - - ////Macros - //var macros = _packageManifest.CreateElement("Macros"); - //foreach (var macroId in pack.Macros) - //{ - // if (int.TryParse(macroId, out outInt)) - // { - // macros.AppendChild(PackagerUtility.Macro(int.Parse(macroId), true, localPath, _packageManifest)); - // } - //} - //AppendElement(macros); - - //Dictionary Items - var dictionaryItems = _packageManifest.CreateElement("DictionaryItems"); - foreach (var dictionaryId in pack.DictionaryItems) - { - if (int.TryParse(dictionaryId, out outInt)) - { - var di = Current.Services.LocalizationService.GetDictionaryItemById(outInt); - var xmlNode = serializer.Serialize(di, false).GetXmlNode(_packageManifest); - dictionaryItems.AppendChild(xmlNode); - } - } - AppendElement(dictionaryItems); - - //Languages - var languages = _packageManifest.CreateElement("Languages"); - foreach (var langId in pack.Languages) - { - if (int.TryParse(langId, out outInt)) - { - var lang = Current.Services.LocalizationService.GetLanguageById(outInt); - - var xml = serializer.Serialize(lang); - var n = xml.GetXmlNode(_packageManifest); - - languages.AppendChild(n); - } - } - AppendElement(languages); - - //TODO: Fix this! ... actually once we use the new packager we don't need to - - ////Datatypes - //var dataTypes = _packageManifest.CreateElement("DataTypes"); - //foreach (var dtId in pack.DataTypes) - //{ - // if (int.TryParse(dtId, out outInt)) - // { - // datatype.DataTypeDefinition dtd = new datatype.DataTypeDefinition(outInt); - // dataTypes.AppendChild(dtd.ToXml(_packageManifest)); - // } - //} - //AppendElement(dataTypes); - - //Files - foreach (var fileName in pack.Files) - { - PackagerUtility.AppendFileToManifest(fileName, localPath, _packageManifest); - } - - //Load control on install... - if (string.IsNullOrEmpty(pack.LoadControl) == false) - { - XmlNode control = _packageManifest.CreateElement("control"); - control.InnerText = pack.LoadControl; - PackagerUtility.AppendFileToManifest(pack.LoadControl, localPath, _packageManifest); - AppendElement(control); - } - - //Actions - if (string.IsNullOrEmpty(pack.Actions) == false) - { - try - { - var xdActions = new XmlDocument(); - xdActions.LoadXml("" + pack.Actions + ""); - var actions = xdActions.DocumentElement.SelectSingleNode("."); - - - if (actions != null) - { - actions = _packageManifest.ImportNode(actions, true).Clone(); - AppendElement(actions); - } - } - catch - { - //TODO: Log!? - } - } - - var manifestFileName = localPath + "/package.xml"; - - if (File.Exists(manifestFileName)) - File.Delete(manifestFileName); - - _packageManifest.Save(manifestFileName); - _packageManifest = null; - - - //string packPath = Settings.PackagerRoot.Replace(System.IO.Path.DirectorySeparatorChar.ToString(), "/") + "/" + pack.Name.Replace(' ', '_') + "_" + pack.Version.Replace(' ', '_') + "." + Settings.PackageFileExtension; - - // check if there's a packages directory below media - var packagesDirectory = SystemDirectories.Media + "/created-packages"; - if (Directory.Exists(IOHelper.MapPath(packagesDirectory)) == false) - { - Directory.CreateDirectory(IOHelper.MapPath(packagesDirectory)); - } - - - var packPath = packagesDirectory + "/" + (pack.Name + "_" + pack.Version).Replace(' ', '_') + "." + Settings.PackageFileExtension; - PackagerUtility.ZipPackage(localPath, IOHelper.MapPath(packPath)); - - pack.PackagePath = packPath; - - if (pack.PackageGuid.Trim() == "") - pack.PackageGuid = Guid.NewGuid().ToString(); - - package.Save(); - - //Clean up.. - File.Delete(localPath + "/package.xml"); - Directory.Delete(localPath, true); - } - - private void AddDocumentType(IContentType dt, ref List dtl) - { - if (dt.ParentId > 0) - { - var parent = Current.Services.ContentTypeService.Get(dt.ParentId); - if (parent != null) // could be a container - { - AddDocumentType(parent, ref dtl); - } - } - - if (dtl.Contains(dt) == false) - { - dtl.Add(dt); - } - } - - - - } -} diff --git a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/InstalledPackage.cs b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/InstalledPackage.cs index 8c106f142d..d7ea239a3f 100644 --- a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/InstalledPackage.cs +++ b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/InstalledPackage.cs @@ -72,7 +72,7 @@ namespace Umbraco.Web._Legacy.Packager.PackageInstance public void Delete(int userId) { - Current.Services.AuditService.Add(AuditType.PackagerUninstall, userId, -1, "Package", string.Format("Package '{0}' uninstalled. Package guid: {1}", Data.Name, Data.PackageGuid)); + Current.Services.AuditService.Add(AuditType.PackagerUninstall, userId, -1, "Package", string.Format("Package '{0}' uninstalled. Package guid: {1}", Data.Name, Data.PackageId)); Delete(); } diff --git a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackagerUtility.cs b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackagerUtility.cs deleted file mode 100644 index af32e8c80a..0000000000 --- a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackagerUtility.cs +++ /dev/null @@ -1,279 +0,0 @@ -using System; -using System.Collections; -using System.IO; -using System.Xml; -using ICSharpCode.SharpZipLib.Zip; -using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Core.IO; -using Umbraco.Core.Services; -using Umbraco.Core.Services.Implement; - -namespace Umbraco.Web._Legacy.Packager.PackageInstance -{ - /// - /// A utillity class for working with packager data. - /// It provides basic methods for adding new items to a package manifest, moving files and other misc. - /// - public class PackagerUtility - { - /// - /// Creates a package manifest containing name, license, version and other meta data. - /// - /// The packinstance. - /// The xml document. - /// - public static XmlNode PackageInfo(Core.Models.Packaging.PackageDefinition pack, XmlDocument doc) - { - XmlNode info = doc.CreateElement("info"); - - //Package info - XmlNode package = doc.CreateElement("package"); - package.AppendChild(CreateNode("name", pack.Name, doc)); - package.AppendChild(CreateNode("version", pack.Version, doc)); - package.AppendChild(CreateNode("iconUrl", pack.IconUrl, doc)); - - XmlNode license = CreateNode("license", pack.License, doc); - license.Attributes.Append(CreateAttribute("url", pack.LicenseUrl, doc)); - package.AppendChild(license); - - package.AppendChild(CreateNode("url", pack.Url, doc)); - - XmlNode requirements = doc.CreateElement("requirements"); - //NOTE: The defaults are 3.0.0 - I'm just leaving that here since that's the way it's been //SD - requirements.AppendChild(CreateNode("major", pack.UmbracoVersion == null ? "3" : pack.UmbracoVersion.Major.ToInvariantString(), doc)); - requirements.AppendChild(CreateNode("minor", pack.UmbracoVersion == null ? "0" : pack.UmbracoVersion.Minor.ToInvariantString(), doc)); - requirements.AppendChild(CreateNode("patch", pack.UmbracoVersion == null ? "0" : pack.UmbracoVersion.Build.ToInvariantString(), doc)); - if (pack.UmbracoVersion != null) - requirements.Attributes.Append(CreateAttribute("type", "strict", doc)); - - package.AppendChild(requirements); - info.AppendChild(package); - - //Author - XmlNode author = CreateNode("author", "", doc); - author.AppendChild(CreateNode("name", pack.Author, doc)); - author.AppendChild(CreateNode("website", pack.AuthorUrl, doc)); - info.AppendChild(author); - - info.AppendChild(CreateNode("readme", "", doc)); - - return info; - } - - /// - /// Converts a umbraco stylesheet to a package xml node - /// - /// The name of the stylesheet. - /// if set to true [incluce properties]. - /// The doc. - /// - public static XmlNode Stylesheet(string name, bool includeProperties, XmlDocument doc) - { - if (doc == null) throw new ArgumentNullException("doc"); - if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value cannot be null or whitespace.", "name"); - - var fileService = Current.Services.FileService; - var sts = fileService.GetStylesheetByName(name); - var stylesheet = doc.CreateElement("Stylesheet"); - stylesheet.AppendChild(CreateNode("Name", sts.Alias, doc)); - stylesheet.AppendChild(CreateNode("FileName", sts.Name, doc)); - stylesheet.AppendChild(CreateNode("Content", "", doc)); - if (includeProperties) - { - var properties = doc.CreateElement("Properties"); - foreach (var ssP in sts.Properties) - { - var property = doc.CreateElement("Property"); - property.AppendChild(CreateNode("Name", ssP.Name, doc)); - property.AppendChild(CreateNode("Alias", ssP.Alias, doc)); - property.AppendChild(CreateNode("Value", ssP.Value, doc)); - } - stylesheet.AppendChild(properties); - } - return stylesheet; - } - - - /// - /// Appends a file to package manifest and copies the file to the correct folder. - /// - /// The path. - /// The package directory. - /// The doc. - public static void AppendFileToManifest(string path, string packageDirectory, XmlDocument doc) - { - if (!path.StartsWith("~/") && !path.StartsWith("/")) - path = "~/" + path; - - string serverPath = IOHelper.MapPath(path); - - if (System.IO.File.Exists(serverPath)) - - AppendFileXml(path, packageDirectory, doc); - else if (System.IO.Directory.Exists(serverPath)) - ProcessDirectory(path, packageDirectory, doc); - } - - - - //Process files in directory and add them to package - private static void ProcessDirectory(string path, string packageDirectory, XmlDocument doc) - { - string serverPath = IOHelper.MapPath(path); - if (System.IO.Directory.Exists(serverPath)) - { - System.IO.DirectoryInfo di = new System.IO.DirectoryInfo(serverPath); - - foreach (System.IO.FileInfo file in di.GetFiles()) - AppendFileXml(path + "/" + file.Name, packageDirectory, doc); - - foreach (System.IO.DirectoryInfo dir in di.GetDirectories()) - ProcessDirectory(path + "/" + dir.Name, packageDirectory, doc); - } - } - - private static void AppendFileXml(string path, string packageDirectory, XmlDocument doc) - { - - string serverPath = IOHelper.MapPath(path); - - string orgPath = path.Substring(0, (path.LastIndexOf('/'))); - string orgName = path.Substring((path.LastIndexOf('/') + 1)); - string newFileName = orgName; - - if (System.IO.File.Exists(packageDirectory + "/" + orgName)) - { - string fileGuid = System.Guid.NewGuid().ToString(); - newFileName = fileGuid + "_" + newFileName; - } - - //Copy file to directory for zipping... - System.IO.File.Copy(serverPath, packageDirectory + "/" + newFileName, true); - - //Append file info to files xml node - XmlNode files = doc.SelectSingleNode("/umbPackage/files"); - - XmlNode file = doc.CreateElement("file"); - file.AppendChild(CreateNode("guid", newFileName, doc)); - file.AppendChild(CreateNode("orgPath", orgPath == "" ? "/" : orgPath, doc)); - file.AppendChild(CreateNode("orgName", orgName, doc)); - - files.AppendChild(file); - } - - /// - /// Determines whether the file is in the package manifest - /// - /// The GUID. - /// The doc. - /// - /// true if [is file in manifest]; otherwise, false. - /// - public static bool IsFileInManifest(string guid, XmlDocument doc) - { - return false; - } - - private static XmlNode CreateNode(string name, string value, XmlDocument doc) - { - var node = doc.CreateElement(name); - node.InnerXml = value; - return node; - } - - private static XmlAttribute CreateAttribute(string name, string value, XmlDocument doc) - { - var attribute = doc.CreateAttribute(name); - attribute.Value = value; - return attribute; - } - - /// - /// Zips the package. - /// - /// The path. - /// The save path. - public static void ZipPackage(string Path, string savePath) - { - string OutPath = savePath; - - ArrayList ar = GenerateFileList(Path); - // generate file list - // find number of chars to remove from orginal file path - int TrimLength = (Directory.GetParent(Path)).ToString().Length; - - TrimLength += 1; - - //remove '\' - FileStream ostream; - - byte[] obuffer; - - ZipOutputStream oZipStream = new ZipOutputStream(System.IO.File.Create(OutPath)); - // create zip stream - - - oZipStream.SetLevel(9); - // 9 = maximum compression level - ZipEntry oZipEntry; - - foreach (string Fil in ar) // for each file, generate a zipentry - { - oZipEntry = new ZipEntry(Fil.Remove(0, TrimLength)); - oZipStream.PutNextEntry(oZipEntry); - - - if (!Fil.EndsWith(@"/")) // if a file ends with '/' its a directory - { - ostream = File.OpenRead(Fil); - - obuffer = new byte[ostream.Length]; - - // byte buffer - ostream.Read(obuffer, 0, obuffer.Length); - - oZipStream.Write(obuffer, 0, obuffer.Length); - ostream.Close(); - } - } - oZipStream.Finish(); - oZipStream.Close(); - oZipStream.Dispose(); - oZipStream = null; - - oZipEntry = null; - - - ostream = null; - ar.Clear(); - ar = null; - } - - private static ArrayList GenerateFileList(string Dir) - { - ArrayList mid = new ArrayList(); - - bool Empty = true; - - // add each file in directory - foreach (string file in Directory.GetFiles(Dir)) - { - mid.Add(file); - Empty = false; - } - - // if directory is completely empty, add it - if (Empty && Directory.GetDirectories(Dir).Length == 0) - mid.Add(Dir + @"/"); - - // do this recursively - foreach (string dirs in Directory.GetDirectories(Dir)) - { - foreach (object obj in GenerateFileList(dirs)) - mid.Add(obj); - } - return mid; // return file list - } - } -} diff --git a/src/Umbraco.Web/_Legacy/Packager/Settings.cs b/src/Umbraco.Web/_Legacy/Packager/Settings.cs index 92b0ae030a..e88f18262f 100644 --- a/src/Umbraco.Web/_Legacy/Packager/Settings.cs +++ b/src/Umbraco.Web/_Legacy/Packager/Settings.cs @@ -8,10 +8,6 @@ namespace Umbraco.Web._Legacy.Packager { public static string InstalledPackagesSettings => SystemDirectories.Packages + IOHelper.DirSepChar + "installedPackages.config"; - public static string CreatedPackagesSettings => SystemDirectories.Packages + IOHelper.DirSepChar + "createdPackages.config"; - - public static string PackageFileExtension => "zip"; - } } diff --git a/src/Umbraco.Web/_Legacy/Packager/data.cs b/src/Umbraco.Web/_Legacy/Packager/data.cs index 1882c151d2..51f0799689 100644 --- a/src/Umbraco.Web/_Legacy/Packager/data.cs +++ b/src/Umbraco.Web/_Legacy/Packager/data.cs @@ -95,7 +95,7 @@ namespace Umbraco.Web._Legacy.Packager instance.Attributes.Append(XmlHelper.AddAttribute(Source, "url", "")); instance.Attributes.Append(XmlHelper.AddAttribute(Source, "name", name)); instance.Attributes.Append(XmlHelper.AddAttribute(Source, "folder", Guid.NewGuid().ToString())); - instance.Attributes.Append(XmlHelper.AddAttribute(Source, "packagepath", "")); + instance.Attributes.Append(XmlHelper.AddAttribute(Source, "packagePath", "")); instance.Attributes.Append(XmlHelper.AddAttribute(Source, "repositoryGuid", "")); instance.Attributes.Append(XmlHelper.AddAttribute(Source, "iconUrl", "")); //set to current version @@ -187,13 +187,11 @@ namespace Umbraco.Web._Legacy.Packager { retVal.Id = int.Parse(SafeAttribute("id", n)); retVal.Name = SafeAttribute("name", n); - retVal.Folder = SafeAttribute("folder", n); - retVal.PackagePath = SafeAttribute("packagepath", n); + retVal.FolderId = Guid.Parse(SafeAttribute("folder", n)); + retVal.PackagePath = SafeAttribute("packagePath", n); retVal.Version = SafeAttribute("version", n); retVal.Url = SafeAttribute("url", n); - retVal.RepositoryGuid = SafeAttribute("repositoryGuid", n); - retVal.PackageGuid = SafeAttribute("packageGuid", n); - retVal.HasUpdate = bool.Parse(SafeAttribute("hasUpdate", n)); + retVal.PackageId = Guid.Parse(SafeAttribute("packageGuid", n)); retVal.IconUrl = SafeAttribute("iconUrl", n); var umbVersion = SafeAttribute("umbVersion", n); @@ -262,10 +260,8 @@ namespace Umbraco.Web._Legacy.Packager XmlHelper.SetAttribute(Source, xmlDef, "name", package.Name); XmlHelper.SetAttribute(Source, xmlDef, "version", package.Version); XmlHelper.SetAttribute(Source, xmlDef, "url", package.Url); - XmlHelper.SetAttribute(Source, xmlDef, "packagepath", package.PackagePath); - XmlHelper.SetAttribute(Source, xmlDef, "repositoryGuid", package.RepositoryGuid); - XmlHelper.SetAttribute(Source, xmlDef, "packageGuid", package.PackageGuid); - XmlHelper.SetAttribute(Source, xmlDef, "hasUpdate", package.HasUpdate.ToString()); + XmlHelper.SetAttribute(Source, xmlDef, "packagePath", package.PackagePath); + XmlHelper.SetAttribute(Source, xmlDef, "packageGuid", package.PackageId.ToString()); XmlHelper.SetAttribute(Source, xmlDef, "iconUrl", package.IconUrl); if (package.UmbracoVersion != null) XmlHelper.SetAttribute(Source, xmlDef, "umbVersion", package.UmbracoVersion.ToString(3)); @@ -359,7 +355,7 @@ namespace Umbraco.Web._Legacy.Packager } - private static string JoinList(List list, char seperator) + private static string JoinList(IList list, char seperator) { string retVal = ""; foreach (string str in list) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx deleted file mode 100644 index d7b71ecde1..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx +++ /dev/null @@ -1,232 +0,0 @@ -<%@ Page Language="C#" ValidateRequest="false" AutoEventWireup="true" MasterPageFile="../../masterpages/umbracoPage.Master" - Title="Package and export content" CodeBehind="editPackage.aspx.cs" Inherits="umbraco.presentation.developer.packages._Default" %> - -<%@ Register TagPrefix="cc2" Namespace="Umbraco.Web._Legacy.Controls" Assembly="Umbraco.Web" %> - - - - - - - - - * - - - - * - - - - * - - - - - - - - - - - - - - * - Invalid version number (eg. 7.5.0) - - - - - - - * - - - - * - - - - - - - * - - - - * - - - - - - - - - - -
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Remember: .xslt and .ascx files for your macros - will be added automaticly, but you will still need to add assemblies, - images and script files manually to the list below. -
-
- - - - - - - - - - - - - - - - - -
- Absolute path to file (ie: /bin/umbraco.bin) - -
- - - -
- - - - - - -
-
- - - - - - - - -
- Load control after installation (ex: /usercontrols/installer.ascx) -
- - - - -
-
- - - - - - - - - - - -
-

- Here you can add custom installer / uninstaller events to perform certain tasks - during installation and uninstallation. -
- All actions are formed as a xml node, containing data for the action to be performed. - Package actions documentation -

- -
- Actions: -
- -
-
- -
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.cs deleted file mode 100644 index 3b863895a4..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.cs +++ /dev/null @@ -1,453 +0,0 @@ -using Umbraco.Core.Services; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Web.UI; -using System.Web.UI.WebControls; - -using System.Xml; -using umbraco.controls; -using Umbraco.Core; -using Umbraco.Core.IO; -using Umbraco.Core.Models.Packaging; -using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.UI; -using Umbraco.Web.UI.Pages; -using Umbraco.Web._Legacy.Packager.PackageInstance; - -namespace umbraco.presentation.developer.packages -{ - public partial class _Default : UmbracoEnsuredPage - { - - public _Default() - { - CurrentApp = Constants.Applications.Packages.ToString(); - - } - public Umbraco.Web._Legacy.Controls.TabPage packageInfo; - public Umbraco.Web._Legacy.Controls.TabPage packageContents; - public Umbraco.Web._Legacy.Controls.TabPage packageFiles; - public Umbraco.Web._Legacy.Controls.TabPage packageOutput; - public Umbraco.Web._Legacy.Controls.TabPage packageAbout; - public Umbraco.Web._Legacy.Controls.TabPage packageActions; - - protected ContentPicker cp; - private PackageDefinition pack; - private CreatedPackage createdPackage; - - protected void Page_Load(object sender, EventArgs e) - { - if (Request.QueryString["id"] != null) - { - createdPackage = CreatedPackage.GetById(int.Parse(Request.QueryString["id"])); - pack = createdPackage.Data; - - /* CONTENT */ - - cp = new ContentPicker(); - content.Controls.Add(cp); - - if (string.IsNullOrEmpty(pack.PackagePath) == false) - { - packageUmbFile.Text = "   Download"; - } - else - { - packageUmbFile.Text = "This package is not published"; - } - - if (Page.IsPostBack == false) - { - ClientTools - .SyncTree("-1,created," + createdPackage.Data.Id, false); - - packageAuthorName.Text = pack.Author; - packageAuthorUrl.Text = pack.AuthorUrl; - packageLicenseName.Text = pack.License; - packageLicenseUrl.Text = pack.LicenseUrl; - packageName.Text = pack.Name; - packageReadme.Text = pack.Readme; - packageVersion.Text = pack.Version; - packageUrl.Text = pack.Url; - iconUrl.Text = pack.IconUrl; - umbracoVersion.Text = pack.UmbracoVersion != null ? pack.UmbracoVersion.ToString(3) : string.Empty; - - /*ACTIONS XML*/ - tb_actions.Text = pack.Actions; - - cp.Value = pack.ContentNodeId.ToString(); - - //startNode.Value = pack.ContentNodeId.ToString(); - - packageContentSubdirs.Checked = pack.ContentLoadChildNodes; - - - /*TEMPLATES */ - var nTemplates = Services.FileService.GetTemplates(); - //Template[] umbTemplates = Template.GetAllAsList().ToArray(); - foreach (var tmp in nTemplates) - { - ListItem li = new ListItem(tmp.Name, tmp.Id.ToString()); - - if (pack.Templates.Contains(tmp.Id.ToString())) - li.Selected = true; - - templates.Items.Add(li); - } - - /* DOC TYPES */ - // fixme - media types? member types? - var nContentTypes = Services.ContentTypeService.GetAll(); - //DocumentType[] docs = DocumentType.GetAllAsList().ToArray(); - foreach (var dc in nContentTypes) - { - ListItem li = new ListItem(dc.Name, dc.Id.ToString()); - if (pack.DocumentTypes.Contains(dc.Id.ToString())) - li.Selected = true; - - documentTypes.Items.Add(li); - } - - /*Stylesheets */ - var sheets = Services.FileService.GetStylesheets(); - foreach (var st in sheets) - { - if (string.IsNullOrEmpty(st.Name) == false) - { - var li = new ListItem(st.Alias, st.Name); - if (pack.Stylesheets.Contains(st.Name)) - li.Selected = true; - stylesheets.Items.Add(li); - } - } - - /* MACROS */ - var nMacros = Services.MacroService.GetAll(); - //Macro[] umbMacros = Macro.GetAll(); - foreach (var m in nMacros) - { - ListItem li = new ListItem(m.Name, m.Id.ToString()); - if (pack.Macros.Contains(m.Id.ToString())) - li.Selected = true; - - macros.Items.Add(li); - } - - /*Langauges */ - var nLanguages = Services.LocalizationService.GetAllLanguages(); - //Language[] umbLanguages = Language.getAll; - foreach (var l in nLanguages) - { - ListItem li = new ListItem(l.CultureName, l.Id.ToString()); - if (pack.Languages.Contains(l.Id.ToString())) - li.Selected = true; - - languages.Items.Add(li); - } - - /*Dictionary Items*/ - var umbDictionary = Services.LocalizationService.GetRootDictionaryItems(); - foreach (var d in umbDictionary) - { - - string liName = d.ItemKey; - var children = Services.LocalizationService.GetDictionaryItemChildren(d.Key); - if (children.Any()) - liName += " (Including all child items)"; - - var li = new ListItem(liName, d.Id.ToString()); - - if (pack.DictionaryItems.Contains(d.Id.ToString())) - li.Selected = true; - - dictionary.Items.Add(li); - } - - //TODO: Fix this with the new services and apis! and then remove since this should all be in angular - - ///*Data types */ - //cms.businesslogic.datatype.DataTypeDefinition[] umbDataType = cms.businesslogic.datatype.DataTypeDefinition.GetAll(); - - // sort array by name - //Array.Sort(umbDataType, delegate(cms.businesslogic.datatype.DataTypeDefinition umbDataType1, cms.businesslogic.datatype.DataTypeDefinition umbDataType2) - //{ - // return umbDataType1.Text.CompareTo(umbDataType2.Text); - //}); - - //foreach (cms.businesslogic.datatype.DataTypeDefinition umbDtd in umbDataType) - //{ - - // ListItem li = new ListItem(umbDtd.Text, umbDtd.Id.ToString()); - - // if (pack.DataTypes.Contains(umbDtd.Id.ToString())) - // li.Selected = true; - - // cbl_datatypes.Items.Add(li); - //} - - /* FILES */ - packageFilesRepeater.DataSource = pack.Files; - packageFilesRepeater.DataBind(); - - packageControlPath.Text = pack.LoadControl; - } - else - { - ClientTools - .SyncTree("-1,created," + createdPackage.Data.Id, true); - } - } - } - - protected void validateActions(object sender, ServerValidateEventArgs e) - { - string actions = tb_actions.Text; - if (!string.IsNullOrEmpty(actions)) - { - - actions = "" + actions + ""; - - try - { - //we try to load an xml document with the potential malformed xml to ensure that this is actual action xml... - XmlDocument xd = new XmlDocument(); - xd.LoadXml(actions); - e.IsValid = true; - } - catch - { - e.IsValid = false; - } - } - else - e.IsValid = true; - } - - //protected void saveOrPublish(object sender, CommandEventArgs e) - //{ - - // if (!Page.IsValid) - // { - // this.ClientTools.ShowSpeechBubble(SpeechBubbleIcon.Error, "Saved failed.", "Some fields have not been filled-out correctly"); - // } - // else - // { - // if (e.CommandName == "save") - // SavePackage(true); - - // if (e.CommandName == "publish") - // { - // SavePackage(false); - // int packageID = int.Parse(Request.QueryString["id"]); - // //string packFileName = cms.businesslogic.packager. Publish.publishPackage(packageID); - - // createdPackage.Publish(); - - - // if (!string.IsNullOrEmpty(pack.PackagePath)) - // { - - // packageUmbFile.Text = "   Download"; - - // this.ClientTools.ShowSpeechBubble(SpeechBubbleIcon.Success, "Package saved and published", ""); - // } - // else - // { - // this.ClientTools.ShowSpeechBubble(SpeechBubbleIcon.Error, "Save failed", "check your umbraco log."); - // } - // } - // } - //} - - - private void SavePackage(bool showNotification) - { - pack.Author = packageAuthorName.Text; - pack.AuthorUrl = packageAuthorUrl.Text; - - pack.License = packageLicenseName.Text; - pack.LicenseUrl = packageLicenseUrl.Text; - - pack.Readme = packageReadme.Text; - pack.Actions = tb_actions.Text; - - pack.Name = packageName.Text; - pack.Url = packageUrl.Text; - pack.Version = packageVersion.Text; - pack.IconUrl = iconUrl.Text; - pack.UmbracoVersion = Version.Parse(umbracoVersion.Text); - - pack.ContentLoadChildNodes = packageContentSubdirs.Checked; - - if (string.IsNullOrEmpty(cp.Value) == false) - pack.ContentNodeId = cp.Value; - else - pack.ContentNodeId = ""; - - - string tmpStylesheets = ""; - foreach (ListItem li in stylesheets.Items) - { - if (li.Selected) - tmpStylesheets += li.Value + ","; - } - pack.Stylesheets = new List(tmpStylesheets.Trim(',').Split(',')); - - - string tmpDoctypes = ""; - foreach (ListItem li in documentTypes.Items) - { - if (li.Selected) - tmpDoctypes += li.Value + ","; - } - pack.DocumentTypes = new List(tmpDoctypes.Trim(',').Split(',')); - - - string tmpMacros = ""; - foreach (ListItem li in macros.Items) - { - if (li.Selected) - tmpMacros += li.Value + ","; - } - pack.Macros = new List(tmpMacros.Trim(',').Split(',')); - - - string tmpLanguages = ""; - foreach (ListItem li in languages.Items) - { - if (li.Selected) - tmpLanguages += li.Value + ","; - } - pack.Languages = new List(tmpLanguages.Trim(',').Split(',')); - - string tmpDictionaries = ""; - foreach (ListItem li in dictionary.Items) - { - if (li.Selected) - tmpDictionaries += li.Value + ","; - } - pack.DictionaryItems = new List(tmpDictionaries.Trim(',').Split(',')); - - - string tmpTemplates = ""; - foreach (ListItem li in templates.Items) - { - if (li.Selected) - tmpTemplates += li.Value + ","; - } - pack.Templates = new List(tmpTemplates.Trim(',').Split(',')); - - string tmpDataTypes = ""; - foreach (ListItem li in cbl_datatypes.Items) - { - if (li.Selected) - tmpDataTypes += li.Value + ","; - } - pack.DataTypes = new List(tmpDataTypes.Trim(',').Split(',')); - - pack.LoadControl = packageControlPath.Text; - - - createdPackage.Save(); - - if (showNotification) - this.ClientTools.ShowSpeechBubble(SpeechBubbleIcon.Save, "Package Saved", ""); - } - - protected void addFileToPackage(object sender, EventArgs e) - { - string newPath = packageFilePathNew.Text; - - if (newPath.Trim() != "") - { - CreatedPackage createdPackage = CreatedPackage.GetById(int.Parse(Request.QueryString["id"])); - PackageDefinition pack = createdPackage.Data; - - pack.Files.Add(newPath); - - createdPackage.Save(); - - packageFilePathNew.Text = ""; - - packageFilesRepeater.DataSource = pack.Files; - packageFilesRepeater.DataBind(); - } - } - - protected void deleteFileFromPackage(object sender, EventArgs e) - { - TextBox filePathControl = (TextBox)((Control)sender).Parent.FindControl("packageFilePath"); - filePathControl.Text = ""; - - string tmpFilePathString = ""; - foreach (RepeaterItem rItem in packageFilesRepeater.Items) - { - string tmpFFFF = ((TextBox)rItem.FindControl("packageFilePath")).Text; - if (tmpFFFF.Trim() != "") - tmpFilePathString += tmpFFFF + "�"; - } - - CreatedPackage createdPackage = CreatedPackage.GetById(int.Parse(Request.QueryString["id"])); - PackageDefinition pack = createdPackage.Data; - - pack.Files = new List(tmpFilePathString.Trim('�').Split('�')); - pack.Files.TrimExcess(); - - createdPackage.Save(); - - packageFilesRepeater.DataSource = pack.Files; - packageFilesRepeater.DataBind(); - } - - protected override void OnInit(EventArgs e) - { - // Tab setup - packageInfo = TabView1.NewTabPage("Package Properties"); - packageInfo.Controls.Add(Pane1); - packageInfo.Controls.Add(Pane5); - packageInfo.Controls.Add(Pane1_1); - packageInfo.Controls.Add(Pane1_2); - packageInfo.Controls.Add(Pane1_3); - - - packageContents = TabView1.NewTabPage("Package Contents"); - packageContents.Controls.Add(Pane2); - packageContents.Controls.Add(Pane2_1); - packageContents.Controls.Add(Pane2_2); - packageContents.Controls.Add(Pane2_3); - packageContents.Controls.Add(Pane2_4); - packageContents.Controls.Add(Pane2_5); - packageContents.Controls.Add(Pane2_6); - packageContents.Controls.Add(Pane2_7); - - packageFiles = TabView1.NewTabPage("Package Files"); - packageFiles.Controls.Add(Pane3); - packageFiles.Controls.Add(Pane3_1); - packageFiles.Controls.Add(Pane3_2); - - packageActions = TabView1.NewTabPage("Package Actions"); - packageActions.Controls.Add(Pane4); - - //var pubs = TabView1.Menu.NewButton(); - //pubs.Text = Services.TextService.Localize("publish"); - //pubs.CommandName = "publish"; - //pubs.Command += new CommandEventHandler(saveOrPublish); - //pubs.ID = "saveAndPublish"; - - //var saves = TabView1.Menu.NewButton(); - //saves.Text = Services.TextService.Localize("save"); - //saves.CommandName = "save"; - //saves.Command += new CommandEventHandler(saveOrPublish); - //saves.ButtonType = Umbraco.Web._Legacy.Controls.MenuButtonType.Primary; - //saves.ID = "save"; - - - - - base.OnInit(e); - } - - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.designer.cs deleted file mode 100644 index d41967444a..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.designer.cs +++ /dev/null @@ -1,609 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.presentation.developer.packages { - - - public partial class _Default { - - /// - /// TabView1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.TabView TabView1; - - /// - /// Pane1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane1; - - /// - /// pp_name control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_name; - - /// - /// packageName control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox packageName; - - /// - /// RequiredFieldValidator0 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator0; - - /// - /// pp_url control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_url; - - /// - /// packageUrl control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox packageUrl; - - /// - /// RequiredFieldValidator1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator1; - - /// - /// pp_version control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_version; - - /// - /// packageVersion control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox packageVersion; - - /// - /// RequiredFieldValidator2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator2; - - /// - /// pp_icon control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_icon; - - /// - /// iconUrl control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox iconUrl; - - /// - /// pp_file control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_file; - - /// - /// packageUmbFile control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal packageUmbFile; - - /// - /// Pane5 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane5; - - /// - /// pp_umbracoVersion control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_umbracoVersion; - - /// - /// umbracoVersion control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox umbracoVersion; - - /// - /// RequiredFieldValidator7 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator7; - - /// - /// VersionValidator control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RegularExpressionValidator VersionValidator; - - /// - /// Pane1_1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane1_1; - - /// - /// pp_author control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_author; - - /// - /// packageAuthorName control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox packageAuthorName; - - /// - /// RequiredFieldValidator3 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator3; - - /// - /// pp_author_url control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_author_url; - - /// - /// packageAuthorUrl control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox packageAuthorUrl; - - /// - /// RequiredFieldValidator4 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator4; - - /// - /// Pane1_2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane1_2; - - /// - /// pp_licens control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_licens; - - /// - /// packageLicenseName control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox packageLicenseName; - - /// - /// RequiredFieldValidator5 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator5; - - /// - /// pp_license_url control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_license_url; - - /// - /// packageLicenseUrl control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox packageLicenseUrl; - - /// - /// RequiredFieldValidator6 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator6; - - /// - /// Pane1_3 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane1_3; - - /// - /// pp_readme control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_readme; - - /// - /// packageReadme control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox packageReadme; - - /// - /// Pane2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane2; - - /// - /// pp_content control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_content; - - /// - /// content control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder content; - - /// - /// packageContentSubdirs control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBox packageContentSubdirs; - - /// - /// packageContentSubdirsLabel control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label packageContentSubdirsLabel; - - /// - /// Pane2_1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane2_1; - - /// - /// documentTypes control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBoxList documentTypes; - - /// - /// Pane2_2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane2_2; - - /// - /// templates control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBoxList templates; - - /// - /// Pane2_3 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane2_3; - - /// - /// stylesheets control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBoxList stylesheets; - - /// - /// Pane2_4 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane2_4; - - /// - /// macros control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBoxList macros; - - /// - /// Pane2_5 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane2_5; - - /// - /// languages control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBoxList languages; - - /// - /// Pane2_6 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane2_6; - - /// - /// dictionary control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBoxList dictionary; - - /// - /// Pane2_7 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane2_7; - - /// - /// cbl_datatypes control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBoxList cbl_datatypes; - - /// - /// Pane3 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane3; - - /// - /// Pane3_1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane3_1; - - /// - /// packageFilesRepeater control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Repeater packageFilesRepeater; - - /// - /// packageFilePathNew control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox packageFilePathNew; - - /// - /// createNewFilePath control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button createNewFilePath; - - /// - /// Pane3_2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane3_2; - - /// - /// packageControlPath control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox packageControlPath; - - /// - /// Pane4 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane4; - - /// - /// actionsVal control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CustomValidator actionsVal; - - /// - /// tb_actions control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox tb_actions; - } -} From 091c9471bb3cc7b63b1f7870ebf6bebc07392024 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 10 Jan 2019 18:51:02 +1100 Subject: [PATCH 49/93] Gets macros in packages, gets the download button working to download a created package, more tests --- .../Models/Packaging/PackageDefinition.cs | 2 + src/Umbraco.Core/Packaging/PackageBuilder.cs | 22 +++++++--- .../src/views/packages/edit.controller.js | 24 +++++++++-- .../src/views/packages/edit.html | 25 +++++++++-- src/Umbraco.Web/Editors/PackageController.cs | 41 ++++++++++++++++++- 5 files changed, 101 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs b/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs index 16934dc4c4..67d0c2970f 100644 --- a/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs +++ b/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Runtime.Serialization; @@ -29,6 +30,7 @@ namespace Umbraco.Core.Models.Packaging [DataMember(Name = "folder")] public Guid FolderId { get; set; } + [ReadOnly(true)] [DataMember(Name = "packagePath")] public string PackagePath { get; set; } = string.Empty; diff --git a/src/Umbraco.Core/Packaging/PackageBuilder.cs b/src/Umbraco.Core/Packaging/PackageBuilder.cs index cc77f9bc78..6b26b09cbe 100644 --- a/src/Umbraco.Core/Packaging/PackageBuilder.cs +++ b/src/Umbraco.Core/Packaging/PackageBuilder.cs @@ -162,7 +162,9 @@ namespace Umbraco.Core.Packaging var actionsXml = new XElement("Actions"); try { - actionsXml.Add(XElement.Parse(definition.Actions)); + //this will be formatted like a full xml block like ... and we want the child nodes + var parsed = XElement.Parse(definition.Actions); + actionsXml.Add(parsed.Elements()); manifestRoot.Add(actionsXml); } catch (Exception e) @@ -596,7 +598,7 @@ namespace Umbraco.Core.Packaging Author = xml.Element("author")?.Value ?? string.Empty, AuthorUrl = xml.Element("author")?.AttributeValue("url") ?? string.Empty, Readme = xml.Element("readme")?.Value ?? string.Empty, - Actions = xml.Element("actions")?.ToString() ?? string.Empty, + Actions = xml.Element("actions")?.ToString(SaveOptions.None) ?? string.Empty, //take the entire outer xml value ContentNodeId = xml.Element("content")?.AttributeValue("nodeId") ?? string.Empty, ContentLoadChildNodes = xml.Element("content")?.AttributeValue("loadChildNodes") ?? false, Macros = xml.Element("macros")?.Value.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), @@ -613,8 +615,18 @@ namespace Umbraco.Core.Packaging return retVal; } - private static XElement PackageDefinitionToXml(PackageDefinition def) + private XElement PackageDefinitionToXml(PackageDefinition def) { + var actionsXml = new XElement("actions"); + try + { + actionsXml = XElement.Parse(def.Actions); + } + catch (Exception e) + { + _logger.Warn(e, "Could not add package actions to the package xml definition, the xml did not parse"); + } + var packageXml = new XElement("package", new XAttribute("id", def.Id), new XAttribute("version", def.Version ?? string.Empty), @@ -634,8 +646,8 @@ namespace Umbraco.Core.Packaging new XCData(def.Author ?? string.Empty), new XAttribute("url", def.AuthorUrl ?? string.Empty)), - new XElement("readme", def.Readme ?? string.Empty), - new XElement("actions", def.Actions ?? string.Empty), + new XElement("readme", new XCData(def.Readme ?? string.Empty)), + actionsXml, new XElement("datatypes", string.Join(",", def.DataTypes ?? Array.Empty())), new XElement("content", diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js index 5f3832f79b..f351456609 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function EditController($scope, $location, $routeParams, entityResource, stylesheetResource, languageResource, packageResource, dictionaryResource, editorService, formHelper) { + function EditController($scope, $location, $routeParams, umbRequestHelper, entityResource, stylesheetResource, languageResource, packageResource, dictionaryResource, editorService, formHelper) { const vm = this; @@ -21,6 +21,7 @@ vm.removeFile = removeFile; vm.openControlPicker = openControlPicker; vm.removeControl = removeControl; + vm.downloadFile = downloadFile; const packageId = $routeParams.id; const create = $routeParams.create; @@ -76,8 +77,14 @@ vm.stylesheets = stylesheets; }); - // TODO: implement macros - vm.macros = []; + entityResource.getAll("Macro").then(macros => { + // a package stores the id as a string so we + // need to convert all ids to string for comparison + macros.forEach(macro => { + macro.id = macro.id.toString(); + }); + vm.macros = macros; + }); // get all languages languageResource.getAll().then(languages => { @@ -111,6 +118,17 @@ } + function downloadFile(id) { + var url = umbRequestHelper.getApiUrl( + "packageApiBaseUrl", + "DownloadCreatedPackage", + { id: id }); + + umbRequestHelper.downloadFile(url).then(function () { + + }); + } + function back() { $location.path("packages/packages/overview").search('create', null);; } diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html index b1a417fd6f..20bc4277f8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html @@ -48,9 +48,10 @@ + @@ -170,7 +171,15 @@ - NOT IMPLEMENTED IN V8 YET +
+ +
+
@@ -301,6 +310,14 @@ + + + + + diff --git a/src/Umbraco.Web/Editors/PackageController.cs b/src/Umbraco.Web/Editors/PackageController.cs index 66a0349417..0369e3eb24 100644 --- a/src/Umbraco.Web/Editors/PackageController.cs +++ b/src/Umbraco.Web/Editors/PackageController.cs @@ -1,9 +1,12 @@ using System.Collections.Generic; +using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Formatting; +using System.Net.Http.Headers; using System.Web.Http; +using Umbraco.Core.IO; using Umbraco.Core.Models.Packaging; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; @@ -33,7 +36,7 @@ namespace Umbraco.Web.Editors var package = Services.PackagingService.GetById(id); if (package == null) throw new HttpResponseException(HttpStatusCode.NotFound); - + return package; } @@ -75,6 +78,40 @@ namespace Umbraco.Web.Editors return Ok(); } - + + [HttpGet] + public HttpResponseMessage DownloadCreatedPackage(int id) + { + var package = Services.PackagingService.GetById(id); + if (package == null) + return Request.CreateResponse(HttpStatusCode.NotFound); + + var fullPath = IOHelper.MapPath(package.PackagePath); + if (!File.Exists(fullPath)) + return Request.CreateNotificationValidationErrorResponse("No file found for path " + package.PackagePath); + + var fileName = Path.GetFileName(package.PackagePath); + + var response = new HttpResponseMessage + { + Content = new StreamContent(File.OpenRead(fullPath)) + { + Headers = + { + ContentDisposition = new ContentDispositionHeaderValue("attachment") + { + FileName = fileName + }, + ContentType = new MediaTypeHeaderValue( "application/octet-stream") + } + } + }; + + // Set custom header so umbRequestHelper.downloadFile can save the correct filename + response.Headers.Add("x-filename", fileName); + + return response; + } + } } From 5cb99bfddceafccb44d900b5ed3ef2bec56a1709 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 10 Jan 2019 18:55:24 +1100 Subject: [PATCH 50/93] forgot to add the test class --- .../Packaging/PackageBuilderTests.cs | 200 ++++++++++++++++++ .../src/views/packages/edit.html | 3 - 2 files changed, 200 insertions(+), 3 deletions(-) create mode 100644 src/Umbraco.Tests/Packaging/PackageBuilderTests.cs diff --git a/src/Umbraco.Tests/Packaging/PackageBuilderTests.cs b/src/Umbraco.Tests/Packaging/PackageBuilderTests.cs new file mode 100644 index 0000000000..6368001c11 --- /dev/null +++ b/src/Umbraco.Tests/Packaging/PackageBuilderTests.cs @@ -0,0 +1,200 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Xml.Linq; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Composing; +using Umbraco.Core.IO; +using Umbraco.Core.Models.Packaging; +using Umbraco.Core.Packaging; +using Umbraco.Core.Services; +using Umbraco.Tests.Services; +using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.Testing; + +namespace Umbraco.Tests.Packaging +{ + [TestFixture] + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] + public class PackageBuilderTests : TestWithDatabaseBase + { + private Guid _testBaseFolder; + + public override void SetUp() + { + base.SetUp(); + _testBaseFolder = Guid.NewGuid(); + } + + public override void TearDown() + { + base.TearDown(); + + //clear out files/folders + Directory.Delete(IOHelper.MapPath("~/" + _testBaseFolder), true); + } + + public IPackageBuilder PackageBuilder => new PackageBuilder( + ServiceContext.ContentService, ServiceContext.ContentTypeService, ServiceContext.DataTypeService, + ServiceContext.FileService, ServiceContext.MacroService, ServiceContext.LocalizationService, + Factory.GetInstance(), Logger, + //temp paths + tempFolderPath: "~/" + _testBaseFolder + "/temp", + packagesFolderPath: "~/" + _testBaseFolder + "/packages", + mediaFolderPath: "~/" + _testBaseFolder + "/media"); + + [Test] + public void Delete() + { + var def1 = new PackageDefinition + { + Name = "test", + Url = "http://test.com", + Author = "Someone", + AuthorUrl = "http://test.com" + }; + + var result = PackageBuilder.SavePackage(def1); + Assert.IsTrue(result); + + PackageBuilder.Delete(def1.Id); + + def1 = PackageBuilder.GetById(def1.Id); + Assert.IsNull(def1); + } + + [Test] + public void Create_New() + { + var def1 = new PackageDefinition + { + Name = "test", + Url = "http://test.com", + Author = "Someone", + AuthorUrl = "http://test.com" + }; + + var result = PackageBuilder.SavePackage(def1); + + Assert.IsTrue(result); + Assert.AreEqual(1, def1.Id); + Assert.AreNotEqual(default(Guid).ToString(), def1.PackageId); + Assert.AreNotEqual(default(Guid).ToString(), def1.FolderId); + + var def2 = new PackageDefinition + { + Name = "test", + Url = "http://test.com", + Author = "Someone", + AuthorUrl = "http://test.com" + }; + + result = PackageBuilder.SavePackage(def2); + + Assert.IsTrue(result); + Assert.AreEqual(2, def2.Id); + Assert.AreNotEqual(default(Guid).ToString(), def2.PackageId); + Assert.AreNotEqual(default(Guid).ToString(), def2.FolderId); + } + + [Test] + public void Update_Not_Found() + { + var def = new PackageDefinition + { + Id = 3, //doesn't exist + Name = "test", + Url = "http://test.com", + Author = "Someone", + AuthorUrl = "http://test.com" + }; + + var result = PackageBuilder.SavePackage(def); + + Assert.IsFalse(result); + } + + [Test] + public void Update() + { + var def = new PackageDefinition + { + Name = "test", + Url = "http://test.com", + Author = "Someone", + AuthorUrl = "http://test.com" + }; + var result = PackageBuilder.SavePackage(def); + + def.Name = "updated"; + def.Files = new List {"hello.txt", "world.png"}; + result = PackageBuilder.SavePackage(def); + Assert.IsTrue(result); + + //re-get + def = PackageBuilder.GetById(def.Id); + Assert.AreEqual("updated", def.Name); + Assert.AreEqual(2, def.Files.Count); + //TODO: There's a whole lot more assertions to be done + + } + + [Test] + public void Export() + { + var file1 = $"~/{_testBaseFolder}/App_Plugins/MyPlugin/package.manifest"; + var file2 = $"~/{_testBaseFolder}/App_Plugins/MyPlugin/styles.css"; + var mappedFile1 = IOHelper.MapPath(file1); + var mappedFile2 = IOHelper.MapPath(file2); + Directory.CreateDirectory(Path.GetDirectoryName(mappedFile1)); + Directory.CreateDirectory(Path.GetDirectoryName(mappedFile2)); + File.WriteAllText(mappedFile1, "hello world"); + File.WriteAllText(mappedFile2, "hello world"); + + var def = new PackageDefinition + { + Name = "test", + Url = "http://test.com", + Author = "Someone", + AuthorUrl = "http://test.com", + Files = new List { file1, file2 }, + Actions = "" + }; + var result = PackageBuilder.SavePackage(def); + Assert.IsTrue(result); + Assert.IsTrue(def.PackagePath.IsNullOrWhiteSpace()); + + var zip = PackageBuilder.ExportPackage(def); + + def = PackageBuilder.GetById(def.Id); //re-get + Assert.IsNotNull(def.PackagePath); + + using (var archive = ZipFile.OpenRead(IOHelper.MapPath(zip))) + { + Assert.AreEqual(3, archive.Entries.Count); + + //the 2 files we manually added + Assert.IsNotNull(archive.Entries.Where(x => x.Name == "package.manifest")); + Assert.IsNotNull(archive.Entries.Where(x => x.Name == "styles.css")); + + //this is the actual package definition/manifest (not the developer manifest!) + var packageXml = archive.Entries.FirstOrDefault(x => x.Name == "package.xml"); + Assert.IsNotNull(packageXml); + + using (var stream = packageXml.Open()) + { + var xml = XDocument.Load(stream); + Assert.AreEqual("umbPackage", xml.Root.Name.ToString()); + Assert.AreEqual(2, xml.Root.Element("files").Elements("file").Count()); + + Assert.AreEqual("", xml.Element("umbPackage").Element("Actions").ToString(SaveOptions.DisableFormatting)); + + //TODO: There's a whole lot more assertions to be done + } + } + } + } +} diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html index 20bc4277f8..955676b806 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html @@ -158,7 +158,6 @@ -
{{vm.stylesheets | json }}
-
{{vm.package | json}}
- From 568d5c2583baf8bb691283ba406e0817718d0712 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 10 Jan 2019 19:08:52 +1100 Subject: [PATCH 51/93] Fixes issue with redirect --- src/Umbraco.Core/Models/Packaging/PackageDefinition.cs | 2 +- src/Umbraco.Core/Packaging/PackageBuilder.cs | 2 +- src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs b/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs index 67d0c2970f..01d10fa2e1 100644 --- a/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs +++ b/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs @@ -94,7 +94,7 @@ namespace Umbraco.Core.Models.Packaging public string LoadControl { get; set; } = string.Empty; [DataMember(Name = "actions")] - public string Actions { get; set; } + public string Actions { get; set; } = ""; [DataMember(Name = "dataTypes")] public IList DataTypes { get; set; } = new List(); diff --git a/src/Umbraco.Core/Packaging/PackageBuilder.cs b/src/Umbraco.Core/Packaging/PackageBuilder.cs index 6b26b09cbe..ae5accf794 100644 --- a/src/Umbraco.Core/Packaging/PackageBuilder.cs +++ b/src/Umbraco.Core/Packaging/PackageBuilder.cs @@ -598,7 +598,7 @@ namespace Umbraco.Core.Packaging Author = xml.Element("author")?.Value ?? string.Empty, AuthorUrl = xml.Element("author")?.AttributeValue("url") ?? string.Empty, Readme = xml.Element("readme")?.Value ?? string.Empty, - Actions = xml.Element("actions")?.ToString(SaveOptions.None) ?? string.Empty, //take the entire outer xml value + Actions = xml.Element("actions")?.ToString(SaveOptions.None) ?? "", //take the entire outer xml value ContentNodeId = xml.Element("content")?.AttributeValue("nodeId") ?? string.Empty, ContentLoadChildNodes = xml.Element("content")?.AttributeValue("loadChildNodes") ?? false, Macros = xml.Element("macros")?.Value.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js index f351456609..057f1160c2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js @@ -146,7 +146,7 @@ if (create) { //if we are creating, then redirect to the correct url and reload - $location.path("packages/packages/overview/" + vm.package.id).search("subview", "created"); + $location.path("packages/packages/edit/" + vm.package.id).search("subview", "created").search("create", null); //don't add a browser history for this $location.replace(); } From 4bf3c1d05b9b228246e580c63785b72b83833311 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 10 Jan 2019 19:27:25 +1100 Subject: [PATCH 52/93] Handles trying to create a package with a duplicate name --- src/Umbraco.Core/Packaging/PackageBuilder.cs | 5 +++++ .../src/common/services/formhelper.service.js | 10 +++++----- src/Umbraco.Web/Editors/PackageController.cs | 6 +++++- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Core/Packaging/PackageBuilder.cs b/src/Umbraco.Core/Packaging/PackageBuilder.cs index ae5accf794..523ed6ed93 100644 --- a/src/Umbraco.Core/Packaging/PackageBuilder.cs +++ b/src/Umbraco.Core/Packaging/PackageBuilder.cs @@ -87,6 +87,11 @@ namespace Umbraco.Core.Packaging if (definition.Id == default) { + //check if the name already exists + var existsByName = packagesXml.Root.Elements("package").Any(x => x.AttributeValue("name") == definition.Name); + if (existsByName) + return false; + //need to gen an id and persist // Find max id var maxId = packagesXml.Root.Elements("package").Max(x => x.AttributeValue("id")) ?? 0; diff --git a/src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js index b21909f573..191e0a22c0 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js @@ -110,7 +110,7 @@ function formHelper(angularHelper, serverValidationManager, notificationsService //Or, some strange server error if (err.status === 400) { //now we need to look through all the validation errors - if (err.data && (err.data.ModelState)) { + if (err.data && err.data.ModelState) { //wire up the server validation errs this.handleServerValidation(err.data.ModelState); @@ -118,11 +118,11 @@ function formHelper(angularHelper, serverValidationManager, notificationsService //execute all server validation events and subscribers serverValidationManager.notifyAndClearAllSubscriptions(); } - else { + } + else { - //TODO: All YSOD handling should be done with an interceptor - overlayService.ysod(err); - } + //TODO: All YSOD handling should be done with an interceptor + overlayService.ysod(err); } }, diff --git a/src/Umbraco.Web/Editors/PackageController.cs b/src/Umbraco.Web/Editors/PackageController.cs index 0369e3eb24..215c81faac 100644 --- a/src/Umbraco.Web/Editors/PackageController.cs +++ b/src/Umbraco.Web/Editors/PackageController.cs @@ -57,7 +57,11 @@ namespace Umbraco.Web.Editors //save it if (!Services.PackagingService.SavePackage(model)) - throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse("The package with id {definition.Id} was not found")); + throw new HttpResponseException( + Request.CreateNotificationValidationErrorResponse( + model.Id == default + ? $"A package with the name {model.Name} already exists" + : $"The package with id {model.Id} was not found")); Services.PackagingService.ExportPackage(model); From 003907358dd7a273992e930d49ca2303e86b50af Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 10 Jan 2019 14:03:25 +0100 Subject: [PATCH 53/93] Better runtime state detection, to support auto-install --- .../Components/RuntimeLevelAttribute.cs | 15 +++- src/Umbraco.Core/IRuntimeState.cs | 5 ++ .../Install/DatabaseSchemaCreator.cs | 12 ++-- .../Persistence/IUmbracoDatabaseFactory.cs | 6 ++ .../Persistence/UmbracoDatabaseFactory.cs | 11 +++ src/Umbraco.Core/Runtime/CoreRuntime.cs | 3 +- src/Umbraco.Core/RuntimeLevel.cs | 3 + src/Umbraco.Core/RuntimeLevelReason.cs | 68 +++++++++++++++++++ src/Umbraco.Core/RuntimeState.cs | 43 ++++++++++-- src/Umbraco.Core/Umbraco.Core.csproj | 1 + 10 files changed, 153 insertions(+), 14 deletions(-) create mode 100644 src/Umbraco.Core/RuntimeLevelReason.cs diff --git a/src/Umbraco.Core/Components/RuntimeLevelAttribute.cs b/src/Umbraco.Core/Components/RuntimeLevelAttribute.cs index 51920660d4..2c698a671a 100644 --- a/src/Umbraco.Core/Components/RuntimeLevelAttribute.cs +++ b/src/Umbraco.Core/Components/RuntimeLevelAttribute.cs @@ -1,13 +1,22 @@ using System; +using Umbraco.Core.Composing; namespace Umbraco.Core.Components { + /// + /// Marks a composer to indicate a minimum and/or maximum runtime level for which the composer would compose. + /// [AttributeUsage(AttributeTargets.Class /*, AllowMultiple = false, Inherited = true*/)] public class RuntimeLevelAttribute : Attribute { - //public RuntimeLevelAttribute() - //{ } + /// + /// Gets or sets the minimum runtime level for which the composer would compose. + /// + public RuntimeLevel MinLevel { get; set; } = RuntimeLevel.Install; - public RuntimeLevel MinLevel { get; set; } = RuntimeLevel.Boot; + /// + /// Gets or sets the maximum runtime level for which the composer would compose. + /// + public RuntimeLevel MaxLevel { get; set; } = RuntimeLevel.Run; } } diff --git a/src/Umbraco.Core/IRuntimeState.cs b/src/Umbraco.Core/IRuntimeState.cs index 5fcfab1518..30c768ab01 100644 --- a/src/Umbraco.Core/IRuntimeState.cs +++ b/src/Umbraco.Core/IRuntimeState.cs @@ -57,6 +57,11 @@ namespace Umbraco.Core /// RuntimeLevel Level { get; } + /// + /// Gets the reason for the runtime level of execution. + /// + RuntimeLevelReason Reason { get; } + /// /// Gets the current migration state. /// diff --git a/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs b/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs index 5525cc4a50..f9f3e5da30 100644 --- a/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs +++ b/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs @@ -14,7 +14,7 @@ namespace Umbraco.Core.Migrations.Install /// /// Creates the initial database schema during install. /// - internal class DatabaseSchemaCreator + public class DatabaseSchemaCreator { private readonly IUmbracoDatabase _database; private readonly ILogger _logger; @@ -28,7 +28,7 @@ namespace Umbraco.Core.Migrations.Install private ISqlSyntaxProvider SqlSyntax => _database.SqlContext.SqlSyntax; // all tables, in order - public static readonly List OrderedTables = new List + internal static readonly List OrderedTables = new List { typeof (UserDto), typeof (NodeDto), @@ -138,7 +138,7 @@ namespace Umbraco.Core.Migrations.Install /// /// Validates the schema of the current database. /// - public DatabaseSchemaResult ValidateSchema() + internal DatabaseSchemaResult ValidateSchema() { var result = new DatabaseSchemaResult(SqlSyntax); @@ -387,7 +387,7 @@ namespace Umbraco.Core.Migrations.Install /// If has been decorated with an , the name from that /// attribute will be used for the table name. If the attribute is not present, the name /// will be used instead. - /// + /// /// If a table with the same name already exists, the parameter will determine /// whether the table is overwritten. If true, the table will be overwritten, whereas this method will /// not do anything if the parameter is false. @@ -409,14 +409,14 @@ namespace Umbraco.Core.Migrations.Install /// If has been decorated with an , the name from /// that attribute will be used for the table name. If the attribute is not present, the name /// will be used instead. - /// + /// /// If a table with the same name already exists, the parameter will determine /// whether the table is overwritten. If true, the table will be overwritten, whereas this method will /// not do anything if the parameter is false. /// /// This need to execute as part of a transaction. /// - public void CreateTable(bool overwrite, Type modelType, DatabaseDataCreator dataCreation) + internal void CreateTable(bool overwrite, Type modelType, DatabaseDataCreator dataCreation) { if (!_database.InTransaction) throw new InvalidOperationException("Database is not in a transaction."); diff --git a/src/Umbraco.Core/Persistence/IUmbracoDatabaseFactory.cs b/src/Umbraco.Core/Persistence/IUmbracoDatabaseFactory.cs index 9ef320e187..0236fc4bd5 100644 --- a/src/Umbraco.Core/Persistence/IUmbracoDatabaseFactory.cs +++ b/src/Umbraco.Core/Persistence/IUmbracoDatabaseFactory.cs @@ -18,6 +18,12 @@ namespace Umbraco.Core.Persistence /// bool Configured { get; } + /// + /// Gets the connection string. + /// + /// Throws if the factory is not configured. + string ConnectionString { get; } + /// /// Gets a value indicating whether the database can connect. /// diff --git a/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs b/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs index c9a509fe94..9ed52ca148 100644 --- a/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs +++ b/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs @@ -2,6 +2,7 @@ using System.Configuration; using System.Data.Common; using System.Threading; +using LightInject; using NPoco; using NPoco.FluentMappings; using Umbraco.Core.Exceptions; @@ -102,6 +103,16 @@ namespace Umbraco.Core.Persistence /// public bool Configured { get; private set; } + /// + public string ConnectionString + { + get + { + EnsureConfigured(); + return _connectionString; + } + } + /// public bool CanConnect { diff --git a/src/Umbraco.Core/Runtime/CoreRuntime.cs b/src/Umbraco.Core/Runtime/CoreRuntime.cs index 5d2359d04c..d6f68d339f 100644 --- a/src/Umbraco.Core/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Core/Runtime/CoreRuntime.cs @@ -252,7 +252,7 @@ namespace Umbraco.Core.Runtime { _state.DetermineRuntimeLevel(databaseFactory, profilingLogger); - profilingLogger.Debug("Runtime level: {RuntimeLevel}", _state.Level); + profilingLogger.Debug("Runtime level: {RuntimeLevel} - {RuntimeLevelReason}", _state.Level, _state.Reason); if (_state.Level == RuntimeLevel.Upgrade) { @@ -263,6 +263,7 @@ namespace Umbraco.Core.Runtime catch { _state.Level = RuntimeLevel.BootFailed; + _state.Reason = RuntimeLevelReason.BootFailedOnException; timer.Fail(); throw; } diff --git a/src/Umbraco.Core/RuntimeLevel.cs b/src/Umbraco.Core/RuntimeLevel.cs index f9ec3c90c5..2645e89226 100644 --- a/src/Umbraco.Core/RuntimeLevel.cs +++ b/src/Umbraco.Core/RuntimeLevel.cs @@ -1,5 +1,8 @@ namespace Umbraco.Core { + /// + /// Describes the levels in which the runtime can run. + /// public enum RuntimeLevel { /// diff --git a/src/Umbraco.Core/RuntimeLevelReason.cs b/src/Umbraco.Core/RuntimeLevelReason.cs new file mode 100644 index 0000000000..c10ac8b206 --- /dev/null +++ b/src/Umbraco.Core/RuntimeLevelReason.cs @@ -0,0 +1,68 @@ +namespace Umbraco.Core +{ + /// + /// Describes the reason for the runtime level. + /// + public enum RuntimeLevelReason + { + /// + /// The code version is lower than the version indicated in web.config, and + /// downgrading Umbraco is not supported. + /// + BootFailedCannotDowngrade, + + /// + /// The runtime cannot connect to the configured database. + /// + BootFailedCannotConnectToDatabase, + + /// + /// The runtime can connect to the configured database, but it cannot + /// retrieve the migrations status. + /// + BootFailedCannotCheckUpgradeState, + + /// + /// An exception was thrown during boot. + /// + BootFailedOnException, + + /// + /// Umbraco is not installed at all. + /// + InstallNoVersion, + + /// + /// A version is specified in web.config but the database is not configured. + /// + /// This is a weird state. + InstallNoDatabase, + + /// + /// A version is specified in web.config and a database is configured, but the + /// database is missing, and installing over a missing database has been enabled. + /// + InstallMissingDatabase, + + /// + /// A version is specified in web.config and a database is configured, but the + /// database is empty, and installing over an empty database has been enabled. + /// + InstallEmptyDatabase, + + /// + /// Umbraco runs an old version. + /// + UpgradeOldVersion, + + /// + /// Umbraco runs the current version but some migrations have not run. + /// + UpgradeMigrations, + + /// + /// Umbraco is running. + /// + Run + } +} diff --git a/src/Umbraco.Core/RuntimeState.cs b/src/Umbraco.Core/RuntimeState.cs index df2ee44a7d..e344a67920 100644 --- a/src/Umbraco.Core/RuntimeState.cs +++ b/src/Umbraco.Core/RuntimeState.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Configuration; using System.Threading; using System.Web; using Semver; @@ -93,6 +94,9 @@ namespace Umbraco.Core internal set { _level = value; if (value == RuntimeLevel.Run) _runLevel.Set(); } } + /// + public RuntimeLevelReason Reason { get; internal set; } + /// /// Ensures that the property has a value. /// @@ -129,6 +133,11 @@ namespace Umbraco.Core /// public BootFailedException BootFailedException { get; internal set; } + // currently configured via app settings + private static bool BoolSetting(string key, bool missing) => ConfigurationManager.AppSettings[key]?.InvariantEquals("true") ?? missing; + private bool InstallMissingDatabase { get; } = BoolSetting("Umbraco.Core.RuntimeState.InstallMissingDatabase", false); + private bool InstallEmptyDatabase { get; } = BoolSetting("Umbraco.Core.RuntimeState.InstallEmptyDatabase", false); + /// /// Determines the runtime level. /// @@ -143,6 +152,7 @@ namespace Umbraco.Core // there is no local version, we are not installed logger.Debug("No local version, need to install Umbraco."); Level = RuntimeLevel.Install; + Reason = RuntimeLevelReason.InstallNoVersion; return; } @@ -152,12 +162,14 @@ namespace Umbraco.Core // need to upgrade logger.Debug("Local version '{LocalVersion}' < code version '{CodeVersion}', need to upgrade Umbraco.", localVersion, codeVersion); Level = RuntimeLevel.Upgrade; + Reason = RuntimeLevelReason.UpgradeOldVersion; } else if (localVersion > codeVersion) { logger.Warn("Local version '{LocalVersion}' > code version '{CodeVersion}', downgrading is not supported.", localVersion, codeVersion); // in fact, this is bad enough that we want to throw + Reason = RuntimeLevelReason.BootFailedCannotDowngrade; throw new BootFailedException($"Local version \"{localVersion}\" > code version \"{codeVersion}\", downgrading is not supported."); } else if (databaseFactory.Configured == false) @@ -166,16 +178,18 @@ namespace Umbraco.Core // install (again? this is a weird situation...) logger.Debug("Database is not configured, need to install Umbraco."); Level = RuntimeLevel.Install; + Reason = RuntimeLevelReason.InstallNoDatabase; return; } // else, keep going, // anything other than install wants a database - see if we can connect // (since this is an already existing database, assume localdb is ready) - for (var i = 0; i < 5; i++) + var tries = InstallMissingDatabase ? 2 : 5; + for (var i = 0;;) { connect = databaseFactory.CanConnect; - if (connect) break; + if (connect || ++i == tries) break; logger.Debug("Could not immediately connect to database, trying again."); Thread.Sleep(1000); } @@ -185,7 +199,16 @@ namespace Umbraco.Core // cannot connect to configured database, this is bad, fail logger.Debug("Could not connect to database."); - // in fact, this is bad enough that we want to throw + if (InstallMissingDatabase) + { + // ok to install on a configured but missing database + Level = RuntimeLevel.Install; + Reason = RuntimeLevelReason.InstallMissingDatabase; + return; + } + + // else it is bad enough that we want to throw + Reason = RuntimeLevelReason.BootFailedCannotConnectToDatabase; throw new BootFailedException("A connection string is configured but Umbraco could not connect to the database."); } @@ -195,7 +218,6 @@ namespace Umbraco.Core // else // look for a matching migration entry - bypassing services entirely - they are not 'up' yet - // fixme - in a LB scenario, ensure that the DB gets upgraded only once! bool noUpgrade; try { @@ -205,6 +227,17 @@ namespace Umbraco.Core { // can connect to the database but cannot check the upgrade state... oops logger.Warn(e, "Could not check the upgrade state."); + + if (InstallEmptyDatabase) + { + // ok to install on an empty database + Level = RuntimeLevel.Install; + Reason = RuntimeLevelReason.InstallEmptyDatabase; + return; + } + + // else it is bad enough that we want to throw + Reason = RuntimeLevelReason.BootFailedCannotCheckUpgradeState; throw new BootFailedException("Could not check the upgrade state.", e); } @@ -216,6 +249,7 @@ namespace Umbraco.Core { // the database version matches the code & files version, all clear, can run Level = RuntimeLevel.Run; + Reason = RuntimeLevelReason.Run; return; } @@ -226,6 +260,7 @@ namespace Umbraco.Core // which means the local files have been upgraded but not the database - need to upgrade logger.Debug("Has not reached the final upgrade step, need to upgrade Umbraco."); Level = RuntimeLevel.Upgrade; + Reason = RuntimeLevelReason.UpgradeMigrations; } protected virtual bool EnsureUmbracoUpgradeState(IUmbracoDatabaseFactory databaseFactory, ILogger logger) diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index a01bbf1746..fd5dfe9294 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -490,6 +490,7 @@ + From 74c01af3695e218aa30d5397a22d24ed856b6e06 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 11 Jan 2019 10:35:37 +1100 Subject: [PATCH 54/93] Splits logic for IPackageBuilder into ICreatedPackagesRepository and IInstalledPackagesRepository since it's nearly the same for both --- .../Composing/Composers/ServicesComposer.cs | 4 +- ...uilder.cs => CreatedPackagesRepository.cs} | 8 +- .../Packaging/ICreatedPackagesRepository.cs | 13 + .../Packaging/IInstalledPackagesRepository.cs | 14 + ...der.cs => IPackageDefinitionRepository.cs} | 15 +- .../Services/IPackagingService.cs | 12 +- .../Services/Implement/PackagingService.cs | 16 +- src/Umbraco.Core/Umbraco.Core.csproj | 6 +- ...s.cs => CreatedPackagesRepositoryTests.cs} | 4 +- src/Umbraco.Tests/TestHelpers/TestObjects.cs | 2 +- src/Umbraco.Tests/Umbraco.Tests.csproj | 2 +- src/Umbraco.Web/Editors/PackageController.cs | 18 +- src/Umbraco.Web/Umbraco.Web.csproj | 2 - .../_Legacy/Controls/BaseTreePicker.cs | 299 ------------------ .../PackageInstance/InstalledPackage.cs | 77 +---- .../umbraco/controls/ContentPicker.cs | 60 ---- 16 files changed, 66 insertions(+), 486 deletions(-) rename src/Umbraco.Core/Packaging/{PackageBuilder.cs => CreatedPackagesRepository.cs} (98%) create mode 100644 src/Umbraco.Core/Packaging/ICreatedPackagesRepository.cs create mode 100644 src/Umbraco.Core/Packaging/IInstalledPackagesRepository.cs rename src/Umbraco.Core/Packaging/{IPackageBuilder.cs => IPackageDefinitionRepository.cs} (61%) rename src/Umbraco.Tests/Packaging/{PackageBuilderTests.cs => CreatedPackagesRepositoryTests.cs} (97%) delete mode 100644 src/Umbraco.Web/_Legacy/Controls/BaseTreePicker.cs delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentPicker.cs diff --git a/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs b/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs index 6395a2c6db..9692a2d87c 100644 --- a/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs +++ b/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs @@ -60,8 +60,8 @@ namespace Umbraco.Core.Composing.Composers composition.RegisterUnique(); composition.RegisterUnique(); - composition.RegisterUnique(factory => - new PackageBuilder( //we are using a factory because there are optional ctor args + composition.RegisterUnique(factory => + new CreatedPackagesRepository( //we are using a factory because there are optional ctor args factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), factory.GetInstance())); diff --git a/src/Umbraco.Core/Packaging/PackageBuilder.cs b/src/Umbraco.Core/Packaging/CreatedPackagesRepository.cs similarity index 98% rename from src/Umbraco.Core/Packaging/PackageBuilder.cs rename to src/Umbraco.Core/Packaging/CreatedPackagesRepository.cs index 523ed6ed93..169b161fbc 100644 --- a/src/Umbraco.Core/Packaging/PackageBuilder.cs +++ b/src/Umbraco.Core/Packaging/CreatedPackagesRepository.cs @@ -15,7 +15,7 @@ using File = System.IO.File; namespace Umbraco.Core.Packaging { - internal class PackageBuilder : IPackageBuilder + internal class CreatedPackagesRepository : ICreatedPackagesRepository { private readonly IContentService _contentService; private readonly IContentTypeService _contentTypeService; @@ -29,7 +29,7 @@ namespace Umbraco.Core.Packaging private readonly string _packagesFolderPath; private readonly string _tempFolderPath; - public PackageBuilder(IContentService contentService, IContentTypeService contentTypeService, + public CreatedPackagesRepository(IContentService contentService, IContentTypeService contentTypeService, IDataTypeService dataTypeService, IFileService fileService, IMacroService macroService, ILocalizationService languageService, IEntityXmlSerializer serializer, ILogger logger, @@ -174,7 +174,7 @@ namespace Umbraco.Core.Packaging } catch (Exception e) { - _logger.Warn(e, "Could not add package actions to the package manifest, the xml did not parse"); + _logger.Warn(e, "Could not add package actions to the package manifest, the xml did not parse"); } } @@ -629,7 +629,7 @@ namespace Umbraco.Core.Packaging } catch (Exception e) { - _logger.Warn(e, "Could not add package actions to the package xml definition, the xml did not parse"); + _logger.Warn(e, "Could not add package actions to the package xml definition, the xml did not parse"); } var packageXml = new XElement("package", diff --git a/src/Umbraco.Core/Packaging/ICreatedPackagesRepository.cs b/src/Umbraco.Core/Packaging/ICreatedPackagesRepository.cs new file mode 100644 index 0000000000..52a16ddff0 --- /dev/null +++ b/src/Umbraco.Core/Packaging/ICreatedPackagesRepository.cs @@ -0,0 +1,13 @@ +using Umbraco.Core.Models.Packaging; + +namespace Umbraco.Core.Packaging +{ + public interface ICreatedPackagesRepository : IPackageDefinitionRepository + { + /// + /// Creates the package file and returns it's physical path + /// + /// + string ExportPackage(PackageDefinition definition); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Packaging/IInstalledPackagesRepository.cs b/src/Umbraco.Core/Packaging/IInstalledPackagesRepository.cs new file mode 100644 index 0000000000..3d601d6b1d --- /dev/null +++ b/src/Umbraco.Core/Packaging/IInstalledPackagesRepository.cs @@ -0,0 +1,14 @@ +using System; + +namespace Umbraco.Core.Packaging +{ + + //TODO: We need to figure out how we want to do this + // we have 2x repositories for saving created and installed packages + // created packages can also be exported + // maybe the below will work? + + public interface IInstalledPackagesRepository : IPackageDefinitionRepository + { + } +} diff --git a/src/Umbraco.Core/Packaging/IPackageBuilder.cs b/src/Umbraco.Core/Packaging/IPackageDefinitionRepository.cs similarity index 61% rename from src/Umbraco.Core/Packaging/IPackageBuilder.cs rename to src/Umbraco.Core/Packaging/IPackageDefinitionRepository.cs index 5a7449545f..343a0a05ea 100644 --- a/src/Umbraco.Core/Packaging/IPackageBuilder.cs +++ b/src/Umbraco.Core/Packaging/IPackageDefinitionRepository.cs @@ -1,13 +1,12 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using Umbraco.Core.Models.Packaging; namespace Umbraco.Core.Packaging { /// - /// Creates packages + /// Defines methods for persisting package definitions to storage /// - public interface IPackageBuilder + public interface IPackageDefinitionRepository { IEnumerable GetAll(); PackageDefinition GetById(int id); @@ -20,11 +19,5 @@ namespace Umbraco.Core.Packaging /// true if creating/updating the package was successful, otherwise false /// bool SavePackage(PackageDefinition definition); - - /// - /// Creates the package file and returns it's physical path - /// - /// - string ExportPackage(PackageDefinition definition); } -} +} \ No newline at end of file diff --git a/src/Umbraco.Core/Services/IPackagingService.cs b/src/Umbraco.Core/Services/IPackagingService.cs index 4d60f3dca4..32d5cd4c59 100644 --- a/src/Umbraco.Core/Services/IPackagingService.cs +++ b/src/Umbraco.Core/Services/IPackagingService.cs @@ -9,23 +9,23 @@ namespace Umbraco.Core.Services { public interface IPackagingService : IService { - #region Package Building + #region Created Packages - IEnumerable GetAll(); - PackageDefinition GetById(int id); - void Delete(int id); + IEnumerable GetAllCreatedPackages(); + PackageDefinition GetCreatedPackageById(int id); + void DeleteCreatedPackage(int id); /// /// Persists a package definition to storage /// /// - bool SavePackage(PackageDefinition definition); + bool SaveCreatedPackage(PackageDefinition definition); /// /// Creates the package file and returns it's physical path /// /// - string ExportPackage(PackageDefinition definition); + string ExportCreatedPackage(PackageDefinition definition); #endregion diff --git a/src/Umbraco.Core/Services/Implement/PackagingService.cs b/src/Umbraco.Core/Services/Implement/PackagingService.cs index 17959f0983..d6003dc842 100644 --- a/src/Umbraco.Core/Services/Implement/PackagingService.cs +++ b/src/Umbraco.Core/Services/Implement/PackagingService.cs @@ -46,7 +46,7 @@ namespace Umbraco.Core.Services.Implement private readonly IAuditRepository _auditRepository; private readonly IContentTypeRepository _contentTypeRepository; private readonly PropertyEditorCollection _propertyEditors; - private readonly IPackageBuilder _packageBuilder; + private readonly ICreatedPackagesRepository _createdPackages; private static HttpClient _httpClient; public PackagingService( @@ -62,7 +62,7 @@ namespace Umbraco.Core.Services.Implement IAuditRepository auditRepository, IContentTypeRepository contentTypeRepository, PropertyEditorCollection propertyEditors, - IPackageBuilder packageBuilder) + ICreatedPackagesRepository createdPackages) { _logger = logger; _contentService = contentService; @@ -76,7 +76,7 @@ namespace Umbraco.Core.Services.Implement _auditRepository = auditRepository; _contentTypeRepository = contentTypeRepository; _propertyEditors = propertyEditors; - _packageBuilder = packageBuilder; + _createdPackages = createdPackages; _importedContentTypes = new Dictionary(); } @@ -1402,15 +1402,15 @@ namespace Umbraco.Core.Services.Implement #region Package Building - public void Delete(int id) => _packageBuilder.Delete(id); + public void DeleteCreatedPackage(int id) => _createdPackages.Delete(id); - public IEnumerable GetAll() => _packageBuilder.GetAll(); + public IEnumerable GetAllCreatedPackages() => _createdPackages.GetAll(); - public PackageDefinition GetById(int id) => _packageBuilder.GetById(id); + public PackageDefinition GetCreatedPackageById(int id) => _createdPackages.GetById(id); - public bool SavePackage(PackageDefinition definition) => _packageBuilder.SavePackage(definition); + public bool SaveCreatedPackage(PackageDefinition definition) => _createdPackages.SavePackage(definition); - public string ExportPackage(PackageDefinition definition) => _packageBuilder.ExportPackage(definition); + public string ExportCreatedPackage(PackageDefinition definition) => _createdPackages.ExportPackage(definition); #endregion diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 00179f562a..bad9ba7ccd 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -444,9 +444,11 @@ - + + + - + diff --git a/src/Umbraco.Tests/Packaging/PackageBuilderTests.cs b/src/Umbraco.Tests/Packaging/CreatedPackagesRepositoryTests.cs similarity index 97% rename from src/Umbraco.Tests/Packaging/PackageBuilderTests.cs rename to src/Umbraco.Tests/Packaging/CreatedPackagesRepositoryTests.cs index 6368001c11..3bd9b50ed4 100644 --- a/src/Umbraco.Tests/Packaging/PackageBuilderTests.cs +++ b/src/Umbraco.Tests/Packaging/CreatedPackagesRepositoryTests.cs @@ -19,7 +19,7 @@ namespace Umbraco.Tests.Packaging { [TestFixture] [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] - public class PackageBuilderTests : TestWithDatabaseBase + public class CreatedPackagesRepositoryTests : TestWithDatabaseBase { private Guid _testBaseFolder; @@ -37,7 +37,7 @@ namespace Umbraco.Tests.Packaging Directory.Delete(IOHelper.MapPath("~/" + _testBaseFolder), true); } - public IPackageBuilder PackageBuilder => new PackageBuilder( + public ICreatedPackagesRepository PackageBuilder => new CreatedPackagesRepository( ServiceContext.ContentService, ServiceContext.ContentTypeService, ServiceContext.DataTypeService, ServiceContext.FileService, ServiceContext.MacroService, ServiceContext.LocalizationService, Factory.GetInstance(), Logger, diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects.cs b/src/Umbraco.Tests/TestHelpers/TestObjects.cs index c6602fc54e..105bc440cc 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects.cs @@ -170,7 +170,7 @@ namespace Umbraco.Tests.TestHelpers var macroService = GetLazyService(factory, c => new MacroService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c))); var packagingService = GetLazyService(factory, c => new PackagingService( logger, contentService.Value, contentTypeService.Value, macroService.Value, dataTypeService.Value, fileService.Value, localizationService.Value, entityService.Value, scopeProvider, GetRepo(c), GetRepo(c), new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())), - new PackageBuilder(contentService.Value, contentTypeService.Value, dataTypeService.Value, fileService.Value, macroService.Value, localizationService.Value, + new CreatedPackagesRepository(contentService.Value, contentTypeService.Value, dataTypeService.Value, fileService.Value, macroService.Value, localizationService.Value, new EntityXmlSerializer(contentService.Value, mediaService.Value, dataTypeService.Value, userService.Value, localizationService.Value, contentTypeService.Value, urlSegmentProviders), logger))); var relationService = GetLazyService(factory, c => new RelationService(scopeProvider, logger, eventMessagesFactory, entityService.Value, GetRepo(c), GetRepo(c))); var treeService = GetLazyService(factory, c => new ApplicationTreeService(logger, cache, typeLoader)); diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 3e619c6f00..7ea7d5d8b1 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -142,7 +142,7 @@ - + diff --git a/src/Umbraco.Web/Editors/PackageController.cs b/src/Umbraco.Web/Editors/PackageController.cs index 215c81faac..86f3e48b7c 100644 --- a/src/Umbraco.Web/Editors/PackageController.cs +++ b/src/Umbraco.Web/Editors/PackageController.cs @@ -1,23 +1,17 @@ using System.Collections.Generic; using System.IO; -using System.Linq; using System.Net; using System.Net.Http; -using System.Net.Http.Formatting; using System.Net.Http.Headers; using System.Web.Http; using Umbraco.Core.IO; using Umbraco.Core.Models.Packaging; -using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; -using Umbraco.Web._Legacy.Packager.PackageInstance; namespace Umbraco.Web.Editors { - //TODO: Packager stuff still lives in business logic - YUK - /// /// A controller used for installing packages and managing all of the data in the packages section in the back office /// @@ -28,12 +22,12 @@ namespace Umbraco.Web.Editors { public IEnumerable GetCreatedPackages() { - return Services.PackagingService.GetAll(); + return Services.PackagingService.GetAllCreatedPackages(); } public PackageDefinition GetCreatedPackageById(int id) { - var package = Services.PackagingService.GetById(id); + var package = Services.PackagingService.GetCreatedPackageById(id); if (package == null) throw new HttpResponseException(HttpStatusCode.NotFound); @@ -56,14 +50,14 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState)); //save it - if (!Services.PackagingService.SavePackage(model)) + if (!Services.PackagingService.SaveCreatedPackage(model)) throw new HttpResponseException( Request.CreateNotificationValidationErrorResponse( model.Id == default ? $"A package with the name {model.Name} already exists" : $"The package with id {model.Id} was not found")); - Services.PackagingService.ExportPackage(model); + Services.PackagingService.ExportCreatedPackage(model); //the packagePath will be on the model return model; @@ -78,7 +72,7 @@ namespace Umbraco.Web.Editors [HttpDelete] public IHttpActionResult DeleteCreatedPackage(int packageId) { - Services.PackagingService.Delete(packageId); + Services.PackagingService.DeleteCreatedPackage(packageId); return Ok(); } @@ -86,7 +80,7 @@ namespace Umbraco.Web.Editors [HttpGet] public HttpResponseMessage DownloadCreatedPackage(int id) { - var package = Services.PackagingService.GetById(id); + var package = Services.PackagingService.GetCreatedPackageById(id); if (package == null) return Request.CreateResponse(HttpStatusCode.NotFound); diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index b9a6c17ef7..ed423b509b 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -1126,7 +1126,6 @@ - @@ -1224,7 +1223,6 @@ Code - FeedProxy.aspx diff --git a/src/Umbraco.Web/_Legacy/Controls/BaseTreePicker.cs b/src/Umbraco.Web/_Legacy/Controls/BaseTreePicker.cs deleted file mode 100644 index c2298e4906..0000000000 --- a/src/Umbraco.Web/_Legacy/Controls/BaseTreePicker.cs +++ /dev/null @@ -1,299 +0,0 @@ -using System; -using System.Web.UI; -using System.Web.UI.WebControls; -using System.Web.UI.HtmlControls; -using Umbraco.Core.Composing; -using Umbraco.Core.Services; - -namespace Umbraco.Web._Legacy.Controls -{ - [ValidationProperty("Value")] - public abstract class BaseTreePicker : Control, INamingContainer - { - - protected HiddenField ItemIdValue; - protected HtmlAnchor DeleteLink; - protected HtmlAnchor ChooseLink; - protected HtmlGenericControl ItemTitle; - protected HtmlGenericControl ButtonContainer; - protected HtmlGenericControl RootContainer; - - public BaseTreePicker() - { - ShowDelete = true; - ModalHeight = 400; - ModalWidth = 300; - ShowHeader = true; - } - - /// - /// Wraps the hidden vield value - /// - public string Value - { - get - { - EnsureChildControls(); - return ItemIdValue.Value; - } - set - { - EnsureChildControls(); - ItemIdValue.Value = value; - } - } - - public int ModalWidth { get; set; } - public int ModalHeight { get; set; } - public bool ShowDelete { get; set; } - public bool ShowHeader { get; set; } - - /// - /// Need to specify the tree picker url (iframe) - /// - public abstract string TreePickerUrl { get; } - - /// - /// The title to specify for the picker window - /// - public abstract string ModalWindowTitle { get; } - - /// - /// If item has been selected or stored, this will query the db for its title - /// - protected virtual string GetItemTitle() - { - if (!string.IsNullOrEmpty(ItemIdValue.Value)) - { - try - { - return Current.Services.EntityService.Get(int.Parse(ItemIdValue.Value)).Name; - } - catch (ArgumentException) { /*the node does not exist! we will ignore*/ } - } - return ""; - } - - /// - /// Just like GetItemTitle, except returns the full path (breadcrumbs) of the node - /// - protected virtual string GetItemBreadcrumbs() - { - //TODO: Shouldn't this use the same/similar logic as the EntityController.GetResultForAncestors ? - - if (!string.IsNullOrEmpty(ItemIdValue.Value)) - { - try - { - int nodeId = -1; - if (int.TryParse(ItemIdValue.Value, out nodeId)) - { - var n = Current.Services.EntityService.Get(nodeId); - string title = n.Name; - string separator = " > "; - while (n.Level > 1) - { - n = Current.Services.EntityService.Get(n.ParentId); - title = n.Name + separator + title; - } - return title; - } - else - { - return ItemIdValue.Value; - } - - } - catch (ArgumentException) { /*the node does not exist! we will ignore*/ } - } - return ""; - } - - /// - /// Outputs the JavaScript instances used to make this control work - /// - protected virtual string GetJSScript() - { - /* 0 = this control's client id - * 1 = label - * 2 = itemIdValueClientID - * 3 = itemTitleClientID - * 4 = itemPickerUrl - * 5 = popup width - * 6 = popup height - * 7 = show header - * 8 = umbraco path - */ - return string.Format(@" - var mc_{0} = new Umbraco.Controls.TreePicker('{0}','{1}','{2}','{3}','{4}',{5},{6},{7},'{8}');", - new string[] - { - this.ClientID, - ModalWindowTitle, - ItemIdValue.ClientID, - ItemTitle.ClientID, - TreePickerUrl, - ModalWidth.ToString(), - ModalHeight.ToString(), - ShowHeader.ToString().ToLower(), - Umbraco.Core.IO.IOHelper.ResolveUrl(Umbraco.Core.IO.SystemDirectories.Umbraco).TrimEnd('/') - }); - } - - /// - /// Registers the required JS classes required to make this control work - /// - protected virtual void RenderJSComponents() - { - const string BaseTreePickerScriptJs = @"/// -(function ($) { - $(document).ready(function () { - // Tooltip only Text - $('.umb-tree-picker a.choose').click(function () { - var that = this; - var s = $(that).data(""section""); - UmbClientMgr.openAngularModalWindow({ - template: 'views/common/dialogs/treepicker.html', - section: s, - callback: function (data) { - //this returns the content object picked - var p = jQuery(that).parent(); - p.find("".buttons"").show(); - - p.find(""input"").val(data.id); - p.find("".treePickerTitle"").text(data.name).show(); - p.find("".clear"").show(); - } - }); - - return false; - }); - - $('.umb-tree-picker a.clear').click(function () { - jQuery(this).parent().parent().find(""input"").val(""-1""); - jQuery(this).parent().parent().find("".treePickerTitle"").text("""").hide(); - jQuery(this).hide(); - }); - }); -})(jQuery); -"; - - const string scriptKey = "BaseTreePickerScripts"; - if (ScriptManager.GetCurrent(Page).IsInAsyncPostBack) - { - ScriptManager.RegisterStartupScript(this, this.GetType(), scriptKey, BaseTreePickerScriptJs, true); - } - else - { - Page.ClientScript.RegisterClientScriptBlock(typeof(BaseTreePicker), scriptKey, BaseTreePickerScriptJs, true); - } - } - - protected override void OnInit(EventArgs e) - { - base.OnInit(e); - - EnsureChildControls(); - - //disable view state for this control - this.EnableViewState = false; - } - - /// - /// Create the native .net child controls for this control - /// - protected override void CreateChildControls() - { - base.CreateChildControls(); - - RootContainer = new HtmlGenericControl("span"); - RootContainer.Attributes.Add("class", "umb-tree-picker"); - this.Controls.Add(RootContainer); - - //create the hidden field - ItemIdValue = new HiddenField(); - ItemIdValue.ID = "ContentIdValue"; - RootContainer.Controls.Add(ItemIdValue); - - - ButtonContainer = new HtmlGenericControl("span"); - ButtonContainer.ID = "btns"; - - //add item title with padding - ItemTitle = new HtmlGenericControl("span"); - ItemTitle.ID = "title"; - ItemTitle.Style.Add(HtmlTextWriterStyle.FontWeight, "bold"); - ItemTitle.Attributes.Add("class", "treePickerTitle"); // solely for styling, e.g. with an underline or dotted border, etc. - ButtonContainer.Controls.Add(ItemTitle); - ButtonContainer.Attributes.Add("class", "buttons"); - ButtonContainer.Controls.Add(new LiteralControl(" ")); - ButtonContainer.Controls.Add(new LiteralControl(" ")); - - //add the delete link with padding - DeleteLink = new HtmlAnchor(); - DeleteLink.HRef = "#"; //set on pre-render - DeleteLink.Style.Add(HtmlTextWriterStyle.Color, "red"); - DeleteLink.Title = Current.Services.TextService.Localize("delete"); - DeleteLink.InnerText = Current.Services.TextService.Localize("delete"); - DeleteLink.Attributes.Add("class", "clear"); - - ButtonContainer.Controls.Add(DeleteLink); - ButtonContainer.Controls.Add(new LiteralControl(" ")); - ButtonContainer.Controls.Add(new LiteralControl(" ")); - if (!ShowDelete) - { - DeleteLink.Style.Add(HtmlTextWriterStyle.Display, "none"); - } - - RootContainer.Controls.Add(ButtonContainer); - - //add choose link with padding - ChooseLink = new HtmlAnchor(); - ChooseLink.HRef = "#"; //filled in on pre-render - ChooseLink.InnerText = Current.Services.TextService.Localize("choose") + "..."; - ChooseLink.Attributes.Add("data-section", this.TreePickerUrl); - ChooseLink.Attributes.Add("class", "choose"); - - RootContainer.Controls.Add(ChooseLink); - } - - /// - /// Registers the JavaScript required for the control to function and hides/shows controls depending on it's properties - /// - /// - protected override void OnPreRender(EventArgs e) - { - base.OnPreRender(e); - - //hide the buttons if no item, otherwise get the item title - if (string.IsNullOrEmpty(ItemIdValue.Value)) - { - ButtonContainer.Style.Add(HtmlTextWriterStyle.Display, "none"); - } - else - { - ItemTitle.InnerText = GetItemTitle(); - ItemTitle.Attributes.Add("title", GetItemBreadcrumbs()); // Adding full path/meta info (Issue U4-192) - } - /* - ChooseLink.HRef = string.Format("javascript:mc_{0}.LaunchPicker();", this.ClientID); - DeleteLink.HRef = string.Format("javascript:mc_{0}.ClearSelection();", this.ClientID); - */ - - RenderJSComponents(); - - /* - if (ScriptManager.GetCurrent(Page).IsInAsyncPostBack) - { - ScriptManager.RegisterStartupScript(this, this.GetType(), this.ClientID + "TreePicker", GetJSScript(), true); - } - else - { - Page.ClientScript.RegisterStartupScript(this.GetType(), this.ClientID + "TreePicker", GetJSScript(), true); - }*/ - - } - - - } -} diff --git a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/InstalledPackage.cs b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/InstalledPackage.cs index d7ea239a3f..e5af44b747 100644 --- a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/InstalledPackage.cs +++ b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/InstalledPackage.cs @@ -22,18 +22,10 @@ namespace Umbraco.Web._Legacy.Packager.PackageInstance return pack; } - public static InstalledPackage GetByGuid(string packageGuid) - { - InstalledPackage pack = new InstalledPackage(); - pack.Data = data.Package(packageGuid, IOHelper.MapPath(Settings.InstalledPackagesSettings)); - return pack; - } - public static InstalledPackage MakeNew(string name) { InstalledPackage pack = new InstalledPackage(); pack.Data = data.MakeNew(name, IOHelper.MapPath(Settings.InstalledPackagesSettings)); - pack.OnNew(EventArgs.Empty); return pack; } @@ -43,9 +35,7 @@ namespace Umbraco.Web._Legacy.Packager.PackageInstance _saveHitCount++; Current.Logger.Info("The InstalledPackage class save method has been hit {Total} times.", _saveHitCount); #endif - this.FireBeforeSave(EventArgs.Empty); data.Save(this.Data, IOHelper.MapPath(Settings.InstalledPackagesSettings)); - this.FireAfterSave(EventArgs.Empty); } public static List GetAllInstalledPackages() @@ -63,12 +53,7 @@ namespace Umbraco.Web._Legacy.Packager.PackageInstance return val; } - private Core.Models.Packaging.PackageDefinition m_data; - public Core.Models.Packaging.PackageDefinition Data - { - get { return m_data; } - set { m_data = value; } - } + public Core.Models.Packaging.PackageDefinition Data { get; set; } public void Delete(int userId) { @@ -78,69 +63,9 @@ namespace Umbraco.Web._Legacy.Packager.PackageInstance public void Delete() { - this.FireBeforeDelete(EventArgs.Empty); data.Delete(this.Data.Id, IOHelper.MapPath(Settings.InstalledPackagesSettings)); - this.FireAfterDelete(EventArgs.Empty); } - public static bool isPackageInstalled(string packageGuid) - { - try - { - if (data.GetFromGuid(packageGuid, IOHelper.MapPath(Settings.InstalledPackagesSettings), true) == null) - return false; - else - return true; - } - catch (Exception ex) - { - Current.Logger.Error(ex, "An error occured in isPackagedInstalled"); - return false; - } - } - - //EVENTS - public delegate void SaveEventHandler(InstalledPackage sender, EventArgs e); - public delegate void NewEventHandler(InstalledPackage sender, EventArgs e); - public delegate void DeleteEventHandler(InstalledPackage sender, EventArgs e); - - /// - /// Occurs when a macro is saved. - /// - public static event SaveEventHandler BeforeSave; - protected virtual void FireBeforeSave(EventArgs e) - { - if (BeforeSave != null) - BeforeSave(this, e); - } - - public static event SaveEventHandler AfterSave; - protected virtual void FireAfterSave(EventArgs e) - { - if (AfterSave != null) - AfterSave(this, e); - } - - public static event NewEventHandler New; - protected virtual void OnNew(EventArgs e) - { - if (New != null) - New(this, e); - } - - public static event DeleteEventHandler BeforeDelete; - protected virtual void FireBeforeDelete(EventArgs e) - { - if (BeforeDelete != null) - BeforeDelete(this, e); - } - - public static event DeleteEventHandler AfterDelete; - protected virtual void FireAfterDelete(EventArgs e) - { - if (AfterDelete != null) - AfterDelete(this, e); - } /// diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentPicker.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentPicker.cs deleted file mode 100644 index 018c49a249..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentPicker.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using Umbraco.Web._Legacy.Controls; -using Umbraco.Core; -using Umbraco.Core.Services; -using Umbraco.Web; -using Umbraco.Web.Composing; - -namespace umbraco.controls -{ - - public class ContentPicker : BaseTreePicker - { - - public ContentPicker() - { - AppAlias = Constants.Applications.Content; - TreeAlias = "content"; - } - - - public string AppAlias { get; set; } - public string TreeAlias { get; set; } - - public override string TreePickerUrl - { - get - { - return AppAlias; - } - } - - public override string ModalWindowTitle - { - get - { - return Current.Services.TextService.Localize("general/choose") + " " + Current.Services.TextService.Localize("sections/" + TreeAlias.ToLower()); - } - } - - protected override string GetItemTitle() - { - string tempTitle = ""; - try - { - if (Value != "" && Value != "-1") - { - //tempTitle = new cms.businesslogic.CMSNode(int.Parse(Value)).Text; - tempTitle = Current.Services.EntityService.Get(int.Parse(Value)).Name; - } - else - { - tempTitle = (!string.IsNullOrEmpty(TreeAlias) ? Current.Services.TextService.Localize(TreeAlias) : Current.Services.TextService.Localize(AppAlias)); - - } - } - catch { } - return tempTitle; - } - } -} From c89b50f0b82063e459d60ed08c664f6911ea8786 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 11 Jan 2019 14:30:04 +1100 Subject: [PATCH 55/93] large refactor/cleanup of most of the old package installer logic dealing with persisting the installedPackages.config which is now in the services, creates new parsers and removes a ton of old code, changes fetching packages to be async, uses XElement instead of old XmlNode apis, next we need to do the actual package installer updates --- .../Composing/Composers/ServicesComposer.cs | 19 +- .../Events/ImportPackageEventArgs.cs | 6 +- .../Events/UninstallPackageEventArgs.cs | 8 +- .../Models/Packaging/CompiledPackage.cs | 48 +++ .../Models/Packaging/IPackageInfo.cs | 19 + .../Models/Packaging/InstallationSummary.cs | 65 ++- .../InstallationSummaryExtentions.cs | 23 -- src/Umbraco.Core/Models/Packaging/MetaData.cs | 23 -- .../Models/Packaging/PackageDefinition.cs | 6 +- .../Packaging/RequirementsType.cs | 2 +- .../Models/Packaging/UninstallationSummary.cs | 21 +- .../UninstallationSummaryExtentions.cs | 22 -- .../Packaging/ICreatedPackagesRepository.cs | 5 +- .../Packaging/IInstalledPackagesRepository.cs | 9 +- .../Packaging/IPackageExtraction.cs | 2 +- .../Packaging/IPackageInstallation.cs | 3 +- .../Packaging/PackageActionRunner.cs | 6 +- .../Packaging/PackageBinaryInspector.cs | 295 -------------- .../Packaging/PackageDefinitionXmlParser.cs | 165 ++++++++ .../Packaging/PackageInstallation.cs | 28 +- ...gesRepository.cs => PackagesRepository.cs} | 153 +++---- .../Services/IPackagingService.cs | 19 +- .../Services/Implement/PackagingService.cs | 199 ++++------ src/Umbraco.Core/Umbraco.Core.csproj | 12 +- src/Umbraco.Core/Xml/XmlHelper.cs | 165 +------- .../_Legacy/PackageActions/IPackageAction.cs | 6 +- .../Composing/PackageActionCollectionTests.cs | 9 +- .../CreatedPackagesRepositoryTests.cs | 3 +- .../Services/PackagingServiceTests.cs | 2 +- src/Umbraco.Tests/TestHelpers/TestObjects.cs | 8 +- src/Umbraco.Web/Editors/PackageController.cs | 2 +- .../Editors/PackageInstallController.cs | 122 +++--- .../Controllers/InstallPackageController.cs | 18 +- .../InstallSteps/ConfigureMachineKey.cs | 5 +- .../InstallSteps/DatabaseConfigureStep.cs | 5 +- .../InstallSteps/DatabaseInstallStep.cs | 7 +- .../InstallSteps/DatabaseUpgradeStep.cs | 5 +- .../InstallSteps/FilePermissionsStep.cs | 5 +- .../Install/InstallSteps/NewInstallStep.cs | 5 +- .../InstallSteps/SetUmbracoVersionStep.cs | 7 +- .../InstallSteps/StarterKitCleanupStep.cs | 5 +- .../InstallSteps/StarterKitDownloadStep.cs | 24 +- .../InstallSteps/StarterKitInstallStep.cs | 5 +- .../Install/InstallSteps/UpgradeStep.cs | 3 +- .../Install/Models/InstallSetupStep.cs | 14 +- .../Models/LocalPackageInstallModel.cs | 3 - src/Umbraco.Web/Umbraco.Web.csproj | 5 - .../_Legacy/PackageActions/PackageHelper.cs | 84 ---- .../_Legacy/PackageActions/addApplication.cs | 20 +- .../PackageActions/addDashboardSection.cs | 44 +-- .../PackageActions/addProxyFeedHost.cs | 81 ++-- .../PackageActions/addStringToHtmlElement.cs | 202 ---------- .../_Legacy/PackageActions/allowDoctype.cs | 17 +- .../PackageActions/publishRootDocument.cs | 16 +- .../removeStringFromTemplate.cs | 34 -- src/Umbraco.Web/_Legacy/Packager/Installer.cs | 247 +++++------- .../PackageInstance/InstalledPackage.cs | 136 ------- src/Umbraco.Web/_Legacy/Packager/data.cs | 374 ------------------ 58 files changed, 810 insertions(+), 2036 deletions(-) create mode 100644 src/Umbraco.Core/Models/Packaging/CompiledPackage.cs create mode 100644 src/Umbraco.Core/Models/Packaging/IPackageInfo.cs delete mode 100644 src/Umbraco.Core/Models/Packaging/InstallationSummaryExtentions.cs delete mode 100644 src/Umbraco.Core/Models/Packaging/MetaData.cs rename src/Umbraco.Core/{ => Models}/Packaging/RequirementsType.cs (65%) delete mode 100644 src/Umbraco.Core/Models/Packaging/UninstallationSummaryExtentions.cs delete mode 100644 src/Umbraco.Core/Packaging/PackageBinaryInspector.cs create mode 100644 src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs rename src/Umbraco.Core/Packaging/{CreatedPackagesRepository.cs => PackagesRepository.cs} (79%) delete mode 100644 src/Umbraco.Web/_Legacy/PackageActions/PackageHelper.cs delete mode 100644 src/Umbraco.Web/_Legacy/PackageActions/addStringToHtmlElement.cs delete mode 100644 src/Umbraco.Web/_Legacy/PackageActions/removeStringFromTemplate.cs delete mode 100644 src/Umbraco.Web/_Legacy/Packager/PackageInstance/InstalledPackage.cs delete mode 100644 src/Umbraco.Web/_Legacy/Packager/data.cs diff --git a/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs b/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs index 9692a2d87c..98f239de3b 100644 --- a/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs +++ b/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs @@ -60,11 +60,9 @@ namespace Umbraco.Core.Composing.Composers composition.RegisterUnique(); composition.RegisterUnique(); - composition.RegisterUnique(factory => - new CreatedPackagesRepository( //we are using a factory because there are optional ctor args - factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), - factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), - factory.GetInstance(), factory.GetInstance())); + + composition.RegisterUnique(factory => CreatePackageRepository(factory, "createdPackages.config")); + composition.RegisterUnique(factory => CreatePackageRepository(factory, "installedPackages.config")); //TODO: These are replaced in the web project - we need to declare them so that // something is wired up, just not sure this is very nice but will work for now. @@ -74,6 +72,17 @@ namespace Umbraco.Core.Composing.Composers return composition; } + /// + /// Creates an instance of PackagesRepository for either the ICreatedPackagesRepository or the IInstalledPackagesRepository + /// + /// + /// + /// + private static PackagesRepository CreatePackageRepository(IFactory factory, string packageRepoFileName) + => new PackagesRepository( + factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), + packageRepoFileName); + private static LocalizedTextServiceFileSources SourcesFactory(IFactory container) { var mainLangFolder = new DirectoryInfo(IOHelper.MapPath(SystemDirectories.Umbraco + "/config/lang/")); diff --git a/src/Umbraco.Core/Events/ImportPackageEventArgs.cs b/src/Umbraco.Core/Events/ImportPackageEventArgs.cs index ba356ff952..61369af59d 100644 --- a/src/Umbraco.Core/Events/ImportPackageEventArgs.cs +++ b/src/Umbraco.Core/Events/ImportPackageEventArgs.cs @@ -7,19 +7,19 @@ namespace Umbraco.Core.Events { public class ImportPackageEventArgs : CancellableEnumerableObjectEventArgs, IEquatable> { - public ImportPackageEventArgs(TEntity eventObject, MetaData packageMetaData, bool canCancel) + public ImportPackageEventArgs(TEntity eventObject, IPackageInfo packageMetaData, bool canCancel) : base(new[] { eventObject }, canCancel) { PackageMetaData = packageMetaData ?? throw new ArgumentNullException(nameof(packageMetaData)); } - public ImportPackageEventArgs(TEntity eventObject, MetaData packageMetaData) + public ImportPackageEventArgs(TEntity eventObject, IPackageInfo packageMetaData) : this(eventObject, packageMetaData, true) { } - public MetaData PackageMetaData { get; } + public IPackageInfo PackageMetaData { get; } public IEnumerable InstallationSummary => EventObject; diff --git a/src/Umbraco.Core/Events/UninstallPackageEventArgs.cs b/src/Umbraco.Core/Events/UninstallPackageEventArgs.cs index 13c260bf3e..2618d04e17 100644 --- a/src/Umbraco.Core/Events/UninstallPackageEventArgs.cs +++ b/src/Umbraco.Core/Events/UninstallPackageEventArgs.cs @@ -5,17 +5,13 @@ namespace Umbraco.Core.Events { public class UninstallPackageEventArgs : CancellableObjectEventArgs> { - public UninstallPackageEventArgs(TEntity eventObject, bool canCancel) + public UninstallPackageEventArgs(TEntity eventObject, IPackageInfo packageMetaData, bool canCancel) : base(new[] { eventObject }, canCancel) - { } - - public UninstallPackageEventArgs(TEntity eventObject, MetaData packageMetaData) - : base(new[] { eventObject }) { PackageMetaData = packageMetaData; } - public MetaData PackageMetaData { get; } + public IPackageInfo PackageMetaData { get; } public IEnumerable UninstallationSummary => EventObject; } diff --git a/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs b/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs new file mode 100644 index 0000000000..cb3fbd7eee --- /dev/null +++ b/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Umbraco.Core.Models.Packaging +{ + /// + /// The model of the package definition within an umbraco (zip) package file + /// + public class CompiledPackage : IPackageInfo + { + public string Name { get; set; } + + public string Version { get; set; } + + public string Url { get; set; } + + public string License { get; set; } + + public string LicenseUrl { get; set; } + + public Version UmbracoVersion { get; set; } + + public RequirementsType UmbracoVersionRequirementsType { get; set; } + + public string Author { get; set; } + + public string AuthorUrl { get; set; } + + public string Readme { get; set; } + + public string Control { get; set; } + + public string IconUrl { get; set; } + + public List Files { get; set; } = new List(); + + } + + public class CompiledPackageFile + { + public string OriginalPath { get; set; } + public string UniqueFileName { get; set; } + public string OriginalName { get; set; } + } + + +} diff --git a/src/Umbraco.Core/Models/Packaging/IPackageInfo.cs b/src/Umbraco.Core/Models/Packaging/IPackageInfo.cs new file mode 100644 index 0000000000..8722ee7811 --- /dev/null +++ b/src/Umbraco.Core/Models/Packaging/IPackageInfo.cs @@ -0,0 +1,19 @@ +using System; + +namespace Umbraco.Core.Models.Packaging +{ + public interface IPackageInfo + { + string Name { get; } + string Version { get; } + string Url { get; } + string License { get; } + string LicenseUrl { get; } + Version UmbracoVersion { get; } + string Author { get; } + string AuthorUrl { get; } + string Readme { get; } + string Control { get; } //fixme - this needs to be an angular view + string IconUrl { get; } + } +} diff --git a/src/Umbraco.Core/Models/Packaging/InstallationSummary.cs b/src/Umbraco.Core/Models/Packaging/InstallationSummary.cs index 39df529300..ddecf53653 100644 --- a/src/Umbraco.Core/Models/Packaging/InstallationSummary.cs +++ b/src/Umbraco.Core/Models/Packaging/InstallationSummary.cs @@ -1,6 +1,9 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Runtime.Serialization; +using Umbraco.Core.IO; +using Umbraco.Core.Services; namespace Umbraco.Core.Models.Packaging { @@ -8,17 +11,57 @@ namespace Umbraco.Core.Models.Packaging [DataContract(IsReference = true)] public class InstallationSummary { - public MetaData MetaData { get; set; } - public IEnumerable DataTypesInstalled { get; set; } - public IEnumerable LanguagesInstalled { get; set; } - public IEnumerable DictionaryItemsInstalled { get; set; } - public IEnumerable MacrosInstalled { get; set; } - public IEnumerable FilesInstalled { get; set; } - public IEnumerable TemplatesInstalled { get; set; } - public IEnumerable ContentTypesInstalled { get; set; } - public IEnumerable StylesheetsInstalled { get; set; } - public IEnumerable ContentInstalled { get; set; } - public IEnumerable Actions { get; set; } + public IPackageInfo MetaData { get; set; } + public IEnumerable DataTypesInstalled { get; set; } = Enumerable.Empty(); + public IEnumerable LanguagesInstalled { get; set; } = Enumerable.Empty(); + public IEnumerable DictionaryItemsInstalled { get; set; } = Enumerable.Empty(); + public IEnumerable MacrosInstalled { get; set; } = Enumerable.Empty(); + public IEnumerable FilesInstalled { get; set; } = Enumerable.Empty(); + public IEnumerable TemplatesInstalled { get; set; } = Enumerable.Empty(); + public IEnumerable ContentTypesInstalled { get; set; } = Enumerable.Empty(); + public IEnumerable StylesheetsInstalled { get; set; } = Enumerable.Empty(); + public IEnumerable ContentInstalled { get; set; } = Enumerable.Empty(); + public IEnumerable Actions { get; set; } = Enumerable.Empty(); public bool PackageInstalled { get; set; } + + public static InstallationSummary FromPackageDefinition(PackageDefinition def, IContentTypeService contentTypeService, IDataTypeService dataTypeService, IFileService fileService, ILocalizationService localizationService, IMacroService macroService) + { + var macros = TryGetIntegerIds(def.Macros).Select(macroService.GetById).ToList(); + var templates = TryGetIntegerIds(def.Templates).Select(fileService.GetTemplate).ToList(); + var contentTypes = TryGetIntegerIds(def.DocumentTypes).Select(contentTypeService.Get).ToList(); // fixme - media types? + var dataTypes = TryGetIntegerIds(def.DataTypes).Select(dataTypeService.GetDataType).ToList(); + var dictionaryItems = TryGetIntegerIds(def.DictionaryItems).Select(localizationService.GetDictionaryItemById).ToList(); + var languages = TryGetIntegerIds(def.Languages).Select(localizationService.GetLanguageById).ToList(); + + for (var i = 0; i < def.Files.Count; i++) + { + var filePath = def.Files[i]; + def.Files[i] = filePath.GetRelativePath(); + } + + return new InstallationSummary + { + ContentTypesInstalled = contentTypes, + DataTypesInstalled = dataTypes, + DictionaryItemsInstalled = dictionaryItems, + FilesInstalled = def.Files, + LanguagesInstalled = languages, + MacrosInstalled = macros, + MetaData = def, + TemplatesInstalled = templates, + }; + } + + private static IEnumerable TryGetIntegerIds(IEnumerable ids) + { + var intIds = new List(); + foreach (var id in ids) + { + if (int.TryParse(id, out var parsed)) + intIds.Add(parsed); + } + return intIds; + } } + } diff --git a/src/Umbraco.Core/Models/Packaging/InstallationSummaryExtentions.cs b/src/Umbraco.Core/Models/Packaging/InstallationSummaryExtentions.cs deleted file mode 100644 index 3b969d84dc..0000000000 --- a/src/Umbraco.Core/Models/Packaging/InstallationSummaryExtentions.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Collections.Generic; - -namespace Umbraco.Core.Models.Packaging -{ - internal static class InstallationSummaryExtentions - { - public static InstallationSummary InitEmpty(this InstallationSummary summary) - { - summary.Actions = new List(); - summary.ContentInstalled = new List(); - summary.ContentTypesInstalled = new List(); - summary.DataTypesInstalled = new List(); - summary.DictionaryItemsInstalled = new List(); - summary.FilesInstalled = new List(); - summary.LanguagesInstalled = new List(); - summary.MacrosInstalled = new List(); - summary.MetaData = new MetaData(); - summary.TemplatesInstalled = new List(); - summary.PackageInstalled = false; - return summary; - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Packaging/MetaData.cs b/src/Umbraco.Core/Models/Packaging/MetaData.cs deleted file mode 100644 index a1cb450c5b..0000000000 --- a/src/Umbraco.Core/Models/Packaging/MetaData.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace Umbraco.Core.Models.Packaging -{ - [Serializable] - [DataContract(IsReference = true)] - public class MetaData - { - public string Name { get; set; } - public string Version { get; set; } - public string Url { get; set; } - public string License { get; set; } - public string LicenseUrl { get; set; } - public int ReqMajor { get; set; } - public int ReqMinor { get; set; } - public int ReqPatch { get; set; } - public string AuthorName { get; set; } - public string AuthorUrl { get; set; } - public string Readme { get; set; } - public string Control { get; set; } - } -} diff --git a/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs b/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs index 01d10fa2e1..65e26419f0 100644 --- a/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs +++ b/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs @@ -7,7 +7,7 @@ using System.Runtime.Serialization; namespace Umbraco.Core.Models.Packaging { [DataContract(Name = "packageInstance")] - public class PackageDefinition + public class PackageDefinition : IPackageInfo { [DataMember(Name = "id")] public int Id { get; set; } @@ -89,9 +89,9 @@ namespace Umbraco.Core.Models.Packaging [DataMember(Name = "files")] public IList Files { get; set; } = new List(); - //TODO: Change this to angular view + //fixme: Change this to angular view [DataMember(Name = "loadControl")] - public string LoadControl { get; set; } = string.Empty; + public string Control { get; set; } = string.Empty; [DataMember(Name = "actions")] public string Actions { get; set; } = ""; diff --git a/src/Umbraco.Core/Packaging/RequirementsType.cs b/src/Umbraco.Core/Models/Packaging/RequirementsType.cs similarity index 65% rename from src/Umbraco.Core/Packaging/RequirementsType.cs rename to src/Umbraco.Core/Models/Packaging/RequirementsType.cs index 38cac482c2..2b6894ed0f 100644 --- a/src/Umbraco.Core/Packaging/RequirementsType.cs +++ b/src/Umbraco.Core/Models/Packaging/RequirementsType.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Core.Packaging +namespace Umbraco.Core.Models.Packaging { public enum RequirementsType { diff --git a/src/Umbraco.Core/Models/Packaging/UninstallationSummary.cs b/src/Umbraco.Core/Models/Packaging/UninstallationSummary.cs index cfa454f91c..20cbedbcf0 100644 --- a/src/Umbraco.Core/Models/Packaging/UninstallationSummary.cs +++ b/src/Umbraco.Core/Models/Packaging/UninstallationSummary.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Runtime.Serialization; namespace Umbraco.Core.Models.Packaging @@ -8,16 +9,16 @@ namespace Umbraco.Core.Models.Packaging [DataContract(IsReference = true)] public class UninstallationSummary { - public MetaData MetaData { get; set; } - public IEnumerable DataTypesUninstalled { get; set; } - public IEnumerable LanguagesUninstalled { get; set; } - public IEnumerable DictionaryItemsUninstalled { get; set; } - public IEnumerable MacrosUninstalled { get; set; } - public IEnumerable FilesUninstalled { get; set; } - public IEnumerable TemplatesUninstalled { get; set; } - public IEnumerable ContentTypesUninstalled { get; set; } - public IEnumerable StylesheetsUninstalled { get; set; } - public IEnumerable ContentUninstalled { get; set; } + public IPackageInfo MetaData { get; set; } + public IEnumerable DataTypesUninstalled { get; set; } = Enumerable.Empty(); + public IEnumerable LanguagesUninstalled { get; set; } = Enumerable.Empty(); + public IEnumerable DictionaryItemsUninstalled { get; set; } = Enumerable.Empty(); + public IEnumerable MacrosUninstalled { get; set; } = Enumerable.Empty(); + public IEnumerable FilesUninstalled { get; set; } = Enumerable.Empty(); + public IEnumerable TemplatesUninstalled { get; set; } = Enumerable.Empty(); + public IEnumerable ContentTypesUninstalled { get; set; } = Enumerable.Empty(); + public IEnumerable StylesheetsUninstalled { get; set; } = Enumerable.Empty(); + public IEnumerable ContentUninstalled { get; set; } = Enumerable.Empty(); public bool PackageUninstalled { get; set; } } } diff --git a/src/Umbraco.Core/Models/Packaging/UninstallationSummaryExtentions.cs b/src/Umbraco.Core/Models/Packaging/UninstallationSummaryExtentions.cs deleted file mode 100644 index 688b78fa1f..0000000000 --- a/src/Umbraco.Core/Models/Packaging/UninstallationSummaryExtentions.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Collections.Generic; - -namespace Umbraco.Core.Models.Packaging -{ - internal static class UninstallationSummaryExtentions - { - public static UninstallationSummary InitEmpty(this UninstallationSummary summary) - { - summary.ContentUninstalled = new List(); - summary.ContentTypesUninstalled = new List(); - summary.DataTypesUninstalled = new List(); - summary.DictionaryItemsUninstalled = new List(); - summary.FilesUninstalled = new List(); - summary.LanguagesUninstalled = new List(); - summary.MacrosUninstalled = new List(); - summary.MetaData = new MetaData(); - summary.TemplatesUninstalled = new List(); - summary.PackageUninstalled = false; - return summary; - } - } -} diff --git a/src/Umbraco.Core/Packaging/ICreatedPackagesRepository.cs b/src/Umbraco.Core/Packaging/ICreatedPackagesRepository.cs index 52a16ddff0..42606da6ef 100644 --- a/src/Umbraco.Core/Packaging/ICreatedPackagesRepository.cs +++ b/src/Umbraco.Core/Packaging/ICreatedPackagesRepository.cs @@ -2,6 +2,9 @@ namespace Umbraco.Core.Packaging { + /// + /// Manages the storage of created package definitions + /// public interface ICreatedPackagesRepository : IPackageDefinitionRepository { /// @@ -10,4 +13,4 @@ namespace Umbraco.Core.Packaging /// string ExportPackage(PackageDefinition definition); } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Packaging/IInstalledPackagesRepository.cs b/src/Umbraco.Core/Packaging/IInstalledPackagesRepository.cs index 3d601d6b1d..61f611edc5 100644 --- a/src/Umbraco.Core/Packaging/IInstalledPackagesRepository.cs +++ b/src/Umbraco.Core/Packaging/IInstalledPackagesRepository.cs @@ -2,12 +2,9 @@ namespace Umbraco.Core.Packaging { - - //TODO: We need to figure out how we want to do this - // we have 2x repositories for saving created and installed packages - // created packages can also be exported - // maybe the below will work? - + /// + /// Manages the storage of installed package definitions + /// public interface IInstalledPackagesRepository : IPackageDefinitionRepository { } diff --git a/src/Umbraco.Core/Packaging/IPackageExtraction.cs b/src/Umbraco.Core/Packaging/IPackageExtraction.cs index 21a5198f55..02e98dc539 100644 --- a/src/Umbraco.Core/Packaging/IPackageExtraction.cs +++ b/src/Umbraco.Core/Packaging/IPackageExtraction.cs @@ -3,7 +3,7 @@ namespace Umbraco.Core.Packaging { /// - /// Used to access an umbraco package file + /// Used to access an umbraco package (zip) file /// Remeber that filenames must be unique /// use "FindDubletFileNames" for sanitycheck /// diff --git a/src/Umbraco.Core/Packaging/IPackageInstallation.cs b/src/Umbraco.Core/Packaging/IPackageInstallation.cs index 6dc4f0fd4e..e8950390e4 100644 --- a/src/Umbraco.Core/Packaging/IPackageInstallation.cs +++ b/src/Umbraco.Core/Packaging/IPackageInstallation.cs @@ -9,8 +9,7 @@ namespace Umbraco.Core.Packaging // there are app domain reboots involved so a single method cannot be used. This needs to either be split into several // methods or return an object with a callback to proceed to the next step. InstallationSummary InstallPackage(string packageFilePath, int userId); - MetaData GetMetaData(string packageFilePath); + IPackageInfo GetMetaData(string packageFilePath); PreInstallWarnings GetPreInstallWarnings(string packageFilePath); - XElement GetConfigXmlElement(string packageFilePath); } } diff --git a/src/Umbraco.Core/Packaging/PackageActionRunner.cs b/src/Umbraco.Core/Packaging/PackageActionRunner.cs index c7545bdae8..64b913989c 100644 --- a/src/Umbraco.Core/Packaging/PackageActionRunner.cs +++ b/src/Umbraco.Core/Packaging/PackageActionRunner.cs @@ -1,5 +1,5 @@ using System; -using System.Xml; +using System.Xml.Linq; using Umbraco.Core.Logging; using Umbraco.Core._Legacy.PackageActions; @@ -26,7 +26,7 @@ namespace Umbraco.Core.Packaging /// Name of the package. /// The action alias. /// The action XML. - public void RunPackageAction(string packageName, string actionAlias, XmlNode actionXml) + public void RunPackageAction(string packageName, string actionAlias, XElement actionXml) { foreach (var ipa in _packageActions) @@ -49,7 +49,7 @@ namespace Umbraco.Core.Packaging /// Name of the package. /// The action alias. /// The action XML. - public void UndoPackageAction(string packageName, string actionAlias, System.Xml.XmlNode actionXml) + public void UndoPackageAction(string packageName, string actionAlias, XElement actionXml) { foreach (var ipa in _packageActions) { diff --git a/src/Umbraco.Core/Packaging/PackageBinaryInspector.cs b/src/Umbraco.Core/Packaging/PackageBinaryInspector.cs deleted file mode 100644 index 7cacb30bd3..0000000000 --- a/src/Umbraco.Core/Packaging/PackageBinaryInspector.cs +++ /dev/null @@ -1,295 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Security; -using System.Security.Permissions; -using Umbraco.Core.Composing; -using Umbraco.Core.Logging; - -namespace Umbraco.Core.Packaging -{ - // Note - // That class uses ReflectionOnlyLoad which does NOT handle policies (bindingRedirect) and - // therefore raised warnings when installing a package, if an exact dependency could not be - // found, though it would be found via policies. So we have to explicitely apply policies - // where appropriate. - - internal class PackageBinaryInspector : MarshalByRefObject - { - /// - /// Entry point to call from your code - /// - /// - /// - /// - /// - /// - /// Will perform the assembly scan in a separate app domain - /// - public static IEnumerable ScanAssembliesForTypeReference(IEnumerable assemblys, out string[] errorReport) - { - // need to wrap in a safe call context in order to prevent whatever Umbraco stores - // in the logical call context from flowing to the created app domain (eg, the - // UmbracoDatabase is *not* serializable and cannot and should not flow). - using (new SafeCallContext()) - { - var appDomain = GetTempAppDomain(); - var type = typeof(PackageBinaryInspector); - try - { - var value = (PackageBinaryInspector)appDomain.CreateInstanceAndUnwrap( - type.Assembly.FullName, - type.FullName); - // do NOT turn PerformScan into static (even if ReSharper says so)! - var result = value.PerformScan(assemblys.ToArray(), out errorReport); - return result; - } - finally - { - AppDomain.Unload(appDomain); - } - } - } - - /// - /// Entry point to call from your code - /// - /// - /// - /// - /// - /// - /// Will perform the assembly scan in a separate app domain - /// - public static IEnumerable ScanAssembliesForTypeReference(string dllPath, out string[] errorReport) - { - var appDomain = GetTempAppDomain(); - var type = typeof(PackageBinaryInspector); - try - { - var value = (PackageBinaryInspector)appDomain.CreateInstanceAndUnwrap( - type.Assembly.FullName, - type.FullName); - // do NOT turn PerformScan into static (even if ReSharper says so)! - var result = value.PerformScan(dllPath, out errorReport); - return result; - } - finally - { - AppDomain.Unload(appDomain); - } - } - - /// - /// Performs the assembly scanning - /// - /// - /// - /// - /// - /// - /// This method is executed in a separate app domain - /// - private IEnumerable PerformScan(IEnumerable assemblies, out string[] errorReport) - { - //we need this handler to resolve assembly dependencies when loading below - AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += (s, e) => - { - var name = AppDomain.CurrentDomain.ApplyPolicy(e.Name); - var a = Assembly.ReflectionOnlyLoad(name); - if (a == null) throw new TypeLoadException("Could not load assembly " + e.Name); - return a; - }; - - //First load each dll file into the context - // do NOT apply policy here: we want to scan the dlls that are in the binaries - var loaded = assemblies.Select(Assembly.ReflectionOnlyLoad).ToList(); - - //scan - return PerformScan(loaded, out errorReport); - } - - /// - /// Performs the assembly scanning - /// - /// - /// - /// - /// - /// - /// This method is executed in a separate app domain - /// - private IEnumerable PerformScan(string dllPath, out string[] errorReport) - { - if (Directory.Exists(dllPath) == false) - { - throw new DirectoryNotFoundException("Could not find directory " + dllPath); - } - - //we need this handler to resolve assembly dependencies when loading below - AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += (s, e) => - { - var name = AppDomain.CurrentDomain.ApplyPolicy(e.Name); - var a = Assembly.ReflectionOnlyLoad(name); - if (a == null) throw new TypeLoadException("Could not load assembly " + e.Name); - return a; - }; - - //First load each dll file into the context - // do NOT apply policy here: we want to scan the dlls that are in the path - var files = Directory.GetFiles(dllPath, "*.dll"); - var loaded = files.Select(Assembly.ReflectionOnlyLoadFrom).ToList(); - - //scan - return PerformScan(loaded, out errorReport); - } - - private static IEnumerable PerformScan(IList loaded, out string[] errorReport) - { - var dllsWithReference = new List(); - var errors = new List(); - var assembliesWithErrors = new List(); - - //load each of the LoadFrom assemblies into the Load context too - foreach (var a in loaded) - { - var name = AppDomain.CurrentDomain.ApplyPolicy(a.FullName); - Assembly.ReflectionOnlyLoad(name); - } - - //get the list of assembly names to compare below - var loadedNames = loaded.Select(x => x.GetName().Name).ToArray(); - - //Then load each referenced assembly into the context - foreach (var a in loaded) - { - //don't load any referenced assemblies that are already found in the loaded array - this is based on name - // regardless of version. We'll assume that if the assembly found in the folder matches the assembly name - // being looked for, that is the version the user has shipped their package with and therefore it 'must' be correct - foreach (var assemblyName in a.GetReferencedAssemblies().Where(ass => loadedNames.Contains(ass.Name) == false)) - { - try - { - var name = AppDomain.CurrentDomain.ApplyPolicy(assemblyName.FullName); - Assembly.ReflectionOnlyLoad(name); - } - catch (FileNotFoundException) - { - //if an exception occurs it means that a referenced assembly could not be found - errors.Add( - string.Concat("This package references the assembly '", - assemblyName.Name, - "' which was not found")); - assembliesWithErrors.Add(a); - } - catch (Exception ex) - { - //if an exception occurs it means that a referenced assembly could not be found - errors.Add( - string.Concat("This package could not be verified for compatibility. An error occurred while loading a referenced assembly '", - assemblyName.Name, - "' see error log for full details.")); - assembliesWithErrors.Add(a); - Current.Logger.Error(ex, "An error occurred scanning package assembly '{AssemblyName}'", assemblyName.FullName); - } - } - } - - var contractType = GetLoadFromContractType(); - - //now that we have all referenced types into the context we can look up stuff - foreach (var a in loaded.Except(assembliesWithErrors)) - { - //now we need to see if they contain any type 'T' - var reflectedAssembly = a; - - try - { - var found = reflectedAssembly.GetExportedTypes() - .Where(contractType.IsAssignableFrom); - - if (found.Any()) - { - dllsWithReference.Add(reflectedAssembly.FullName); - } - } - catch (Exception ex) - { - //This is a hack that nobody can seem to get around, I've read everything and it seems that - // this is quite a common thing when loading types into reflection only load context, so - // we're just going to ignore this specific one for now - var typeLoadEx = ex as TypeLoadException; - if (typeLoadEx != null) - { - if (typeLoadEx.Message.InvariantContains("does not have an implementation")) - { - //ignore - continue; - } - } - else - { - errors.Add( - string.Concat("This package could not be verified for compatibility. An error occurred while scanning a packaged assembly '", - a.GetName().Name, - "' see error log for full details.")); - assembliesWithErrors.Add(a); - Current.Logger.Error(ex, "An error occurred scanning package assembly '{AssemblyName}'", a.GetName().FullName); - } - } - - } - - errorReport = errors.ToArray(); - return dllsWithReference; - } - - /// - /// In order to compare types, the types must be in the same context, this method will return the type that - /// we are checking against but from the Load context. - /// - /// - /// - private static Type GetLoadFromContractType() - { - var name = AppDomain.CurrentDomain.ApplyPolicy(typeof(T).Assembly.FullName); - var contractAssemblyLoadFrom = Assembly.ReflectionOnlyLoad(name); - - var contractType = contractAssemblyLoadFrom.GetExportedTypes() - .FirstOrDefault(x => x.FullName == typeof(T).FullName && x.Assembly.FullName == typeof(T).Assembly.FullName); - - if (contractType == null) - { - throw new InvalidOperationException("Could not find type " + typeof(T) + " in the LoadFrom assemblies"); - } - return contractType; - } - - /// - /// Create an app domain - /// - /// - private static AppDomain GetTempAppDomain() - { - //copy the current app domain setup but don't shadow copy files - var appName = "TempDomain" + Guid.NewGuid(); - var domainSetup = new AppDomainSetup - { - ApplicationName = appName, - ShadowCopyFiles = "false", - ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase, - ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile, - DynamicBase = AppDomain.CurrentDomain.SetupInformation.DynamicBase, - LicenseFile = AppDomain.CurrentDomain.SetupInformation.LicenseFile, - LoaderOptimization = AppDomain.CurrentDomain.SetupInformation.LoaderOptimization, - PrivateBinPath = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath, - PrivateBinPathProbe = AppDomain.CurrentDomain.SetupInformation.PrivateBinPathProbe - }; - - // create new domain with full trust - return AppDomain.CreateDomain(appName, AppDomain.CurrentDomain.Evidence, domainSetup, new PermissionSet(PermissionState.Unrestricted)); - } - } -} diff --git a/src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs b/src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs new file mode 100644 index 0000000000..6f3e4a3603 --- /dev/null +++ b/src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs @@ -0,0 +1,165 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; +using System.Xml.XPath; +using Umbraco.Core.Logging; +using Umbraco.Core.Models.Packaging; + +namespace Umbraco.Core.Packaging +{ + /// + /// Parses the xml document contained in a compiled (zip) Umbraco package + /// + public class CompiledPackageXmlParser + { + public CompiledPackageXmlParser() + { + + } + + public CompiledPackage ToCompiledPackage(XDocument xml) + { + if (xml == null) throw new ArgumentNullException(nameof(xml)); + if (xml.Root == null) throw new ArgumentException(nameof(xml), "The xml document is invalid"); + if (xml.Root.Name != Constants.Packaging.UmbPackageNodeName) throw new FormatException("The xml document is invalid"); + + var info = xml.Root.Element("info"); + if (info == null) throw new FormatException("The xml document is invalid"); + var package = xml.Element("package"); + if (package == null) throw new FormatException("The xml document is invalid"); + var author = package.Element("author"); + if (author == null) throw new FormatException("The xml document is invalid"); + var requirements = package.Element("requirements"); + if (requirements == null) throw new FormatException("The xml document is invalid"); + + var def = new CompiledPackage + { + Name = package.Element("name")?.Value, + Author = author.Element("name")?.Value, + AuthorUrl = author.Element("website")?.Value, + Version = package.Element("version")?.Value, + Readme = info.Element("readme")?.Value, + License = package.Element("license")?.Value, + LicenseUrl = package.Element("license")?.AttributeValue("url"), + Url = package.Element("url")?.Value, + IconUrl = package.Element("iconUrl")?.Value, + UmbracoVersion = new Version((int)requirements.Element("major"), (int)requirements.Element("minor"), (int)requirements.Element("patch")), + UmbracoVersionRequirementsType = Enum.Parse(requirements.AttributeValue("type")), + Control = package.Element("control")?.Value, + + Files = xml.Root.Element("files")?.Elements("files")?.Select(x => new CompiledPackageFile + { + UniqueFileName = x.Element("guid")?.Value, + OriginalName = x.Element("orgPath")?.Value, + OriginalPath = x.Element("orgName")?.Value + }).ToList() ?? new List(), + + }; + + return def; + } + + } + + /// + /// Converts a to and from XML + /// + public class PackageDefinitionXmlParser + { + private readonly ILogger _logger; + + public PackageDefinitionXmlParser(ILogger logger) + { + _logger = logger; + } + + public PackageDefinition ToPackageDefinition(XElement xml) + { + if (xml == null) return null; + + var retVal = new PackageDefinition + { + Id = xml.AttributeValue("id"), + Name = xml.AttributeValue("name") ?? string.Empty, + FolderId = xml.AttributeValue("folder"), + PackagePath = xml.AttributeValue("packagePath") ?? string.Empty, + Version = xml.AttributeValue("version") ?? string.Empty, + Url = xml.AttributeValue("url") ?? string.Empty, + PackageId = xml.AttributeValue("packageGuid"), + IconUrl = xml.AttributeValue("iconUrl") ?? string.Empty, + UmbracoVersion = xml.AttributeValue("umbVersion"), + License = xml.Element("license")?.Value ?? string.Empty, + LicenseUrl = xml.Element("license")?.AttributeValue("url") ?? string.Empty, + Author = xml.Element("author")?.Value ?? string.Empty, + AuthorUrl = xml.Element("author")?.AttributeValue("url") ?? string.Empty, + Readme = xml.Element("readme")?.Value ?? string.Empty, + Actions = xml.Element("actions")?.ToString(SaveOptions.None) ?? "", //take the entire outer xml value + ContentNodeId = xml.Element("content")?.AttributeValue("nodeId") ?? string.Empty, + ContentLoadChildNodes = xml.Element("content")?.AttributeValue("loadChildNodes") ?? false, + Macros = xml.Element("macros")?.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), + Templates = xml.Element("templates")?.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), + Stylesheets = xml.Element("stylesheets")?.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), + DocumentTypes = xml.Element("documentTypes")?.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), + Languages = xml.Element("languages")?.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), + DictionaryItems = xml.Element("dictionaryitems")?.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), + DataTypes = xml.Element("datatypes")?.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), + Files = xml.Element("files")?.Elements("file").Select(x => x.Value).ToList() ?? new List(), + Control = xml.Element("loadcontrol")?.Value ?? string.Empty + }; + + return retVal; + } + + public XElement ToXml(PackageDefinition def) + { + var actionsXml = new XElement("actions"); + try + { + actionsXml = XElement.Parse(def.Actions); + } + catch (Exception e) + { + _logger.Warn(e, "Could not add package actions to the package xml definition, the xml did not parse"); + } + + var packageXml = new XElement("package", + new XAttribute("id", def.Id), + new XAttribute("version", def.Version ?? string.Empty), + new XAttribute("url", def.Url ?? string.Empty), + new XAttribute("name", def.Name ?? string.Empty), + new XAttribute("folder", def.FolderId), + new XAttribute("packagePath", def.PackagePath ?? string.Empty), + new XAttribute("iconUrl", def.IconUrl ?? string.Empty), + new XAttribute("umbVersion", def.UmbracoVersion), + new XAttribute("packageGuid", def.PackageId), + + new XElement("license", + new XCData(def.License ?? string.Empty), + new XAttribute("url", def.LicenseUrl ?? string.Empty)), + + new XElement("author", + new XCData(def.Author ?? string.Empty), + new XAttribute("url", def.AuthorUrl ?? string.Empty)), + + new XElement("readme", new XCData(def.Readme ?? string.Empty)), + actionsXml, + new XElement("datatypes", string.Join(",", def.DataTypes ?? Array.Empty())), + + new XElement("content", + new XAttribute("nodeId", def.ContentNodeId), + new XAttribute("loadChildNodes", def.ContentLoadChildNodes)), + + new XElement("templates", string.Join(",", def.Templates ?? Array.Empty())), + new XElement("stylesheets", string.Join(",", def.Stylesheets ?? Array.Empty())), + new XElement("documentTypes", string.Join(",", def.DocumentTypes ?? Array.Empty())), + new XElement("macros", string.Join(",", def.Macros ?? Array.Empty())), + new XElement("files", (def.Files ?? Array.Empty()).Where(x => !x.IsNullOrWhiteSpace()).Select(x => new XElement("file", x))), + new XElement("languages", string.Join(",", def.Languages ?? Array.Empty())), + new XElement("dictionaryitems", string.Join(",", def.DictionaryItems ?? Array.Empty())), + new XElement("loadcontrol", def.Control ?? string.Empty)); //fixme: no more loadcontrol, needs to be an angular view + + return packageXml; + } + } +} diff --git a/src/Umbraco.Core/Packaging/PackageInstallation.cs b/src/Umbraco.Core/Packaging/PackageInstallation.cs index 556c5203ec..6c9cd57451 100644 --- a/src/Umbraco.Core/Packaging/PackageInstallation.cs +++ b/src/Umbraco.Core/Packaging/PackageInstallation.cs @@ -78,7 +78,7 @@ namespace Umbraco.Core.Packaging } } - public MetaData GetMetaData(string packageFilePath) + public IPackageInfo GetMetaData(string packageFilePath) { try { @@ -117,14 +117,14 @@ namespace Umbraco.Core.Packaging XElement documentSet; XElement documents; XElement actions; - MetaData metaData; + IPackageInfo metaData; InstallationSummary installationSummary; try { XElement rootElement = GetConfigXmlElement(packageFile); PackageSupportedCheck(rootElement); - PackageStructureSanetyCheck(packageFile, rootElement); + PackageStructureSanityCheck(packageFile, rootElement); dataTypes = rootElement.Element(Constants.Packaging.DataTypesNodeName); languages = rootElement.Element(Constants.Packaging.LanguagesNodeName); dictionaryItems = rootElement.Element(Constants.Packaging.DictionaryItemsNodeName); @@ -220,7 +220,7 @@ namespace Umbraco.Core.Packaging public XElement GetConfigXmlElement(string packageFilePath) { - XDocument document = GetConfigXmlDoc(packageFilePath); + var document = GetConfigXmlDoc(packageFilePath); if (document.Root == null || document.Root.Name.LocalName.Equals(Constants.Packaging.UmbPackageNodeName) == false) { @@ -229,13 +229,7 @@ namespace Umbraco.Core.Packaging return document.Root; } - internal void PackageStructureSanetyCheck(string packageFilePath) - { - XElement rootElement = GetConfigXmlElement(packageFilePath); - PackageStructureSanetyCheck(packageFilePath, rootElement); - } - - private void PackageStructureSanetyCheck(string packageFilePath, XElement rootElement) + private void PackageStructureSanityCheck(string packageFilePath, XElement rootElement) { XElement filesElement = rootElement.Element(Constants.Packaging.FilesNodeName); if (filesElement != null) @@ -513,7 +507,7 @@ namespace Umbraco.Core.Packaging return pathElement.TrimStart(new[] {'\\', '/', '~'}).Replace("/", "\\"); } - private MetaData GetMetaData(XElement xRootElement) + private IPackageInfo GetMetaData(XElement xRootElement) { XElement infoElement = xRootElement.Element(Constants.Packaging.InfoNodeName); @@ -535,21 +529,19 @@ namespace Umbraco.Core.Packaging var readmeElement = infoElement.XPathSelectElement(Constants.Packaging.ReadmeXpath); XElement controlElement = xRootElement.Element(Constants.Packaging.ControlNodeName); - - return new MetaData + + return new PackageDefinition { Name = StringValue(nameElement), Version = StringValue(versionElement), Url = StringValue(urlElement), License = StringValue(licenseElement), LicenseUrl = StringAttribute(licenseElement, Constants.Packaging.PackageLicenseXpathUrlAttribute), - AuthorName = StringValue(authorNameElement), + Author = StringValue(authorNameElement), AuthorUrl = StringValue(authorUrlElement), Readme = StringValue(readmeElement), Control = StringValue(controlElement), - ReqMajor = IntValue(majorElement), - ReqMinor = IntValue(minorElement), - ReqPatch = IntValue(patchElement) + UmbracoVersion = new Version(IntValue(majorElement), IntValue(minorElement), IntValue(patchElement)) }; } diff --git a/src/Umbraco.Core/Packaging/CreatedPackagesRepository.cs b/src/Umbraco.Core/Packaging/PackagesRepository.cs similarity index 79% rename from src/Umbraco.Core/Packaging/CreatedPackagesRepository.cs rename to src/Umbraco.Core/Packaging/PackagesRepository.cs index 169b161fbc..9f37f8a546 100644 --- a/src/Umbraco.Core/Packaging/CreatedPackagesRepository.cs +++ b/src/Umbraco.Core/Packaging/PackagesRepository.cs @@ -15,7 +15,10 @@ using File = System.IO.File; namespace Umbraco.Core.Packaging { - internal class CreatedPackagesRepository : ICreatedPackagesRepository + /// + /// Manages the storage of installed/created package definitions + /// + internal class PackagesRepository : ICreatedPackagesRepository, IInstalledPackagesRepository { private readonly IContentService _contentService; private readonly IContentTypeService _contentTypeService; @@ -25,16 +28,37 @@ namespace Umbraco.Core.Packaging private readonly ILocalizationService _languageService; private readonly IEntityXmlSerializer _serializer; private readonly ILogger _logger; + private readonly string _packageRepositoryFileName; private readonly string _mediaFolderPath; private readonly string _packagesFolderPath; private readonly string _tempFolderPath; - - public CreatedPackagesRepository(IContentService contentService, IContentTypeService contentTypeService, + private readonly PackageDefinitionXmlParser _parser; + + /// + /// Constructor + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// The file name for storing the package definitions (i.e. "createdPackages.config") + /// + /// + /// + /// + public PackagesRepository(IContentService contentService, IContentTypeService contentTypeService, IDataTypeService dataTypeService, IFileService fileService, IMacroService macroService, ILocalizationService languageService, IEntityXmlSerializer serializer, ILogger logger, + string packageRepositoryFileName, string tempFolderPath = null, string packagesFolderPath = null, string mediaFolderPath = null) { + if (string.IsNullOrWhiteSpace(packageRepositoryFileName)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(packageRepositoryFileName)); _contentService = contentService; _contentTypeService = contentTypeService; _dataTypeService = dataTypeService; @@ -43,32 +67,38 @@ namespace Umbraco.Core.Packaging _languageService = languageService; _serializer = serializer; _logger = logger; - + _packageRepositoryFileName = packageRepositoryFileName; + _tempFolderPath = tempFolderPath ?? SystemDirectories.Data + "/TEMP/PackageFiles"; _packagesFolderPath = packagesFolderPath ?? SystemDirectories.Packages; _mediaFolderPath = mediaFolderPath ?? SystemDirectories.Media + "/created-packages"; + + _parser = new PackageDefinitionXmlParser(logger); } - private string CreatedPackagesFile => _packagesFolderPath.EnsureEndsWith('/') + "createdPackages.config"; + private string CreatedPackagesFile => _packagesFolderPath.EnsureEndsWith('/') + _packageRepositoryFileName; public IEnumerable GetAll() { var packagesXml = EnsureStorage(out _); + if (packagesXml?.Root == null) + yield break;; + foreach (var packageXml in packagesXml.Root.Elements("package")) - yield return XmlToPackageDefinition(packageXml); + yield return _parser.ToPackageDefinition(packageXml); } public PackageDefinition GetById(int id) { var packagesXml = EnsureStorage(out _); - var packageXml = packagesXml.Root.Elements("package").FirstOrDefault(x => x.AttributeValue("id") == id); - return packageXml == null ? null : XmlToPackageDefinition(packageXml); + var packageXml = packagesXml?.Root?.Elements("package").FirstOrDefault(x => x.AttributeValue("id") == id); + return packageXml == null ? null : _parser.ToPackageDefinition(packageXml); } public void Delete(int id) { var packagesXml = EnsureStorage(out var packagesFile); - var packageXml = packagesXml.Root.Elements("package").FirstOrDefault(x => x.AttributeValue("id") == id); + var packageXml = packagesXml?.Root?.Elements("package").FirstOrDefault(x => x.AttributeValue("id") == id); if (packageXml == null) return; packageXml.Remove(); @@ -82,6 +112,9 @@ namespace Umbraco.Core.Packaging var packagesXml = EnsureStorage(out var packagesFile); + if (packagesXml?.Root == null) + return false; + //ensure it's valid ValidatePackage(definition); @@ -97,9 +130,9 @@ namespace Umbraco.Core.Packaging var maxId = packagesXml.Root.Elements("package").Max(x => x.AttributeValue("id")) ?? 0; var newId = maxId + 1; definition.Id = newId; - definition.PackageId = Guid.NewGuid(); + definition.PackageId = definition.PackageId == default ? Guid.NewGuid() : definition.PackageId; definition.FolderId = Guid.NewGuid(); - var packageXml = PackageDefinitionToXml(definition); + var packageXml = _parser.ToXml(definition); packagesXml.Root.Add(packageXml); } else @@ -109,7 +142,7 @@ namespace Umbraco.Core.Packaging if (packageXml == null) return false; - var updatedXml = PackageDefinitionToXml(definition); + var updatedXml = _parser.ToXml(definition); packageXml.ReplaceWith(updatedXml); } @@ -154,10 +187,10 @@ namespace Umbraco.Core.Packaging AppendFileToManifest(fileName, temporaryPath, filesXml); //Load control on install... - if (!string.IsNullOrEmpty(definition.LoadControl)) + if (!string.IsNullOrEmpty(definition.Control)) { - var control = new XElement("control", definition.LoadControl); - AppendFileToManifest(definition.LoadControl, temporaryPath, filesXml); + var control = new XElement("control", definition.Control); + AppendFileToManifest(definition.Control, temporaryPath, filesXml); manifestRoot.Add(control); } @@ -174,7 +207,7 @@ namespace Umbraco.Core.Packaging } catch (Exception e) { - _logger.Warn(e, "Could not add package actions to the package manifest, the xml did not parse"); + _logger.Warn(e, "Could not add package actions to the package manifest, the xml did not parse"); } } @@ -583,93 +616,9 @@ namespace Umbraco.Core.Packaging return packagesXml; } - private static PackageDefinition XmlToPackageDefinition(XElement xml) - { - if (xml == null) return null; + - var retVal = new PackageDefinition - { - Id = xml.AttributeValue("id"), - Name = xml.AttributeValue("name") ?? string.Empty, - FolderId = xml.AttributeValue("folder"), - PackagePath = xml.AttributeValue("packagePath") ?? string.Empty, - Version = xml.AttributeValue("version") ?? string.Empty, - Url = xml.AttributeValue("url") ?? string.Empty, - PackageId = xml.AttributeValue("packageGuid"), - IconUrl = xml.AttributeValue("iconUrl") ?? string.Empty, - UmbracoVersion = xml.AttributeValue("umbVersion"), - License = xml.Element("license")?.Value ?? string.Empty, - LicenseUrl = xml.Element("license")?.AttributeValue("url") ?? string.Empty, - Author = xml.Element("author")?.Value ?? string.Empty, - AuthorUrl = xml.Element("author")?.AttributeValue("url") ?? string.Empty, - Readme = xml.Element("readme")?.Value ?? string.Empty, - Actions = xml.Element("actions")?.ToString(SaveOptions.None) ?? "", //take the entire outer xml value - ContentNodeId = xml.Element("content")?.AttributeValue("nodeId") ?? string.Empty, - ContentLoadChildNodes = xml.Element("content")?.AttributeValue("loadChildNodes") ?? false, - Macros = xml.Element("macros")?.Value.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), - Templates = xml.Element("templates")?.Value.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), - Stylesheets = xml.Element("stylesheets")?.Value.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), - DocumentTypes = xml.Element("documentTypes")?.Value.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), - Languages = xml.Element("languages")?.Value.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), - DictionaryItems = xml.Element("dictionaryitems")?.Value.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), - DataTypes = xml.Element("datatypes")?.Value.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), - Files = xml.Element("files")?.Elements("file").Select(x => x.Value).ToList() ?? new List(), - LoadControl = xml.Element("loadcontrol")?.Value ?? string.Empty - }; - - return retVal; - } - - private XElement PackageDefinitionToXml(PackageDefinition def) - { - var actionsXml = new XElement("actions"); - try - { - actionsXml = XElement.Parse(def.Actions); - } - catch (Exception e) - { - _logger.Warn(e, "Could not add package actions to the package xml definition, the xml did not parse"); - } - - var packageXml = new XElement("package", - new XAttribute("id", def.Id), - new XAttribute("version", def.Version ?? string.Empty), - new XAttribute("url", def.Url ?? string.Empty), - new XAttribute("name", def.Name ?? string.Empty), - new XAttribute("folder", def.FolderId), - new XAttribute("packagePath", def.PackagePath ?? string.Empty), - new XAttribute("iconUrl", def.IconUrl ?? string.Empty), - new XAttribute("umbVersion", def.UmbracoVersion), - new XAttribute("packageGuid", def.PackageId), - - new XElement("license", - new XCData(def.License ?? string.Empty), - new XAttribute("url", def.LicenseUrl ?? string.Empty)), - - new XElement("author", - new XCData(def.Author ?? string.Empty), - new XAttribute("url", def.AuthorUrl ?? string.Empty)), - - new XElement("readme", new XCData(def.Readme ?? string.Empty)), - actionsXml, - new XElement("datatypes", string.Join(",", def.DataTypes ?? Array.Empty())), - - new XElement("content", - new XAttribute("nodeId", def.ContentNodeId), - new XAttribute("loadChildNodes", def.ContentLoadChildNodes)), - - new XElement("templates", string.Join(",", def.Templates ?? Array.Empty())), - new XElement("stylesheets", string.Join(",", def.Stylesheets ?? Array.Empty())), - new XElement("documentTypes", string.Join(",", def.DocumentTypes ?? Array.Empty())), - new XElement("macros", string.Join(",", def.Macros ?? Array.Empty())), - new XElement("files", (def.Files ?? Array.Empty()).Where(x => !x.IsNullOrWhiteSpace()).Select(x => new XElement("file", x))), - new XElement("languages", string.Join(",", def.Languages ?? Array.Empty())), - new XElement("dictionaryitems", string.Join(",", def.DictionaryItems ?? Array.Empty())), - new XElement("loadcontrol", def.LoadControl ?? string.Empty)); //fixme: no more loadcontrol, needs to be an angular view - - return packageXml; - } + } } diff --git a/src/Umbraco.Core/Services/IPackagingService.cs b/src/Umbraco.Core/Services/IPackagingService.cs index 32d5cd4c59..5b432d3339 100644 --- a/src/Umbraco.Core/Services/IPackagingService.cs +++ b/src/Umbraco.Core/Services/IPackagingService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; using System.Xml.Linq; using Umbraco.Core.Models; using Umbraco.Core.Models.Packaging; @@ -9,11 +10,25 @@ namespace Umbraco.Core.Services { public interface IPackagingService : IService { + #region Installed Packages + + IEnumerable GetAllInstalledPackages(); + PackageDefinition GetInstalledPackageById(int id); + void DeleteInstalledPackage(int packageId, int userId = 0); + + /// + /// Persists a package definition to storage + /// + /// + bool SaveInstalledPackage(PackageDefinition definition); + + #endregion + #region Created Packages IEnumerable GetAllCreatedPackages(); PackageDefinition GetCreatedPackageById(int id); - void DeleteCreatedPackage(int id); + void DeleteCreatedPackage(int id, int userId = 0); /// /// Persists a package definition to storage @@ -111,6 +126,6 @@ namespace Umbraco.Core.Services /// /// The current user id performing the operation /// - string FetchPackageFile(Guid packageId, Version umbracoVersion, int userId); + Task FetchPackageFileAsync(Guid packageId, Version umbracoVersion, int userId); } } diff --git a/src/Umbraco.Core/Services/Implement/PackagingService.cs b/src/Umbraco.Core/Services/Implement/PackagingService.cs index d6003dc842..e7b33dd86d 100644 --- a/src/Umbraco.Core/Services/Implement/PackagingService.cs +++ b/src/Umbraco.Core/Services/Implement/PackagingService.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Net.Http; using System.Text.RegularExpressions; +using System.Threading.Tasks; using System.Web; using System.Xml.Linq; using Umbraco.Core.Collections; @@ -41,12 +42,11 @@ namespace Umbraco.Core.Services.Implement private readonly IFileService _fileService; private readonly ILocalizationService _localizationService; private readonly IEntityService _entityService; - private readonly IScopeProvider _scopeProvider; private Dictionary _importedContentTypes; - private readonly IAuditRepository _auditRepository; - private readonly IContentTypeRepository _contentTypeRepository; + private readonly IAuditService _auditService; private readonly PropertyEditorCollection _propertyEditors; private readonly ICreatedPackagesRepository _createdPackages; + private readonly IInstalledPackagesRepository _installedPackages; private static HttpClient _httpClient; public PackagingService( @@ -58,11 +58,10 @@ namespace Umbraco.Core.Services.Implement IFileService fileService, ILocalizationService localizationService, IEntityService entityService, - IScopeProvider scopeProvider, - IAuditRepository auditRepository, - IContentTypeRepository contentTypeRepository, + IAuditService auditService, PropertyEditorCollection propertyEditors, - ICreatedPackagesRepository createdPackages) + ICreatedPackagesRepository createdPackages, + IInstalledPackagesRepository installedPackages) { _logger = logger; _contentService = contentService; @@ -72,21 +71,15 @@ namespace Umbraco.Core.Services.Implement _fileService = fileService; _localizationService = localizationService; _entityService = entityService; - _scopeProvider = scopeProvider; - _auditRepository = auditRepository; - _contentTypeRepository = contentTypeRepository; + _auditService = auditService; _propertyEditors = propertyEditors; _createdPackages = createdPackages; + _installedPackages = installedPackages; _importedContentTypes = new Dictionary(); } - protected IQuery Query() => _scopeProvider.SqlContext.Query(); - #region Content - - - /// /// Imports and saves package xml as @@ -229,43 +222,17 @@ namespace Umbraco.Core.Services.Implement content.Key = key; } - using (var scope = _scopeProvider.CreateScope(autoComplete: true)) + foreach (var property in properties) { - foreach (var property in properties) + string propertyTypeAlias = property.Name.LocalName; + if (content.HasProperty(propertyTypeAlias)) { - string propertyTypeAlias = property.Name.LocalName; - if (content.HasProperty(propertyTypeAlias)) - { - var propertyValue = property.Value; + var propertyValue = property.Value; - var propertyType = contentType.PropertyTypes.FirstOrDefault(pt => pt.Alias == propertyTypeAlias); + var propertyType = contentType.PropertyTypes.FirstOrDefault(pt => pt.Alias == propertyTypeAlias); - //TODO: It would be heaps nicer if we didn't have to hard code references to specific property editors - // we'd have to modify the packaging format to denote how to parse/store the value instead of relying on this - - if (propertyType != null) - { - // fixme - wtf is this very specific thing here?! - //if (propertyType.PropertyEditorAlias == Constants.PropertyEditors.Aliases.CheckBoxList) - //{ - - // //TODO: We need to refactor this so the packager isn't making direct db calls for an 'edge' case - // var database = scope.Database; - // var dtos = database.Fetch("WHERE datatypeNodeId = @Id", new { Id = propertyType.DataTypeId }); - - // var propertyValueList = new List(); - // foreach (var preValue in propertyValue.Split(',')) - // { - // propertyValueList.Add(dtos.Single(x => x.Value == preValue).Id.ToString(CultureInfo.InvariantCulture)); - // } - - // propertyValue = string.Join(",", propertyValueList.ToArray()); - - //} - } - //set property value - content.SetValue(propertyTypeAlias, propertyValue); - } + //set property value + content.SetValue(propertyTypeAlias, propertyValue); } } @@ -734,17 +701,12 @@ namespace Umbraco.Core.Services.Implement /// private IContentType FindContentTypeByAlias(string contentTypeAlias) { - using (var scope = _scopeProvider.CreateScope()) - { - var query = Query().Where(x => x.Alias == contentTypeAlias); - var contentType = _contentTypeRepository.Get(query).FirstOrDefault(); + var contentType = _contentTypeService.Get(contentTypeAlias); - if (contentType == null) - throw new Exception($"ContentType matching the passed in Alias: '{contentTypeAlias}' was null"); + if (contentType == null) + throw new Exception($"ContentType matching the passed in Alias: '{contentTypeAlias}' was null"); - scope.Complete(); - return contentType; - } + return contentType; } #endregion @@ -1165,59 +1127,45 @@ namespace Umbraco.Core.Services.Implement #region Package Files - /// - /// This will fetch an Umbraco package file from the package repository and return the relative file path to the downloaded package file - /// - /// - /// - /// /// The current user id performing the operation - /// - public string FetchPackageFile(Guid packageId, Version umbracoVersion, int userId) + /// + public async Task FetchPackageFileAsync(Guid packageId, Version umbracoVersion, int userId) { - using (var scope = _scopeProvider.CreateScope()) + //includeHidden = true because we don't care if it's hidden we want to get the file regardless + var url = $"{Constants.PackageRepository.RestApiBaseUrl}/{packageId}?version={umbracoVersion.ToString(3)}&includeHidden=true&asFile=true"; + byte[] bytes; + try { - //includeHidden = true because we don't care if it's hidden we want to get the file regardless - var url = $"{Constants.PackageRepository.RestApiBaseUrl}/{packageId}?version={umbracoVersion.ToString(3)}&includeHidden=true&asFile=true"; - byte[] bytes; - try + if (_httpClient == null) { - if (_httpClient == null) - { - _httpClient = new HttpClient(); - } - bytes = _httpClient.GetByteArrayAsync(url).GetAwaiter().GetResult(); + _httpClient = new HttpClient(); } - catch (HttpRequestException ex) - { - throw new ConnectionException("An error occuring downloading the package from " + url, ex); - } - - //successfull - if (bytes.Length > 0) - { - var packagePath = IOHelper.MapPath(SystemDirectories.Packages); - - // Check for package directory - if (Directory.Exists(packagePath) == false) - Directory.CreateDirectory(packagePath); - - var packageFilePath = Path.Combine(packagePath, packageId + ".umb"); - - using (var fs1 = new FileStream(packageFilePath, FileMode.Create)) - { - fs1.Write(bytes, 0, bytes.Length); - return "packages\\" + packageId + ".umb"; - } - } - - Audit(AuditType.PackagerInstall, $"Package {packageId} fetched from {Constants.PackageRepository.DefaultRepositoryId}", userId, -1); - return null; + bytes = await _httpClient.GetByteArrayAsync(url); + } + catch (HttpRequestException ex) + { + throw new ConnectionException("An error occuring downloading the package from " + url, ex); } - } - private void Audit(AuditType type, string message, int userId, int objectId) - { - _auditRepository.Save(new AuditItem(objectId, type, userId, "Package", message)); + //successfull + if (bytes.Length > 0) + { + var packagePath = IOHelper.MapPath(SystemDirectories.Packages); + + // Check for package directory + if (Directory.Exists(packagePath) == false) + Directory.CreateDirectory(packagePath); + + var packageFilePath = Path.Combine(packagePath, packageId + ".umb"); + + using (var fs1 = new FileStream(packageFilePath, FileMode.Create)) + { + fs1.Write(bytes, 0, bytes.Length); + return "packages\\" + packageId + ".umb"; + } + } + + _auditService.Add(AuditType.PackagerInstall, userId, -1, "Package", $"Package {packageId} fetched from {Constants.PackageRepository.DefaultRepositoryId}"); + return null; } #endregion @@ -1369,21 +1317,13 @@ namespace Umbraco.Core.Services.Implement { var metaData = GetPackageMetaData(packageFilePath); - if (raiseEvents) - { - if (ImportingPackage.IsRaisedEventCancelled(new ImportPackageEventArgs(packageFilePath, metaData), this)) - { - var initEmpty = new InstallationSummary().InitEmpty(); - initEmpty.MetaData = metaData; - return initEmpty; - } - } + if (raiseEvents && ImportingPackage.IsRaisedEventCancelled(new ImportPackageEventArgs(packageFilePath, metaData), this)) + return new InstallationSummary { MetaData = metaData }; + var installationSummary = PackageInstallation.InstallPackage(packageFilePath, userId); if (raiseEvents) - { ImportedPackage.RaiseEvent(new ImportPackageEventArgs(installationSummary, metaData, false), this); - } return installationSummary; } @@ -1393,16 +1333,23 @@ namespace Umbraco.Core.Services.Implement return PackageInstallation.GetPreInstallWarnings(packageFilePath); } - internal MetaData GetPackageMetaData(string packageFilePath) + internal IPackageInfo GetPackageMetaData(string packageFilePath) { return PackageInstallation.GetMetaData(packageFilePath); } #endregion - #region Package Building + #region Created/Installed Package Repositories - public void DeleteCreatedPackage(int id) => _createdPackages.Delete(id); + public void DeleteCreatedPackage(int id, int userId = 0) + { + 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 GetAllCreatedPackages() => _createdPackages.GetAll(); @@ -1412,6 +1359,22 @@ namespace Umbraco.Core.Services.Implement public string ExportCreatedPackage(PackageDefinition definition) => _createdPackages.ExportPackage(definition); + + public IEnumerable GetAllInstalledPackages() => _installedPackages.GetAll(); + + public PackageDefinition GetInstalledPackageById(int id) => _installedPackages.GetById(id); + + public bool SaveInstalledPackage(PackageDefinition definition) => _installedPackages.SavePackage(definition); + + public void DeleteInstalledPackage(int packageId, int userId = 0) + { + 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); + } + #endregion /// diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index bad9ba7ccd..038d531bd4 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -430,7 +430,8 @@ - + + @@ -443,13 +444,13 @@ - - - + + + @@ -769,7 +770,6 @@ - @@ -893,7 +893,6 @@ - @@ -1535,7 +1534,6 @@ - \ No newline at end of file diff --git a/src/Umbraco.Core/Xml/XmlHelper.cs b/src/Umbraco.Core/Xml/XmlHelper.cs index d4f2d40656..e058858799 100644 --- a/src/Umbraco.Core/Xml/XmlHelper.cs +++ b/src/Umbraco.Core/Xml/XmlHelper.cs @@ -14,7 +14,7 @@ namespace Umbraco.Core.Xml /// /// The XmlHelper class contains general helper methods for working with xml in umbraco. /// - public class XmlHelper + internal class XmlHelper { /// /// Creates or sets an attribute on the XmlNode if an Attributes collection is available @@ -126,44 +126,6 @@ namespace Umbraco.Core.Xml return false; } - /// - /// Tries to create a new XElement from a property value. - /// - /// The value of the property. - /// The Xml element. - /// A value indicating whether it has been possible to create the element. - /// The value can be anything... Performance-wise, this is bad. - public static bool TryCreateXElementFromPropertyValue(object value, out XElement elt) - { - // see note above in TryCreateXPathDocumentFromPropertyValue... - - elt = null; - var xml = value as string; - if (xml == null) return false; // not a string - if (CouldItBeXml(xml) == false) return false; // string does not look like it's xml - if (IsXmlWhitespace(xml)) return false; // string is whitespace, xml-wise - - try - { - elt = XElement.Parse(xml, LoadOptions.None); - } - catch - { - elt = null; - return false; // string can't be parsed into xml - } - - //SD: This used to do this but the razor macros and the entire razor macros section is gone, it was all legacy, it seems this method isn't even - // used apart from for tests so don't think this matters. In any case, we no longer check for this! - - //var name = elt.Name.LocalName; // must not match an excluded tag - //if (UmbracoConfig.For.UmbracoSettings().Scripting.NotDynamicXmlDocumentElements.All(x => x.Element.InvariantEquals(name) == false)) - // return true; - //elt = null; - //return false; - - return true; - } /// /// Sorts the children of a parentNode. @@ -186,47 +148,6 @@ namespace Umbraco.Core.Xml parentNode.AppendChild(node); // moves the node to the last position } - /// - /// Sorts the children of a parentNode if needed. - /// - /// The parent node. - /// An XPath expression to select children of to sort. - /// A function returning the value to order the nodes by. - /// A value indicating whether sorting was needed. - /// same as SortNodes but will do nothing if nodes are already sorted - should improve performances. - internal static bool SortNodesIfNeeded( - XmlNode parentNode, - string childNodesXPath, - Func orderBy) - { - // ensure orderBy runs only once per node - // checks whether nodes are already ordered - // and actually sorts only if needed - - var childNodesAndOrder = parentNode.SelectNodes(childNodesXPath).Cast() - .Select(x => Tuple.Create(x, orderBy(x))).ToArray(); - - var a = 0; - foreach (var x in childNodesAndOrder) - { - if (a > x.Item2) - { - a = -1; - break; - } - a = x.Item2; - } - - if (a >= 0) - return false; - - // append child nodes to last position, in sort-order - // so all child nodes will go after the property nodes - foreach (var x in childNodesAndOrder.OrderBy(x => x.Item2)) - parentNode.AppendChild(x.Item1); // moves the node to the last position - - return true; - } /// /// Sorts a single child node of a parentNode. @@ -281,72 +202,6 @@ namespace Umbraco.Core.Xml return false; } - // used by DynamicNode only, see note in TryCreateXPathDocumentFromPropertyValue - public static string StripDashesInElementOrAttributeNames(string xml) - { - using (var outputms = new MemoryStream()) - { - using (TextWriter outputtw = new StreamWriter(outputms)) - { - using (var ms = new MemoryStream()) - { - using (var tw = new StreamWriter(ms)) - { - tw.Write(xml); - tw.Flush(); - ms.Position = 0; - using (var tr = new StreamReader(ms)) - { - bool IsInsideElement = false, IsInsideQuotes = false; - int ic = 0; - while ((ic = tr.Read()) != -1) - { - if (ic == (int)'<' && !IsInsideQuotes) - { - if (tr.Peek() != (int)'!') - { - IsInsideElement = true; - } - } - if (ic == (int)'>' && !IsInsideQuotes) - { - IsInsideElement = false; - } - if (ic == (int)'"') - { - IsInsideQuotes = !IsInsideQuotes; - } - if (!IsInsideElement || ic != (int)'-' || IsInsideQuotes) - { - outputtw.Write((char)ic); - } - } - - } - } - } - outputtw.Flush(); - outputms.Position = 0; - using (TextReader outputtr = new StreamReader(outputms)) - { - return outputtr.ReadToEnd(); - } - } - } - } - - - /// - /// Imports a XML node from text. - /// - /// The text. - /// The XML doc. - /// - public static XmlNode ImportXmlNodeFromText(string text, ref XmlDocument xmlDoc) - { - xmlDoc.LoadXml(text); - return xmlDoc.FirstChild; - } /// /// Opens a file as a XmlDocument. @@ -355,16 +210,14 @@ namespace Umbraco.Core.Xml /// Returns a XmlDocument class public static XmlDocument OpenAsXmlDocument(string filePath) { - - var reader = new XmlTextReader(IOHelper.MapPath(filePath)) {WhitespaceHandling = WhitespaceHandling.All}; - - var xmlDoc = new XmlDocument(); - //Load the file into the XmlDocument - xmlDoc.Load(reader); - //Close off the connection to the file. - reader.Close(); - - return xmlDoc; + using (var reader = new XmlTextReader(IOHelper.MapPath(filePath)) {WhitespaceHandling = WhitespaceHandling.All}) + { + var xmlDoc = new XmlDocument(); + //Load the file into the XmlDocument + xmlDoc.Load(reader); + + return xmlDoc; + } } /// diff --git a/src/Umbraco.Core/_Legacy/PackageActions/IPackageAction.cs b/src/Umbraco.Core/_Legacy/PackageActions/IPackageAction.cs index 502aee36b3..b58ea34a60 100644 --- a/src/Umbraco.Core/_Legacy/PackageActions/IPackageAction.cs +++ b/src/Umbraco.Core/_Legacy/PackageActions/IPackageAction.cs @@ -1,13 +1,13 @@ using System.Xml; +using System.Xml.Linq; using Umbraco.Core.Composing; namespace Umbraco.Core._Legacy.PackageActions { public interface IPackageAction : IDiscoverable { - bool Execute(string packageName, XmlNode xmlData); + bool Execute(string packageName, XElement xmlData); string Alias(); - bool Undo(string packageName, XmlNode xmlData); - XmlNode SampleXml(); + bool Undo(string packageName, XElement xmlData); } } diff --git a/src/Umbraco.Tests/Composing/PackageActionCollectionTests.cs b/src/Umbraco.Tests/Composing/PackageActionCollectionTests.cs index d100713102..75030decbf 100644 --- a/src/Umbraco.Tests/Composing/PackageActionCollectionTests.cs +++ b/src/Umbraco.Tests/Composing/PackageActionCollectionTests.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Xml; +using System.Xml.Linq; using Moq; using NUnit.Framework; using Umbraco.Core; @@ -41,7 +42,7 @@ namespace Umbraco.Tests.Composing public class PackageAction1 : IPackageAction { - public bool Execute(string packageName, XmlNode xmlData) + public bool Execute(string packageName, XElement xmlData) { throw new NotImplementedException(); } @@ -51,7 +52,7 @@ namespace Umbraco.Tests.Composing return "pa1"; } - public bool Undo(string packageName, XmlNode xmlData) + public bool Undo(string packageName, XElement xmlData) { throw new NotImplementedException(); } @@ -64,7 +65,7 @@ namespace Umbraco.Tests.Composing public class PackageAction2 : IPackageAction { - public bool Execute(string packageName, XmlNode xmlData) + public bool Execute(string packageName, XElement xmlData) { throw new NotImplementedException(); } @@ -74,7 +75,7 @@ namespace Umbraco.Tests.Composing return "pa2"; } - public bool Undo(string packageName, XmlNode xmlData) + public bool Undo(string packageName, XElement xmlData) { throw new NotImplementedException(); } diff --git a/src/Umbraco.Tests/Packaging/CreatedPackagesRepositoryTests.cs b/src/Umbraco.Tests/Packaging/CreatedPackagesRepositoryTests.cs index 3bd9b50ed4..40500d6bcb 100644 --- a/src/Umbraco.Tests/Packaging/CreatedPackagesRepositoryTests.cs +++ b/src/Umbraco.Tests/Packaging/CreatedPackagesRepositoryTests.cs @@ -37,10 +37,11 @@ namespace Umbraco.Tests.Packaging Directory.Delete(IOHelper.MapPath("~/" + _testBaseFolder), true); } - public ICreatedPackagesRepository PackageBuilder => new CreatedPackagesRepository( + public ICreatedPackagesRepository PackageBuilder => new PackagesRepository( ServiceContext.ContentService, ServiceContext.ContentTypeService, ServiceContext.DataTypeService, ServiceContext.FileService, ServiceContext.MacroService, ServiceContext.LocalizationService, Factory.GetInstance(), Logger, + "createdPackages.config", //temp paths tempFolderPath: "~/" + _testBaseFolder + "/temp", packagesFolderPath: "~/" + _testBaseFolder + "/packages", diff --git a/src/Umbraco.Tests/Services/PackagingServiceTests.cs b/src/Umbraco.Tests/Services/PackagingServiceTests.cs index 2caf3e08b3..0f57298c88 100644 --- a/src/Umbraco.Tests/Services/PackagingServiceTests.cs +++ b/src/Umbraco.Tests/Services/PackagingServiceTests.cs @@ -46,7 +46,7 @@ namespace Umbraco.Tests.Services string testPackagePath = GetTestPackagePath(documentTypePickerUmb); - MetaData packageMetaData = packagingService.GetPackageMetaData(testPackagePath); + var packageMetaData = packagingService.GetPackageMetaData(testPackagePath); Assert.IsNotNull(packageMetaData); } diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects.cs b/src/Umbraco.Tests/TestHelpers/TestObjects.cs index 105bc440cc..f48b72daf9 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects.cs @@ -169,9 +169,11 @@ namespace Umbraco.Tests.TestHelpers var macroService = GetLazyService(factory, c => new MacroService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c))); var packagingService = GetLazyService(factory, c => new PackagingService( - logger, contentService.Value, contentTypeService.Value, macroService.Value, dataTypeService.Value, fileService.Value, localizationService.Value, entityService.Value, scopeProvider, GetRepo(c), GetRepo(c), new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())), - new CreatedPackagesRepository(contentService.Value, contentTypeService.Value, dataTypeService.Value, fileService.Value, macroService.Value, localizationService.Value, - new EntityXmlSerializer(contentService.Value, mediaService.Value, dataTypeService.Value, userService.Value, localizationService.Value, contentTypeService.Value, urlSegmentProviders), logger))); + logger, contentService.Value, contentTypeService.Value, macroService.Value, dataTypeService.Value, fileService.Value, localizationService.Value, entityService.Value, auditService.Value, new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())), + createdPackages: new PackagesRepository(contentService.Value, contentTypeService.Value, dataTypeService.Value, fileService.Value, macroService.Value, localizationService.Value, + new EntityXmlSerializer(contentService.Value, mediaService.Value, dataTypeService.Value, userService.Value, localizationService.Value, contentTypeService.Value, urlSegmentProviders), logger, "createdPackages.config"), + installedPackages: new PackagesRepository(contentService.Value, contentTypeService.Value, dataTypeService.Value, fileService.Value, macroService.Value, localizationService.Value, + new EntityXmlSerializer(contentService.Value, mediaService.Value, dataTypeService.Value, userService.Value, localizationService.Value, contentTypeService.Value, urlSegmentProviders), logger, "installedPackages.config"))); var relationService = GetLazyService(factory, c => new RelationService(scopeProvider, logger, eventMessagesFactory, entityService.Value, GetRepo(c), GetRepo(c))); var treeService = GetLazyService(factory, c => new ApplicationTreeService(logger, cache, typeLoader)); var tagService = GetLazyService(factory, c => new TagService(scopeProvider, logger, eventMessagesFactory, GetRepo(c))); diff --git a/src/Umbraco.Web/Editors/PackageController.cs b/src/Umbraco.Web/Editors/PackageController.cs index 86f3e48b7c..4b452ac6a6 100644 --- a/src/Umbraco.Web/Editors/PackageController.cs +++ b/src/Umbraco.Web/Editors/PackageController.cs @@ -72,7 +72,7 @@ namespace Umbraco.Web.Editors [HttpDelete] public IHttpActionResult DeleteCreatedPackage(int packageId) { - Services.PackagingService.DeleteCreatedPackage(packageId); + Services.PackagingService.DeleteCreatedPackage(packageId, Security.GetUserId().ResultOr(0)); return Ok(); } diff --git a/src/Umbraco.Web/Editors/PackageInstallController.cs b/src/Umbraco.Web/Editors/PackageInstallController.cs index 55e843aceb..73d50eb48a 100644 --- a/src/Umbraco.Web/Editors/PackageInstallController.cs +++ b/src/Umbraco.Web/Editors/PackageInstallController.cs @@ -8,6 +8,7 @@ using System.Net.Http; using System.Threading.Tasks; using System.Web.Http; using System.Xml; +using System.Xml.Linq; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; @@ -29,7 +30,6 @@ using Umbraco.Web.UI.JavaScript; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; using Umbraco.Web._Legacy.Packager; -using Umbraco.Web._Legacy.Packager.PackageInstance; using File = System.IO.File; using Notification = Umbraco.Web.Models.ContentEditing.Notification; using Version = System.Version; @@ -73,17 +73,17 @@ namespace Umbraco.Web.Editors { try { - var pack = InstalledPackage.GetById(packageId); - if (pack == null) return NotFound(); + var package = Services.PackagingService.GetInstalledPackageById(packageId); + if (package == null) return NotFound(); - PerformUninstall(pack); + PerformUninstall(package); //now get all other packages by this name since we'll uninstall all versions - foreach (var installed in InstalledPackage.GetAllInstalledPackages() - .Where(x => x.Data.Name == pack.Data.Name && x.Data.Id != pack.Data.Id)) + foreach (var installed in Services.PackagingService.GetAllInstalledPackages() + .Where(x => x.Name == package.Name && x.Id != package.Id)) { - //remove from teh xml - installed.Delete(Security.GetUserId().ResultOr(0)); + //remove from the xml + Services.PackagingService.DeleteInstalledPackage(installed.Id, Security.GetUserId().ResultOr(0)); } } catch (Exception ex) @@ -98,10 +98,10 @@ namespace Umbraco.Web.Editors /// /// SORRY :( I didn't have time to put this in a service somewhere - the old packager did this all manually too /// - /// - protected void PerformUninstall(InstalledPackage pack) + /// + protected void PerformUninstall(PackageDefinition package) { - if (pack == null) throw new ArgumentNullException("pack"); + if (package == null) throw new ArgumentNullException("package"); var removedTemplates = new List(); var removedMacros = new List(); @@ -111,7 +111,7 @@ namespace Umbraco.Web.Editors var removedFiles = new List(); //Uninstall templates - foreach (var item in pack.Data.Templates.ToArray()) + foreach (var item in package.Templates.ToArray()) { int nId; if (int.TryParse(item, out nId) == false) continue; @@ -121,11 +121,11 @@ namespace Umbraco.Web.Editors removedTemplates.Add(found); Services.FileService.DeleteTemplate(found.Alias, Security.GetUserId().ResultOr(0)); } - pack.Data.Templates.Remove(nId.ToString()); + package.Templates.Remove(nId.ToString()); } //Uninstall macros - foreach (var item in pack.Data.Macros.ToArray()) + foreach (var item in package.Macros.ToArray()) { int nId; if (int.TryParse(item, out nId) == false) continue; @@ -135,20 +135,20 @@ namespace Umbraco.Web.Editors removedMacros.Add(macro); Services.MacroService.Delete(macro); } - pack.Data.Macros.Remove(nId.ToString()); + package.Macros.Remove(nId.ToString()); } //Remove Document Types var contentTypes = new List(); var contentTypeService = Services.ContentTypeService; - foreach (var item in pack.Data.DocumentTypes.ToArray()) + foreach (var item in package.DocumentTypes.ToArray()) { int nId; if (int.TryParse(item, out nId) == false) continue; var contentType = contentTypeService.Get(nId); if (contentType == null) continue; contentTypes.Add(contentType); - pack.Data.DocumentTypes.Remove(nId.ToString(CultureInfo.InvariantCulture)); + package.DocumentTypes.Remove(nId.ToString(CultureInfo.InvariantCulture)); } //Order the DocumentTypes before removing them @@ -163,7 +163,7 @@ namespace Umbraco.Web.Editors } //Remove Dictionary items - foreach (var item in pack.Data.DictionaryItems.ToArray()) + foreach (var item in package.DictionaryItems.ToArray()) { int nId; if (int.TryParse(item, out nId) == false) continue; @@ -173,11 +173,11 @@ namespace Umbraco.Web.Editors removedDictionaryItems.Add(di); Services.LocalizationService.Delete(di); } - pack.Data.DictionaryItems.Remove(nId.ToString()); + package.DictionaryItems.Remove(nId.ToString()); } //Remove Data types - foreach (var item in pack.Data.DataTypes.ToArray()) + foreach (var item in package.DataTypes.ToArray()) { int nId; if (int.TryParse(item, out nId) == false) continue; @@ -187,28 +187,27 @@ namespace Umbraco.Web.Editors removedDataTypes.Add(dtd); Services.DataTypeService.Delete(dtd); } - pack.Data.DataTypes.Remove(nId.ToString()); + package.DataTypes.Remove(nId.ToString()); } - pack.Save(); + Services.PackagingService.SaveInstalledPackage(package); // uninstall actions //TODO: We should probably report errors to the UI!! // This never happened before though, but we should do something now - if (pack.Data.Actions.IsNullOrWhiteSpace() == false) + if (package.Actions.IsNullOrWhiteSpace() == false) { try { - var actionsXml = new XmlDocument(); - actionsXml.LoadXml("" + pack.Data.Actions + ""); + var actionsXml = XDocument.Parse("" + package.Actions + ""); - Logger.Debug("Executing undo actions: {UndoActionsXml}", actionsXml.OuterXml); + Logger.Debug("Executing undo actions: {UndoActionsXml}", actionsXml.ToString(SaveOptions.DisableFormatting)); - foreach (XmlNode n in actionsXml.DocumentElement.SelectNodes("//Action")) + foreach (var n in actionsXml.Root.Elements("Action")) { try { - _packageActionRunner.UndoPackageAction(pack.Data.Name, n.Attributes["alias"].Value, n); + _packageActionRunner.UndoPackageAction(package.Name, n.AttributeValue("alias"), n); } catch (Exception ex) { @@ -224,7 +223,7 @@ namespace Umbraco.Web.Editors //moved remove of files here so custom package actions can still undo //Remove files - foreach (var item in pack.Data.Files.ToArray()) + foreach (var item in package.Files.ToArray()) { removedFiles.Add(item.GetRelativePath()); @@ -240,15 +239,17 @@ namespace Umbraco.Web.Editors File.Delete(filePath); } - pack.Data.Files.Remove(file); + package.Files.Remove(file); } - pack.Save(); - pack.Delete(Security.GetUserId().ResultOr(0)); + + Services.PackagingService.SaveInstalledPackage(package); + + Services.PackagingService.DeleteInstalledPackage(package.Id, Security.GetUserId().ResultOr(0)); // create a summary of what was actually removed, for PackagingService.UninstalledPackage var summary = new UninstallationSummary { - MetaData = pack.GetMetaData(), + MetaData = package, TemplatesUninstalled = removedTemplates, MacrosUninstalled = removedMacros, ContentTypesUninstalled = removedContentTypes, @@ -259,7 +260,7 @@ namespace Umbraco.Web.Editors }; // trigger the UninstalledPackage event - PackagingService.OnUninstalledPackage(new UninstallPackageEventArgs(summary, false)); + PackagingService.OnUninstalledPackage(new UninstallPackageEventArgs(summary, package, false)); } @@ -269,18 +270,14 @@ namespace Umbraco.Web.Editors /// public IEnumerable GetInstalled() { - return InstalledPackage.GetAllInstalledPackages() + return Services.PackagingService.GetAllInstalledPackages() .GroupBy( //group by name - x => x.Data.Name, + x => x.Name, //select the package with a parsed version - pck => - { - Version pckVersion; - return Version.TryParse(pck.Data.Version, out pckVersion) - ? new { package = pck, version = pckVersion } - : new { package = pck, version = new Version(0, 0, 0) }; - }) + pck => Version.TryParse(pck.Version, out var pckVersion) + ? new { package = pck, version = pckVersion } + : new { package = pck, version = new Version(0, 0, 0) }) .Select(grouping => { //get the max version for the package @@ -290,15 +287,15 @@ namespace Umbraco.Web.Editors }) .Select(pack => new InstalledPackageModel { - Name = pack.Data.Name, - Id = pack.Data.Id, - Author = pack.Data.Author, - Version = pack.Data.Version, - Url = pack.Data.Url, - License = pack.Data.License, - LicenseUrl = pack.Data.LicenseUrl, - Files = pack.Data.Files, - IconUrl = pack.Data.IconUrl + Name = pack.Name, + Id = pack.Id, + Author = pack.Author, + Version = pack.Version, + Url = pack.Url, + License = pack.License, + LicenseUrl = pack.LicenseUrl, + Files = pack.Files, + IconUrl = pack.IconUrl }) .ToList(); } @@ -320,7 +317,6 @@ namespace Umbraco.Web.Editors model.ConflictingMacroAliases = ins.ConflictingMacroAliases; model.ConflictingStyleSheetNames = ins.ConflictingStyleSheetNames; model.ConflictingTemplateAliases = ins.ConflictingTemplateAliases; - model.ContainsBinaryFileErrors = ins.ContainsBinaryFileErrors; model.ContainsMacroConflict = ins.ContainsMacroConflict; model.ContainsStyleSheetConflicts = ins.ContainsStyleSheeConflicts; model.ContainsTemplateConflicts = ins.ContainsTemplateConflicts; @@ -346,19 +342,17 @@ namespace Umbraco.Web.Editors private bool ValidateInstalledInternal(string name, string version) { - var allInstalled = InstalledPackage.GetAllInstalledPackages(); + var allInstalled = Services.PackagingService.GetAllInstalledPackages(); var found = allInstalled.FirstOrDefault(x => { - if (x.Data.Name != name) return false; + if (x.Name != name) return false; //match the exact version - if (x.Data.Version == version) + if (x.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 (Version.TryParse(x.Version, out var installed) && Version.TryParse(version, out var selected)) { if (installed >= selected) return true; } @@ -456,13 +450,13 @@ namespace Umbraco.Web.Editors /// /// [HttpGet] - public LocalPackageInstallModel Fetch(string packageGuid) + public async Task Fetch(string packageGuid) { //Default path string path = Path.Combine("packages", packageGuid + ".umb"); if (File.Exists(IOHelper.MapPath(Path.Combine(SystemDirectories.Data, path))) == false) { - path = Services.PackagingService.FetchPackageFile(Guid.Parse(packageGuid), UmbracoVersion.Current, Security.GetUserId().ResultOr(0)); + path = await Services.PackagingService.FetchPackageFileAsync(Guid.Parse(packageGuid), UmbracoVersion.Current, Security.GetUserId().ResultOr(0)); } var model = new LocalPackageInstallModel @@ -495,7 +489,7 @@ namespace Umbraco.Web.Editors [HttpPost] public PackageInstallModel Import(PackageInstallModel model) { - var ins = new global::Umbraco.Web._Legacy.Packager.Installer(Security.CurrentUser.Id); + var ins = new Installer(Security.CurrentUser.Id); var tempPath = ins.Import(model.ZipFilePath); //now we need to check for version comparison @@ -510,7 +504,7 @@ namespace Umbraco.Web.Editors } model.TemporaryDirectoryPath = Path.Combine(SystemDirectories.Data, tempPath); - model.Id = ins.CreateManifest(IOHelper.MapPath(model.TemporaryDirectoryPath), model.PackageGuid, model.RepositoryGuid.ToString()); + model.Id = ins.CreateManifest(model.PackageGuid); return model; } @@ -523,7 +517,7 @@ namespace Umbraco.Web.Editors [HttpPost] public PackageInstallModel InstallFiles(PackageInstallModel model) { - var ins = new global::Umbraco.Web._Legacy.Packager.Installer(Security.CurrentUser.Id); + var ins = new Installer(Security.CurrentUser.Id); ins.LoadConfig(IOHelper.MapPath(model.TemporaryDirectoryPath)); ins.InstallFiles(model.Id, IOHelper.MapPath(model.TemporaryDirectoryPath)); diff --git a/src/Umbraco.Web/Install/Controllers/InstallPackageController.cs b/src/Umbraco.Web/Install/Controllers/InstallPackageController.cs index 55680084e5..ac859ee3aa 100644 --- a/src/Umbraco.Web/Install/Controllers/InstallPackageController.cs +++ b/src/Umbraco.Web/Install/Controllers/InstallPackageController.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Net; using System.Net.Http; using System.Text; +using System.Threading.Tasks; using System.Web; using System.Web.Http; using Newtonsoft.Json.Linq; @@ -10,6 +11,7 @@ using umbraco; using Umbraco.Core; using Umbraco.Web.Cache; using Umbraco.Core.Configuration; +using Umbraco.Core.Services; using Umbraco.Web.Composing; using Umbraco.Web.Install.Models; using Umbraco.Web.WebApi; @@ -28,8 +30,12 @@ namespace Umbraco.Web.Install.Controllers [Obsolete("This is only used for the legacy way of installing starter kits in the back office")] public class InstallPackageController : ApiController { - public InstallPackageController() - { } + private readonly IPackagingService _packagingService; + + public InstallPackageController(IPackagingService packagingService) + { + _packagingService = packagingService; + } private const string RepoGuid = "65194810-1f85-11dd-bd0b-0800200c9a66"; @@ -49,18 +55,18 @@ namespace Umbraco.Web.Install.Controllers /// /// [HttpPost] - public HttpResponseMessage DownloadPackageFiles(InstallPackageModel model) + public async Task DownloadPackageFiles(InstallPackageModel model) { - var packageFile = Current.Services.PackagingService.FetchPackageFile( + var packageFile = await _packagingService.FetchPackageFileAsync( model.KitGuid, UmbracoVersion.Current, UmbracoContext.Current.Security.CurrentUser.Id); - var installer = new global::Umbraco.Web._Legacy.Packager.Installer(UmbracoContext.Current.Security.CurrentUser.Id); + var installer = new _Legacy.Packager.Installer(UmbracoContext.Current.Security.CurrentUser.Id); var tempFile = installer.Import(packageFile); installer.LoadConfig(tempFile); - var pId = installer.CreateManifest(tempFile, model.KitGuid, RepoGuid); + var pId = installer.CreateManifest(model.KitGuid); return Json(new { success = true, diff --git a/src/Umbraco.Web/Install/InstallSteps/ConfigureMachineKey.cs b/src/Umbraco.Web/Install/InstallSteps/ConfigureMachineKey.cs index de53fd60cf..f71d486f5a 100644 --- a/src/Umbraco.Web/Install/InstallSteps/ConfigureMachineKey.cs +++ b/src/Umbraco.Web/Install/InstallSteps/ConfigureMachineKey.cs @@ -1,4 +1,5 @@ using System.Linq; +using System.Threading.Tasks; using System.Web.Configuration; using System.Xml.Linq; using Umbraco.Core.IO; @@ -30,7 +31,7 @@ namespace Umbraco.Web.Install.InstallSteps /// /// /// - public override InstallSetupResult Execute(bool? model) + public override Task ExecuteAsync(bool? model) { if (model.HasValue && model.Value == false) return null; @@ -50,7 +51,7 @@ namespace Umbraco.Web.Install.InstallSteps xml.Save(fileName, SaveOptions.DisableFormatting); - return null; + return Task.FromResult(null); } public override bool RequiresExecution(bool? model) diff --git a/src/Umbraco.Web/Install/InstallSteps/DatabaseConfigureStep.cs b/src/Umbraco.Web/Install/InstallSteps/DatabaseConfigureStep.cs index 2fe6c0ceda..9d1aec1c71 100644 --- a/src/Umbraco.Web/Install/InstallSteps/DatabaseConfigureStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/DatabaseConfigureStep.cs @@ -1,5 +1,6 @@ using System; using System.Configuration; +using System.Threading.Tasks; using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Core.Migrations.Install; @@ -20,7 +21,7 @@ namespace Umbraco.Web.Install.InstallSteps _databaseBuilder = databaseBuilder; } - public override InstallSetupResult Execute(DatabaseModel database) + public override Task ExecuteAsync(DatabaseModel database) { //if the database model is null then we will apply the defaults if (database == null) @@ -33,7 +34,7 @@ namespace Umbraco.Web.Install.InstallSteps throw new InstallException("Could not connect to the database"); } ConfigureConnection(database); - return null; + return Task.FromResult(null); } private void ConfigureConnection(DatabaseModel database) diff --git a/src/Umbraco.Web/Install/InstallSteps/DatabaseInstallStep.cs b/src/Umbraco.Web/Install/InstallSteps/DatabaseInstallStep.cs index a9daee6e95..812889b977 100644 --- a/src/Umbraco.Web/Install/InstallSteps/DatabaseInstallStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/DatabaseInstallStep.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Configuration; +using System.Threading.Tasks; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; @@ -24,7 +25,7 @@ namespace Umbraco.Web.Install.InstallSteps _logger = logger; } - public override InstallSetupResult Execute(object model) + public override Task ExecuteAsync(object model) { if (_runtime.Level == RuntimeLevel.Run) throw new Exception("Umbraco is already configured!"); @@ -43,10 +44,10 @@ namespace Umbraco.Web.Install.InstallSteps } //upgrade is required so set the flag for the next step - return new InstallSetupResult(new Dictionary + return Task.FromResult(new InstallSetupResult(new Dictionary { {"upgrade", true} - }); + })); } internal static void HandleConnectionStrings(ILogger logger) diff --git a/src/Umbraco.Web/Install/InstallSteps/DatabaseUpgradeStep.cs b/src/Umbraco.Web/Install/InstallSteps/DatabaseUpgradeStep.cs index 8283eb6bef..e8046bd196 100644 --- a/src/Umbraco.Web/Install/InstallSteps/DatabaseUpgradeStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/DatabaseUpgradeStep.cs @@ -1,6 +1,7 @@ using System; using System.Configuration; using System.Linq; +using System.Threading.Tasks; using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Core.Migrations.Install; @@ -23,7 +24,7 @@ namespace Umbraco.Web.Install.InstallSteps _logger = logger; } - public override InstallSetupResult Execute(object model) + public override Task ExecuteAsync(object model) { var installSteps = InstallStatusTracker.GetStatus().ToArray(); var previousStep = installSteps.Single(x => x.Name == "DatabaseInstall"); @@ -43,7 +44,7 @@ namespace Umbraco.Web.Install.InstallSteps DatabaseInstallStep.HandleConnectionStrings(_logger); } - return null; + return Task.FromResult(null); } public override bool RequiresExecution(object model) diff --git a/src/Umbraco.Web/Install/InstallSteps/FilePermissionsStep.cs b/src/Umbraco.Web/Install/InstallSteps/FilePermissionsStep.cs index 6d07190b4f..c5bc9ac047 100644 --- a/src/Umbraco.Web/Install/InstallSteps/FilePermissionsStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/FilePermissionsStep.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Threading.Tasks; using umbraco; using Umbraco.Core; using Umbraco.Core.IO; @@ -13,7 +14,7 @@ namespace Umbraco.Web.Install.InstallSteps PerformsAppRestart = true)] internal class FilePermissionsStep : InstallSetupStep { - public override InstallSetupResult Execute(object model) + public override Task ExecuteAsync(object model) { // validate file permissions Dictionary> report; @@ -22,7 +23,7 @@ namespace Umbraco.Web.Install.InstallSteps if (permissionsOk == false) throw new InstallException("Permission check failed", "permissionsreport", new { errors = report }); - return null; + return Task.FromResult(null); } public override bool RequiresExecution(object model) diff --git a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs index 64b8568fe5..9132f5cd49 100644 --- a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs @@ -4,6 +4,7 @@ using System.Configuration; using System.Net; using System.Net.Http; using System.Text; +using System.Threading.Tasks; using System.Web; using System.Web.Security; using Newtonsoft.Json; @@ -51,7 +52,7 @@ namespace Umbraco.Web.Install.InstallSteps } } - public override InstallSetupResult Execute(UserModel user) + public override Task ExecuteAsync(UserModel user) { var admin = _userService.GetUserById(Constants.Security.SuperUserId); if (admin == null) @@ -99,7 +100,7 @@ namespace Umbraco.Web.Install.InstallSteps catch { /* fail in silence */ } } - return null; + return Task.FromResult(null); } /// diff --git a/src/Umbraco.Web/Install/InstallSteps/SetUmbracoVersionStep.cs b/src/Umbraco.Web/Install/InstallSteps/SetUmbracoVersionStep.cs index 0cac3fe91f..3e836397fd 100644 --- a/src/Umbraco.Web/Install/InstallSteps/SetUmbracoVersionStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/SetUmbracoVersionStep.cs @@ -1,4 +1,5 @@ -using System.Web; +using System.Threading.Tasks; +using System.Web; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; @@ -31,7 +32,7 @@ namespace Umbraco.Web.Install.InstallSteps _distributedCache = distributedCache; } - public override InstallSetupResult Execute(object model) + public override Task ExecuteAsync(object model) { //During a new install we'll log the default user in (which is id = 0). // During an upgrade, the user will already need to be logged in in order to run the installer. @@ -56,7 +57,7 @@ namespace Umbraco.Web.Install.InstallSteps //reports the ended install _installHelper.InstallStatus(true, ""); - return null; + return Task.FromResult(null); } public override bool RequiresExecution(object model) diff --git a/src/Umbraco.Web/Install/InstallSteps/StarterKitCleanupStep.cs b/src/Umbraco.Web/Install/InstallSteps/StarterKitCleanupStep.cs index 9345a0fc96..075a61ca95 100644 --- a/src/Umbraco.Web/Install/InstallSteps/StarterKitCleanupStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/StarterKitCleanupStep.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Threading.Tasks; using System.Web; using Umbraco.Web.Install.Models; using Umbraco.Web._Legacy.Packager; @@ -10,7 +11,7 @@ namespace Umbraco.Web.Install.InstallSteps "StarterKitCleanup", 32, "Almost done")] internal class StarterKitCleanupStep : InstallSetupStep { - public override InstallSetupResult Execute(object model) + public override Task ExecuteAsync(object model) { var installSteps = InstallStatusTracker.GetStatus().ToArray(); var previousStep = installSteps.Single(x => x.Name == "StarterKitDownload"); @@ -19,7 +20,7 @@ namespace Umbraco.Web.Install.InstallSteps CleanupInstallation(manifestId, packageFile); - return null; + return Task.FromResult(null); } private void CleanupInstallation(int manifestId, string packageFile) diff --git a/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs b/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs index c79be96a93..66d1b0a20c 100644 --- a/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs @@ -1,14 +1,13 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using System.Web; using Umbraco.Core.Services; using Umbraco.Core.Configuration; using Umbraco.Web.Composing; using Umbraco.Web.Install.Models; -using Umbraco.Web.Security; using Umbraco.Web._Legacy.Packager; -using Umbraco.Web._Legacy.Packager.PackageInstance; namespace Umbraco.Web.Install.InstallSteps { @@ -20,17 +19,19 @@ namespace Umbraco.Web.Install.InstallSteps private readonly InstallHelper _installHelper; private readonly UmbracoContext _umbracoContext; private readonly IContentService _contentService; + private readonly IPackagingService _packageService; - public StarterKitDownloadStep(IContentService contentService, InstallHelper installHelper, UmbracoContext umbracoContext) + public StarterKitDownloadStep(IContentService contentService, IPackagingService packageService, InstallHelper installHelper, UmbracoContext umbracoContext) { _installHelper = installHelper; _umbracoContext = umbracoContext; _contentService = contentService; + _packageService = packageService; } private const string RepoGuid = "65194810-1f85-11dd-bd0b-0800200c9a66"; - public override InstallSetupResult Execute(Guid? starterKitId) + public override async Task ExecuteAsync(Guid? starterKitId) { //if there is no value assigned then use the default starter kit if (starterKitId.HasValue == false) @@ -48,7 +49,7 @@ namespace Umbraco.Web.Install.InstallSteps return null; } - var result = DownloadPackageFiles(starterKitId.Value); + var result = await DownloadPackageFilesAsync(starterKitId.Value); Current.RestartAppPool(); @@ -59,16 +60,16 @@ namespace Umbraco.Web.Install.InstallSteps }); } - private Tuple DownloadPackageFiles(Guid kitGuid) + private async Task> DownloadPackageFilesAsync(Guid kitGuid) { var installer = new Installer(); //Go get the package file from the package repo - var packageFile = Current.Services.PackagingService.FetchPackageFile(kitGuid, UmbracoVersion.Current, _umbracoContext.Security.GetUserId().ResultOr(0)); + var packageFile = await _packageService.FetchPackageFileAsync(kitGuid, UmbracoVersion.Current, _umbracoContext.Security.GetUserId().ResultOr(0)); var tempFile = installer.Import(packageFile); installer.LoadConfig(tempFile); - var pId = installer.CreateManifest(tempFile, kitGuid, RepoGuid); + var pId = installer.CreateManifest(kitGuid); InstallPackageFiles(pId, tempFile); @@ -84,10 +85,7 @@ namespace Umbraco.Web.Install.InstallSteps } - public override string View - { - get { return (InstalledPackage.GetAllInstalledPackages().Count > 0) ? string.Empty : base.View; } - } + public override string View => _packageService.GetAllInstalledPackages().Any() ? string.Empty : base.View; public override bool RequiresExecution(Guid? model) { @@ -97,7 +95,7 @@ namespace Umbraco.Web.Install.InstallSteps return false; } - if (InstalledPackage.GetAllInstalledPackages().Count > 0) + if (_packageService.GetAllInstalledPackages().Any()) return false; if (_contentService.GetRootContent().Any()) diff --git a/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs b/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs index 8bec4ca199..0e189c5a6d 100644 --- a/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Threading.Tasks; using System.Web; using Umbraco.Web.Composing; using Umbraco.Web.Install.Models; @@ -20,7 +21,7 @@ namespace Umbraco.Web.Install.InstallSteps } - public override InstallSetupResult Execute(object model) + public override Task ExecuteAsync(object model) { var installSteps = InstallStatusTracker.GetStatus().ToArray(); var previousStep = installSteps.Single(x => x.Name == "StarterKitDownload"); @@ -31,7 +32,7 @@ namespace Umbraco.Web.Install.InstallSteps Current.RestartAppPool(_httContext); - return null; + return Task.FromResult(null); } private void InstallBusinessLogic(int manifestId, string packageFile) diff --git a/src/Umbraco.Web/Install/InstallSteps/UpgradeStep.cs b/src/Umbraco.Web/Install/InstallSteps/UpgradeStep.cs index 7ed3de6471..5f44555092 100644 --- a/src/Umbraco.Web/Install/InstallSteps/UpgradeStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/UpgradeStep.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Web.Install.Models; @@ -13,7 +14,7 @@ namespace Umbraco.Web.Install.InstallSteps { public override bool RequiresExecution(object model) => true; - public override InstallSetupResult Execute(object model) => null; + public override Task ExecuteAsync(object model) => Task.FromResult(null); public override object ViewModel { diff --git a/src/Umbraco.Web/Install/Models/InstallSetupStep.cs b/src/Umbraco.Web/Install/Models/InstallSetupStep.cs index 36cf838a82..3b017368f9 100644 --- a/src/Umbraco.Web/Install/Models/InstallSetupStep.cs +++ b/src/Umbraco.Web/Install/Models/InstallSetupStep.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.Serialization; +using System.Threading.Tasks; using Umbraco.Core; namespace Umbraco.Web.Install.Models @@ -14,17 +15,14 @@ namespace Umbraco.Web.Install.Models /// Defines the step model type on the server side so we can bind it /// [IgnoreDataMember] - public override Type StepType - { - get { return typeof(T); } - } + public override Type StepType => typeof(T); /// /// The step execution method /// /// /// - public abstract InstallSetupResult Execute(T model); + public abstract Task ExecuteAsync(T model); /// /// Determines if this step needs to execute based on the current state of the application and/or install process @@ -86,10 +84,6 @@ namespace Umbraco.Web.Install.Models public abstract Type StepType { get; } [IgnoreDataMember] - public bool HasUIElement - { - get { return View.IsNullOrWhiteSpace() == false; } - } - + public bool HasUIElement => View.IsNullOrWhiteSpace() == false; } } diff --git a/src/Umbraco.Web/Models/LocalPackageInstallModel.cs b/src/Umbraco.Web/Models/LocalPackageInstallModel.cs index 06216597cb..64ce51f539 100644 --- a/src/Umbraco.Web/Models/LocalPackageInstallModel.cs +++ b/src/Umbraco.Web/Models/LocalPackageInstallModel.cs @@ -55,9 +55,6 @@ namespace Umbraco.Web.Models [DataMember(Name = "containsMacroConflict")] public bool ContainsMacroConflict { get; set; } - [DataMember(Name = "containsBinaryFileErrors")] - public bool ContainsBinaryFileErrors { get; set; } - [DataMember(Name = "conflictingTemplateAliases")] public IDictionary ConflictingTemplateAliases { get; set; } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 3d11684c8a..1bd2860781 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -1134,14 +1134,9 @@ - - - - - diff --git a/src/Umbraco.Web/_Legacy/PackageActions/PackageHelper.cs b/src/Umbraco.Web/_Legacy/PackageActions/PackageHelper.cs deleted file mode 100644 index 55171c2643..0000000000 --- a/src/Umbraco.Web/_Legacy/PackageActions/PackageHelper.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; -using System.Xml; -using Umbraco.Core; -using Umbraco.Core.IO; -using Umbraco.Core.Models; -using Umbraco.Core.Xml; - -namespace Umbraco.Web._Legacy.PackageActions -{ - internal class PackageHelper - { - //Helper method to replace umbraco tags that breaks the xml format.. - public static string ParseToValidXml(ITemplate templateObj, ref bool hasAspNetContentBeginning, string template, bool toValid) - { - string retVal = template; - if (toValid) - { - // test for asp:content as the first part of the design - if (retVal.StartsWith("") + 1); - retVal = retVal.Substring(0, retVal.Length - 14); - } - //shorten empty macro tags.. - retVal = retVal.Replace(">", " />"); - retVal = retVal.Replace(">", " />"); - - retVal = retVal.Replace("", ""); - retVal = retVal.Replace("", ""); - retVal = retVal.Replace("", ""); - - // add asp content element - if (hasAspNetContentBeginning) - { - retVal = MasterPageHelper.GetMasterContentElement(templateObj/*templateObj.MasterTemplate*/) + retVal + ""; - } - } - - return retVal; - } - - public static XmlNode ParseStringToXmlNode(string value) - { - XmlDocument doc = new XmlDocument(); - XmlNode node = XmlHelper.AddTextNode(doc, "error", ""); - - try - { - doc.LoadXml(value); - return doc.SelectSingleNode("."); - } - catch - { - return node; - } - - } - } -} diff --git a/src/Umbraco.Web/_Legacy/PackageActions/addApplication.cs b/src/Umbraco.Web/_Legacy/PackageActions/addApplication.cs index 09c6df23c4..75614f68aa 100644 --- a/src/Umbraco.Web/_Legacy/PackageActions/addApplication.cs +++ b/src/Umbraco.Web/_Legacy/PackageActions/addApplication.cs @@ -1,5 +1,6 @@ using System; using System.Xml; +using System.Xml.Linq; using Umbraco.Core; using Umbraco.Core._Legacy.PackageActions; using Umbraco.Web.Composing; @@ -24,20 +25,20 @@ namespace Umbraco.Web._Legacy.PackageActions /// /// /// true if successfull - public bool Execute(string packageName, XmlNode xmlData) + public bool Execute(string packageName, XElement xmlData) { - string name = xmlData.Attributes["appName"].Value; - string alias = xmlData.Attributes["appAlias"].Value; - string icon = xmlData.Attributes["appIcon"].Value; + string name = xmlData.AttributeValue("appName"); + string alias = xmlData.AttributeValue("appAlias"); + string icon = xmlData.AttributeValue("appIcon"); Current.Services.SectionService.MakeNew(name, alias, icon); return true; } - public bool Undo(string packageName, XmlNode xmlData) + public bool Undo(string packageName, XElement xmlData) { - string alias = xmlData.Attributes["appAlias"].Value; + string alias = xmlData.AttributeValue("appAlias"); var section = Current.Services.SectionService.GetByAlias(alias); if (section != null) { @@ -55,11 +56,6 @@ namespace Umbraco.Web._Legacy.PackageActions } #endregion - - public XmlNode SampleXml() - { - throw new NotImplementedException(); - } - + } } diff --git a/src/Umbraco.Web/_Legacy/PackageActions/addDashboardSection.cs b/src/Umbraco.Web/_Legacy/PackageActions/addDashboardSection.cs index 9b3bc5829b..1e2e396d2c 100644 --- a/src/Umbraco.Web/_Legacy/PackageActions/addDashboardSection.cs +++ b/src/Umbraco.Web/_Legacy/PackageActions/addDashboardSection.cs @@ -1,5 +1,8 @@ using System; +using System.Linq; using System.Xml; +using System.Xml.Linq; +using System.Xml.XPath; using Umbraco.Core; using Umbraco.Core.IO; using Umbraco.Core.Xml; @@ -39,30 +42,23 @@ namespace Umbraco.Web._Legacy.PackageActions /// /// /// - public bool Execute(string packageName, XmlNode xmlData) + public bool Execute(string packageName, XElement xmlData) { //this will need a complete section node to work... - if (xmlData.HasChildNodes) + if (xmlData.HasElements) { - string sectionAlias = xmlData.Attributes["dashboardAlias"].Value; + string sectionAlias = xmlData.AttributeValue("dashboardAlias"); string dbConfig = SystemFiles.DashboardConfig; - XmlNode section = xmlData.SelectSingleNode("./section"); - XmlDocument dashboardFile = XmlHelper.OpenAsXmlDocument(dbConfig); + var section = xmlData.Element("section"); + var dashboardFile = XDocument.Load(IOHelper.MapPath(dbConfig)); //don't continue if it already exists - var found = dashboardFile.SelectNodes("//section[@alias='" + sectionAlias + "']"); - if (found == null || found.Count <= 0) + var found = dashboardFile.XPathSelectElements("//section[@alias='" + sectionAlias + "']"); + if (!found.Any()) { - - XmlNode importedSection = dashboardFile.ImportNode(section, true); - - XmlAttribute alias = XmlHelper.AddAttribute(dashboardFile, "alias", sectionAlias); - importedSection.Attributes.Append(alias); - - dashboardFile.DocumentElement.AppendChild(importedSection); - + dashboardFile.Root.Add(section); dashboardFile.Save(IOHelper.MapPath(dbConfig)); } @@ -78,19 +74,18 @@ namespace Umbraco.Web._Legacy.PackageActions return "addDashboardSection"; } - public bool Undo(string packageName, XmlNode xmlData) + public bool Undo(string packageName, XElement xmlData) { - string sectionAlias = xmlData.Attributes["dashboardAlias"].Value; + string sectionAlias = xmlData.AttributeValue("dashboardAlias"); string dbConfig = SystemFiles.DashboardConfig; - XmlDocument dashboardFile = XmlHelper.OpenAsXmlDocument(dbConfig); + var dashboardFile = XDocument.Load(IOHelper.MapPath(dbConfig)); - XmlNode section = dashboardFile.SelectSingleNode("//section [@alias = '" + sectionAlias + "']"); + var section = dashboardFile.XPathSelectElement("//section [@alias = '" + sectionAlias + "']"); if (section != null) { - - dashboardFile.SelectSingleNode("/dashBoard").RemoveChild(section); + section.Remove(); dashboardFile.Save(IOHelper.MapPath(dbConfig)); } @@ -98,11 +93,6 @@ namespace Umbraco.Web._Legacy.PackageActions } #endregion - - public XmlNode SampleXml() - { - throw new NotImplementedException(); - } - + } } diff --git a/src/Umbraco.Web/_Legacy/PackageActions/addProxyFeedHost.cs b/src/Umbraco.Web/_Legacy/PackageActions/addProxyFeedHost.cs index ac02bc0c33..bcd7d7c52f 100644 --- a/src/Umbraco.Web/_Legacy/PackageActions/addProxyFeedHost.cs +++ b/src/Umbraco.Web/_Legacy/PackageActions/addProxyFeedHost.cs @@ -1,4 +1,5 @@ using System.Xml; +using System.Xml.Linq; using Umbraco.Core; using Umbraco.Core.IO; using Umbraco.Core.Xml; @@ -10,42 +11,32 @@ namespace Umbraco.Web._Legacy.PackageActions { #region IPackageAction Members - public bool Execute(string packageName, XmlNode xmlData) + public bool Execute(string packageName, XElement xmlData) { - var hostname = xmlData.Attributes["host"].Value; + var hostname = xmlData.AttributeValue("host"); if (string.IsNullOrEmpty(hostname)) return false; - var xdoc = XmlHelper.OpenAsXmlDocument(SystemFiles.FeedProxyConfig); + var xdoc = XDocument.Load(IOHelper.MapPath(SystemFiles.FeedProxyConfig)); - xdoc.PreserveWhitespace = true; + var insert = true; - var xn = xdoc.SelectSingleNode("//feedProxy"); - if (xn != null) + if (xdoc.Root.HasElements) { - var insert = true; - - if (xn.HasChildNodes) + foreach (var node in xdoc.Root.Elements("allow")) { - foreach (XmlNode node in xn.SelectNodes("//allow")) - { - if (node.Attributes["host"] != null && node.Attributes["host"].Value == hostname) - insert = false; - } - } - - if (insert) - { - var newHostname = XmlHelper.AddTextNode(xdoc, "allow", string.Empty); - newHostname.Attributes.Append(XmlHelper.AddAttribute(xdoc, "host", hostname)); - xn.AppendChild(newHostname); - - xdoc.Save(IOHelper.MapPath(SystemFiles.FeedProxyConfig)); - - return true; + if (node.AttributeValue("host") != null && node.AttributeValue("host") == hostname) + insert = false; } } + if (insert) + { + xdoc.Root.Add(new XElement("allow", new XAttribute("host", hostname))); + xdoc.Save(IOHelper.MapPath(SystemFiles.FeedProxyConfig)); + + return true; + } return false; } @@ -54,47 +45,37 @@ namespace Umbraco.Web._Legacy.PackageActions return "addProxyFeedHost"; } - public bool Undo(string packageName, XmlNode xmlData) + public bool Undo(string packageName, XElement xmlData) { - var hostname = xmlData.Attributes["host"].Value; + var hostname = xmlData.AttributeValue("host"); if (string.IsNullOrEmpty(hostname)) return false; - var xdoc = XmlHelper.OpenAsXmlDocument(SystemFiles.FeedProxyConfig); - xdoc.PreserveWhitespace = true; + var xdoc = XDocument.Load(IOHelper.MapPath(SystemFiles.FeedProxyConfig)); - var xn = xdoc.SelectSingleNode("//feedProxy"); - if (xn != null) + bool inserted = false; + if (xdoc.Root.HasElements) { - bool inserted = false; - if (xn.HasChildNodes) + foreach (var node in xdoc.Root.Elements("allow")) { - foreach (XmlNode node in xn.SelectNodes("//allow")) + if (node.AttributeValue("host") != null && node.AttributeValue("host") == hostname) { - if (node.Attributes["host"] != null && node.Attributes["host"].Value == hostname) - { - xn.RemoveChild(node); - inserted = true; - } + node.Remove(); + inserted = true; } } + } - if (inserted) - { - xdoc.Save(IOHelper.MapPath(SystemFiles.FeedProxyConfig)); - return true; - } + if (inserted) + { + xdoc.Save(IOHelper.MapPath(SystemFiles.FeedProxyConfig)); + return true; } return false; } #endregion - - public XmlNode SampleXml() - { - string sample = ""; - return PackageHelper.ParseStringToXmlNode(sample); - } + } } diff --git a/src/Umbraco.Web/_Legacy/PackageActions/addStringToHtmlElement.cs b/src/Umbraco.Web/_Legacy/PackageActions/addStringToHtmlElement.cs deleted file mode 100644 index 56ebb76980..0000000000 --- a/src/Umbraco.Web/_Legacy/PackageActions/addStringToHtmlElement.cs +++ /dev/null @@ -1,202 +0,0 @@ -using System; -using System.Xml; -using Umbraco.Core; -using Umbraco.Core.Configuration; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; -using Umbraco.Core.Models; -using Umbraco.Core.Xml; -using Umbraco.Core._Legacy.PackageActions; -using Umbraco.Web.Composing; - -namespace Umbraco.Web._Legacy.PackageActions -{ - /// - /// This class implements the IPackageAction Interface, used to execute code when packages are installed. - /// All IPackageActions only takes a PackageName and a XmlNode as input, and executes based on the data in the xmlnode. - /// addStringToHtmlElement adds a string to specific HTML element in a specific template, and can either append or prepend it. - /// It uses the action xml node to do this, exemple action xml node: - /// - /// The above will add the string "hello world!" to the first html element with the id "newsSection" in the template "news" - /// - public class addStringToHtmlElement : IPackageAction - { - #region IPackageAction Members - - /// - /// Executes the specified package action. - /// - /// Name of the package. - /// The XML data. - /// - /// - /// - /// True if executed successfully - public bool Execute(string packageName, XmlNode xmlData) - { - - - string templateAlias = xmlData.Attributes["templateAlias"].Value; - string htmlElementId = xmlData.Attributes["htmlElementId"].Value; - string position = xmlData.Attributes["position"].Value; - string value = XmlHelper.GetNodeValue(xmlData); - var tmp = Current.Services.FileService.GetTemplate(templateAlias); - - value = MasterPageHelper.EnsureMasterPageSyntax(templateAlias, value); - - _addStringToHtmlElement(tmp, value, htmlElementId, position); - - return true; - } - - - /// - /// Undoes the addStringToHtml Execute() method, by removing the same string from the same template. - /// - /// Name of the package. - /// The XML data. - /// - public bool Undo(string packageName, XmlNode xmlData) - { - string templateAlias = xmlData.Attributes["templateAlias"].Value; - string htmlElementId = xmlData.Attributes["htmlElementId"].Value; - string value = XmlHelper.GetNodeValue(xmlData); - var tmp = Current.Services.FileService.GetTemplate(templateAlias); - - value = MasterPageHelper.EnsureMasterPageSyntax(templateAlias, value); - - _removeStringFromHtmlElement(tmp, value, htmlElementId); - return true; - } - - /// - /// Action alias. - /// - /// - public string Alias() - { - return "addStringToHtmlElement"; - } - - private void _addStringToHtmlElement(ITemplate tmp, string value, string htmlElementId, string position) - { - bool hasAspNetContentBeginning = false; - string design = ""; - string directive = ""; - - if (tmp != null) - { - try - { - XmlDocument templateXml = new XmlDocument(); - templateXml.PreserveWhitespace = true; - - //Make sure that directive is remove before hacked non html4 compatiple replacement action... - design = tmp.Content; - - - splitDesignAndDirective(ref design, ref directive); - - //making sure that the template xml has a root node... - if (string.IsNullOrWhiteSpace(tmp.MasterTemplateAlias) == false) - templateXml.LoadXml(PackageHelper.ParseToValidXml(tmp, ref hasAspNetContentBeginning, "" + design + "", true)); - else - templateXml.LoadXml(PackageHelper.ParseToValidXml(tmp, ref hasAspNetContentBeginning, design, true)); - - XmlNode xmlElement = templateXml.SelectSingleNode("//* [@id = '" + htmlElementId + "']"); - - if (xmlElement != null) - { - - if (position == "beginning") - { - xmlElement.InnerXml = "\n" + PackageHelper.ParseToValidXml(tmp, ref hasAspNetContentBeginning, value, true) + "\n" + xmlElement.InnerXml; - } - else - { - xmlElement.InnerXml = xmlElement.InnerXml + "\n" + PackageHelper.ParseToValidXml(tmp, ref hasAspNetContentBeginning, value, true) + "\n"; - } - } - - tmp.Content = directive + "\n" + PackageHelper.ParseToValidXml(tmp, ref hasAspNetContentBeginning, templateXml.OuterXml, false); - Current.Services.FileService.SaveTemplate(tmp); - } - catch (Exception ex) - { - Current.Logger.Error(ex, "An error occurred"); - } - } - else - { - Current.Logger.Debug("template not found"); - } - } - - private void _removeStringFromHtmlElement(ITemplate tmp, string value, string htmlElementId) - { - bool hasAspNetContentBeginning = false; - string design = ""; - string directive = ""; - - - if (tmp != null) - { - try - { - XmlDocument templateXml = new XmlDocument(); - templateXml.PreserveWhitespace = true; - - //Make sure that directive is remove before hacked non html4 compatiple replacement action... - design = tmp.Content; - splitDesignAndDirective(ref design, ref directive); - - //making sure that the template xml has a root node... - if (string.IsNullOrWhiteSpace(tmp.MasterTemplateAlias) == false) - templateXml.LoadXml(PackageHelper.ParseToValidXml(tmp, ref hasAspNetContentBeginning, "" + design + "", true)); - else - templateXml.LoadXml(PackageHelper.ParseToValidXml(tmp, ref hasAspNetContentBeginning, design, true)); - - XmlNode xmlElement = templateXml.SelectSingleNode("//* [@id = '" + htmlElementId + "']"); - - - - if (xmlElement != null) - { - string repValue = PackageHelper.ParseToValidXml(tmp, ref hasAspNetContentBeginning, value, true); - xmlElement.InnerXml = xmlElement.InnerXml.Replace(repValue, ""); - } - - tmp.Content = directive + "\n" + PackageHelper.ParseToValidXml(tmp, ref hasAspNetContentBeginning, templateXml.OuterXml, false); - Current.Services.FileService.SaveTemplate(tmp); - } - catch (Exception ex) - { - Current.Logger.Error(ex, "An error occurred"); - } - } - else - { - Current.Logger.Debug("template not found"); - } - } - - - - private void splitDesignAndDirective(ref string design, ref string directive) - { - if (design.StartsWith("<%@")) - { - directive = design.Substring(0, design.IndexOf("%>") + 2).Trim(Environment.NewLine.ToCharArray()); - design = design.Substring(design.IndexOf("%>") + 3).Trim(Environment.NewLine.ToCharArray()); - } - } - - #endregion - - public XmlNode SampleXml() - { - throw new NotImplementedException(); - } - - } -} diff --git a/src/Umbraco.Web/_Legacy/PackageActions/allowDoctype.cs b/src/Umbraco.Web/_Legacy/PackageActions/allowDoctype.cs index 146925b85f..2ed16e3842 100644 --- a/src/Umbraco.Web/_Legacy/PackageActions/allowDoctype.cs +++ b/src/Umbraco.Web/_Legacy/PackageActions/allowDoctype.cs @@ -2,6 +2,8 @@ using System.Collections; using System.Linq; using System.Xml; +using System.Xml.Linq; +using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core._Legacy.PackageActions; using Umbraco.Web.Composing; @@ -26,10 +28,10 @@ namespace Umbraco.Web._Legacy.PackageActions /// /// /// Returns true on success - public bool Execute(string packageName, XmlNode xmlData) + public bool Execute(string packageName, XElement xmlData) { - string doctypeName = xmlData.Attributes["documentTypeAlias"].Value; - string parentDoctypeName = xmlData.Attributes["parentDocumentTypeAlias"].Value; + string doctypeName = xmlData.AttributeValue("documentTypeAlias"); + string parentDoctypeName = xmlData.AttributeValue("parentDocumentTypeAlias"); //global::umbraco.cms.businesslogic.ContentType ct = global::umbraco.cms.businesslogic.ContentType.GetByAlias(doctypeName); //global::umbraco.cms.businesslogic.ContentType parentct = global::umbraco.cms.businesslogic.ContentType.GetByAlias(parentDoctypeName); @@ -73,7 +75,7 @@ namespace Umbraco.Web._Legacy.PackageActions /// Name of the package. /// The XML data. /// - public bool Undo(string packageName, XmlNode xmlData) + public bool Undo(string packageName, XElement xmlData) { return true; } @@ -88,11 +90,6 @@ namespace Umbraco.Web._Legacy.PackageActions } #endregion - - public XmlNode SampleXml() - { - throw new NotImplementedException(); - } - + } } diff --git a/src/Umbraco.Web/_Legacy/PackageActions/publishRootDocument.cs b/src/Umbraco.Web/_Legacy/PackageActions/publishRootDocument.cs index 32147342f6..f4fe17e999 100644 --- a/src/Umbraco.Web/_Legacy/PackageActions/publishRootDocument.cs +++ b/src/Umbraco.Web/_Legacy/PackageActions/publishRootDocument.cs @@ -1,5 +1,6 @@ using System; using System.Xml; +using System.Xml.Linq; using Umbraco.Core; using Umbraco.Core._Legacy.PackageActions; using Umbraco.Web.Composing; @@ -23,17 +24,17 @@ namespace Umbraco.Web._Legacy.PackageActions /// /// /// True if executed succesfully - public bool Execute(string packageName, XmlNode xmlData) + public bool Execute(string packageName, XElement xmlData) { - string documentName = xmlData.Attributes["documentName"].Value; + string documentName = xmlData.AttributeValue("documentName"); //global::umbraco.cms.businesslogic.web.Document[] rootDocs = global::umbraco.cms.businesslogic.web.Document.GetRootDocuments(); var rootDocs = Current.Services.ContentService.GetRootContent(); foreach (var rootDoc in rootDocs) { - if (rootDoc.Name.Trim() == documentName.Trim() && rootDoc != null && rootDoc.ContentType != null) + if (rootDoc.Name.Trim() == documentName.Trim() && rootDoc.ContentType != null) { // fixme variants? Current.Services.ContentService.SaveAndPublishBranch(rootDoc, true); @@ -50,7 +51,7 @@ namespace Umbraco.Web._Legacy.PackageActions /// Name of the package. /// The XML data. /// - public bool Undo(string packageName, XmlNode xmlData) + public bool Undo(string packageName, XElement xmlData) { return true; } @@ -64,11 +65,6 @@ namespace Umbraco.Web._Legacy.PackageActions return "publishRootDocument"; } #endregion - - public XmlNode SampleXml() - { - throw new NotImplementedException(); - } - + } } diff --git a/src/Umbraco.Web/_Legacy/PackageActions/removeStringFromTemplate.cs b/src/Umbraco.Web/_Legacy/PackageActions/removeStringFromTemplate.cs deleted file mode 100644 index fd2f40178b..0000000000 --- a/src/Umbraco.Web/_Legacy/PackageActions/removeStringFromTemplate.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Xml; -using Umbraco.Core._Legacy.PackageActions; - -namespace Umbraco.Web._Legacy.PackageActions -{ - public class removeStringFromTemplate : IPackageAction - { - #region IPackageAction Members - - public bool Execute(string packageName, XmlNode xmlData) - { - addStringToHtmlElement ast = new addStringToHtmlElement(); - return ast.Undo(packageName, xmlData); - } - - public string Alias() - { - return "removeStringFromHtmlElement"; - } - - public bool Undo(string packageName, XmlNode xmlData) - { - return true; - } - - public XmlNode SampleXml() - { - throw new NotImplementedException(); - } - - #endregion - } -} diff --git a/src/Umbraco.Web/_Legacy/Packager/Installer.cs b/src/Umbraco.Web/_Legacy/Packager/Installer.cs index 15a7839227..286f41f61a 100644 --- a/src/Umbraco.Web/_Legacy/Packager/Installer.cs +++ b/src/Umbraco.Web/_Legacy/Packager/Installer.cs @@ -6,6 +6,8 @@ using System.IO; using System.Linq; using System.Net; using System.Xml; +using System.Xml.Linq; +using System.Xml.XPath; using ICSharpCode.SharpZipLib.Zip; using Umbraco.Core; using Umbraco.Core.Composing; @@ -16,8 +18,6 @@ using Umbraco.Core.Models; using Umbraco.Core.Models.Packaging; using Umbraco.Core.Packaging; using Umbraco.Core.Services.Implement; -using Umbraco.Core.Xml; -using Umbraco.Web._Legacy.Packager.PackageInstance; using File = System.IO.File; namespace Umbraco.Web._Legacy.Packager @@ -66,16 +66,6 @@ namespace Umbraco.Web._Legacy.Packager public bool ContainsTemplateConflicts { get; private set; } public IDictionary ConflictingTemplateAliases => _conflictingTemplateAliases; - /// - /// Indicates that the package contains assembly reference errors - /// - public bool ContainsBinaryFileErrors { get; private set; } - - /// - /// List each assembly reference error - /// - public List BinaryFileErrors { get; } = new List(); - public bool ContainsStyleSheeConflicts { get; private set; } public IDictionary ConflictingStyleSheetNames => _conflictingStyleSheetNames; @@ -88,9 +78,9 @@ namespace Umbraco.Web._Legacy.Packager public string IconUrl { get; private set; } /// - /// The xmldocument, describing the contents of a package. + /// The xml of the compiled package /// - public XmlDocument Config { get; private set; } + public XDocument Config { get; private set; } /// /// Constructor @@ -108,7 +98,6 @@ namespace Umbraco.Web._Legacy.Packager private void Initialize() { - ContainsBinaryFileErrors = false; ContainsTemplateConflicts = false; ContainsUnsecureFiles = false; ContainsMacroConflict = false; @@ -136,7 +125,6 @@ namespace Umbraco.Web._Legacy.Packager /// public Installer(string name, string version, string url, string license, string licenseUrl, string author, string authorUrl, int requirementsMajor, int requirementsMinor, int requirementsPatch, string readme, string control, RequirementsType requirementsType, string iconUrl) { - ContainsBinaryFileErrors = false; ContainsTemplateConflicts = false; ContainsUnsecureFiles = false; ContainsMacroConflict = false; @@ -209,54 +197,46 @@ namespace Umbraco.Web._Legacy.Packager return Import(inputFile, true); } - public int CreateManifest(string tempDir, Guid guid, string repoGuid) + public int CreateManifest(Guid guid) { //This is the new improved install rutine, which chops up the process into 3 steps, creating the manifest, moving files, and finally handling umb objects - var packName = XmlHelper.GetNodeValue(Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/name")); - var packAuthor = XmlHelper.GetNodeValue(Config.DocumentElement.SelectSingleNode("/umbPackage/info/author/name")); - var packAuthorUrl = XmlHelper.GetNodeValue(Config.DocumentElement.SelectSingleNode("/umbPackage/info/author/website")); - var packVersion = XmlHelper.GetNodeValue(Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/version")); - var packReadme = XmlHelper.GetNodeValue(Config.DocumentElement.SelectSingleNode("/umbPackage/info/readme")); - var packLicense = XmlHelper.GetNodeValue(Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/license ")); - var packUrl = XmlHelper.GetNodeValue(Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/url ")); - var iconUrl = XmlHelper.GetNodeValue(Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/iconUrl")); - var enableSkins = false; - var skinRepoGuid = ""; + var parser = new CompiledPackageXmlParser(); + var def = parser.ToCompiledPackage(Config); - if (Config.DocumentElement.SelectSingleNode("/umbPackage/enableSkins") != null) + //create a new entry in the installedPackages.config + var installedPackage = new PackageDefinition { - var skinNode = Config.DocumentElement.SelectSingleNode("/umbPackage/enableSkins"); - enableSkins = bool.Parse(XmlHelper.GetNodeValue(skinNode)); - if (skinNode.Attributes["repository"] != null && string.IsNullOrEmpty(skinNode.Attributes["repository"].Value) == false) - skinRepoGuid = skinNode.Attributes["repository"].Value; - } + Author = def.Author, + AuthorUrl = def.AuthorUrl, + Control = def.Control, + IconUrl = def.IconUrl, + License = def.License, + LicenseUrl = def.LicenseUrl, + Name = def.Name, + Readme = def.Readme, + UmbracoVersion = def.UmbracoVersion, + Url = def.Url, + Version = def.Version, + PackageId = guid + }; - //Create a new package instance to record all the installed package adds - this is the same format as the created packages has. - //save the package meta data - var insPack = InstalledPackage.MakeNew(packName); - insPack.Data.Author = packAuthor; - insPack.Data.AuthorUrl = packAuthorUrl; - insPack.Data.Version = packVersion; - insPack.Data.Readme = packReadme; - insPack.Data.License = packLicense; - insPack.Data.Url = packUrl; - insPack.Data.IconUrl = iconUrl; + if (!Current.Services.PackagingService.SaveInstalledPackage(installedPackage)) + throw new InvalidOperationException("Could not save package definition"); - insPack.Data.PackageId = guid; //the package unique key. - insPack.Save(); - - return insPack.Data.Id; + return installedPackage.Id; } public void InstallFiles(int packageId, string tempDir) { + var parser = new CompiledPackageXmlParser(); + using (Current.ProfilingLogger.DebugDuration( "Installing package files for package id " + packageId + " into temp folder " + tempDir, "Package file installation complete for package id " + packageId)) { //retrieve the manifest to continue installation - var insPack = InstalledPackage.GetById(packageId); + var insPack = Current.Services.PackagingService.GetInstalledPackageById(packageId); //TODO: Depending on some files, some files should be installed differently. //i.e. if stylsheets should probably be installed via business logic, media items should probably use the media IFileSystem! @@ -265,13 +245,15 @@ namespace Umbraco.Web._Legacy.Packager //string virtualBasePath = System.Web.HttpContext.Current.Request.ApplicationPath; string basePath = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath; + var def = parser.ToCompiledPackage(Config); + try { - foreach (XmlNode n in Config.DocumentElement.SelectNodes("//file")) + foreach (var f in def.Files) { - var destPath = GetFileName(basePath, XmlHelper.GetNodeValue(n.SelectSingleNode("orgPath"))); - var sourceFile = GetFileName(tempDir, XmlHelper.GetNodeValue(n.SelectSingleNode("guid"))); - var destFile = GetFileName(destPath, XmlHelper.GetNodeValue(n.SelectSingleNode("orgName"))); + var destPath = GetFileName(basePath, f.OriginalPath); + var sourceFile = GetFileName(tempDir, f.UniqueFileName); + var destFile = GetFileName(destPath, f.OriginalName); // Create the destination directory if it doesn't exist if (Directory.Exists(destPath) == false) @@ -300,14 +282,14 @@ namespace Umbraco.Web._Legacy.Packager File.Copy(sourceFile, destFile); //PPH log file install - insPack.Data.Files.Add(XmlHelper.GetNodeValue(n.SelectSingleNode("orgPath")) + "/" + XmlHelper.GetNodeValue(n.SelectSingleNode("orgName"))); + insPack.Files.Add(f.OriginalPath.EnsureEndsWith('/') + f.OriginalName); } // Once we're done copying, remove all the files - foreach (XmlNode n in Config.DocumentElement.SelectNodes("//file")) + foreach (var f in def.Files) { - var sourceFile = GetFileName(tempDir, XmlHelper.GetNodeValue(n.SelectSingleNode("guid"))); + var sourceFile = GetFileName(tempDir, f.UniqueFileName); if (File.Exists(sourceFile)) File.Delete(sourceFile); } @@ -323,10 +305,10 @@ namespace Umbraco.Web._Legacy.Packager { Current.Services.AuditService.Add(AuditType.PackagerInstall, _currentUserId, - -1, "Package", string.Format("Package '{0}' installed. Package guid: {1}", insPack.Data.Name, insPack.Data.PackageId)); + -1, "Package", string.Format("Package '{0}' installed. Package guid: {1}", insPack.Name, insPack.PackageId)); } - insPack.Save(); + Current.Services.PackagingService.SaveInstalledPackage(insPack); } } @@ -336,11 +318,11 @@ namespace Umbraco.Web._Legacy.Packager "Installing business logic for package id " + packageId + " into temp folder " + tempDir, "Package business logic installation complete for package id " + packageId)) { - InstalledPackage insPack; + PackageDefinition insPack; try { //retrieve the manifest to continue installation - insPack = InstalledPackage.GetById(packageId); + insPack = Current.Services.PackagingService.GetInstalledPackageById(packageId); //bool saveNeeded = false; // Get current user, with a fallback @@ -348,9 +330,7 @@ namespace Umbraco.Web._Legacy.Packager //TODO: Get rid of this entire class! Until then all packages will be installed by the admin user - - //Xml as XElement which is used with the new PackagingService - var rootElement = Config.DocumentElement.GetXElement(); + var rootElement = Config.Root; var packagingService = Current.Services.PackagingService; //Perhaps it would have been a good idea to put the following into methods eh?!? @@ -362,7 +342,7 @@ namespace Umbraco.Web._Legacy.Packager var dataTypeDefinitions = packagingService.ImportDataTypeDefinitions(dataTypeElement, currentUser.Id); foreach (var dataTypeDefinition in dataTypeDefinitions) { - insPack.Data.DataTypes.Add(dataTypeDefinition.Id.ToString(CultureInfo.InvariantCulture)); + insPack.DataTypes.Add(dataTypeDefinition.Id.ToString(CultureInfo.InvariantCulture)); } } #endregion @@ -373,7 +353,7 @@ namespace Umbraco.Web._Legacy.Packager { var insertedLanguages = packagingService.ImportLanguages(languageItemsElement); foreach(var x in insertedLanguages.Select(l => l.Id.ToString(CultureInfo.InvariantCulture))) - insPack.Data.Languages.Add(x); + insPack.Languages.Add(x); } #endregion @@ -384,7 +364,7 @@ namespace Umbraco.Web._Legacy.Packager { var insertedDictionaryItems = packagingService.ImportDictionaryItems(dictionaryItemsElement); foreach (var x in insertedDictionaryItems.Select(d => d.Id.ToString(CultureInfo.InvariantCulture))) - insPack.Data.DictionaryItems.Add(x); + insPack.DictionaryItems.Add(x); } #endregion @@ -394,7 +374,7 @@ namespace Umbraco.Web._Legacy.Packager { var insertedMacros = packagingService.ImportMacros(macroItemsElement); foreach (var x in insertedMacros.Select(m => m.Id.ToString(CultureInfo.InvariantCulture))) - insPack.Data.Macros.Add(x); + insPack.Macros.Add(x); } #endregion @@ -406,7 +386,7 @@ namespace Umbraco.Web._Legacy.Packager var templates = packagingService.ImportTemplates(templateElement, currentUser.Id); foreach (var template in templates) { - insPack.Data.Templates.Add(template.Id.ToString(CultureInfo.InvariantCulture)); + insPack.Templates.Add(template.Id.ToString(CultureInfo.InvariantCulture)); } } #endregion @@ -423,32 +403,35 @@ namespace Umbraco.Web._Legacy.Packager var contentTypes = packagingService.ImportContentTypes(docTypeElement, currentUser.Id); foreach (var contentType in contentTypes) { - insPack.Data.DocumentTypes.Add(contentType.Id.ToString(CultureInfo.InvariantCulture)); + insPack.DocumentTypes.Add(contentType.Id.ToString(CultureInfo.InvariantCulture)); //saveNeeded = true; } } #endregion #region Stylesheets - foreach (XmlNode n in Config.DocumentElement.SelectNodes("Stylesheets/Stylesheet")) + foreach (var n in Config.Root.XPathSelectElements("Stylesheets/Stylesheet")) { - //StyleSheet s = StyleSheet.Import(n, currentUser); + string stylesheetName = n.Element("Name")?.Value; + if (stylesheetName.IsNullOrWhiteSpace()) continue; - - string stylesheetName = XmlHelper.GetNodeValue(n.SelectSingleNode("Name")); - //StyleSheet s = GetByName(stylesheetName); var s = Current.Services.FileService.GetStylesheetByName(stylesheetName); if (s == null) { - s = new Stylesheet(XmlHelper.GetNodeValue(n.SelectSingleNode("FileName"))) { Content = XmlHelper.GetNodeValue(n.SelectSingleNode("Content")) }; + var fileName = n.Element("FileName")?.Value; + if (fileName == null) continue; + var content = n.Element("Content")?.Value; + if (content == null) continue; + + s = new Stylesheet(fileName) { Content = content }; Current.Services.FileService.SaveStylesheet(s); } - foreach (XmlNode prop in n.SelectNodes("Properties/Property")) + foreach (var prop in n.XPathSelectElements("Properties/Property")) { - string alias = XmlHelper.GetNodeValue(prop.SelectSingleNode("Alias")); + string alias = prop.Element("Alias")?.Value; var sp = s.Properties.SingleOrDefault(p => p != null && p.Alias == alias); - string name = XmlHelper.GetNodeValue(prop.SelectSingleNode("Name")); + string name = prop.Element("Name")?.Value; if (sp == null) { //sp = StylesheetProperty.MakeNew( @@ -472,7 +455,7 @@ namespace Umbraco.Web._Legacy.Packager } } sp.Alias = alias; - sp.Value = XmlHelper.GetNodeValue(prop.SelectSingleNode("Value")); + sp.Value = prop.Element("Value")?.Value; } //s.saveCssToFile(); Current.Services.FileService.SaveStylesheet(s); @@ -480,7 +463,7 @@ namespace Umbraco.Web._Legacy.Packager - insPack.Data.Stylesheets.Add(s.Id.ToString(CultureInfo.InvariantCulture)); + insPack.Stylesheets.Add(s.Id.ToString(CultureInfo.InvariantCulture)); //saveNeeded = true; } @@ -493,33 +476,34 @@ namespace Umbraco.Web._Legacy.Packager { var content = packagingService.ImportContent(documentElement, -1, currentUser.Id); var firstContentItem = content.First(); - insPack.Data.ContentNodeId = firstContentItem.Id.ToString(CultureInfo.InvariantCulture); + insPack.ContentNodeId = firstContentItem.Id.ToString(CultureInfo.InvariantCulture); } #endregion #region Package Actions - foreach (XmlNode n in Config.DocumentElement.SelectNodes("Actions/Action")) + foreach (var n in Config.Root.XPathSelectElements("Actions/Action")) { - if (n.Attributes["undo"] == null || n.Attributes["undo"].Value == "true") + var undo = n.AttributeValue("undo"); + if (undo == null || undo == "true") { - insPack.Data.Actions += n.OuterXml; + insPack.Actions += n.ToString(); } //Run the actions tagged only for 'install' + var runat = n.AttributeValue("runat"); - if (n.Attributes["runat"] != null && n.Attributes["runat"].Value == "install") + if (runat != null && runat == "install") { - var alias = n.Attributes["alias"] != null ? n.Attributes["alias"].Value : ""; - + var alias = n.AttributeValue("alias"); if (alias.IsNullOrWhiteSpace() == false) { - Current.PackageActionRunner.RunPackageAction(insPack.Data.Name, alias, n); + Current.PackageActionRunner.RunPackageAction(insPack.Name, alias, n); } } } #endregion - insPack.Save(); + Current.Services.PackagingService.SaveInstalledPackage(insPack); } catch (Exception ex) { @@ -527,7 +511,6 @@ namespace Umbraco.Web._Legacy.Packager throw; } - OnPackageBusinessLogicInstalled(insPack); OnPackageInstalled(insPack); } } @@ -551,40 +534,35 @@ namespace Umbraco.Web._Legacy.Packager /// The folder to which the contents of the package is extracted public void LoadConfig(string tempDir) { - Config = new XmlDocument(); - Config.Load(tempDir + Path.DirectorySeparatorChar + "package.xml"); + Config = XDocument.Load(tempDir + Path.DirectorySeparatorChar + "package.xml"); - Name = Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/name").FirstChild.Value; - Version = Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/version").FirstChild.Value; - Url = Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/url").FirstChild.Value; - License = Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/license").FirstChild.Value; - LicenseUrl = Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/license").Attributes.GetNamedItem("url").Value; + var parser = new CompiledPackageXmlParser(); + var def = parser.ToCompiledPackage(Config); - RequirementsMajor = int.Parse(Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/requirements/major").FirstChild.Value); - RequirementsMinor = int.Parse(Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/requirements/minor").FirstChild.Value); - RequirementsPatch = int.Parse(Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/requirements/patch").FirstChild.Value); + Name = def.Name; + Version = def.Version; + Url = def.Url; + License = def.License; + LicenseUrl = def.LicenseUrl; - var reqNode = Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/requirements"); - RequirementsType = reqNode != null && reqNode.Attributes != null && reqNode.Attributes["type"] != null - ? Enum.Parse(reqNode.Attributes["type"].Value, true) - : RequirementsType.Legacy; - var iconNode = Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/iconUrl"); - if (iconNode != null && iconNode.FirstChild != null) - { - IconUrl = iconNode.FirstChild.Value; - } - - Author = Config.DocumentElement.SelectSingleNode("/umbPackage/info/author/name").FirstChild.Value; - AuthorUrl = Config.DocumentElement.SelectSingleNode("/umbPackage/info/author/website").FirstChild.Value; + RequirementsMajor = def.UmbracoVersion.Major; + RequirementsMinor = def.UmbracoVersion.Minor; + RequirementsPatch = def.UmbracoVersion.Build; + RequirementsType = def.UmbracoVersionRequirementsType; + IconUrl = def.IconUrl; + Author = def.Author; + AuthorUrl = def.AuthorUrl; + ReadMe = def.Readme; + Control = def.Control; var basePath = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath; var dllBinFiles = new List(); - foreach (XmlNode n in Config.DocumentElement.SelectNodes("//file")) + foreach (var f in def.Files) { var badFile = false; - var destPath = GetFileName(basePath, XmlHelper.GetNodeValue(n.SelectSingleNode("orgPath"))); - var orgName = XmlHelper.GetNodeValue(n.SelectSingleNode("orgName")); + var destPath = GetFileName(basePath, f.OriginalPath); + var orgName = f.OriginalName; var destFile = GetFileName(destPath, orgName); if (destPath.ToLower().Contains(IOHelper.DirSepChar + "app_code")) @@ -606,7 +584,7 @@ namespace Umbraco.Web._Legacy.Packager if (badFile) { ContainsUnsecureFiles = true; - UnsecureFiles.Add(XmlHelper.GetNodeValue(n.SelectSingleNode("orgName"))); + UnsecureFiles.Add(f.OriginalName); } } @@ -614,9 +592,9 @@ namespace Umbraco.Web._Legacy.Packager //this will check for existing macros with the same alias //since we will not overwrite on import it's a good idea to inform the user what will be overwritten - foreach (XmlNode n in Config.DocumentElement.SelectNodes("//macro")) + foreach (var n in Config.Root.XPathSelectElements("//macro")) { - var alias = n.SelectSingleNode("alias").InnerText; + var alias = n.Element("alias")?.Value; if (!string.IsNullOrEmpty(alias)) { var m = Current.Services.MacroService.GetByAlias(alias); @@ -631,9 +609,9 @@ namespace Umbraco.Web._Legacy.Packager } } - foreach (XmlNode n in Config.DocumentElement.SelectNodes("Templates/Template")) + foreach (var n in Config.Root.XPathSelectElements("Templates/Template")) { - var alias = n.SelectSingleNode("Alias").InnerText; + var alias = n.Element("Alias")?.Value; if (!string.IsNullOrEmpty(alias)) { var t = Current.Services.FileService.GetTemplate(alias); @@ -648,9 +626,9 @@ namespace Umbraco.Web._Legacy.Packager } } - foreach (XmlNode n in Config.DocumentElement.SelectNodes("Stylesheets/Stylesheet")) + foreach (var n in Config.Root.XPathSelectElements("Stylesheets/Stylesheet")) { - var alias = n.SelectSingleNode("Name").InnerText; + var alias = n.Element("Name")?.Value; if (!string.IsNullOrEmpty(alias)) { var s = Current.Services.FileService.GetStylesheetByName(alias); @@ -665,17 +643,7 @@ namespace Umbraco.Web._Legacy.Packager } } - var readmeNode = Config.DocumentElement.SelectSingleNode("/umbPackage/info/readme"); - if (readmeNode != null) - { - ReadMe = XmlHelper.GetNodeValue(readmeNode); - } - - var controlNode = Config.DocumentElement.SelectSingleNode("/umbPackage/control"); - if (controlNode != null) - { - Control = XmlHelper.GetNodeValue(controlNode); - } + } /// @@ -815,15 +783,8 @@ namespace Umbraco.Web._Legacy.Packager #endregion - internal static event EventHandler PackageBusinessLogicInstalled; - private static void OnPackageBusinessLogicInstalled(InstalledPackage e) - { - var handler = PackageBusinessLogicInstalled; - handler?.Invoke(null, e); - } - - private void OnPackageInstalled(InstalledPackage insPack) + private void OnPackageInstalled(PackageDefinition insPack) { // getting an InstallationSummary for sending to the PackagingService.ImportedPackage event var fileService = Current.Services.FileService; @@ -832,12 +793,10 @@ namespace Umbraco.Web._Legacy.Packager var dataTypeService = Current.Services.DataTypeService; var localizationService = Current.Services.LocalizationService; - var installationSummary = insPack.GetInstallationSummary(contentTypeService, dataTypeService, fileService, localizationService, macroService); + var installationSummary = InstallationSummary.FromPackageDefinition(insPack, contentTypeService, dataTypeService, fileService, localizationService, macroService); installationSummary.PackageInstalled = true; - var meta = insPack.GetMetaData(); - - var args = new ImportPackageEventArgs(installationSummary, meta, false); + var args = new ImportPackageEventArgs(installationSummary, insPack, false); PackagingService.OnImportedPackage(args); } } diff --git a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/InstalledPackage.cs b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/InstalledPackage.cs deleted file mode 100644 index e5af44b747..0000000000 --- a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/InstalledPackage.cs +++ /dev/null @@ -1,136 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Umbraco.Core.Composing; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Packaging; -using Umbraco.Core.Services; - -namespace Umbraco.Web._Legacy.Packager.PackageInstance -{ - public class InstalledPackage - { - - private int _saveHitCount = 0; - - public static InstalledPackage GetById(int id) - { - InstalledPackage pack = new InstalledPackage(); - pack.Data = data.Package(id, IOHelper.MapPath(Settings.InstalledPackagesSettings)); - return pack; - } - - public static InstalledPackage MakeNew(string name) - { - InstalledPackage pack = new InstalledPackage(); - pack.Data = data.MakeNew(name, IOHelper.MapPath(Settings.InstalledPackagesSettings)); - return pack; - } - - public void Save() - { -#if DEBUG - _saveHitCount++; - Current.Logger.Info("The InstalledPackage class save method has been hit {Total} times.", _saveHitCount); -#endif - data.Save(this.Data, IOHelper.MapPath(Settings.InstalledPackagesSettings)); - } - - public static List GetAllInstalledPackages() - { - - List val = new List(); - - foreach (Core.Models.Packaging.PackageDefinition pack in data.GetAllPackages(IOHelper.MapPath(Settings.InstalledPackagesSettings))) - { - InstalledPackage insPackage = new InstalledPackage(); - insPackage.Data = pack; - val.Add(insPackage); - } - - return val; - } - - public Core.Models.Packaging.PackageDefinition Data { get; set; } - - public void Delete(int userId) - { - Current.Services.AuditService.Add(AuditType.PackagerUninstall, userId, -1, "Package", string.Format("Package '{0}' uninstalled. Package guid: {1}", Data.Name, Data.PackageId)); - Delete(); - } - - public void Delete() - { - data.Delete(this.Data.Id, IOHelper.MapPath(Settings.InstalledPackagesSettings)); - } - - - - /// - /// Used internally for creating an InstallationSummary (used in new PackagingService) representation of this InstalledPackage object. - /// - /// - /// - /// - /// - /// - /// - internal InstallationSummary GetInstallationSummary(IContentTypeService contentTypeService, IDataTypeService dataTypeService, IFileService fileService, ILocalizationService localizationService, IMacroService macroService) - { - var macros = TryGetIntegerIds(Data.Macros).Select(macroService.GetById).ToList(); - var templates = TryGetIntegerIds(Data.Templates).Select(fileService.GetTemplate).ToList(); - var contentTypes = TryGetIntegerIds(Data.DocumentTypes).Select(contentTypeService.Get).ToList(); // fixme - media types? - var dataTypes = TryGetIntegerIds(Data.DataTypes).Select(dataTypeService.GetDataType).ToList(); - var dictionaryItems = TryGetIntegerIds(Data.DictionaryItems).Select(localizationService.GetDictionaryItemById).ToList(); - var languages = TryGetIntegerIds(Data.Languages).Select(localizationService.GetLanguageById).ToList(); - - for (var i = 0; i < Data.Files.Count; i++) - { - var filePath = Data.Files[i]; - Data.Files[i] = filePath.GetRelativePath(); - } - - return new InstallationSummary - { - ContentTypesInstalled = contentTypes, - DataTypesInstalled = dataTypes, - DictionaryItemsInstalled = dictionaryItems, - FilesInstalled = Data.Files, - LanguagesInstalled = languages, - MacrosInstalled = macros, - MetaData = GetMetaData(), - TemplatesInstalled = templates, - }; - } - - internal MetaData GetMetaData() - { - return new MetaData() - { - AuthorName = Data.Author, - AuthorUrl = Data.AuthorUrl, - Control = Data.LoadControl, - License = Data.License, - LicenseUrl = Data.LicenseUrl, - Name = Data.Name, - Readme = Data.Readme, - Url = Data.Url, - Version = Data.Version - }; - } - - private static IEnumerable TryGetIntegerIds(IEnumerable ids) - { - var intIds = new List(); - foreach (var id in ids) - { - int parsed; - if (int.TryParse(id, out parsed)) - intIds.Add(parsed); - } - return intIds; - } - } -} diff --git a/src/Umbraco.Web/_Legacy/Packager/data.cs b/src/Umbraco.Web/_Legacy/Packager/data.cs deleted file mode 100644 index 51f0799689..0000000000 --- a/src/Umbraco.Web/_Legacy/Packager/data.cs +++ /dev/null @@ -1,374 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Xml; -using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; -using Umbraco.Core.Xml; - -namespace Umbraco.Web._Legacy.Packager -{ - /// - /// This is the xml data for installed packages. This is not the same xml as a pckage format! - /// - public class data - { - public static XmlDocument Source { get; private set; } - - public static void Reload(string dataSource) - { - //do some error checking and create the folders/files if they don't exist - if (!File.Exists(dataSource)) - { - if (!Directory.Exists(IOHelper.MapPath(SystemDirectories.Packages))) - { - Directory.CreateDirectory(IOHelper.MapPath(SystemDirectories.Packages)); - } - - using (var sw = File.CreateText(dataSource)) - { - sw.Write($@"{Environment.NewLine}{Environment.NewLine}"); - sw.Flush(); - } - - } - - if (Source == null) - { - Source = new XmlDocument(); - } - - //error checking here - if (File.Exists(dataSource)) - { - var isEmpty = false; - using (var sr = new StreamReader(dataSource)) - { - if (sr.ReadToEnd().IsNullOrWhiteSpace()) - { - isEmpty = true; - } - } - if (isEmpty) - { - File.WriteAllText(dataSource, @""); - } - } - - Source.Load(dataSource); - } - - public static XmlNode GetFromId(int Id, string dataSource, bool reload) - { - if (reload) - Reload(dataSource); - - return Source.SelectSingleNode("/packages/package [@id = '" + Id.ToString().ToUpper() + "']"); - } - - public static XmlNode GetFromGuid(string guid, string dataSource, bool reload) - { - if (reload) - Reload(dataSource); - - return Source.SelectSingleNode("/packages/package [@packageGuid = '" + guid + "']"); - } - - public static Core.Models.Packaging.PackageDefinition MakeNew(string name, string dataSource) - { - Reload(dataSource); - - int maxId = 1; - // Find max id - foreach (XmlNode n in Source.SelectNodes("packages/package")) - { - if (int.Parse(n.Attributes.GetNamedItem("id").Value) >= maxId) - maxId = int.Parse(n.Attributes.GetNamedItem("id").Value) + 1; - } - - XmlElement instance = Source.CreateElement("package"); - instance.Attributes.Append(XmlHelper.AddAttribute(Source, "id", maxId.ToString())); - instance.Attributes.Append(XmlHelper.AddAttribute(Source, "version", "")); - instance.Attributes.Append(XmlHelper.AddAttribute(Source, "url", "")); - instance.Attributes.Append(XmlHelper.AddAttribute(Source, "name", name)); - instance.Attributes.Append(XmlHelper.AddAttribute(Source, "folder", Guid.NewGuid().ToString())); - instance.Attributes.Append(XmlHelper.AddAttribute(Source, "packagePath", "")); - instance.Attributes.Append(XmlHelper.AddAttribute(Source, "repositoryGuid", "")); - instance.Attributes.Append(XmlHelper.AddAttribute(Source, "iconUrl", "")); - //set to current version - instance.Attributes.Append(XmlHelper.AddAttribute(Source, "umbVersion", UmbracoVersion.Current.ToString(3))); - instance.Attributes.Append(XmlHelper.AddAttribute(Source, "packageGuid", Guid.NewGuid().ToString())); - instance.Attributes.Append(XmlHelper.AddAttribute(Source, "hasUpdate", "false")); - - instance.Attributes.Append(XmlHelper.AddAttribute(Source, "enableSkins", "false")); - instance.Attributes.Append(XmlHelper.AddAttribute(Source, "skinRepoGuid", "")); - - XmlElement license = Source.CreateElement("license"); - license.InnerText = "MIT License"; - license.Attributes.Append(XmlHelper.AddAttribute(Source, "url", "http://opensource.org/licenses/MIT")); - instance.AppendChild(license); - - XmlElement author = Source.CreateElement("author"); - author.InnerText = ""; - author.Attributes.Append(XmlHelper.AddAttribute(Source, "url", "")); - instance.AppendChild(author); - - instance.AppendChild(XmlHelper.AddTextNode(Source, "readme", "")); - instance.AppendChild(XmlHelper.AddTextNode(Source, "actions", "")); - - instance.AppendChild(XmlHelper.AddTextNode(Source, "datatypes", "")); - - XmlElement content = Source.CreateElement("content"); - content.InnerText = ""; - content.Attributes.Append(XmlHelper.AddAttribute(Source, "nodeId", "")); - content.Attributes.Append(XmlHelper.AddAttribute(Source, "loadChildNodes", "false")); - instance.AppendChild(content); - - instance.AppendChild(XmlHelper.AddTextNode(Source, "templates", "")); - instance.AppendChild(XmlHelper.AddTextNode(Source, "stylesheets", "")); - instance.AppendChild(XmlHelper.AddTextNode(Source, "documentTypes", "")); - instance.AppendChild(XmlHelper.AddTextNode(Source, "macros", "")); - instance.AppendChild(XmlHelper.AddTextNode(Source, "files", "")); - instance.AppendChild(XmlHelper.AddTextNode(Source, "languages", "")); - instance.AppendChild(XmlHelper.AddTextNode(Source, "dictionaryitems", "")); - instance.AppendChild(XmlHelper.AddTextNode(Source, "loadcontrol", "")); - - Source.SelectSingleNode("packages").AppendChild(instance); - Source.Save(dataSource); - var retVal = data.Package(maxId, dataSource); - - return retVal; - } - - public static Core.Models.Packaging.PackageDefinition Package(int id, string datasource) - { - return ConvertXmlToPackage(GetFromId(id, datasource, true)); - } - - public static Core.Models.Packaging.PackageDefinition Package(string guid, string datasource) - { - XmlNode node = GetFromGuid(guid, datasource, true); - if (node != null) - return ConvertXmlToPackage(node); - else - return new Core.Models.Packaging.PackageDefinition(); - } - - public static List GetAllPackages(string dataSource) - { - Reload(dataSource); - XmlNodeList nList = data.Source.SelectNodes("packages/package"); - - List retVal = new List(); - - for (int i = 0; i < nList.Count; i++) - { - try - { - retVal.Add(ConvertXmlToPackage(nList[i])); - } - catch (Exception ex) - { - Current.Logger.Error(ex, "An error occurred in GetAllPackages"); - } - } - - return retVal; - } - - private static Core.Models.Packaging.PackageDefinition ConvertXmlToPackage(XmlNode n) - { - Core.Models.Packaging.PackageDefinition retVal = new Core.Models.Packaging.PackageDefinition(); - - if (n != null) - { - retVal.Id = int.Parse(SafeAttribute("id", n)); - retVal.Name = SafeAttribute("name", n); - retVal.FolderId = Guid.Parse(SafeAttribute("folder", n)); - retVal.PackagePath = SafeAttribute("packagePath", n); - retVal.Version = SafeAttribute("version", n); - retVal.Url = SafeAttribute("url", n); - retVal.PackageId = Guid.Parse(SafeAttribute("packageGuid", n)); - - retVal.IconUrl = SafeAttribute("iconUrl", n); - var umbVersion = SafeAttribute("umbVersion", n); - Version parsedVersion; - if (umbVersion.IsNullOrWhiteSpace() == false && Version.TryParse(umbVersion, out parsedVersion)) - { - retVal.UmbracoVersion = parsedVersion; - } - - retVal.License = SafeNodeValue(n.SelectSingleNode("license")); - retVal.LicenseUrl = n.SelectSingleNode("license").Attributes.GetNamedItem("url").Value; - - retVal.Author = SafeNodeValue(n.SelectSingleNode("author")); - retVal.AuthorUrl = SafeAttribute("url", n.SelectSingleNode("author")); - - retVal.Readme = SafeNodeValue(n.SelectSingleNode("readme")); - retVal.Actions = SafeNodeInnerXml(n.SelectSingleNode("actions")); - - retVal.ContentNodeId = SafeAttribute("nodeId", n.SelectSingleNode("content")); - retVal.ContentLoadChildNodes = bool.Parse(SafeAttribute("loadChildNodes", n.SelectSingleNode("content"))); - - retVal.Macros = new List(SafeNodeValue(n.SelectSingleNode("macros")).Trim(',').Split(',')); - retVal.Macros = new List(SafeNodeValue(n.SelectSingleNode("macros")).Trim(',').Split(',')); - retVal.Templates = new List(SafeNodeValue(n.SelectSingleNode("templates")).Trim(',').Split(',')); - retVal.Stylesheets = new List(SafeNodeValue(n.SelectSingleNode("stylesheets")).Trim(',').Split(',')); - retVal.DocumentTypes = new List(SafeNodeValue(n.SelectSingleNode("documentTypes")).Trim(',').Split(',')); - retVal.Languages = new List(SafeNodeValue(n.SelectSingleNode("languages")).Trim(',').Split(',')); - retVal.DictionaryItems = new List(SafeNodeValue(n.SelectSingleNode("dictionaryitems")).Trim(',').Split(',')); - retVal.DataTypes = new List(SafeNodeValue(n.SelectSingleNode("datatypes")).Trim(',').Split(',')); - - XmlNodeList xmlFiles = n.SelectNodes("files/file"); - retVal.Files = new List(); - - for (int i = 0; i < xmlFiles.Count; i++) - retVal.Files.Add(xmlFiles[i].InnerText); - - retVal.LoadControl = SafeNodeValue(n.SelectSingleNode("loadcontrol")); - } - - return retVal; - } - - public static void Delete(int Id, string dataSource) - { - Reload(dataSource); - // Remove physical xml file if any - //PackageInstance p = new PackageInstance(Id); - - //TODO DELETE PACKAGE FOLDER... - //p.Folder - - XmlNode n = data.GetFromId(Id, dataSource, true); - if (n != null) - { - data.Source.SelectSingleNode("/packages").RemoveChild(n); - data.Source.Save(dataSource); - } - - } - - - public static void Save(Core.Models.Packaging.PackageDefinition package, string dataSource) - { - Reload(dataSource); - var xmlDef = GetFromId(package.Id, dataSource, false); - XmlHelper.SetAttribute(Source, xmlDef, "name", package.Name); - XmlHelper.SetAttribute(Source, xmlDef, "version", package.Version); - XmlHelper.SetAttribute(Source, xmlDef, "url", package.Url); - XmlHelper.SetAttribute(Source, xmlDef, "packagePath", package.PackagePath); - XmlHelper.SetAttribute(Source, xmlDef, "packageGuid", package.PackageId.ToString()); - XmlHelper.SetAttribute(Source, xmlDef, "iconUrl", package.IconUrl); - if (package.UmbracoVersion != null) - XmlHelper.SetAttribute(Source, xmlDef, "umbVersion", package.UmbracoVersion.ToString(3)); - - var licenseNode = xmlDef.SelectSingleNode("license"); - if (licenseNode == null) - { - licenseNode = Source.CreateElement("license"); - xmlDef.AppendChild(licenseNode); - } - licenseNode.InnerText = package.License; - XmlHelper.SetAttribute(Source, licenseNode, "url", package.LicenseUrl); - - var authorNode = xmlDef.SelectSingleNode("author"); - if (authorNode == null) - { - authorNode = Source.CreateElement("author"); - xmlDef.AppendChild(authorNode); - } - authorNode.InnerText = package.Author; - XmlHelper.SetAttribute(Source, authorNode, "url", package.AuthorUrl); - - XmlHelper.SetCDataNode(Source, xmlDef, "readme", package.Readme); - XmlHelper.SetInnerXmlNode(Source, xmlDef, "actions", package.Actions); - - var contentNode = xmlDef.SelectSingleNode("content"); - if (contentNode == null) - { - contentNode = Source.CreateElement("content"); - xmlDef.AppendChild(contentNode); - } - XmlHelper.SetAttribute(Source, contentNode, "nodeId", package.ContentNodeId); - XmlHelper.SetAttribute(Source, contentNode, "loadChildNodes", package.ContentLoadChildNodes.ToString()); - - XmlHelper.SetTextNode(Source, xmlDef, "macros", JoinList(package.Macros, ',')); - XmlHelper.SetTextNode(Source, xmlDef, "templates", JoinList(package.Templates, ',')); - XmlHelper.SetTextNode(Source, xmlDef, "stylesheets", JoinList(package.Stylesheets, ',')); - XmlHelper.SetTextNode(Source, xmlDef, "documentTypes", JoinList(package.DocumentTypes, ',')); - XmlHelper.SetTextNode(Source, xmlDef, "languages", JoinList(package.Languages, ',')); - XmlHelper.SetTextNode(Source, xmlDef, "dictionaryitems", JoinList(package.DictionaryItems, ',')); - XmlHelper.SetTextNode(Source, xmlDef, "datatypes", JoinList(package.DataTypes, ',')); - - var filesNode = xmlDef.SelectSingleNode("files"); - if (filesNode == null) - { - filesNode = Source.CreateElement("files"); - xmlDef.AppendChild(filesNode); - } - filesNode.InnerXml = ""; - - foreach (var fileStr in package.Files) - { - if (string.IsNullOrWhiteSpace(fileStr) == false) - filesNode.AppendChild(XmlHelper.AddTextNode(Source, "file", fileStr)); - } - - XmlHelper.SetTextNode(Source, xmlDef, "loadcontrol", package.LoadControl); - - Source.Save(dataSource); - } - - - - private static string SafeAttribute(string name, XmlNode n) - { - return n.Attributes == null || n.Attributes[name] == null ? string.Empty : n.Attributes[name].Value; - } - - private static string SafeNodeValue(XmlNode n) - { - try - { - return XmlHelper.GetNodeValue(n); - } - catch - { - return string.Empty; - } - } - - private static string SafeNodeInnerXml(XmlNode n) - { - try - { - return n.InnerXml; - } - catch - { - return string.Empty; - } - } - - - private static string JoinList(IList list, char seperator) - { - string retVal = ""; - foreach (string str in list) - { - retVal += str + seperator; - } - - return retVal.Trim(seperator); - } - - public data() - { - - } - } -} From 18c90c532574ae4630b3f58e2080471b6d9a3084 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 14 Jan 2019 14:28:00 +1100 Subject: [PATCH 56/93] Package file installation logic all migrated and tests are running, now to do the package data installation --- .../Composing/Composers/ServicesComposer.cs | 10 + .../Models/Packaging/CompiledPackage.cs | 23 +- .../Models/Packaging/InstallationSummary.cs | 70 +- .../Models/Packaging/PackageDefinition.cs | 31 + .../Models/Packaging/PreInstallWarnings.cs | 15 +- .../Packaging/CompiledPackageXmlParser.cs | 150 ++ .../Packaging/ConflictingPackageData.cs | 61 +- .../Packaging/IConflictingPackageData.cs | 13 - .../Packaging/IPackageExtraction.cs | 62 - .../Packaging/IPackageInstallation.cs | 34 +- .../Packaging/PackageDefinitionXmlParser.cs | 54 - .../Packaging/PackageExtraction.cs | 76 +- .../Packaging/PackageFileInstallation.cs | 63 + .../Packaging/PackageInstallation.cs | 574 ++----- .../Services/IPackagingService.cs | 36 +- .../Services/Implement/PackagingService.cs | 56 +- src/Umbraco.Core/Umbraco.Core.csproj | 6 +- .../CreatedPackagesRepositoryTests.cs | 8 +- .../Packaging/PackageExtractionTests.cs | 4 +- .../Packaging/PackageInstallationTest.cs | 151 +- .../Services/PackagingServiceTests.cs | 49 +- .../views/packages/views/install-local.html | 2 +- .../src/views/packages/views/repo.html | 2 +- src/Umbraco.Web/Composing/Current.cs | 4 +- .../Editors/PackageInstallController.cs | 72 +- .../Controllers/InstallPackageController.cs | 58 +- .../InstallSteps/StarterKitCleanupStep.cs | 16 +- .../InstallSteps/StarterKitDownloadStep.cs | 31 +- .../InstallSteps/StarterKitInstallStep.cs | 24 +- .../Install/Models/InstallPackageModel.cs | 6 +- .../Models/LocalPackageInstallModel.cs | 19 +- src/Umbraco.Web/Models/PackageInstallModel.cs | 3 +- src/Umbraco.Web/Umbraco.Web.csproj | 1 - src/Umbraco.Web/_Legacy/Packager/Installer.cs | 1471 ++++++++--------- src/Umbraco.Web/_Legacy/Packager/Settings.cs | 13 - 35 files changed, 1534 insertions(+), 1734 deletions(-) create mode 100644 src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs delete mode 100644 src/Umbraco.Core/Packaging/IConflictingPackageData.cs delete mode 100644 src/Umbraco.Core/Packaging/IPackageExtraction.cs create mode 100644 src/Umbraco.Core/Packaging/PackageFileInstallation.cs delete mode 100644 src/Umbraco.Web/_Legacy/Packager/Settings.cs diff --git a/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs b/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs index 98f239de3b..ac8f4beeb0 100644 --- a/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs +++ b/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs @@ -61,8 +61,18 @@ namespace Umbraco.Core.Composing.Composers composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); composition.RegisterUnique(factory => CreatePackageRepository(factory, "createdPackages.config")); composition.RegisterUnique(factory => CreatePackageRepository(factory, "installedPackages.config")); + composition.RegisterUnique(); + var appRoot = new DirectoryInfo(IOHelper.GetRootDirectorySafe()); + composition.RegisterUnique(factory => //factory required because we need to pass in a string path + new PackageInstallation( + factory.GetInstance(), factory.GetInstance(), + factory.GetInstance(), + SystemDirectories.Packages, + appRoot, appRoot)); //TODO: These are replaced in the web project - we need to declare them so that // something is wired up, just not sure this is very nice but will work for now. diff --git a/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs b/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs index cb3fbd7eee..96159d1bbf 100644 --- a/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs +++ b/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Xml.Linq; namespace Umbraco.Core.Models.Packaging { @@ -9,32 +10,31 @@ namespace Umbraco.Core.Models.Packaging /// public class CompiledPackage : IPackageInfo { + public string PackageFileName { get; set; } + public string Name { get; set; } - public string Version { get; set; } - public string Url { get; set; } - public string License { get; set; } - public string LicenseUrl { get; set; } - public Version UmbracoVersion { get; set; } - public RequirementsType UmbracoVersionRequirementsType { get; set; } - public string Author { get; set; } - public string AuthorUrl { get; set; } - public string Readme { get; set; } - public string Control { get; set; } - public string IconUrl { get; set; } + public string Actions { get; set; } //fixme: Should we make this strongly typed to IEnumerable ? + + public PreInstallWarnings Warnings { get; set; } = new PreInstallWarnings(); + public List Files { get; set; } = new List(); + public IEnumerable Macros { get; set; } //fixme: make strongly typed + public IEnumerable Templates { get; set; } //fixme: make strongly typed + public IEnumerable Stylesheets { get; set; } //fixme: make strongly typed + } public class CompiledPackageFile @@ -42,6 +42,7 @@ namespace Umbraco.Core.Models.Packaging public string OriginalPath { get; set; } public string UniqueFileName { get; set; } public string OriginalName { get; set; } + } diff --git a/src/Umbraco.Core/Models/Packaging/InstallationSummary.cs b/src/Umbraco.Core/Models/Packaging/InstallationSummary.cs index ddecf53653..1405259da2 100644 --- a/src/Umbraco.Core/Models/Packaging/InstallationSummary.cs +++ b/src/Umbraco.Core/Models/Packaging/InstallationSummary.cs @@ -24,44 +24,44 @@ namespace Umbraco.Core.Models.Packaging public IEnumerable Actions { get; set; } = Enumerable.Empty(); public bool PackageInstalled { get; set; } - public static InstallationSummary FromPackageDefinition(PackageDefinition def, IContentTypeService contentTypeService, IDataTypeService dataTypeService, IFileService fileService, ILocalizationService localizationService, IMacroService macroService) - { - var macros = TryGetIntegerIds(def.Macros).Select(macroService.GetById).ToList(); - var templates = TryGetIntegerIds(def.Templates).Select(fileService.GetTemplate).ToList(); - var contentTypes = TryGetIntegerIds(def.DocumentTypes).Select(contentTypeService.Get).ToList(); // fixme - media types? - var dataTypes = TryGetIntegerIds(def.DataTypes).Select(dataTypeService.GetDataType).ToList(); - var dictionaryItems = TryGetIntegerIds(def.DictionaryItems).Select(localizationService.GetDictionaryItemById).ToList(); - var languages = TryGetIntegerIds(def.Languages).Select(localizationService.GetLanguageById).ToList(); + //public static InstallationSummary FromPackageDefinition(PackageDefinition def, IContentTypeService contentTypeService, IDataTypeService dataTypeService, IFileService fileService, ILocalizationService localizationService, IMacroService macroService) + //{ + // var macros = TryGetIntegerIds(def.Macros).Select(macroService.GetById).ToList(); + // var templates = TryGetIntegerIds(def.Templates).Select(fileService.GetTemplate).ToList(); + // var contentTypes = TryGetIntegerIds(def.DocumentTypes).Select(contentTypeService.Get).ToList(); // fixme - media types? + // var dataTypes = TryGetIntegerIds(def.DataTypes).Select(dataTypeService.GetDataType).ToList(); + // var dictionaryItems = TryGetIntegerIds(def.DictionaryItems).Select(localizationService.GetDictionaryItemById).ToList(); + // var languages = TryGetIntegerIds(def.Languages).Select(localizationService.GetLanguageById).ToList(); - for (var i = 0; i < def.Files.Count; i++) - { - var filePath = def.Files[i]; - def.Files[i] = filePath.GetRelativePath(); - } + // for (var i = 0; i < def.Files.Count; i++) + // { + // var filePath = def.Files[i]; + // def.Files[i] = filePath.GetRelativePath(); + // } - return new InstallationSummary - { - ContentTypesInstalled = contentTypes, - DataTypesInstalled = dataTypes, - DictionaryItemsInstalled = dictionaryItems, - FilesInstalled = def.Files, - LanguagesInstalled = languages, - MacrosInstalled = macros, - MetaData = def, - TemplatesInstalled = templates, - }; - } + // return new InstallationSummary + // { + // ContentTypesInstalled = contentTypes, + // DataTypesInstalled = dataTypes, + // DictionaryItemsInstalled = dictionaryItems, + // FilesInstalled = def.Files, + // LanguagesInstalled = languages, + // MacrosInstalled = macros, + // MetaData = def, + // TemplatesInstalled = templates, + // }; + //} - private static IEnumerable TryGetIntegerIds(IEnumerable ids) - { - var intIds = new List(); - foreach (var id in ids) - { - if (int.TryParse(id, out var parsed)) - intIds.Add(parsed); - } - return intIds; - } + //private static IEnumerable TryGetIntegerIds(IEnumerable ids) + //{ + // var intIds = new List(); + // foreach (var id in ids) + // { + // if (int.TryParse(id, out var parsed)) + // intIds.Add(parsed); + // } + // return intIds; + //} } } diff --git a/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs b/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs index 65e26419f0..56a316ff81 100644 --- a/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs +++ b/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; +using System.Linq; using System.Runtime.Serialization; namespace Umbraco.Core.Models.Packaging @@ -9,6 +10,35 @@ namespace Umbraco.Core.Models.Packaging [DataContract(Name = "packageInstance")] public class PackageDefinition : IPackageInfo { + /// + /// Converts a model to a model + /// + /// + /// + /// + /// This is used only for conversions and will not 'get' a PackageDefinition from the repository with a valid ID + /// + internal static PackageDefinition FromCompiledPackage(CompiledPackage compiled) + { + return new PackageDefinition + { + Actions = compiled.Actions, + Author = compiled.Author, + AuthorUrl = compiled.AuthorUrl, + Control = compiled.Control, + IconUrl = compiled.IconUrl, + License = compiled.License, + LicenseUrl = compiled.LicenseUrl, + Name = compiled.Name, + Readme = compiled.Readme, + UmbracoVersion = compiled.UmbracoVersion, + Url = compiled.Url, + Version = compiled.Version, + //fixme: Is OriginalPath correct here? + Files = compiled.Files.Select(x => x.OriginalPath).ToList() + }; + } + [DataMember(Name = "id")] public int Id { get; set; } @@ -24,6 +54,7 @@ namespace Umbraco.Core.Models.Packaging [Url] public string Url { get; set; } = string.Empty; + //fixme: remove this /// /// This is a generated GUID which is used to determine a temporary folder name for processing the package /// diff --git a/src/Umbraco.Core/Models/Packaging/PreInstallWarnings.cs b/src/Umbraco.Core/Models/Packaging/PreInstallWarnings.cs index 5850e2321c..18b63ced88 100644 --- a/src/Umbraco.Core/Models/Packaging/PreInstallWarnings.cs +++ b/src/Umbraco.Core/Models/Packaging/PreInstallWarnings.cs @@ -1,17 +1,16 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Runtime.Serialization; namespace Umbraco.Core.Models.Packaging { - [Serializable] - [DataContract(IsReference = true)] - internal class PreInstallWarnings + public class PreInstallWarnings { - public KeyValuePair[] UnsecureFiles { get; set; } - public KeyValuePair[] FilesReplaced { get; set; } - public IEnumerable ConflictingMacroAliases { get; set; } - public IEnumerable ConflictingTemplateAliases { get; set; } - public IEnumerable ConflictingStylesheetNames { get; set; } + public IEnumerable UnsecureFiles { get; set; } = Enumerable.Empty(); + public IEnumerable FilesReplaced { get; set; } = Enumerable.Empty(); + public IEnumerable ConflictingMacros { get; set; } = Enumerable.Empty(); + public IEnumerable ConflictingTemplates { get; set; } = Enumerable.Empty(); + public IEnumerable ConflictingStylesheets { get; set; } = Enumerable.Empty(); } } diff --git a/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs new file mode 100644 index 0000000000..44dbded5d2 --- /dev/null +++ b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml.Linq; +using Umbraco.Core.IO; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Packaging; +using File = System.IO.File; + +namespace Umbraco.Core.Packaging +{ + /// + /// Parses the xml document contained in a compiled (zip) Umbraco package + /// + internal class CompiledPackageXmlParser + { + private readonly ConflictingPackageData _conflictingPackageData; + + public CompiledPackageXmlParser(ConflictingPackageData conflictingPackageData) + { + _conflictingPackageData = conflictingPackageData; + } + + public CompiledPackage ToCompiledPackage(XDocument xml, string packageFileName, string applicationRootFolder) + { + if (xml == null) throw new ArgumentNullException(nameof(xml)); + if (xml.Root == null) throw new ArgumentException(nameof(xml), "The xml document is invalid"); + if (xml.Root.Name != Constants.Packaging.UmbPackageNodeName) throw new FormatException("The xml document is invalid"); + + var info = xml.Root.Element("info"); + if (info == null) throw new FormatException("The xml document is invalid"); + var package = info.Element("package"); + if (package == null) throw new FormatException("The xml document is invalid"); + var author = info.Element("author"); + if (author == null) throw new FormatException("The xml document is invalid"); + var requirements = package.Element("requirements"); + if (requirements == null) throw new FormatException("The xml document is invalid"); + + var def = new CompiledPackage + { + PackageFileName = packageFileName, + Name = package.Element("name")?.Value, + Author = author.Element("name")?.Value, + AuthorUrl = author.Element("website")?.Value, + Version = package.Element("version")?.Value, + Readme = info.Element("readme")?.Value, + License = package.Element("license")?.Value, + LicenseUrl = package.Element("license")?.AttributeValue("url"), + Url = package.Element("url")?.Value, + IconUrl = package.Element("iconUrl")?.Value, + UmbracoVersion = new Version((int)requirements.Element("major"), (int)requirements.Element("minor"), (int)requirements.Element("patch")), + UmbracoVersionRequirementsType = requirements.AttributeValue("type").IsNullOrWhiteSpace() ? RequirementsType.Legacy : Enum.Parse(requirements.AttributeValue("type")), + Control = package.Element("control")?.Value, + Actions = xml.Element("Actions")?.ToString(SaveOptions.None) ?? "", //take the entire outer xml value + + Files = xml.Root.Element("files")?.Elements("file")?.Select(x => new CompiledPackageFile + { + UniqueFileName = x.Element("guid")?.Value, + OriginalName = x.Element("orgName")?.Value, + OriginalPath = x.Element("orgPath")?.Value + }).ToList() ?? new List(), + + Macros = xml.Element("Macros")?.Elements("macro") ?? Enumerable.Empty(), + Templates = xml.Element("Templates")?.Elements("Template") ?? Enumerable.Empty(), + Stylesheets = xml.Element("Stylesheets")?.Elements("styleSheet") ?? Enumerable.Empty(), + + }; + + def.Warnings = GetPreInstallWarnings(def, applicationRootFolder); + + return def; + } + + private PreInstallWarnings GetPreInstallWarnings(CompiledPackage package, string applicationRootFolder) + { + var sourceDestination = ExtractSourceDestinationFileInformation(package.Files); + + var installWarnings = new PreInstallWarnings + { + ConflictingMacros = _conflictingPackageData.FindConflictingMacros(package.Macros), + ConflictingTemplates = _conflictingPackageData.FindConflictingTemplates(package.Templates), + ConflictingStylesheets = _conflictingPackageData.FindConflictingStylesheets(package.Stylesheets), + UnsecureFiles = FindUnsecureFiles(sourceDestination), + FilesReplaced = FindFilesToBeReplaced(sourceDestination, applicationRootFolder) + }; + + return installWarnings; + } + + /// + /// Returns a tuple of the zip file's unique file name and it's application relative path + /// + /// + /// + public (string packageUniqueFile, string appRelativePath)[] ExtractSourceDestinationFileInformation(IEnumerable packageFiles) + { + return packageFiles + .Select(e => + { + var fileName = PrepareAsFilePathElement(e.OriginalName); + var relativeDir = UpdatePathPlaceholders(PrepareAsFilePathElement(e.OriginalPath)); + var relativePath = Path.Combine(relativeDir, fileName); + return (e.UniqueFileName, relativePath); + }).ToArray(); + } + + private IEnumerable FindFilesToBeReplaced(IEnumerable<(string packageUniqueFile, string appRelativePath)> sourceDestination, string applicationRootFolder) + { + return sourceDestination.Where(sd => File.Exists(Path.Combine(applicationRootFolder, sd.appRelativePath))) + .Select(x => x.appRelativePath) + .ToArray(); + } + + private IEnumerable FindUnsecureFiles(IEnumerable<(string packageUniqueFile, string appRelativePath)> sourceDestinationPair) + { + return sourceDestinationPair.Where(sd => IsFileDestinationUnsecure(sd.appRelativePath)) + .Select(x => x.appRelativePath) + .ToList(); + } + + private bool IsFileDestinationUnsecure(string destination) + { + var unsecureDirNames = new[] { "bin", "app_code" }; + if (unsecureDirNames.Any(ud => destination.StartsWith(ud, StringComparison.InvariantCultureIgnoreCase))) + return true; + + string extension = Path.GetExtension(destination); + return extension != null && extension.Equals(".dll", StringComparison.InvariantCultureIgnoreCase); + } + + private static string PrepareAsFilePathElement(string pathElement) + { + return pathElement.TrimStart(new[] { '\\', '/', '~' }).Replace("/", "\\"); + } + + //fixme: This is duplicated in the parser + public static string UpdatePathPlaceholders(string path) + { + if (path.Contains("[$")) + { + //this is experimental and undocumented... + path = path.Replace("[$UMBRACO]", SystemDirectories.Umbraco); + path = path.Replace("[$CONFIG]", SystemDirectories.Config); + path = path.Replace("[$DATA]", SystemDirectories.Data); + } + return path; + } + } +} diff --git a/src/Umbraco.Core/Packaging/ConflictingPackageData.cs b/src/Umbraco.Core/Packaging/ConflictingPackageData.cs index b0424067bf..401d3b6a85 100644 --- a/src/Umbraco.Core/Packaging/ConflictingPackageData.cs +++ b/src/Umbraco.Core/Packaging/ConflictingPackageData.cs @@ -7,82 +7,53 @@ using Umbraco.Core.Services; namespace Umbraco.Core.Packaging { - internal class ConflictingPackageData : IConflictingPackageData + internal class ConflictingPackageData { private readonly IMacroService _macroService; private readonly IFileService _fileService; - public ConflictingPackageData(IMacroService macroService, - IFileService fileService) + public ConflictingPackageData(IMacroService macroService, IFileService fileService) { - if (fileService != null) _fileService = fileService; - else throw new ArgumentNullException("fileService"); - if (macroService != null) _macroService = macroService; - else throw new ArgumentNullException("macroService"); + _fileService = fileService ?? throw new ArgumentNullException(nameof(fileService)); + _macroService = macroService ?? throw new ArgumentNullException(nameof(macroService)); } - public IEnumerable FindConflictingStylesheets(XElement stylesheetNotes) + public IEnumerable FindConflictingStylesheets(IEnumerable stylesheetNodes) { - if (string.Equals(Constants.Packaging.StylesheetsNodeName, stylesheetNotes.Name.LocalName) == false) - { - throw new ArgumentException("the root element must be \"" + Constants.Packaging.StylesheetsNodeName + "\"", "stylesheetNotes"); - } - - return stylesheetNotes.Elements(Constants.Packaging.StylesheetNodeName) + return stylesheetNodes .Select(n => { - XElement xElement = n.Element(Constants.Packaging.NameNodeName); + var xElement = n.Element(Constants.Packaging.NameNodeName); if (xElement == null) - { - throw new ArgumentException("Missing \"" + Constants.Packaging.NameNodeName + "\" element", - "stylesheetNotes"); - } + throw new FormatException($"Missing \"{Constants.Packaging.NameNodeName}\" element"); return _fileService.GetStylesheetByName(xElement.Value) as IFile; }) .Where(v => v != null); } - public IEnumerable FindConflictingTemplates(XElement templateNotes) + public IEnumerable FindConflictingTemplates(IEnumerable templateNodes) { - if (string.Equals(Constants.Packaging.TemplatesNodeName, templateNotes.Name.LocalName) == false) - { - throw new ArgumentException("Node must be a \"" + Constants.Packaging.TemplatesNodeName + "\" node", - "templateNotes"); - } - - return templateNotes.Elements(Constants.Packaging.TemplateNodeName) + return templateNodes .Select(n => { - XElement xElement = n.Element(Constants.Packaging.AliasNodeNameCapital) ?? n.Element(Constants.Packaging.AliasNodeNameSmall); + var xElement = n.Element(Constants.Packaging.AliasNodeNameCapital) ?? n.Element(Constants.Packaging.AliasNodeNameSmall); if (xElement == null) - { - throw new ArgumentException("missing a \"" + Constants.Packaging.AliasNodeNameCapital + "\" element", - "templateNotes"); - } + throw new FormatException($"missing a \"{Constants.Packaging.AliasNodeNameCapital}\" element"); return _fileService.GetTemplate(xElement.Value); }) .Where(v => v != null); } - public IEnumerable FindConflictingMacros(XElement macroNodes) + public IEnumerable FindConflictingMacros(IEnumerable macroNodes) { - if (string.Equals(Constants.Packaging.MacrosNodeName, macroNodes.Name.LocalName) == false) - { - throw new ArgumentException("Node must be a \"" + Constants.Packaging.MacrosNodeName + "\" node", - "macroNodes"); - } - - return macroNodes.Elements(Constants.Packaging.MacroNodeName) + return macroNodes .Select(n => { - XElement xElement = n.Element(Constants.Packaging.AliasNodeNameSmall) ?? n.Element(Constants.Packaging.AliasNodeNameCapital); + var xElement = n.Element(Constants.Packaging.AliasNodeNameSmall) ?? n.Element(Constants.Packaging.AliasNodeNameCapital); if (xElement == null) - { - throw new ArgumentException(string.Format("missing a \"{0}\" element in {0} element", Constants.Packaging.AliasNodeNameSmall), - "macroNodes"); - } + throw new FormatException($"missing a \"{Constants.Packaging.AliasNodeNameSmall}\" element in {Constants.Packaging.AliasNodeNameSmall} element"); return _macroService.GetByAlias(xElement.Value); }) diff --git a/src/Umbraco.Core/Packaging/IConflictingPackageData.cs b/src/Umbraco.Core/Packaging/IConflictingPackageData.cs deleted file mode 100644 index 12f3e8f8a4..0000000000 --- a/src/Umbraco.Core/Packaging/IConflictingPackageData.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.Generic; -using System.Xml.Linq; -using Umbraco.Core.Models; - -namespace Umbraco.Core.Packaging -{ - internal interface IConflictingPackageData - { - IEnumerable FindConflictingStylesheets(XElement stylesheetNotes); - IEnumerable FindConflictingTemplates(XElement templateNotes); - IEnumerable FindConflictingMacros(XElement macroNodes); - } -} diff --git a/src/Umbraco.Core/Packaging/IPackageExtraction.cs b/src/Umbraco.Core/Packaging/IPackageExtraction.cs deleted file mode 100644 index 02e98dc539..0000000000 --- a/src/Umbraco.Core/Packaging/IPackageExtraction.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System.Collections.Generic; - -namespace Umbraco.Core.Packaging -{ - /// - /// Used to access an umbraco package (zip) file - /// Remeber that filenames must be unique - /// use "FindDubletFileNames" for sanitycheck - /// - internal interface IPackageExtraction - { - /// - /// Returns the content of the file with the given filename - /// - /// Full path to the umbraco package file - /// filename of the file for wich to get the text content - /// this is the relative directory for the location of the file in the package - /// I dont know why umbraco packages contains directories in the first place?? - /// text content of the file - string ReadTextFileFromArchive(string packageFilePath, string fileToRead, out string directoryInPackage); - - /// - /// Copies a file from package to given destination - /// - /// Full path to the ubraco package file - /// filename of the file to copy - /// destination path (including destination filename) - /// True a file was overwritten - void CopyFileFromArchive(string packageFilePath, string fileInPackageName, string destinationfilePath); - - /// - /// Copies a file from package to given destination - /// - /// Full path to the ubraco package file - /// Key: Source file in package. Value: Destination path inclusive file name - void CopyFilesFromArchive(string packageFilePath, IEnumerable> sourceDestination); - - /// - /// Check if given list of files can be found in the package - /// - /// Full path to the umbraco package file - /// a list of files you would like to find in the package - /// a subset if any of the files in "expectedFiles" that could not be found in the package - IEnumerable FindMissingFiles(string packageFilePath, IEnumerable expectedFiles); - - - /// - /// Sanitycheck - should return en empty collection if package is valid - /// - /// Full path to the umbraco package file - /// list of files that are found more than ones (accross directories) in the package - IEnumerable FindDubletFileNames(string packageFilePath); - - /// - /// Reads the given files from archive and returns them as a collection of byte arrays - /// - /// - /// - /// - IEnumerable ReadFilesFromArchive(string packageFilePath, IEnumerable filesToGet); - } -} diff --git a/src/Umbraco.Core/Packaging/IPackageInstallation.cs b/src/Umbraco.Core/Packaging/IPackageInstallation.cs index e8950390e4..8ef3546c3d 100644 --- a/src/Umbraco.Core/Packaging/IPackageInstallation.cs +++ b/src/Umbraco.Core/Packaging/IPackageInstallation.cs @@ -1,15 +1,35 @@ -using System.Xml.Linq; +using System.Collections.Generic; +using System.Xml.Linq; using Umbraco.Core.Models.Packaging; namespace Umbraco.Core.Packaging { internal interface IPackageInstallation { - //fixme: The reason why this isn't used currently is because package installation needs to be done in phases since - // there are app domain reboots involved so a single method cannot be used. This needs to either be split into several - // methods or return an object with a callback to proceed to the next step. - InstallationSummary InstallPackage(string packageFilePath, int userId); - IPackageInfo GetMetaData(string packageFilePath); - PreInstallWarnings GetPreInstallWarnings(string packageFilePath); + + /// + /// Installs a packages data and entities + /// + /// + /// + /// + /// + InstallationSummary InstallPackageData(PackageDefinition packageDefinition, CompiledPackage compiledPackage, int userId); + + /// + /// Installs a packages files + /// + /// + /// + /// + /// + IEnumerable InstallPackageFiles(PackageDefinition packageDefinition, CompiledPackage compiledPackage, int userId); + + /// + /// Reads the package (zip) file and returns the model + /// + /// + /// + CompiledPackage ReadPackage(string packageFileName); } } diff --git a/src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs b/src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs index 6f3e4a3603..9a996d201f 100644 --- a/src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs +++ b/src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs @@ -8,60 +8,6 @@ using Umbraco.Core.Models.Packaging; namespace Umbraco.Core.Packaging { - /// - /// Parses the xml document contained in a compiled (zip) Umbraco package - /// - public class CompiledPackageXmlParser - { - public CompiledPackageXmlParser() - { - - } - - public CompiledPackage ToCompiledPackage(XDocument xml) - { - if (xml == null) throw new ArgumentNullException(nameof(xml)); - if (xml.Root == null) throw new ArgumentException(nameof(xml), "The xml document is invalid"); - if (xml.Root.Name != Constants.Packaging.UmbPackageNodeName) throw new FormatException("The xml document is invalid"); - - var info = xml.Root.Element("info"); - if (info == null) throw new FormatException("The xml document is invalid"); - var package = xml.Element("package"); - if (package == null) throw new FormatException("The xml document is invalid"); - var author = package.Element("author"); - if (author == null) throw new FormatException("The xml document is invalid"); - var requirements = package.Element("requirements"); - if (requirements == null) throw new FormatException("The xml document is invalid"); - - var def = new CompiledPackage - { - Name = package.Element("name")?.Value, - Author = author.Element("name")?.Value, - AuthorUrl = author.Element("website")?.Value, - Version = package.Element("version")?.Value, - Readme = info.Element("readme")?.Value, - License = package.Element("license")?.Value, - LicenseUrl = package.Element("license")?.AttributeValue("url"), - Url = package.Element("url")?.Value, - IconUrl = package.Element("iconUrl")?.Value, - UmbracoVersion = new Version((int)requirements.Element("major"), (int)requirements.Element("minor"), (int)requirements.Element("patch")), - UmbracoVersionRequirementsType = Enum.Parse(requirements.AttributeValue("type")), - Control = package.Element("control")?.Value, - - Files = xml.Root.Element("files")?.Elements("files")?.Select(x => new CompiledPackageFile - { - UniqueFileName = x.Element("guid")?.Value, - OriginalName = x.Element("orgPath")?.Value, - OriginalPath = x.Element("orgName")?.Value - }).ToList() ?? new List(), - - }; - - return def; - } - - } - /// /// Converts a to and from XML /// diff --git a/src/Umbraco.Core/Packaging/PackageExtraction.cs b/src/Umbraco.Core/Packaging/PackageExtraction.cs index cc3c394732..48093da45f 100644 --- a/src/Umbraco.Core/Packaging/PackageExtraction.cs +++ b/src/Umbraco.Core/Packaging/PackageExtraction.cs @@ -6,15 +6,15 @@ using System.IO.Compression; namespace Umbraco.Core.Packaging { - internal class PackageExtraction : IPackageExtraction + internal class PackageExtraction { - public string ReadTextFileFromArchive(string packageFilePath, string fileToRead, out string directoryInPackage) + public string ReadTextFileFromArchive(FileInfo packageFile, string fileToRead, out string directoryInPackage) { string retVal = null; bool fileFound = false; string foundDir = null; - ReadZipfileEntries(packageFilePath, entry => + ReadZipfileEntries(packageFile, entry => { string fileName = Path.GetFileName(entry.Name); @@ -36,55 +36,50 @@ namespace Umbraco.Core.Packaging if (fileFound == false) { directoryInPackage = null; - throw new FileNotFoundException(string.Format("Could not find file in package {0}", packageFilePath), fileToRead); + throw new FileNotFoundException($"Could not find file in package {packageFile}", fileToRead); } directoryInPackage = foundDir; return retVal; } - private static void CheckPackageExists(string packageFilePath) + private static void CheckPackageExists(FileInfo packageFile) { - if (string.IsNullOrEmpty(packageFilePath)) - { - throw new ArgumentNullException("packageFilePath"); - } + if (packageFile == null) throw new ArgumentNullException(nameof(packageFile)); + + if (!packageFile.Exists) + throw new ArgumentException($"Package file: {packageFile} could not be found"); - if (File.Exists(packageFilePath) == false) - { - if (File.Exists(packageFilePath) == false) - throw new ArgumentException(string.Format("Package file: {0} could not be found", packageFilePath)); - } - - string extension = Path.GetExtension(packageFilePath).ToLower(); + var extension = packageFile.Extension; var alowedExtension = new[] { ".umb", ".zip" }; // Check if the file is a valid package - if (alowedExtension.All(ae => ae.Equals(extension) == false)) + if (alowedExtension.All(ae => ae.InvariantEquals(extension) == false)) { - throw new ArgumentException( - string.Format("Error - file isn't a package. only extentions: \"{0}\" is allowed", string.Join(", ", alowedExtension))); + throw new ArgumentException("Error - file isn't a package. only extentions: \"{string.Join(", ", alowedExtension)}\" is allowed"); } } - public void CopyFileFromArchive(string packageFilePath, string fileInPackageName, string destinationfilePath) + public void CopyFileFromArchive(FileInfo packageFile, string fileInPackageName, string destinationfilePath) { - CopyFilesFromArchive(packageFilePath, new[]{new KeyValuePair(fileInPackageName, destinationfilePath) } ); + CopyFilesFromArchive(packageFile, new[] {(fileInPackageName, destinationfilePath)}); } - public void CopyFilesFromArchive(string packageFilePath, IEnumerable> sourceDestination) + public void CopyFilesFromArchive(FileInfo packageFile, IEnumerable<(string packageUniqueFile, string appAbsolutePath)> sourceDestination) { - var d = sourceDestination.ToDictionary(k => k.Key.ToLower(), v => v.Value); + var d = sourceDestination.ToDictionary(k => k.packageUniqueFile.ToLower(), v => v.appAbsolutePath); - ReadZipfileEntries(packageFilePath, entry => + ReadZipfileEntries(packageFile, entry => { - string fileName = (Path.GetFileName(entry.Name) ?? string.Empty).ToLower(); + var fileName = (Path.GetFileName(entry.Name) ?? string.Empty).ToLower(); if (fileName == string.Empty) { return true; } - string destination; - if (string.IsNullOrEmpty(fileName) == false && d.TryGetValue(fileName, out destination)) + if (string.IsNullOrEmpty(fileName) == false && d.TryGetValue(fileName, out var destination)) { + //ensure the dir exists + Directory.CreateDirectory(Path.GetDirectoryName(destination)); + using (var streamWriter = File.Open(destination, FileMode.Create)) using (var entryStream = entry.Open()) { @@ -99,15 +94,15 @@ namespace Umbraco.Core.Packaging if (d.Any()) { - throw new ArgumentException(string.Format("The following source file(s): \"{0}\" could not be found in archive: \"{1}\"", string.Join("\", \"",d.Keys), packageFilePath)); + throw new ArgumentException(string.Format("The following source file(s): \"{0}\" could not be found in archive: \"{1}\"", string.Join("\", \"",d.Keys), packageFile)); } } - public IEnumerable FindMissingFiles(string packageFilePath, IEnumerable expectedFiles) + public IEnumerable FindMissingFiles(FileInfo packageFile, IEnumerable expectedFiles) { var retVal = expectedFiles.ToList(); - ReadZipfileEntries(packageFilePath, zipEntry => + ReadZipfileEntries(packageFile, zipEntry => { string fileName = Path.GetFileName(zipEntry.Name); @@ -121,17 +116,16 @@ namespace Umbraco.Core.Packaging } - public IEnumerable FindDubletFileNames(string packageFilePath) + public IEnumerable FindDuplicateFileNames(FileInfo packageFile) { var dictionary = new Dictionary>(); - ReadZipfileEntries(packageFilePath, entry => + ReadZipfileEntries(packageFile, entry => { - string fileName = (Path.GetFileName(entry.Name) ?? string.Empty).ToLower(); + var fileName = (Path.GetFileName(entry.Name) ?? string.Empty).ToLower(); - List list; - if (dictionary.TryGetValue(fileName, out list) == false) + if (dictionary.TryGetValue(fileName, out var list) == false) { list = new List(); dictionary.Add(fileName, list); @@ -145,13 +139,13 @@ namespace Umbraco.Core.Packaging return dictionary.Values.Where(v => v.Count > 1).SelectMany(v => v); } - public IEnumerable ReadFilesFromArchive(string packageFilePath, IEnumerable filesToGet) + public IEnumerable ReadFilesFromArchive(FileInfo packageFile, IEnumerable filesToGet) { - CheckPackageExists(packageFilePath); + CheckPackageExists(packageFile); var files = new HashSet(filesToGet.Select(f => f.ToLowerInvariant())); - using (var fs = File.OpenRead(packageFilePath)) + using (var fs = packageFile.OpenRead()) using (var zipArchive = new ZipArchive(fs)) { foreach (var zipEntry in zipArchive.Entries) @@ -172,11 +166,11 @@ namespace Umbraco.Core.Packaging } } - private void ReadZipfileEntries(string packageFilePath, Func entryFunc, bool skipsDirectories = true) + private void ReadZipfileEntries(FileInfo packageFile, Func entryFunc, bool skipsDirectories = true) { - CheckPackageExists(packageFilePath); + CheckPackageExists(packageFile); - using (var fs = File.OpenRead(packageFilePath)) + using (var fs = packageFile.OpenRead()) using (var zipArchive = new ZipArchive(fs)) { foreach (var zipEntry in zipArchive.Entries) diff --git a/src/Umbraco.Core/Packaging/PackageFileInstallation.cs b/src/Umbraco.Core/Packaging/PackageFileInstallation.cs new file mode 100644 index 0000000000..55b8bdc63e --- /dev/null +++ b/src/Umbraco.Core/Packaging/PackageFileInstallation.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Xml.Linq; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Packaging; +using Umbraco.Core.Services; +using File = System.IO.File; + +namespace Umbraco.Core.Packaging +{ + /// + /// Installs package files + /// + internal class PackageFileInstallation + { + private readonly CompiledPackageXmlParser _parser; + private readonly IProfilingLogger _logger; + private readonly PackageExtraction _packageExtraction; + + public PackageFileInstallation(CompiledPackageXmlParser parser, IProfilingLogger logger) + { + _parser = parser; + _logger = logger; + _packageExtraction = new PackageExtraction(); + } + + /// + /// Returns a list of all installed file paths + /// + /// + /// + /// + /// The absolute path of where to extract the package files (normally the application root) + /// + /// + public IEnumerable InstallFiles(CompiledPackage compiledPackage, FileInfo packageFile, string targetRootFolder) + { + using (_logger.DebugDuration( + "Installing package files for package " + compiledPackage.Name, + "Package file installation complete for package " + compiledPackage.Name)) + { + var sourceAndRelativeDest = _parser.ExtractSourceDestinationFileInformation(compiledPackage.Files); + var sourceAndAbsDest = AppendRootToDestination(targetRootFolder, sourceAndRelativeDest); + + _packageExtraction.CopyFilesFromArchive(packageFile, sourceAndAbsDest); + + return sourceAndRelativeDest.Select(sd => sd.appRelativePath).ToArray(); + } + } + + private static IEnumerable<(string packageUniqueFile, string appAbsolutePath)> AppendRootToDestination(string applicationRootFolder, IEnumerable<(string packageUniqueFile, string appRelativePath)> sourceDestination) + { + return + sourceDestination.Select( + sd => (sd.packageUniqueFile, Path.Combine(applicationRootFolder, sd.appRelativePath))).ToArray(); + } + } +} diff --git a/src/Umbraco.Core/Packaging/PackageInstallation.cs b/src/Umbraco.Core/Packaging/PackageInstallation.cs index 6c9cd57451..5dd88e5a56 100644 --- a/src/Umbraco.Core/Packaging/PackageInstallation.cs +++ b/src/Umbraco.Core/Packaging/PackageInstallation.cs @@ -1,262 +1,221 @@ using System; using System.Collections.Generic; -using System.Data; using System.IO; using System.Linq; using System.Xml.Linq; -using System.Xml.XPath; -using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.Models; using Umbraco.Core.Models.Packaging; using Umbraco.Core.Services; -using File = System.IO.File; namespace Umbraco.Core.Packaging { + internal class PackageInstallation : IPackageInstallation { - private readonly IFileService _fileService; - private readonly IMacroService _macroService; private readonly IPackagingService _packagingService; - private IConflictingPackageData _conflictingPackageData; - private readonly IPackageExtraction _packageExtraction; - private string _fullPathToRoot; - - public PackageInstallation(IPackagingService packagingService, IMacroService macroService, - IFileService fileService, IPackageExtraction packageExtraction) - : this(packagingService, macroService, fileService, packageExtraction, IOHelper.GetRootDirectorySafe()) - {} - - public PackageInstallation(IPackagingService packagingService, IMacroService macroService, - IFileService fileService, IPackageExtraction packageExtraction, string fullPathToRoot) - { - if (packageExtraction != null) _packageExtraction = packageExtraction; - else throw new ArgumentNullException("packageExtraction"); - - if (macroService != null) _macroService = macroService; - else throw new ArgumentNullException("macroService"); - - if (fileService != null) _fileService = fileService; - else throw new ArgumentNullException("fileService"); - - if (packagingService != null) _packagingService = packagingService; - else throw new ArgumentNullException("packagingService"); - - _fullPathToRoot = fullPathToRoot; - } - - public IConflictingPackageData ConflictingPackageData - { - private get - { - return _conflictingPackageData ?? - (_conflictingPackageData = new ConflictingPackageData(_macroService, _fileService)); - } - set - { - if (_conflictingPackageData != null) - { - throw new PropertyConstraintException("This property already have a value"); - } - _conflictingPackageData = value; - } - } - - public string FullPathToRoot - { - private get { return _fullPathToRoot; } - set - { - - if (_fullPathToRoot != null) - { - throw new PropertyConstraintException("This property already have a value"); - } - - _fullPathToRoot = value; - } - } - - public IPackageInfo GetMetaData(string packageFilePath) - { - try - { - XElement rootElement = GetConfigXmlElement(packageFilePath); - return GetMetaData(rootElement); - } - catch (Exception e) - { - throw new Exception("Error reading " + packageFilePath, e); - } - } - - public PreInstallWarnings GetPreInstallWarnings(string packageFilePath) - { - try - { - XElement rootElement = GetConfigXmlElement(packageFilePath); - return GetPreInstallWarnings(rootElement); - } - catch (Exception e) - { - throw new Exception("Error reading " + packageFilePath, e); - } - } - - public InstallationSummary InstallPackage(string packageFile, int userId) - { - XElement dataTypes; - XElement languages; - XElement dictionaryItems; - XElement macroes; - XElement files; - XElement templates; - XElement documentTypes; - XElement styleSheets; - XElement documentSet; - XElement documents; - XElement actions; - IPackageInfo metaData; - InstallationSummary installationSummary; - - try - { - XElement rootElement = GetConfigXmlElement(packageFile); - PackageSupportedCheck(rootElement); - PackageStructureSanityCheck(packageFile, rootElement); - dataTypes = rootElement.Element(Constants.Packaging.DataTypesNodeName); - languages = rootElement.Element(Constants.Packaging.LanguagesNodeName); - dictionaryItems = rootElement.Element(Constants.Packaging.DictionaryItemsNodeName); - macroes = rootElement.Element(Constants.Packaging.MacrosNodeName); - files = rootElement.Element(Constants.Packaging.FilesNodeName); - templates = rootElement.Element(Constants.Packaging.TemplatesNodeName); - documentTypes = rootElement.Element(Constants.Packaging.DocumentTypesNodeName); - styleSheets = rootElement.Element(Constants.Packaging.StylesheetsNodeName); - documentSet = rootElement.Element(Constants.Packaging.DocumentSetNodeName); - documents = rootElement.Element(Constants.Packaging.DocumentsNodeName); - actions = rootElement.Element(Constants.Packaging.ActionsNodeName); - - metaData = GetMetaData(rootElement); - installationSummary = new InstallationSummary {MetaData = metaData}; - } - catch (Exception e) - { - throw new Exception("Error reading " + packageFile, e); - } - - try - { - var dataTypeDefinitions = EmptyEnumerableIfNull(dataTypes) ?? InstallDataTypes(dataTypes, userId); - installationSummary.DataTypesInstalled = dataTypeDefinitions; - - var languagesInstalled = EmptyEnumerableIfNull(languages) ?? InstallLanguages(languages, userId); - installationSummary.LanguagesInstalled = languagesInstalled; - - var dictionaryInstalled = EmptyEnumerableIfNull(dictionaryItems) ?? InstallDictionaryItems(dictionaryItems); - installationSummary.DictionaryItemsInstalled = dictionaryInstalled; - - var macros = EmptyEnumerableIfNull(macroes) ?? InstallMacros(macroes, userId); - installationSummary.MacrosInstalled = macros; - - var keyValuePairs = EmptyEnumerableIfNull(packageFile) ?? InstallFiles(packageFile, files); - installationSummary.FilesInstalled = keyValuePairs; - - var templatesInstalled = EmptyEnumerableIfNull(templates) ?? InstallTemplats(templates, userId); - installationSummary.TemplatesInstalled = templatesInstalled; - - var documentTypesInstalled = EmptyEnumerableIfNull(documentTypes) ?? InstallDocumentTypes(documentTypes, userId); - installationSummary.ContentTypesInstalled =documentTypesInstalled; - - var stylesheetsInstalled = EmptyEnumerableIfNull(styleSheets) ?? InstallStylesheets(styleSheets); - installationSummary.StylesheetsInstalled = stylesheetsInstalled; - - var documentsInstalled = documents != null ? InstallDocuments(documents, userId) - : EmptyEnumerableIfNull(documentSet) - ?? InstallDocuments(documentSet, userId); - installationSummary.ContentInstalled = documentsInstalled; - - var packageActions = EmptyEnumerableIfNull(actions) ?? GetPackageActions(actions, metaData.Name); - installationSummary.Actions = packageActions; - - installationSummary.PackageInstalled = true; - - return installationSummary; - } - catch (Exception e) - { - throw new Exception("Error installing package " + packageFile, e); - } - } + private readonly PackageExtraction _packageExtraction; + private readonly PackageFileInstallation _packageFileInstallation; + private readonly CompiledPackageXmlParser _parser; + private readonly string _packagesFolderPath; + private readonly DirectoryInfo _packageExtractionFolder; + private readonly DirectoryInfo _applicationRootFolder; /// - /// Temperary check to test that we support stylesheets + /// Constructor /// - /// - private void PackageSupportedCheck(XElement rootElement) + /// + /// + /// + /// + /// The relative path of the package storage folder (i.e. ~/App_Data/Packages ) + /// + /// + /// The root folder of the application + /// + /// + /// The destination root folder to extract the package files (generally the same as applicationRoot) but can be modified for testing + /// + public PackageInstallation(IPackagingService packagingService, PackageFileInstallation packageFileInstallation, CompiledPackageXmlParser parser, + string packagesFolderPath, DirectoryInfo applicationRootFolder, DirectoryInfo packageExtractionFolder) { - XElement styleSheets = rootElement.Element(Constants.Packaging.StylesheetsNodeName); - if (styleSheets != null && styleSheets.Elements().Any()) - throw new NotSupportedException("Stylesheets is not suported in this version of umbraco"); + _packageExtraction = new PackageExtraction(); + _packageFileInstallation = packageFileInstallation; + _packagingService = packagingService ?? throw new ArgumentNullException(nameof(packagingService)); + _parser = parser; + _packagesFolderPath = packagesFolderPath; + _applicationRootFolder = applicationRootFolder; + _packageExtractionFolder = packageExtractionFolder; } - private static T[] EmptyArrayIfNull(object obj) + public CompiledPackage ReadPackage(string packageFileName) { - return obj == null ? new T[0] : null; + if (packageFileName == null) throw new ArgumentNullException(nameof(packageFileName)); + var packageZipFile = GetPackageZipFile(packageFileName); + var doc = GetConfigXmlDoc(packageZipFile); + + var compiledPackage = _parser.ToCompiledPackage(doc, Path.GetFileName(packageZipFile.FullName), _applicationRootFolder.FullName); + + ValidatePackageFile(packageZipFile, compiledPackage); + + return compiledPackage; } + //fixme: Should we move all of the ImportXXXX methods here instead of on the IPackagingService? we don't want to have cicurlar refs + + public IEnumerable InstallPackageFiles(PackageDefinition packageDefinition, CompiledPackage compiledPackage, int userId) + { + if (packageDefinition == null) throw new ArgumentNullException(nameof(packageDefinition)); + if (compiledPackage == null) throw new ArgumentNullException(nameof(compiledPackage)); + + //these should be the same, TODO: we should have a better validator for this + if (packageDefinition.Name != compiledPackage.Name) + throw new InvalidOperationException("The package definition does not match the compiled package manifest"); + + var packageZipFile = GetPackageZipFile(compiledPackage.PackageFileName); + + return _packageFileInstallation.InstallFiles(compiledPackage, packageZipFile, _packageExtractionFolder.FullName); + } + + public InstallationSummary InstallPackageData(PackageDefinition packageDefinition, CompiledPackage compiledPackage, int userId) + { + //fixme: fill this in + throw new NotImplementedException(); + } + + //public InstallationSummary InstallPackage(FileInfo packageFile, int userId) + //{ + // XElement dataTypes; + // XElement languages; + // XElement dictionaryItems; + // XElement macroes; + // XElement files; + // XElement templates; + // XElement documentTypes; + // XElement styleSheets; + // XElement documentSet; + // XElement documents; + // XElement actions; + // IPackageInfo metaData; + // InstallationSummary installationSummary; + + // try + // { + // XElement rootElement = GetConfigXmlElement(packageFile); + // PackageSupportedCheck(rootElement); + // PackageStructureSanityCheck(packageFile, rootElement); + // dataTypes = rootElement.Element(Constants.Packaging.DataTypesNodeName); + // languages = rootElement.Element(Constants.Packaging.LanguagesNodeName); + // dictionaryItems = rootElement.Element(Constants.Packaging.DictionaryItemsNodeName); + // macroes = rootElement.Element(Constants.Packaging.MacrosNodeName); + // files = rootElement.Element(Constants.Packaging.FilesNodeName); + // templates = rootElement.Element(Constants.Packaging.TemplatesNodeName); + // documentTypes = rootElement.Element(Constants.Packaging.DocumentTypesNodeName); + // styleSheets = rootElement.Element(Constants.Packaging.StylesheetsNodeName); + // documentSet = rootElement.Element(Constants.Packaging.DocumentSetNodeName); + // documents = rootElement.Element(Constants.Packaging.DocumentsNodeName); + // actions = rootElement.Element(Constants.Packaging.ActionsNodeName); + + // metaData = GetMetaData(rootElement); + // installationSummary = new InstallationSummary {MetaData = metaData}; + // } + // catch (Exception e) + // { + // throw new Exception("Error reading " + packageFile, e); + // } + + // try + // { + // var dataTypeDefinitions = EmptyEnumerableIfNull(dataTypes) ?? InstallDataTypes(dataTypes, userId); + // installationSummary.DataTypesInstalled = dataTypeDefinitions; + + // var languagesInstalled = EmptyEnumerableIfNull(languages) ?? InstallLanguages(languages, userId); + // installationSummary.LanguagesInstalled = languagesInstalled; + + // var dictionaryInstalled = EmptyEnumerableIfNull(dictionaryItems) ?? InstallDictionaryItems(dictionaryItems); + // installationSummary.DictionaryItemsInstalled = dictionaryInstalled; + + // var macros = EmptyEnumerableIfNull(macroes) ?? InstallMacros(macroes, userId); + // installationSummary.MacrosInstalled = macros; + + // var templatesInstalled = EmptyEnumerableIfNull(templates) ?? InstallTemplats(templates, userId); + // installationSummary.TemplatesInstalled = templatesInstalled; + + // var documentTypesInstalled = EmptyEnumerableIfNull(documentTypes) ?? InstallDocumentTypes(documentTypes, userId); + // installationSummary.ContentTypesInstalled =documentTypesInstalled; + + // var stylesheetsInstalled = EmptyEnumerableIfNull(styleSheets) ?? InstallStylesheets(styleSheets); + // installationSummary.StylesheetsInstalled = stylesheetsInstalled; + + // var documentsInstalled = documents != null ? InstallDocuments(documents, userId) + // : EmptyEnumerableIfNull(documentSet) + // ?? InstallDocuments(documentSet, userId); + // installationSummary.ContentInstalled = documentsInstalled; + + // var packageActions = EmptyEnumerableIfNull(actions) ?? GetPackageActions(actions, metaData.Name); + // installationSummary.Actions = packageActions; + + // installationSummary.PackageInstalled = true; + + // return installationSummary; + // } + // catch (Exception e) + // { + // throw new Exception("Error installing package " + packageFile, e); + // } + //} + + private FileInfo GetPackageZipFile(string packageFileName) => new FileInfo(IOHelper.MapPath(_packagesFolderPath).EnsureEndsWith('\\') + packageFileName); + private static IEnumerable EmptyEnumerableIfNull(object obj) { return obj == null ? Enumerable.Empty() : null; } - private XDocument GetConfigXmlDoc(string packageFilePath) + private XDocument GetConfigXmlDoc(FileInfo packageFile) { - var configXmlContent = _packageExtraction.ReadTextFileFromArchive(packageFilePath, - Constants.Packaging.PackageXmlFileName, out _); + var configXmlContent = _packageExtraction.ReadTextFileFromArchive(packageFile, Constants.Packaging.PackageXmlFileName, out _); - return XDocument.Parse(configXmlContent); - } + var document = XDocument.Parse(configXmlContent); - public XElement GetConfigXmlElement(string packageFilePath) - { - var document = GetConfigXmlDoc(packageFilePath); if (document.Root == null || document.Root.Name.LocalName.Equals(Constants.Packaging.UmbPackageNodeName) == false) - { - throw new ArgumentException("xml does not have a root node called \"umbPackage\"", packageFilePath); - } + throw new FormatException("xml does not have a root node called \"umbPackage\""); + + return document; + } + + public XElement GetConfigXmlElement(FileInfo packageFile) + { + var document = GetConfigXmlDoc(packageFile); return document.Root; } - private void PackageStructureSanityCheck(string packageFilePath, XElement rootElement) + private void ValidatePackageFile(FileInfo packageFile, CompiledPackage package) { - XElement filesElement = rootElement.Element(Constants.Packaging.FilesNodeName); - if (filesElement != null) + if (!(package.Files?.Count > 0)) return; + + var sourceDestination = _parser.ExtractSourceDestinationFileInformation(package.Files).ToArray(); + + var missingFiles = _packageExtraction.FindMissingFiles(packageFile, sourceDestination.Select(i => i.packageUniqueFile)).ToArray(); + + if (missingFiles.Any()) { - var sourceDestination = ExtractSourceDestinationFileInformation(filesElement).ToArray(); + throw new Exception("The following file(s) are missing in the package: " + + string.Join(", ", missingFiles.Select( + mf => + { + var sd = sourceDestination.Single(fi => fi.packageUniqueFile == mf); + return $"source: \"{sd.packageUniqueFile}\" destination: \"{sd.appRelativePath}\""; + }))); + } - var missingFiles = _packageExtraction.FindMissingFiles(packageFilePath, sourceDestination.Select(i => i.Key)).ToArray(); + IEnumerable duplicates = _packageExtraction.FindDuplicateFileNames(packageFile).ToArray(); - if (missingFiles.Any()) - { - throw new Exception("The following file(s) are missing in the package: " + - string.Join(", ", missingFiles.Select( - mf => - { - var sd = sourceDestination.Single(fi => fi.Key == mf); - return string.Format("source: \"{0}\" destination: \"{1}\"", - sd.Key, sd.Value); - }))); - } - - IEnumerable dubletFileNames = _packageExtraction.FindDubletFileNames(packageFilePath).ToArray(); - - if (dubletFileNames.Any()) - { - throw new Exception("The following filename(s) are found more than one time in the package, since the filename is used ad primary key, this is not allowed: " + - string.Join(", ", dubletFileNames)); - } + if (duplicates.Any()) + { + throw new Exception("The following filename(s) are found more than one time in the package, since the filename is used ad primary key, this is not allowed: " + + string.Join(", ", duplicates)); } } @@ -346,7 +305,7 @@ namespace Umbraco.Core.Packaging return _packagingService.ImportContentTypes(documentTypes, userId); } - private IEnumerable InstallTemplats(XElement templateElement, int userId = 0) + private IEnumerable InstallTemplates(XElement templateElement, int userId = 0) { if (string.Equals(Constants.Packaging.TemplatesNodeName, templateElement.Name.LocalName) == false) { @@ -356,23 +315,6 @@ namespace Umbraco.Core.Packaging return _packagingService.ImportTemplates(templateElement, userId); } - private IEnumerable InstallFiles(string packageFilePath, XElement filesElement) - { - var sourceDestination = ExtractSourceDestinationFileInformation(filesElement); - sourceDestination = AppendRootToDestination(FullPathToRoot, sourceDestination); - - _packageExtraction.CopyFilesFromArchive(packageFilePath, sourceDestination); - - return sourceDestination.Select(sd => sd.Value).ToArray(); - } - - private KeyValuePair[] AppendRootToDestination(string fullpathToRoot, IEnumerable> sourceDestination) - { - return - sourceDestination.Select( - sd => new KeyValuePair(sd.Key, Path.Combine(fullpathToRoot, sd.Value))).ToArray(); - } - private IEnumerable InstallMacros(XElement macroElements, int userId = 0) { if (string.Equals(Constants.Packaging.MacrosNodeName, macroElements.Name.LocalName) == false) @@ -414,164 +356,6 @@ namespace Umbraco.Core.Packaging } return _packagingService.ImportDataTypeDefinitions(dataTypeElements, userId); } - - private PreInstallWarnings GetPreInstallWarnings(XElement rootElement) - { - XElement files = rootElement.Element(Constants.Packaging.FilesNodeName); - XElement styleSheets = rootElement.Element(Constants.Packaging.StylesheetsNodeName); - XElement templates = rootElement.Element(Constants.Packaging.TemplatesNodeName); - XElement alias = rootElement.Element(Constants.Packaging.MacrosNodeName); - - var sourceDestination = EmptyArrayIfNull>(files) ?? ExtractSourceDestinationFileInformation(files); - - var installWarnings = new PreInstallWarnings(); - - var macroAliases = EmptyEnumerableIfNull(alias) ?? ConflictingPackageData.FindConflictingMacros(alias); - installWarnings.ConflictingMacroAliases = macroAliases; - - var templateAliases = EmptyEnumerableIfNull(templates) ?? ConflictingPackageData.FindConflictingTemplates(templates); - installWarnings.ConflictingTemplateAliases = templateAliases; - - var stylesheetNames = EmptyEnumerableIfNull(styleSheets) ?? ConflictingPackageData.FindConflictingStylesheets(styleSheets); - installWarnings.ConflictingStylesheetNames = stylesheetNames; - - installWarnings.UnsecureFiles = FindUnsecureFiles(sourceDestination); - installWarnings.FilesReplaced = FindFilesToBeReplaced(sourceDestination); - - return installWarnings; - } - - private KeyValuePair[] FindFilesToBeReplaced(IEnumerable> sourceDestination) - { - return sourceDestination.Where(sd => File.Exists(Path.Combine(FullPathToRoot, sd.Value))).ToArray(); - } - - private KeyValuePair[] FindUnsecureFiles(IEnumerable> sourceDestinationPair) - { - return sourceDestinationPair.Where(sd => IsFileDestinationUnsecure(sd.Value)).ToArray(); - } - - private bool IsFileDestinationUnsecure(string destination) - { - var unsecureDirNames = new[] {"bin", "app_code"}; - if(unsecureDirNames.Any(ud => destination.StartsWith(ud, StringComparison.InvariantCultureIgnoreCase))) - return true; - - string extension = Path.GetExtension(destination); - return extension != null && extension.Equals(".dll", StringComparison.InvariantCultureIgnoreCase); - } - - private KeyValuePair[] ExtractSourceDestinationFileInformation(XElement filesElement) - { - if (string.Equals(Constants.Packaging.FilesNodeName, filesElement.Name.LocalName) == false) - { - throw new ArgumentException("the root element must be \"Files\"", "filesElement"); - } - - return filesElement.Elements(Constants.Packaging.FileNodeName) - .Select(e => - { - XElement guidElement = e.Element(Constants.Packaging.GuidNodeName); - if (guidElement == null) - { - throw new ArgumentException("Missing element \"" + Constants.Packaging.GuidNodeName + "\"", - "filesElement"); - } - - XElement orgPathElement = e.Element(Constants.Packaging.OrgPathNodeName); - if (orgPathElement == null) - { - throw new ArgumentException("Missing element \"" + Constants.Packaging.OrgPathNodeName + "\"", - "filesElement"); - } - - XElement orgNameElement = e.Element(Constants.Packaging.OrgNameNodeName); - if (orgNameElement == null) - { - throw new ArgumentException("Missing element \"" + Constants.Packaging.OrgNameNodeName + "\"", - "filesElement"); - } - - var fileName = PrepareAsFilePathElement(orgNameElement.Value); - var relativeDir = UpdatePathPlaceholders(PrepareAsFilePathElement(orgPathElement.Value)); - - var relativePath = Path.Combine(relativeDir, fileName); - - - return new KeyValuePair(guidElement.Value, relativePath); - }).ToArray(); - } - - private static string PrepareAsFilePathElement(string pathElement) - { - return pathElement.TrimStart(new[] {'\\', '/', '~'}).Replace("/", "\\"); - } - - private IPackageInfo GetMetaData(XElement xRootElement) - { - XElement infoElement = xRootElement.Element(Constants.Packaging.InfoNodeName); - - if (infoElement == null) - { - throw new ArgumentException("Did not hold a \"" + Constants.Packaging.InfoNodeName + "\" element", - "xRootElement"); - } - - var majorElement = infoElement.XPathSelectElement(Constants.Packaging.PackageRequirementsMajorXpath); - var minorElement = infoElement.XPathSelectElement(Constants.Packaging.PackageRequirementsMinorXpath); - var patchElement = infoElement.XPathSelectElement(Constants.Packaging.PackageRequirementsPatchXpath); - var nameElement = infoElement.XPathSelectElement(Constants.Packaging.PackageNameXpath); - var versionElement = infoElement.XPathSelectElement(Constants.Packaging.PackageVersionXpath); - var urlElement = infoElement.XPathSelectElement(Constants.Packaging.PackageUrlXpath); - var licenseElement = infoElement.XPathSelectElement(Constants.Packaging.PackageLicenseXpath); - var authorNameElement = infoElement.XPathSelectElement(Constants.Packaging.AuthorNameXpath); - var authorUrlElement = infoElement.XPathSelectElement(Constants.Packaging.AuthorWebsiteXpath); - var readmeElement = infoElement.XPathSelectElement(Constants.Packaging.ReadmeXpath); - - XElement controlElement = xRootElement.Element(Constants.Packaging.ControlNodeName); - - return new PackageDefinition - { - Name = StringValue(nameElement), - Version = StringValue(versionElement), - Url = StringValue(urlElement), - License = StringValue(licenseElement), - LicenseUrl = StringAttribute(licenseElement, Constants.Packaging.PackageLicenseXpathUrlAttribute), - Author = StringValue(authorNameElement), - AuthorUrl = StringValue(authorUrlElement), - Readme = StringValue(readmeElement), - Control = StringValue(controlElement), - UmbracoVersion = new Version(IntValue(majorElement), IntValue(minorElement), IntValue(patchElement)) - }; - } - - private static string StringValue(XElement xElement, string defaultValue = "") - { - return xElement == null ? defaultValue : xElement.Value; - } - - private static string StringAttribute(XElement xElement, string attribute, string defaultValue = "") - { - return xElement == null - ? defaultValue - : xElement.HasAttributes ? xElement.AttributeValue(attribute) : defaultValue; - } - - private static int IntValue(XElement xElement, int defaultValue = 0) - { - return xElement == null ? defaultValue : int.TryParse(xElement.Value, out var val) ? val : defaultValue; - } - - private static string UpdatePathPlaceholders(string path) - { - if (path.Contains("[$")) - { - //this is experimental and undocumented... - path = path.Replace("[$UMBRACO]", SystemDirectories.Umbraco); - path = path.Replace("[$CONFIG]", SystemDirectories.Config); - path = path.Replace("[$DATA]", SystemDirectories.Data); - } - return path; - } + } } diff --git a/src/Umbraco.Core/Services/IPackagingService.cs b/src/Umbraco.Core/Services/IPackagingService.cs index 5b432d3339..fcb0c66401 100644 --- a/src/Umbraco.Core/Services/IPackagingService.cs +++ b/src/Umbraco.Core/Services/IPackagingService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Threading.Tasks; using System.Xml.Linq; using Umbraco.Core.Models; @@ -10,6 +11,33 @@ namespace Umbraco.Core.Services { public interface IPackagingService : IService { + #region Package Installation + + /// + /// Returns a result from an umbraco package file (zip) + /// + /// + /// + CompiledPackage GetCompiledPackageInfo(string packageFileName); + + /// + /// Installs the package files contained in an umbraco package file (zip) + /// + /// + /// + /// + IEnumerable InstallCompiledPackageFiles(PackageDefinition packageDefinition, string packageFileName, int userId = 0); + + /// + /// Installs the data, entities, objects contained in an umbraco package file (zip) + /// + /// + /// + /// + InstallationSummary InstallCompiledPackageData(PackageDefinition packageDefinition, string packageFileName, int userId = 0); + + #endregion + #region Installed Packages IEnumerable GetAllInstalledPackages(); @@ -116,16 +144,18 @@ namespace Umbraco.Core.Services /// Optional id of the User performing the operation. Default is zero (admin) /// Optional parameter indicating whether or not to raise events /// An enumrable list of generated Templates - IEnumerable ImportTemplates(XElement element, int userId = 0, bool raiseEvents = true); + IEnumerable ImportTemplates(XElement element, int userId = 0, bool raiseEvents = true); #endregion /// - /// This will fetch an Umbraco package file from the package repository and return the relative file path to the downloaded package file + /// This will fetch an Umbraco package file from the package repository and return the file name of the downloaded package /// /// /// /// The current user id performing the operation - /// + /// + /// The file name of the downloaded package which will exist in ~/App_Data/packages + /// Task FetchPackageFileAsync(Guid packageId, Version umbracoVersion, int userId); } } diff --git a/src/Umbraco.Core/Services/Implement/PackagingService.cs b/src/Umbraco.Core/Services/Implement/PackagingService.cs index e7b33dd86d..7c379dc2dc 100644 --- a/src/Umbraco.Core/Services/Implement/PackagingService.cs +++ b/src/Umbraco.Core/Services/Implement/PackagingService.cs @@ -32,7 +32,7 @@ namespace Umbraco.Core.Services.Implement public class PackagingService : IPackagingService { //fixme: inject when ready to use this - private IPackageInstallation _packageInstallation; + private readonly IPackageInstallation _packageInstallation; private readonly ILogger _logger; private readonly IContentService _contentService; @@ -961,9 +961,6 @@ namespace Umbraco.Core.Services.Implement #endregion - #region Files - #endregion - #region Languages @@ -1160,7 +1157,7 @@ namespace Umbraco.Core.Services.Implement using (var fs1 = new FileStream(packageFilePath, FileMode.Create)) { fs1.Write(bytes, 0, bytes.Length); - return "packages\\" + packageId + ".umb"; + return packageId + ".umb"; } } @@ -1305,37 +1302,46 @@ namespace Umbraco.Core.Services.Implement #region Installation - //fixme: None of these methods are actually used! They have unit tests for them though, but we don't actively use this yet but we should! + public CompiledPackage GetCompiledPackageInfo(string packageFileName) => _packageInstallation.ReadPackage(packageFileName); - internal IPackageInstallation PackageInstallation + public IEnumerable InstallCompiledPackageFiles(PackageDefinition packageDefinition, string packageFileName, int userId = 0) { - private get { return _packageInstallation ?? new PackageInstallation(this, _macroService, _fileService, new PackageExtraction()); } - set { _packageInstallation = value; } + 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"); + + var compiledPackage = GetCompiledPackageInfo(packageFileName); + if (compiledPackage == null) throw new InvalidOperationException("Could not read the package file " + packageFileName); + + var files = _packageInstallation.InstallPackageFiles(packageDefinition, compiledPackage, userId); + + SaveInstalledPackage(packageDefinition); + + if (userId > -1) + _auditService.Add(AuditType.PackagerInstall, userId, -1, "Package", $"Package files installed for package '{compiledPackage.Name}'."); + + return files; } - internal InstallationSummary InstallPackage(string packageFilePath, int userId = 0, bool raiseEvents = false) + public InstallationSummary InstallCompiledPackageData(PackageDefinition packageDefinition, string packageFileName, int userId = 0) { - var metaData = GetPackageMetaData(packageFilePath); + 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"); - if (raiseEvents && ImportingPackage.IsRaisedEventCancelled(new ImportPackageEventArgs(packageFilePath, metaData), this)) - return new InstallationSummary { MetaData = metaData }; + var compiledPackage = GetCompiledPackageInfo(packageFileName); + if (compiledPackage == null) throw new InvalidOperationException("Could not read the package file " + packageFileName); - var installationSummary = PackageInstallation.InstallPackage(packageFilePath, userId); + if (ImportingPackage.IsRaisedEventCancelled(new ImportPackageEventArgs(packageFileName, compiledPackage), this)) + return new InstallationSummary { MetaData = compiledPackage }; - if (raiseEvents) - ImportedPackage.RaiseEvent(new ImportPackageEventArgs(installationSummary, metaData, false), this); + var summary = _packageInstallation.InstallPackageData(packageDefinition, compiledPackage, userId); - return installationSummary; - } + _auditService.Add(AuditType.PackagerInstall, userId, -1, "Package", $"Package data installed for package '{compiledPackage.Name}'."); - internal PreInstallWarnings GetPackageWarnings(string packageFilePath) - { - return PackageInstallation.GetPreInstallWarnings(packageFilePath); - } + ImportedPackage.RaiseEvent(new ImportPackageEventArgs(summary, compiledPackage, false), this); - internal IPackageInfo GetPackageMetaData(string packageFilePath) - { - return PackageInstallation.GetMetaData(packageFilePath); + return summary; } #endregion diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 038d531bd4..2ee5d13f6d 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -444,11 +444,14 @@ + + + @@ -889,9 +892,6 @@ - - - diff --git a/src/Umbraco.Tests/Packaging/CreatedPackagesRepositoryTests.cs b/src/Umbraco.Tests/Packaging/CreatedPackagesRepositoryTests.cs index 40500d6bcb..e059d87524 100644 --- a/src/Umbraco.Tests/Packaging/CreatedPackagesRepositoryTests.cs +++ b/src/Umbraco.Tests/Packaging/CreatedPackagesRepositoryTests.cs @@ -87,10 +87,10 @@ namespace Umbraco.Tests.Packaging var def2 = new PackageDefinition { - Name = "test", - Url = "http://test.com", - Author = "Someone", - AuthorUrl = "http://test.com" + Name = "test2", + Url = "http://test2.com", + Author = "Someone2", + AuthorUrl = "http://test2.com" }; result = PackageBuilder.SavePackage(def2); diff --git a/src/Umbraco.Tests/Packaging/PackageExtractionTests.cs b/src/Umbraco.Tests/Packaging/PackageExtractionTests.cs index 5416162ed2..61494166ff 100644 --- a/src/Umbraco.Tests/Packaging/PackageExtractionTests.cs +++ b/src/Umbraco.Tests/Packaging/PackageExtractionTests.cs @@ -12,11 +12,11 @@ namespace Umbraco.Tests.Packaging { private const string PackageFileName = "Document_Type_Picker_1.1.umb"; - private static string GetTestPackagePath(string packageName) + private static FileInfo GetTestPackagePath(string packageName) { const string testPackagesDirName = "Packaging\\Packages"; string path = Path.Combine(IOHelper.GetRootDirectorySafe(), testPackagesDirName, packageName); - return path; + return new FileInfo(path); } [Test] diff --git a/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs b/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs index 0855d81548..fe7a5fbe5c 100644 --- a/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs +++ b/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs @@ -1,74 +1,113 @@ -using Moq; +using System; +using System.IO; +using System.Linq; +using Moq; using NUnit.Framework; using Umbraco.Core; +using Umbraco.Core.IO; using Umbraco.Core.Models.Packaging; using Umbraco.Core.Packaging; using Umbraco.Core.Services; +using Umbraco.Core.Services.Implement; +using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.Testing; namespace Umbraco.Tests.Packaging { [TestFixture] - public class PackageInstallationTest + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] + public class PackageInstallationTest : TestWithDatabaseBase { - private const string Xml = @" - - - 095e064b-ba4d-442d-9006-3050983c13d8.dll/binAuros.DocumentTypePicker.dll - - - Document Type Picker - 1.1 - MIT - http://www.auros.co.uk - - 3 - 0 - 0 - - - - @tentonipete - auros.co.uk - - - - - - - - - - - - - - -"; + private Guid _testBaseFolder; + + public override void SetUp() + { + base.SetUp(); + _testBaseFolder = Guid.NewGuid(); + } + + public override void TearDown() + { + base.TearDown(); + + //clear out files/folders + var path = IOHelper.MapPath("~/" + _testBaseFolder); + if (Directory.Exists(path)) + Directory.Delete(path, true); + } + + private CompiledPackageXmlParser Parser => new CompiledPackageXmlParser(new ConflictingPackageData(ServiceContext.MacroService, ServiceContext.FileService)); + + private IPackageInstallation PackageInstallation => new PackageInstallation( + ServiceContext.PackagingService, + new PackageFileInstallation(Parser, ProfilingLogger), + Parser, + packagesFolderPath: "~/Packaging/packages",//this is where our test zip file is + applicationRootFolder: new DirectoryInfo(IOHelper.GetRootDirectorySafe()), + packageExtractionFolder: new DirectoryInfo(IOHelper.MapPath("~/" + _testBaseFolder))); //we don't want to extract package files to the real root, so extract to a test folder + + const string documentTypePickerUmb = "Document_Type_Picker_1.1.umb"; + + //[Test] + //public void PackagingService_Can_ImportPackage() + //{ + // const string documentTypePickerUmb = "Document_Type_Picker_1.1.umb"; + + // string testPackagePath = GetTestPackagePath(documentTypePickerUmb); + + // InstallationSummary installationSummary = packagingService.InstallPackage(testPackagePath); + + // Assert.IsNotNull(installationSummary); + //} + [Test] - public void Test() + public void Can_Read_Compiled_Package() { - // Arrange - const string pagePath = "Test.umb"; + var package = PackageInstallation.ReadPackage(documentTypePickerUmb); + Assert.IsNotNull(package); + Assert.AreEqual(1, package.Files.Count); + Assert.AreEqual("095e064b-ba4d-442d-9006-3050983c13d8.dll", package.Files[0].UniqueFileName); + Assert.AreEqual("/bin", package.Files[0].OriginalPath); + Assert.AreEqual("Auros.DocumentTypePicker.dll", package.Files[0].OriginalName); + Assert.AreEqual("Document Type Picker", package.Name); + Assert.AreEqual("1.1", package.Version); + Assert.AreEqual("http://www.opensource.org/licenses/mit-license.php", package.LicenseUrl); + Assert.AreEqual("MIT", package.License); + Assert.AreEqual(3, package.UmbracoVersion.Major); + Assert.AreEqual(RequirementsType.Legacy, package.UmbracoVersionRequirementsType); + Assert.AreEqual("@tentonipete", package.Author); + Assert.AreEqual("auros.co.uk", package.AuthorUrl); + Assert.AreEqual("Document Type Picker datatype that enables back office user to select one or many document types.", package.Readme); - var packageExtraction = new Mock(); - - string test; - packageExtraction.Setup(a => a.ReadTextFileFromArchive(pagePath, Constants.Packaging.PackageXmlFileName, out test)).Returns(Xml); - - var fileService = new Mock(); - var macroService = new Mock(); - var packagingService = new Mock(); - - var sut = new PackageInstallation(packagingService.Object, macroService.Object, fileService.Object, packageExtraction.Object); - - // Act - InstallationSummary installationSummary = sut.InstallPackage(pagePath, -1); - - // Assert - Assert.IsNotNull(installationSummary); - //Assert.Inconclusive("Lots of more tests can be written"); } + [Test] + public void Can_Read_Compiled_Package_Warnings() + { + + + var preInstallWarnings = PackageInstallation.ReadPackage(documentTypePickerUmb).Warnings; + Assert.IsNotNull(preInstallWarnings); + + //TODO: Assert! + } + + [Test] + public void Install_Files() + { + var package = PackageInstallation.ReadPackage(documentTypePickerUmb); + var def = PackageDefinition.FromCompiledPackage(package); + def.Id = 1; + def.PackageId = Guid.NewGuid(); + + var result = PackageInstallation.InstallPackageFiles(def, package, -1).ToList(); + + Assert.AreEqual(1, result.Count); + Assert.AreEqual("bin\\Auros.DocumentTypePicker.dll", result[0]); + Assert.IsTrue(File.Exists(Path.Combine(IOHelper.MapPath("~/" + _testBaseFolder), result[0]))); + } + + } } diff --git a/src/Umbraco.Tests/Services/PackagingServiceTests.cs b/src/Umbraco.Tests/Services/PackagingServiceTests.cs index 0f57298c88..f6878e9407 100644 --- a/src/Umbraco.Tests/Services/PackagingServiceTests.cs +++ b/src/Umbraco.Tests/Services/PackagingServiceTests.cs @@ -14,54 +14,7 @@ namespace Umbraco.Tests.Services { - private static string GetTestPackagePath(string packageName) - { - const string testPackagesDirName = "Packaging\\Packages"; - string path = Path.Combine(IOHelper.GetRootDirectorySafe(), testPackagesDirName, packageName); - return path; - } - - - [Test] - public void PackagingService_Can_ImportPackage() - { - var packagingService = (PackagingService)ServiceContext.PackagingService; - - const string documentTypePickerUmb = "Document_Type_Picker_1.1.umb"; - - string testPackagePath = GetTestPackagePath(documentTypePickerUmb); - - InstallationSummary installationSummary = packagingService.InstallPackage(testPackagePath); - - Assert.IsNotNull(installationSummary); - } - - - [Test] - public void PackagingService_Can_GetPackageMetaData() - { - var packagingService = (PackagingService)ServiceContext.PackagingService; - - const string documentTypePickerUmb = "Document_Type_Picker_1.1.umb"; - - string testPackagePath = GetTestPackagePath(documentTypePickerUmb); - - var packageMetaData = packagingService.GetPackageMetaData(testPackagePath); - Assert.IsNotNull(packageMetaData); - } - - [Test] - public void PackagingService_Can_GetPackageWarnings() - { - var packagingService = (PackagingService)ServiceContext.PackagingService; - - const string documentTypePickerUmb = "Document_Type_Picker_1.1.umb"; - - string testPackagePath = GetTestPackagePath(documentTypePickerUmb); - - PreInstallWarnings preInstallWarnings = packagingService.GetPackageWarnings(testPackagePath); - Assert.IsNotNull(preInstallWarnings); - } + } diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.html b/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.html index 7f22b7ace8..5e3ef6ad1c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.html @@ -128,7 +128,7 @@
Read me
- +
diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/views/repo.html b/src/Umbraco.Web.UI.Client/src/views/packages/views/repo.html index fc272c86ff..93c2ce05ed 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/views/repo.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/views/repo.html @@ -325,7 +325,7 @@
Read me
- +
diff --git a/src/Umbraco.Web/Composing/Current.cs b/src/Umbraco.Web/Composing/Current.cs index 3e7c14147b..daf152c29b 100644 --- a/src/Umbraco.Web/Composing/Current.cs +++ b/src/Umbraco.Web/Composing/Current.cs @@ -161,7 +161,7 @@ namespace Umbraco.Web.Composing #region Web Actions - public static void RestartAppPool() + internal static void RestartAppPool() { // see notes in overload @@ -175,7 +175,7 @@ namespace Umbraco.Web.Composing HttpRuntime.UnloadAppDomain(); } - public static void RestartAppPool(HttpContextBase httpContext) + internal static void RestartAppPool(HttpContextBase httpContext) { // we're going to put an application wide flag to show that the application is about to restart. // we're doing this because if there is a script checking if the app pool is fully restarted, then diff --git a/src/Umbraco.Web/Editors/PackageInstallController.cs b/src/Umbraco.Web/Editors/PackageInstallController.cs index 73d50eb48a..24cc999c6b 100644 --- a/src/Umbraco.Web/Editors/PackageInstallController.cs +++ b/src/Umbraco.Web/Editors/PackageInstallController.cs @@ -29,7 +29,6 @@ using Umbraco.Web.UI; using Umbraco.Web.UI.JavaScript; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; -using Umbraco.Web._Legacy.Packager; using File = System.IO.File; using Notification = Umbraco.Web.Models.ContentEditing.Notification; using Version = System.Version; @@ -302,37 +301,31 @@ namespace Umbraco.Web.Editors private void PopulateFromPackageData(LocalPackageInstallModel model) { - var ins = new global::Umbraco.Web._Legacy.Packager.Installer(Security.CurrentUser.Id); - //this will load in all the metadata too - var tempDir = ins.Import(model.ZipFilePath, false); + var ins = Services.PackagingService.GetCompiledPackageInfo(model.ZipFilePath); - model.TemporaryDirectoryPath = Path.Combine(SystemDirectories.Data, tempDir); model.Name = ins.Name; model.Author = ins.Author; model.AuthorUrl = ins.AuthorUrl; model.IconUrl = ins.IconUrl; model.License = ins.License; model.LicenseUrl = ins.LicenseUrl; - model.ReadMe = ins.ReadMe; - model.ConflictingMacroAliases = ins.ConflictingMacroAliases; - model.ConflictingStyleSheetNames = ins.ConflictingStyleSheetNames; - model.ConflictingTemplateAliases = ins.ConflictingTemplateAliases; - model.ContainsMacroConflict = ins.ContainsMacroConflict; - model.ContainsStyleSheetConflicts = ins.ContainsStyleSheeConflicts; - model.ContainsTemplateConflicts = ins.ContainsTemplateConflicts; - model.ContainsUnsecureFiles = ins.ContainsUnsecureFiles; + 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(); model.Url = ins.Url; model.Version = ins.Version; - model.UmbracoVersion = ins.RequirementsType == RequirementsType.Strict - ? string.Format("{0}.{1}.{2}", ins.RequirementsMajor, ins.RequirementsMinor, ins.RequirementsPatch) + model.UmbracoVersion = ins.UmbracoVersionRequirementsType == RequirementsType.Strict + ? ins.UmbracoVersion.ToString(3) : string.Empty; //now we need to check for version comparison model.IsCompatible = true; - if (ins.RequirementsType == RequirementsType.Strict) + if (ins.UmbracoVersionRequirementsType == RequirementsType.Strict) { - var packageMinVersion = new System.Version(ins.RequirementsMajor, ins.RequirementsMinor, ins.RequirementsPatch); + var packageMinVersion = ins.UmbracoVersion; if (UmbracoVersion.Current < packageMinVersion) { model.IsCompatible = false; @@ -489,13 +482,12 @@ namespace Umbraco.Web.Editors [HttpPost] public PackageInstallModel Import(PackageInstallModel model) { - var ins = new Installer(Security.CurrentUser.Id); + var packageInfo = Services.PackagingService.GetCompiledPackageInfo(model.ZipFilePath); - var tempPath = ins.Import(model.ZipFilePath); //now we need to check for version comparison - if (ins.RequirementsType == RequirementsType.Strict) + if (packageInfo.UmbracoVersionRequirementsType == RequirementsType.Strict) { - var packageMinVersion = new System.Version(ins.RequirementsMajor, ins.RequirementsMinor, ins.RequirementsPatch); + var packageMinVersion = packageInfo.UmbracoVersion; if (UmbracoVersion.Current < packageMinVersion) { throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse( @@ -503,8 +495,13 @@ namespace Umbraco.Web.Editors } } - model.TemporaryDirectoryPath = Path.Combine(SystemDirectories.Data, tempPath); - model.Id = ins.CreateManifest(model.PackageGuid); + var packageDefinition = PackageDefinition.FromCompiledPackage(packageInfo); + + //save to the installedPackages.config + packageDefinition.PackageId = model.PackageGuid; //fixme: why are we doing this? + Services.PackagingService.SaveInstalledPackage(packageDefinition); + + model.Id = packageDefinition.Id; return model; } @@ -517,9 +514,10 @@ namespace Umbraco.Web.Editors [HttpPost] public PackageInstallModel InstallFiles(PackageInstallModel model) { - var ins = new Installer(Security.CurrentUser.Id); - ins.LoadConfig(IOHelper.MapPath(model.TemporaryDirectoryPath)); - ins.InstallFiles(model.Id, IOHelper.MapPath(model.TemporaryDirectoryPath)); + var definition = Services.PackagingService.GetInstalledPackageById(model.Id); + if (definition == null) throw new InvalidOperationException("Not package definition found with id " + model.Id); + + Services.PackagingService.InstallCompiledPackageFiles(definition, model.ZipFilePath, Security.GetUserId().ResultOr(0)); //set a restarting marker and reset the app pool Current.RestartAppPool(Request.TryGetHttpContext().Result); @@ -551,9 +549,10 @@ namespace Umbraco.Web.Editors [HttpPost] public PackageInstallModel InstallData(PackageInstallModel model) { - var ins = new global::Umbraco.Web._Legacy.Packager.Installer(Security.CurrentUser.Id); - ins.LoadConfig(IOHelper.MapPath(model.TemporaryDirectoryPath)); - ins.InstallBusinessLogic(model.Id, IOHelper.MapPath(model.TemporaryDirectoryPath)); + var definition = Services.PackagingService.GetInstalledPackageById(model.Id); + if (definition == null) throw new InvalidOperationException("Not package definition found with id " + model.Id); + + Services.PackagingService.InstallCompiledPackageData(definition, model.ZipFilePath, Security.GetUserId().ResultOr(0)); return model; } @@ -565,23 +564,21 @@ namespace Umbraco.Web.Editors [HttpPost] public PackageInstallResult CleanUp(PackageInstallModel model) { - var ins = new global::Umbraco.Web._Legacy.Packager.Installer(Security.CurrentUser.Id); - var tempDir = IOHelper.MapPath(model.TemporaryDirectoryPath); - ins.LoadConfig(IOHelper.MapPath(model.TemporaryDirectoryPath)); - ins.InstallCleanUp(model.Id, IOHelper.MapPath(model.TemporaryDirectoryPath)); + var packageInfo = Services.PackagingService.GetCompiledPackageInfo(model.ZipFilePath); var clientDependencyConfig = new ClientDependencyConfiguration(Logger); var clientDependencyUpdated = clientDependencyConfig.UpdateVersionNumber( UmbracoVersion.SemanticVersion, DateTime.UtcNow, "yyyyMMdd"); + //fixme: when do we delete the zip file? var redirectUrl = ""; - if (ins.Control.IsNullOrWhiteSpace() == false) + if (packageInfo.Control.IsNullOrWhiteSpace() == false) { //fixme: this needs to be replaced with an angular view the installer.aspx no longer exists. - redirectUrl = string.Format("/developer/framed/{0}", - Uri.EscapeDataString( - string.Format("/umbraco/developer/Packages/installer.aspx?installing=custominstaller&dir={0}&pId={1}&customControl={2}&customUrl={3}", tempDir, model.Id, ins.Control, ins.Url))); + //redirectUrl = string.Format("/developer/framed/{0}", + // Uri.EscapeDataString( + // string.Format("/umbraco/developer/Packages/installer.aspx?installing=custominstaller&dir={0}&pId={1}&customControl={2}&customUrl={3}", tempDir, model.Id, ins.Control, ins.Url))); } return new PackageInstallResult @@ -590,7 +587,6 @@ namespace Umbraco.Web.Editors ZipFilePath = model.ZipFilePath, PackageGuid = model.PackageGuid, RepositoryGuid = model.RepositoryGuid, - TemporaryDirectoryPath = model.TemporaryDirectoryPath, PostInstallationPath = redirectUrl }; diff --git a/src/Umbraco.Web/Install/Controllers/InstallPackageController.cs b/src/Umbraco.Web/Install/Controllers/InstallPackageController.cs index ac859ee3aa..dd9b0a42d0 100644 --- a/src/Umbraco.Web/Install/Controllers/InstallPackageController.cs +++ b/src/Umbraco.Web/Install/Controllers/InstallPackageController.cs @@ -11,6 +11,7 @@ using umbraco; using Umbraco.Core; using Umbraco.Web.Cache; using Umbraco.Core.Configuration; +using Umbraco.Core.Models.Packaging; using Umbraco.Core.Services; using Umbraco.Web.Composing; using Umbraco.Web.Install.Models; @@ -28,17 +29,18 @@ namespace Umbraco.Web.Install.Controllers [HttpInstallAuthorize] [AngularJsonOnlyConfiguration] [Obsolete("This is only used for the legacy way of installing starter kits in the back office")] + //fixme: Is this used anymore?? public class InstallPackageController : ApiController { private readonly IPackagingService _packagingService; + private readonly UmbracoContext _umbracoContext; - public InstallPackageController(IPackagingService packagingService) + public InstallPackageController(IPackagingService packagingService, UmbracoContext umbracoContext) { _packagingService = packagingService; + _umbracoContext = umbracoContext; } - private const string RepoGuid = "65194810-1f85-11dd-bd0b-0800200c9a66"; - /// /// Empty action, useful for retrieving the base url for this controller /// @@ -50,7 +52,7 @@ namespace Umbraco.Web.Install.Controllers } /// - /// Connects to the repo, downloads the package and creates the manifest + /// Connects to the repo, downloads the package and creates the definition /// /// /// @@ -62,16 +64,19 @@ namespace Umbraco.Web.Install.Controllers UmbracoVersion.Current, UmbracoContext.Current.Security.CurrentUser.Id); - var installer = new _Legacy.Packager.Installer(UmbracoContext.Current.Security.CurrentUser.Id); + + var packageInfo = _packagingService.GetCompiledPackageInfo(packageFile); + if (packageInfo == null) throw new InvalidOperationException("Could not read package file " + packageFile); + + //save to the installedPackages.config + var packageDefinition = PackageDefinition.FromCompiledPackage(packageInfo); + _packagingService.SaveInstalledPackage(packageDefinition); - var tempFile = installer.Import(packageFile); - installer.LoadConfig(tempFile); - var pId = installer.CreateManifest(model.KitGuid); return Json(new { success = true, - manifestId = pId, - packageFile = tempFile, + packageId = packageDefinition.Id, + packageFile = packageInfo.PackageFileName, percentage = 10, message = "Downloading starter kit files..." }, HttpStatusCode.OK); @@ -85,13 +90,16 @@ namespace Umbraco.Web.Install.Controllers public HttpResponseMessage InstallPackageFiles(InstallPackageModel model) { model.PackageFile = HttpUtility.UrlDecode(model.PackageFile); - var installer = new global::Umbraco.Web._Legacy.Packager.Installer(UmbracoContext.Current.Security.CurrentUser.Id); - installer.LoadConfig(model.PackageFile); - installer.InstallFiles(model.ManifestId, model.PackageFile); + + var definition = _packagingService.GetInstalledPackageById(model.PackageId); + if (definition == null) throw new InvalidOperationException("Not package definition found with id " + model.PackageId); + + _packagingService.InstallCompiledPackageFiles(definition, model.PackageFile, _umbracoContext.Security.GetUserId().ResultOr(0)); + return Json(new { success = true, - model.ManifestId, + ManifestId = model.PackageId, model.PackageFile, percentage = 20, message = "Installing starter kit files" @@ -141,13 +149,16 @@ namespace Umbraco.Web.Install.Controllers public HttpResponseMessage InstallBusinessLogic(InstallPackageModel model) { model.PackageFile = HttpUtility.UrlDecode(model.PackageFile); - var installer = new global::Umbraco.Web._Legacy.Packager.Installer(UmbracoContext.Current.Security.CurrentUser.Id); - installer.LoadConfig(model.PackageFile); - installer.InstallBusinessLogic(model.ManifestId, model.PackageFile); + + var definition = _packagingService.GetInstalledPackageById(model.PackageId); + if (definition == null) throw new InvalidOperationException("Not package definition found with id " + model.PackageId); + + _packagingService.InstallCompiledPackageData(definition, model.PackageFile, _umbracoContext.Security.GetUserId().ResultOr(0)); + return Json(new { success = true, - model.ManifestId, + ManifestId = model.PackageId, model.PackageFile, percentage = 70, message = "Installing starter kit files" @@ -162,18 +173,11 @@ namespace Umbraco.Web.Install.Controllers public HttpResponseMessage CleanupInstallation(InstallPackageModel model) { model.PackageFile = HttpUtility.UrlDecode(model.PackageFile); - var installer = new global::Umbraco.Web._Legacy.Packager.Installer(UmbracoContext.Current.Security.CurrentUser.Id); - installer.LoadConfig(model.PackageFile); - installer.InstallCleanUp(model.ManifestId, model.PackageFile); - - // library.RefreshContent is obsolete, would need to RefreshAll... snapshot, - // but it should be managed automatically by services and caches! - //DistributedCache.Instance.RefreshAll...(); - + return Json(new { success = true, - model.ManifestId, + ManifestId = model.PackageId, model.PackageFile, percentage = 100, message = "Starter kit has been installed" diff --git a/src/Umbraco.Web/Install/InstallSteps/StarterKitCleanupStep.cs b/src/Umbraco.Web/Install/InstallSteps/StarterKitCleanupStep.cs index 075a61ca95..8c168f7230 100644 --- a/src/Umbraco.Web/Install/InstallSteps/StarterKitCleanupStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/StarterKitCleanupStep.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Threading.Tasks; using System.Web; using Umbraco.Web.Install.Models; -using Umbraco.Web._Legacy.Packager; namespace Umbraco.Web.Install.InstallSteps { @@ -15,31 +14,26 @@ namespace Umbraco.Web.Install.InstallSteps { var installSteps = InstallStatusTracker.GetStatus().ToArray(); var previousStep = installSteps.Single(x => x.Name == "StarterKitDownload"); - var manifestId = Convert.ToInt32(previousStep.AdditionalData["manifestId"]); + var packageId = Convert.ToInt32(previousStep.AdditionalData["packageId"]); var packageFile = (string)previousStep.AdditionalData["packageFile"]; - CleanupInstallation(manifestId, packageFile); + CleanupInstallation(packageId, packageFile); return Task.FromResult(null); } - private void CleanupInstallation(int manifestId, string packageFile) + private void CleanupInstallation(int packageId, string packageFile) { packageFile = HttpUtility.UrlDecode(packageFile); - var installer = new Installer(); - installer.LoadConfig(packageFile); - installer.InstallCleanUp(manifestId, packageFile); - // library.RefreshContent is obsolete, would need to RefreshAll... snapshot, - // but it should be managed automatically by services and caches! - //DistributedCache.Instance.RefreshAll...(); + //fixme: When does the zip file get deleted? } public override bool RequiresExecution(object model) { var installSteps = InstallStatusTracker.GetStatus().ToArray(); //this step relies on the preious one completed - because it has stored some information we need - if (installSteps.Any(x => x.Name == "StarterKitDownload" && x.AdditionalData.ContainsKey("manifestId")) == false) + if (installSteps.Any(x => x.Name == "StarterKitDownload" && x.AdditionalData.ContainsKey("packageId")) == false) { return false; } diff --git a/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs b/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs index 66d1b0a20c..0fe1e333d3 100644 --- a/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs @@ -5,9 +5,9 @@ using System.Threading.Tasks; using System.Web; using Umbraco.Core.Services; using Umbraco.Core.Configuration; +using Umbraco.Core.Models.Packaging; using Umbraco.Web.Composing; using Umbraco.Web.Install.Models; -using Umbraco.Web._Legacy.Packager; namespace Umbraco.Web.Install.InstallSteps { @@ -29,7 +29,7 @@ namespace Umbraco.Web.Install.InstallSteps _packageService = packageService; } - private const string RepoGuid = "65194810-1f85-11dd-bd0b-0800200c9a66"; + //private const string RepoGuid = "65194810-1f85-11dd-bd0b-0800200c9a66"; public override async Task ExecuteAsync(Guid? starterKitId) { @@ -55,34 +55,33 @@ namespace Umbraco.Web.Install.InstallSteps return new InstallSetupResult(new Dictionary { - {"manifestId", result.Item2}, + {"packageId", result.Item2}, {"packageFile", result.Item1} }); } private async Task> DownloadPackageFilesAsync(Guid kitGuid) { - var installer = new Installer(); - //Go get the package file from the package repo - var packageFile = await _packageService.FetchPackageFileAsync(kitGuid, UmbracoVersion.Current, _umbracoContext.Security.GetUserId().ResultOr(0)); + var packageFileName = await _packageService.FetchPackageFileAsync(kitGuid, UmbracoVersion.Current, _umbracoContext.Security.GetUserId().ResultOr(0)); + if (packageFileName == null) throw new InvalidOperationException("Could not fetch package file " + kitGuid); - var tempFile = installer.Import(packageFile); - installer.LoadConfig(tempFile); - var pId = installer.CreateManifest(kitGuid); + //add an entry to the installedPackages.config + var compiledPackage = _packageService.GetCompiledPackageInfo(packageFileName); + var packageDefinition = PackageDefinition.FromCompiledPackage(compiledPackage); + _packageService.SaveInstalledPackage(packageDefinition); - InstallPackageFiles(pId, tempFile); + InstallPackageFiles(packageDefinition, compiledPackage.PackageFileName); - return new Tuple(tempFile, pId); + return new Tuple(compiledPackage.PackageFileName, packageDefinition.Id); } - private void InstallPackageFiles(int manifestId, string packageFile) + private void InstallPackageFiles(PackageDefinition packageDefinition, string packageFileName) { - packageFile = HttpUtility.UrlDecode(packageFile); - var installer = new Installer(); - installer.LoadConfig(packageFile); - installer.InstallFiles(manifestId, packageFile); + if (packageDefinition == null) throw new ArgumentNullException(nameof(packageDefinition)); + packageFileName = HttpUtility.UrlDecode(packageFileName); + _packageService.InstallCompiledPackageData(packageDefinition, packageFileName, _umbracoContext.Security.GetUserId().ResultOr(0)); } public override string View => _packageService.GetAllInstalledPackages().Any() ? string.Empty : base.View; diff --git a/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs b/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs index 0e189c5a6d..9d3f38b061 100644 --- a/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs @@ -2,9 +2,9 @@ using System.Linq; using System.Threading.Tasks; using System.Web; +using Umbraco.Core.Services; using Umbraco.Web.Composing; using Umbraco.Web.Install.Models; -using Umbraco.Web._Legacy.Packager; namespace Umbraco.Web.Install.InstallSteps { @@ -14,10 +14,14 @@ namespace Umbraco.Web.Install.InstallSteps internal class StarterKitInstallStep : InstallSetupStep { private readonly HttpContextBase _httContext; + private readonly UmbracoContext _umbracoContext; + private readonly IPackagingService _packagingService; - public StarterKitInstallStep(HttpContextBase httContext) + public StarterKitInstallStep(HttpContextBase httContext, UmbracoContext umbracoContext, IPackagingService packagingService) { _httContext = httContext; + _umbracoContext = umbracoContext; + _packagingService = packagingService; } @@ -25,29 +29,31 @@ namespace Umbraco.Web.Install.InstallSteps { var installSteps = InstallStatusTracker.GetStatus().ToArray(); var previousStep = installSteps.Single(x => x.Name == "StarterKitDownload"); - var manifestId = Convert.ToInt32(previousStep.AdditionalData["manifestId"]); + var packageId = Convert.ToInt32(previousStep.AdditionalData["packageId"]); var packageFile = (string)previousStep.AdditionalData["packageFile"]; - InstallBusinessLogic(manifestId, packageFile); + InstallBusinessLogic(packageId, packageFile); Current.RestartAppPool(_httContext); return Task.FromResult(null); } - private void InstallBusinessLogic(int manifestId, string packageFile) + private void InstallBusinessLogic(int packageId, string packageFile) { packageFile = HttpUtility.UrlDecode(packageFile); - var installer = new Installer(); - installer.LoadConfig(packageFile); - installer.InstallBusinessLogic(manifestId, packageFile); + + var definition = _packagingService.GetInstalledPackageById(packageId); + if (definition == null) throw new InvalidOperationException("Not package definition found with id " + packageId); + + _packagingService.InstallCompiledPackageData(definition, packageFile, _umbracoContext.Security.GetUserId().ResultOr(0)); } public override bool RequiresExecution(object model) { var installSteps = InstallStatusTracker.GetStatus().ToArray(); //this step relies on the preious one completed - because it has stored some information we need - if (installSteps.Any(x => x.Name == "StarterKitDownload" && x.AdditionalData.ContainsKey("manifestId")) == false) + if (installSteps.Any(x => x.Name == "StarterKitDownload" && x.AdditionalData.ContainsKey("packageId")) == false) { return false; } diff --git a/src/Umbraco.Web/Install/Models/InstallPackageModel.cs b/src/Umbraco.Web/Install/Models/InstallPackageModel.cs index 01eb368b6a..3ab74fa5e4 100644 --- a/src/Umbraco.Web/Install/Models/InstallPackageModel.cs +++ b/src/Umbraco.Web/Install/Models/InstallPackageModel.cs @@ -7,15 +7,15 @@ using System.Threading.Tasks; namespace Umbraco.Web.Install.Models { - + //fixme: do we need this? [Obsolete("This is only used for the obsolete controller InstallPackageController")] [DataContract(Name = "installPackage", Namespace = "")] public class InstallPackageModel { [DataMember(Name = "kitGuid")] public Guid KitGuid { get; set; } - [DataMember(Name = "manifestId")] - public int ManifestId { get; set; } + [DataMember(Name = "packageId")] + public int PackageId { get; set; } [DataMember(Name = "packageFile")] public string PackageFile { get; set; } diff --git a/src/Umbraco.Web/Models/LocalPackageInstallModel.cs b/src/Umbraco.Web/Models/LocalPackageInstallModel.cs index 64ce51f539..d1d51e1fcc 100644 --- a/src/Umbraco.Web/Models/LocalPackageInstallModel.cs +++ b/src/Umbraco.Web/Models/LocalPackageInstallModel.cs @@ -47,25 +47,34 @@ namespace Umbraco.Web.Models public bool ContainsUnsecureFiles { get; set; } [DataMember(Name = "containsTemplateConflicts")] - public bool ContainsTemplateConflicts { get; set; } + public bool ContainsTemplateConflicts => ConflictingTemplateAliases != null && ConflictingTemplateAliases.Count > 0; [DataMember(Name = "containsStyleSheetConflicts")] - public bool ContainsStyleSheetConflicts { get; set; } + public bool ContainsStyleSheetConflicts => ConflictingStyleSheetNames != null && ConflictingStyleSheetNames.Count > 0; [DataMember(Name = "containsMacroConflict")] - public bool ContainsMacroConflict { get; set; } + public bool ContainsMacroConflict => ConflictingMacroAliases != null && ConflictingMacroAliases.Count > 0; + /// + /// Key value of name + alias + /// [DataMember(Name = "conflictingTemplateAliases")] public IDictionary ConflictingTemplateAliases { get; set; } + /// + /// Key value of name + alias + /// [DataMember(Name = "conflictingStyleSheetNames")] public IDictionary ConflictingStyleSheetNames { get; set; } + /// + /// Key value of name + alias + /// [DataMember(Name = "conflictingMacroAliases")] public IDictionary ConflictingMacroAliases { get; set; } - [DataMember(Name = "readMe")] - public string ReadMe { get; set; } + [DataMember(Name = "readme")] + public string Readme { get; set; } [DataMember(Name = "licenseUrl")] public string LicenseUrl { get; set; } diff --git a/src/Umbraco.Web/Models/PackageInstallModel.cs b/src/Umbraco.Web/Models/PackageInstallModel.cs index 7748129a40..1cf4a483ae 100644 --- a/src/Umbraco.Web/Models/PackageInstallModel.cs +++ b/src/Umbraco.Web/Models/PackageInstallModel.cs @@ -15,11 +15,10 @@ namespace Umbraco.Web.Models [DataMember(Name = "packageGuid")] public Guid PackageGuid { get; set; } + //TODO: Do we need this? [DataMember(Name = "repositoryGuid")] public Guid RepositoryGuid { get; set; } - [DataMember(Name = "temporaryDirectoryPath")] - public string TemporaryDirectoryPath { get; set; } [DataMember(Name = "zipFilePath")] public string ZipFilePath { get; set; } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 1bd2860781..64e17af221 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -1137,7 +1137,6 @@ - diff --git a/src/Umbraco.Web/_Legacy/Packager/Installer.cs b/src/Umbraco.Web/_Legacy/Packager/Installer.cs index 286f41f61a..89dff406b0 100644 --- a/src/Umbraco.Web/_Legacy/Packager/Installer.cs +++ b/src/Umbraco.Web/_Legacy/Packager/Installer.cs @@ -1,803 +1,688 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Net; -using System.Xml; -using System.Xml.Linq; -using System.Xml.XPath; -using ICSharpCode.SharpZipLib.Zip; -using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Core.Events; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Packaging; -using Umbraco.Core.Packaging; -using Umbraco.Core.Services.Implement; -using File = System.IO.File; - -namespace Umbraco.Web._Legacy.Packager -{ - /// - /// The packager is a component which enables sharing of both data and functionality components between different umbraco installations. - /// - /// The output is a .umb (a zip compressed file) which contains the exported documents/medias/macroes/documentTypes (etc.) - /// in a Xml document, along with the physical files used (images/usercontrols/xsl documents etc.) - /// - /// Partly implemented, import of packages is done, the export is *under construction*. - /// - /// - /// Ruben Verborgh 31/12/2007: I had to change some code, I marked my changes with "DATALAYER". - /// Reason: @@IDENTITY can't be used with the new datalayer. - /// I wasn't able to test the code, since I'm not aware how the code functions. - /// - public class Installer - { - private const string PackageServer = "packages.umbraco.org"; - - private readonly Dictionary _conflictingMacroAliases = new Dictionary(); - private readonly Dictionary _conflictingTemplateAliases = new Dictionary(); - private readonly Dictionary _conflictingStyleSheetNames = new Dictionary(); - - private readonly int _currentUserId = -1; - private static WebClient _webClient; - - - public string Name { get; private set; } - public string Version { get; private set; } - public string Url { get; private set; } - public string License { get; private set; } - public string LicenseUrl { get; private set; } - public string Author { get; private set; } - public string AuthorUrl { get; private set; } - public string ReadMe { get; private set; } - public string Control { get; private set; } - - public bool ContainsMacroConflict { get; private set; } - public IDictionary ConflictingMacroAliases => _conflictingMacroAliases; - - public bool ContainsUnsecureFiles { get; private set; } - public List UnsecureFiles { get; } = new List(); - - public bool ContainsTemplateConflicts { get; private set; } - public IDictionary ConflictingTemplateAliases => _conflictingTemplateAliases; - - public bool ContainsStyleSheeConflicts { get; private set; } - public IDictionary ConflictingStyleSheetNames => _conflictingStyleSheetNames; - - public int RequirementsMajor { get; private set; } - public int RequirementsMinor { get; private set; } - public int RequirementsPatch { get; private set; } - - public RequirementsType RequirementsType { get; private set; } - - public string IconUrl { get; private set; } - - /// - /// The xml of the compiled package - /// - public XDocument Config { get; private set; } - - /// - /// Constructor - /// - public Installer() - { - Initialize(); - } - - public Installer(int currentUserId) - { - Initialize(); - _currentUserId = currentUserId; - } - - private void Initialize() - { - ContainsTemplateConflicts = false; - ContainsUnsecureFiles = false; - ContainsMacroConflict = false; - ContainsStyleSheeConflicts = false; - } - - - - /// - /// Constructor - /// - /// The name of the package - /// The version of the package - /// The url to a descriptionpage - /// The license under which the package is released (preferably GPL ;)) - /// The url to a licensedescription - /// The original author of the package - /// The url to the Authors website - /// Umbraco version major - /// Umbraco version minor - /// Umbraco version patch - /// The readme text - /// The name of the usercontrol used to configure the package after install - /// - /// - public Installer(string name, string version, string url, string license, string licenseUrl, string author, string authorUrl, int requirementsMajor, int requirementsMinor, int requirementsPatch, string readme, string control, RequirementsType requirementsType, string iconUrl) - { - ContainsTemplateConflicts = false; - ContainsUnsecureFiles = false; - ContainsMacroConflict = false; - ContainsStyleSheeConflicts = false; - this.Name = name; - this.Version = version; - this.Url = url; - this.License = license; - this.LicenseUrl = licenseUrl; - this.RequirementsMajor = requirementsMajor; - this.RequirementsMinor = requirementsMinor; - this.RequirementsPatch = requirementsPatch; - this.RequirementsType = requirementsType; - this.Author = author; - this.AuthorUrl = authorUrl; - this.IconUrl = iconUrl; - ReadMe = readme; - this.Control = control; - } - - #region Public Methods - - /// - /// Imports the specified package - /// - /// Filename of the umbracopackage - /// true if the input file should be deleted after import - /// - public string Import(string inputFile, bool deleteFile) - { - using (Current.ProfilingLogger.DebugDuration( - $"Importing package file {inputFile}.", - $"Package file {inputFile} imported.")) - { - var tempDir = ""; - if (File.Exists(IOHelper.MapPath(SystemDirectories.Data + Path.DirectorySeparatorChar + inputFile))) - { - var fi = new FileInfo(IOHelper.MapPath(SystemDirectories.Data + Path.DirectorySeparatorChar + inputFile)); - // Check if the file is a valid package - if (fi.Extension.ToLower() == ".umb") - { - try - { - tempDir = UnPack(fi.FullName, deleteFile); - LoadConfig(tempDir); - } - catch (Exception ex) - { - Current.Logger.Error(ex, "Error importing file {FileName}", fi.FullName); - throw; - } - } - else - throw new Exception("Error - file isn't a package (doesn't have a .umb extension). Check if the file automatically got named '.zip' upon download."); - } - else - throw new Exception("Error - file not found. Could find file named '" + IOHelper.MapPath(SystemDirectories.Data + Path.DirectorySeparatorChar + inputFile) + "'"); - return tempDir; - } - - } - - /// - /// Imports the specified package - /// - /// Filename of the umbracopackage - /// - public string Import(string inputFile) - { - return Import(inputFile, true); - } - - public int CreateManifest(Guid guid) - { - //This is the new improved install rutine, which chops up the process into 3 steps, creating the manifest, moving files, and finally handling umb objects - - var parser = new CompiledPackageXmlParser(); - var def = parser.ToCompiledPackage(Config); - - //create a new entry in the installedPackages.config - var installedPackage = new PackageDefinition - { - Author = def.Author, - AuthorUrl = def.AuthorUrl, - Control = def.Control, - IconUrl = def.IconUrl, - License = def.License, - LicenseUrl = def.LicenseUrl, - Name = def.Name, - Readme = def.Readme, - UmbracoVersion = def.UmbracoVersion, - Url = def.Url, - Version = def.Version, - PackageId = guid - }; - - if (!Current.Services.PackagingService.SaveInstalledPackage(installedPackage)) - throw new InvalidOperationException("Could not save package definition"); - - return installedPackage.Id; - } - - public void InstallFiles(int packageId, string tempDir) - { - var parser = new CompiledPackageXmlParser(); - - using (Current.ProfilingLogger.DebugDuration( - "Installing package files for package id " + packageId + " into temp folder " + tempDir, - "Package file installation complete for package id " + packageId)) - { - //retrieve the manifest to continue installation - var insPack = Current.Services.PackagingService.GetInstalledPackageById(packageId); - - //TODO: Depending on some files, some files should be installed differently. - //i.e. if stylsheets should probably be installed via business logic, media items should probably use the media IFileSystem! - - // Move files - //string virtualBasePath = System.Web.HttpContext.Current.Request.ApplicationPath; - string basePath = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath; - - var def = parser.ToCompiledPackage(Config); - - try - { - foreach (var f in def.Files) - { - var destPath = GetFileName(basePath, f.OriginalPath); - var sourceFile = GetFileName(tempDir, f.UniqueFileName); - var destFile = GetFileName(destPath, f.OriginalName); - - // Create the destination directory if it doesn't exist - if (Directory.Exists(destPath) == false) - Directory.CreateDirectory(destPath); - //If a file with this name exists, delete it - else if (File.Exists(destFile)) - File.Delete(destFile); - - // Copy the file - // SJ: Note - this used to do a move but some packages included the same file to be - // copied to multiple locations like so: - // - // - // my-icon.png - // /umbraco/Images/ - // my-icon.png - // - // - // my-icon.png - // /App_Plugins/MyPlugin/Images - // my-icon.png - // - // - // Since this file unzips as a flat list of files, moving the file the first time means - // that when you try to do that a second time, it would result in a FileNotFoundException - File.Copy(sourceFile, destFile); - - //PPH log file install - insPack.Files.Add(f.OriginalPath.EnsureEndsWith('/') + f.OriginalName); - - } - - // Once we're done copying, remove all the files - foreach (var f in def.Files) - { - var sourceFile = GetFileName(tempDir, f.UniqueFileName); - if (File.Exists(sourceFile)) - File.Delete(sourceFile); - } - } - catch (Exception ex) - { - Current.Logger.Error(ex, "Package install error"); - throw; - } - - // log that a user has install files - if (_currentUserId > -1) - { - Current.Services.AuditService.Add(AuditType.PackagerInstall, - _currentUserId, - -1, "Package", string.Format("Package '{0}' installed. Package guid: {1}", insPack.Name, insPack.PackageId)); - } - - Current.Services.PackagingService.SaveInstalledPackage(insPack); - } - } - - public void InstallBusinessLogic(int packageId, string tempDir) - { - using (Current.ProfilingLogger.DebugDuration( - "Installing business logic for package id " + packageId + " into temp folder " + tempDir, - "Package business logic installation complete for package id " + packageId)) - { - PackageDefinition insPack; - try - { - //retrieve the manifest to continue installation - insPack = Current.Services.PackagingService.GetInstalledPackageById(packageId); - //bool saveNeeded = false; - - // Get current user, with a fallback - var currentUser = Current.Services.UserService.GetUserById(Constants.Security.SuperUserId); - - //TODO: Get rid of this entire class! Until then all packages will be installed by the admin user - - var rootElement = Config.Root; - var packagingService = Current.Services.PackagingService; - - //Perhaps it would have been a good idea to put the following into methods eh?!? - - #region DataTypes - var dataTypeElement = rootElement.Descendants("DataTypes").FirstOrDefault(); - if (dataTypeElement != null) - { - var dataTypeDefinitions = packagingService.ImportDataTypeDefinitions(dataTypeElement, currentUser.Id); - foreach (var dataTypeDefinition in dataTypeDefinitions) - { - insPack.DataTypes.Add(dataTypeDefinition.Id.ToString(CultureInfo.InvariantCulture)); - } - } - #endregion - - #region Languages - var languageItemsElement = rootElement.Descendants("Languages").FirstOrDefault(); - if (languageItemsElement != null) - { - var insertedLanguages = packagingService.ImportLanguages(languageItemsElement); - foreach(var x in insertedLanguages.Select(l => l.Id.ToString(CultureInfo.InvariantCulture))) - insPack.Languages.Add(x); - } - - #endregion - - #region Dictionary items - var dictionaryItemsElement = rootElement.Descendants("DictionaryItems").FirstOrDefault(); - if (dictionaryItemsElement != null) - { - var insertedDictionaryItems = packagingService.ImportDictionaryItems(dictionaryItemsElement); - foreach (var x in insertedDictionaryItems.Select(d => d.Id.ToString(CultureInfo.InvariantCulture))) - insPack.DictionaryItems.Add(x); - } - #endregion - - #region Macros - var macroItemsElement = rootElement.Descendants("Macros").FirstOrDefault(); - if (macroItemsElement != null) - { - var insertedMacros = packagingService.ImportMacros(macroItemsElement); - foreach (var x in insertedMacros.Select(m => m.Id.ToString(CultureInfo.InvariantCulture))) - insPack.Macros.Add(x); - - } - #endregion - - #region Templates - var templateElement = rootElement.Descendants("Templates").FirstOrDefault(); - if (templateElement != null) - { - var templates = packagingService.ImportTemplates(templateElement, currentUser.Id); - foreach (var template in templates) - { - insPack.Templates.Add(template.Id.ToString(CultureInfo.InvariantCulture)); - } - } - #endregion - - #region DocumentTypes - //Check whether the root element is a doc type rather then a complete package - var docTypeElement = rootElement.Name.LocalName.Equals("DocumentType") || - rootElement.Name.LocalName.Equals("DocumentTypes") - ? rootElement - : rootElement.Descendants("DocumentTypes").FirstOrDefault(); - - if (docTypeElement != null) - { - var contentTypes = packagingService.ImportContentTypes(docTypeElement, currentUser.Id); - foreach (var contentType in contentTypes) - { - insPack.DocumentTypes.Add(contentType.Id.ToString(CultureInfo.InvariantCulture)); - //saveNeeded = true; - } - } - #endregion - - #region Stylesheets - foreach (var n in Config.Root.XPathSelectElements("Stylesheets/Stylesheet")) - { - string stylesheetName = n.Element("Name")?.Value; - if (stylesheetName.IsNullOrWhiteSpace()) continue; - - var s = Current.Services.FileService.GetStylesheetByName(stylesheetName); - if (s == null) - { - var fileName = n.Element("FileName")?.Value; - if (fileName == null) continue; - var content = n.Element("Content")?.Value; - if (content == null) continue; - - s = new Stylesheet(fileName) { Content = content }; - Current.Services.FileService.SaveStylesheet(s); - } - - foreach (var prop in n.XPathSelectElements("Properties/Property")) - { - string alias = prop.Element("Alias")?.Value; - var sp = s.Properties.SingleOrDefault(p => p != null && p.Alias == alias); - string name = prop.Element("Name")?.Value; - if (sp == null) - { - //sp = StylesheetProperty.MakeNew( - // name, - // s, - // u); - - sp = new StylesheetProperty(name, "#" + name.ToSafeAlias(), ""); - s.AddProperty(sp); - } - else - { - //sp.Text = name; - //Changing the name requires removing the current property and then adding another new one - if (sp.Name != name) - { - s.RemoveProperty(sp.Name); - var newProp = new StylesheetProperty(name, sp.Alias, sp.Value); - s.AddProperty(newProp); - sp = newProp; - } - } - sp.Alias = alias; - sp.Value = prop.Element("Value")?.Value; - } - //s.saveCssToFile(); - Current.Services.FileService.SaveStylesheet(s); - - - - - insPack.Stylesheets.Add(s.Id.ToString(CultureInfo.InvariantCulture)); - //saveNeeded = true; - } - - //if (saveNeeded) { insPack.Save(); saveNeeded = false; } - #endregion - - #region Documents - var documentElement = rootElement.Descendants("DocumentSet").FirstOrDefault(); - if (documentElement != null) - { - var content = packagingService.ImportContent(documentElement, -1, currentUser.Id); - var firstContentItem = content.First(); - insPack.ContentNodeId = firstContentItem.Id.ToString(CultureInfo.InvariantCulture); - } - #endregion - - #region Package Actions - foreach (var n in Config.Root.XPathSelectElements("Actions/Action")) - { - var undo = n.AttributeValue("undo"); - if (undo == null || undo == "true") - { - insPack.Actions += n.ToString(); - } - - //Run the actions tagged only for 'install' - var runat = n.AttributeValue("runat"); - - if (runat != null && runat == "install") - { - var alias = n.AttributeValue("alias"); - if (alias.IsNullOrWhiteSpace() == false) - { - Current.PackageActionRunner.RunPackageAction(insPack.Name, alias, n); - } - } - } - #endregion - - Current.Services.PackagingService.SaveInstalledPackage(insPack); - } - catch (Exception ex) - { - Current.Logger.Error(ex, "Error installing businesslogic"); - throw; - } - - OnPackageInstalled(insPack); - } - } - - /// - /// Remove the temp installation folder - /// - /// - /// - public void InstallCleanUp(int packageId, string tempDir) - { - if (Directory.Exists(tempDir)) - { - Directory.Delete(tempDir, true); - } - } - - /// - /// Reads the configuration of the package from the configuration xmldocument - /// - /// The folder to which the contents of the package is extracted - public void LoadConfig(string tempDir) - { - Config = XDocument.Load(tempDir + Path.DirectorySeparatorChar + "package.xml"); - - var parser = new CompiledPackageXmlParser(); - var def = parser.ToCompiledPackage(Config); - - Name = def.Name; - Version = def.Version; - Url = def.Url; - License = def.License; - LicenseUrl = def.LicenseUrl; - - RequirementsMajor = def.UmbracoVersion.Major; - RequirementsMinor = def.UmbracoVersion.Minor; - RequirementsPatch = def.UmbracoVersion.Build; - RequirementsType = def.UmbracoVersionRequirementsType; - IconUrl = def.IconUrl; - Author = def.Author; - AuthorUrl = def.AuthorUrl; - ReadMe = def.Readme; - Control = def.Control; - - var basePath = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath; - var dllBinFiles = new List(); - - foreach (var f in def.Files) - { - var badFile = false; - var destPath = GetFileName(basePath, f.OriginalPath); - var orgName = f.OriginalName; - var destFile = GetFileName(destPath, orgName); - - if (destPath.ToLower().Contains(IOHelper.DirSepChar + "app_code")) - { - badFile = true; - } - - if (destPath.ToLower().Contains(IOHelper.DirSepChar + "bin")) - { - badFile = true; - } - - if (destFile.ToLower().EndsWith(".dll")) - { - badFile = true; - dllBinFiles.Add(Path.Combine(tempDir, orgName)); - } - - if (badFile) - { - ContainsUnsecureFiles = true; - UnsecureFiles.Add(f.OriginalName); - } - } - - - - //this will check for existing macros with the same alias - //since we will not overwrite on import it's a good idea to inform the user what will be overwritten - foreach (var n in Config.Root.XPathSelectElements("//macro")) - { - var alias = n.Element("alias")?.Value; - if (!string.IsNullOrEmpty(alias)) - { - var m = Current.Services.MacroService.GetByAlias(alias); - if (m != null) - { - ContainsMacroConflict = true; - if (_conflictingMacroAliases.ContainsKey(m.Name) == false) - { - _conflictingMacroAliases.Add(m.Name, alias); - } - } - } - } - - foreach (var n in Config.Root.XPathSelectElements("Templates/Template")) - { - var alias = n.Element("Alias")?.Value; - if (!string.IsNullOrEmpty(alias)) - { - var t = Current.Services.FileService.GetTemplate(alias); - if (t != null) - { - ContainsTemplateConflicts = true; - if (_conflictingTemplateAliases.ContainsKey(t.Alias) == false) - { - _conflictingTemplateAliases.Add(t.Alias, alias); - } - } - } - } - - foreach (var n in Config.Root.XPathSelectElements("Stylesheets/Stylesheet")) - { - var alias = n.Element("Name")?.Value; - if (!string.IsNullOrEmpty(alias)) - { - var s = Current.Services.FileService.GetStylesheetByName(alias); - if (s != null) - { - ContainsStyleSheeConflicts = true; - if (_conflictingStyleSheetNames.ContainsKey(s.Alias) == false) - { - _conflictingStyleSheetNames.Add(s.Alias, alias); - } - } - } - } +//using System; +//using System.Collections.Generic; +//using System.Diagnostics; +//using System.Globalization; +//using System.IO; +//using System.Linq; +//using System.Net; +//using System.Xml; +//using System.Xml.Linq; +//using System.Xml.XPath; +//using ICSharpCode.SharpZipLib.Zip; +//using Umbraco.Core; +//using Umbraco.Core.Composing; +//using Umbraco.Core.Events; +//using Umbraco.Core.IO; +//using Umbraco.Core.Logging; +//using Umbraco.Core.Models; +//using Umbraco.Core.Models.Packaging; +//using Umbraco.Core.Packaging; +//using Umbraco.Core.Services.Implement; +//using File = System.IO.File; + +//namespace Umbraco.Web._Legacy.Packager +//{ +// /// +// /// The packager is a component which enables sharing of both data and functionality components between different umbraco installations. +// /// +// /// The output is a .umb (a zip compressed file) which contains the exported documents/medias/macroes/documentTypes (etc.) +// /// in a Xml document, along with the physical files used (images/usercontrols/xsl documents etc.) +// /// +// /// Partly implemented, import of packages is done, the export is *under construction*. +// /// +// /// +// /// Ruben Verborgh 31/12/2007: I had to change some code, I marked my changes with "DATALAYER". +// /// Reason: @@IDENTITY can't be used with the new datalayer. +// /// I wasn't able to test the code, since I'm not aware how the code functions. +// /// +// public class Installer +// { +// private const string PackageServer = "packages.umbraco.org"; + +// private readonly Dictionary _conflictingMacroAliases = new Dictionary(); +// private readonly Dictionary _conflictingTemplateAliases = new Dictionary(); +// private readonly Dictionary _conflictingStyleSheetNames = new Dictionary(); + +// private readonly int _currentUserId = -1; +// private static WebClient _webClient; + + +// public string Name { get; private set; } +// public string Version { get; private set; } +// public string Url { get; private set; } +// public string License { get; private set; } +// public string LicenseUrl { get; private set; } +// public string Author { get; private set; } +// public string AuthorUrl { get; private set; } +// public string ReadMe { get; private set; } +// public string Control { get; private set; } + +// public bool ContainsMacroConflict { get; private set; } +// public IDictionary ConflictingMacroAliases => _conflictingMacroAliases; + +// public bool ContainsUnsecureFiles { get; private set; } +// public List UnsecureFiles { get; } = new List(); + +// public bool ContainsTemplateConflicts { get; private set; } +// public IDictionary ConflictingTemplateAliases => _conflictingTemplateAliases; + +// public bool ContainsStyleSheeConflicts { get; private set; } +// public IDictionary ConflictingStyleSheetNames => _conflictingStyleSheetNames; + +// public int RequirementsMajor { get; private set; } +// public int RequirementsMinor { get; private set; } +// public int RequirementsPatch { get; private set; } + +// public RequirementsType RequirementsType { get; private set; } + +// public string IconUrl { get; private set; } + +// /// +// /// The xml of the compiled package +// /// +// public XDocument Config { get; private set; } + +// /// +// /// Constructor +// /// +// public Installer() +// { +// Initialize(); +// } + +// public Installer(int currentUserId) +// { +// Initialize(); +// _currentUserId = currentUserId; +// } + +// private void Initialize() +// { +// ContainsTemplateConflicts = false; +// ContainsUnsecureFiles = false; +// ContainsMacroConflict = false; +// ContainsStyleSheeConflicts = false; +// } + + + +// /// +// /// Constructor +// /// +// /// The name of the package +// /// The version of the package +// /// The url to a descriptionpage +// /// The license under which the package is released (preferably GPL ;)) +// /// The url to a licensedescription +// /// The original author of the package +// /// The url to the Authors website +// /// Umbraco version major +// /// Umbraco version minor +// /// Umbraco version patch +// /// The readme text +// /// The name of the usercontrol used to configure the package after install +// /// +// /// +// public Installer(string name, string version, string url, string license, string licenseUrl, string author, string authorUrl, int requirementsMajor, int requirementsMinor, int requirementsPatch, string readme, string control, RequirementsType requirementsType, string iconUrl) +// { +// ContainsTemplateConflicts = false; +// ContainsUnsecureFiles = false; +// ContainsMacroConflict = false; +// ContainsStyleSheeConflicts = false; +// this.Name = name; +// this.Version = version; +// this.Url = url; +// this.License = license; +// this.LicenseUrl = licenseUrl; +// this.RequirementsMajor = requirementsMajor; +// this.RequirementsMinor = requirementsMinor; +// this.RequirementsPatch = requirementsPatch; +// this.RequirementsType = requirementsType; +// this.Author = author; +// this.AuthorUrl = authorUrl; +// this.IconUrl = iconUrl; +// ReadMe = readme; +// this.Control = control; +// } + +// #region Public Methods + +// /// +// /// Imports the specified package +// /// +// /// Filename of the umbracopackage +// /// true if the input file should be deleted after import +// /// +// public string Import(string inputFile, bool deleteFile) +// { +// using (Current.ProfilingLogger.DebugDuration( +// $"Importing package file {inputFile}.", +// $"Package file {inputFile} imported.")) +// { +// var tempDir = ""; +// if (File.Exists(IOHelper.MapPath(SystemDirectories.Data + "/" + inputFile))) +// { +// var fi = new FileInfo(IOHelper.MapPath(SystemDirectories.Data + "/" + inputFile)); +// // Check if the file is a valid package +// if (fi.Extension.ToLower() == ".umb") +// { +// try +// { +// tempDir = UnPack(fi.FullName, deleteFile); +// LoadConfig(tempDir); +// } +// catch (Exception ex) +// { +// Current.Logger.Error(ex, "Error importing file {FileName}", fi.FullName); +// throw; +// } +// } +// else +// throw new Exception("Error - file isn't a package (doesn't have a .umb extension). Check if the file automatically got named '.zip' upon download."); +// } +// else +// throw new Exception("Error - file not found. Could find file named '" + IOHelper.MapPath(SystemDirectories.Data + Path.DirectorySeparatorChar + inputFile) + "'"); +// return tempDir; +// } + +// } + +// /// +// /// Imports the specified package +// /// +// /// Filename of the umbracopackage +// /// +// public string Import(string inputFile) +// { +// return Import(inputFile, true); +// } + +// public int CreateManifest(Guid guid) +// { +// //This is the new improved install rutine, which chops up the process into 3 steps, creating the manifest, moving files, and finally handling umb objects + +// var parser = new CompiledPackageXmlParser(); +// var def = parser.ToCompiledPackage(Config); + +// //create a new entry in the installedPackages.config +// var installedPackage = new PackageDefinition +// { +// Author = def.Author, +// AuthorUrl = def.AuthorUrl, +// Control = def.Control, +// IconUrl = def.IconUrl, +// License = def.License, +// LicenseUrl = def.LicenseUrl, +// Name = def.Name, +// Readme = def.Readme, +// UmbracoVersion = def.UmbracoVersion, +// Url = def.Url, +// Version = def.Version, +// PackageId = guid +// }; + +// if (!Current.Services.PackagingService.SaveInstalledPackage(installedPackage)) +// throw new InvalidOperationException("Could not save package definition"); + +// return installedPackage.Id; +// } + +// public void InstallFiles(int packageId, string tempDir) +// { +// var parser = new CompiledPackageXmlParser(); + +// using (Current.ProfilingLogger.DebugDuration( +// "Installing package files for package id " + packageId + " into temp folder " + tempDir, +// "Package file installation complete for package id " + packageId)) +// { +// //retrieve the manifest to continue installation +// var insPack = Current.Services.PackagingService.GetInstalledPackageById(packageId); + +// //TODO: Depending on some files, some files should be installed differently. +// //i.e. if stylsheets should probably be installed via business logic, media items should probably use the media IFileSystem! + +// // Move files +// //string virtualBasePath = System.Web.HttpContext.Current.Request.ApplicationPath; +// string basePath = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath; + +// var def = parser.ToCompiledPackage(Config); + +// try +// { +// foreach (var f in def.Files) +// { +// var destPath = GetFileName(basePath, f.OriginalPath); +// var sourceFile = GetFileName(tempDir, f.UniqueFileName); +// var destFile = GetFileName(destPath, f.OriginalName); + +// // Create the destination directory if it doesn't exist +// if (Directory.Exists(destPath) == false) +// Directory.CreateDirectory(destPath); +// //If a file with this name exists, delete it +// else if (File.Exists(destFile)) +// File.Delete(destFile); + +// // Copy the file +// // SJ: Note - this used to do a move but some packages included the same file to be +// // copied to multiple locations like so: +// // +// // +// // my-icon.png +// // /umbraco/Images/ +// // my-icon.png +// // +// // +// // my-icon.png +// // /App_Plugins/MyPlugin/Images +// // my-icon.png +// // +// // +// // Since this file unzips as a flat list of files, moving the file the first time means +// // that when you try to do that a second time, it would result in a FileNotFoundException +// File.Copy(sourceFile, destFile); + +// //PPH log file install +// insPack.Files.Add(f.OriginalPath.EnsureEndsWith('/') + f.OriginalName); + +// } + +// // Once we're done copying, remove all the files +// foreach (var f in def.Files) +// { +// var sourceFile = GetFileName(tempDir, f.UniqueFileName); +// if (File.Exists(sourceFile)) +// File.Delete(sourceFile); +// } +// } +// catch (Exception ex) +// { +// Current.Logger.Error(ex, "Package install error"); +// throw; +// } + +// // log that a user has install files +// if (_currentUserId > -1) +// { +// Current.Services.AuditService.Add(AuditType.PackagerInstall, +// _currentUserId, +// -1, "Package", string.Format("Package '{0}' installed. Package guid: {1}", insPack.Name, insPack.PackageId)); +// } + +// Current.Services.PackagingService.SaveInstalledPackage(insPack); +// } +// } + +// public void InstallBusinessLogic(int packageId, string tempDir) +// { +// using (Current.ProfilingLogger.DebugDuration( +// "Installing business logic for package id " + packageId + " into temp folder " + tempDir, +// "Package business logic installation complete for package id " + packageId)) +// { +// PackageDefinition insPack; +// try +// { +// //retrieve the manifest to continue installation +// insPack = Current.Services.PackagingService.GetInstalledPackageById(packageId); +// //bool saveNeeded = false; + +// // Get current user, with a fallback +// var currentUser = Current.Services.UserService.GetUserById(Constants.Security.SuperUserId); + +// //TODO: Get rid of this entire class! Until then all packages will be installed by the admin user + +// var rootElement = Config.Root; +// var packagingService = Current.Services.PackagingService; + +// //Perhaps it would have been a good idea to put the following into methods eh?!? + +// #region DataTypes +// var dataTypeElement = rootElement.Descendants("DataTypes").FirstOrDefault(); +// if (dataTypeElement != null) +// { +// var dataTypeDefinitions = packagingService.ImportDataTypeDefinitions(dataTypeElement, currentUser.Id); +// foreach (var dataTypeDefinition in dataTypeDefinitions) +// { +// insPack.DataTypes.Add(dataTypeDefinition.Id.ToString(CultureInfo.InvariantCulture)); +// } +// } +// #endregion + +// #region Languages +// var languageItemsElement = rootElement.Descendants("Languages").FirstOrDefault(); +// if (languageItemsElement != null) +// { +// var insertedLanguages = packagingService.ImportLanguages(languageItemsElement); +// foreach(var x in insertedLanguages.Select(l => l.Id.ToString(CultureInfo.InvariantCulture))) +// insPack.Languages.Add(x); +// } + +// #endregion + +// #region Dictionary items +// var dictionaryItemsElement = rootElement.Descendants("DictionaryItems").FirstOrDefault(); +// if (dictionaryItemsElement != null) +// { +// var insertedDictionaryItems = packagingService.ImportDictionaryItems(dictionaryItemsElement); +// foreach (var x in insertedDictionaryItems.Select(d => d.Id.ToString(CultureInfo.InvariantCulture))) +// insPack.DictionaryItems.Add(x); +// } +// #endregion + +// #region Macros +// var macroItemsElement = rootElement.Descendants("Macros").FirstOrDefault(); +// if (macroItemsElement != null) +// { +// var insertedMacros = packagingService.ImportMacros(macroItemsElement); +// foreach (var x in insertedMacros.Select(m => m.Id.ToString(CultureInfo.InvariantCulture))) +// insPack.Macros.Add(x); + +// } +// #endregion + +// #region Templates +// var templateElement = rootElement.Descendants("Templates").FirstOrDefault(); +// if (templateElement != null) +// { +// var templates = packagingService.ImportTemplates(templateElement, currentUser.Id); +// foreach (var template in templates) +// { +// insPack.Templates.Add(template.Id.ToString(CultureInfo.InvariantCulture)); +// } +// } +// #endregion + +// #region DocumentTypes +// //Check whether the root element is a doc type rather then a complete package +// var docTypeElement = rootElement.Name.LocalName.Equals("DocumentType") || +// rootElement.Name.LocalName.Equals("DocumentTypes") +// ? rootElement +// : rootElement.Descendants("DocumentTypes").FirstOrDefault(); + +// if (docTypeElement != null) +// { +// var contentTypes = packagingService.ImportContentTypes(docTypeElement, currentUser.Id); +// foreach (var contentType in contentTypes) +// { +// insPack.DocumentTypes.Add(contentType.Id.ToString(CultureInfo.InvariantCulture)); +// //saveNeeded = true; +// } +// } +// #endregion + +// #region Stylesheets +// foreach (var n in Config.Root.XPathSelectElements("Stylesheets/Stylesheet")) +// { +// string stylesheetName = n.Element("Name")?.Value; +// if (stylesheetName.IsNullOrWhiteSpace()) continue; + +// var s = Current.Services.FileService.GetStylesheetByName(stylesheetName); +// if (s == null) +// { +// var fileName = n.Element("FileName")?.Value; +// if (fileName == null) continue; +// var content = n.Element("Content")?.Value; +// if (content == null) continue; + +// s = new Stylesheet(fileName) { Content = content }; +// Current.Services.FileService.SaveStylesheet(s); +// } + +// foreach (var prop in n.XPathSelectElements("Properties/Property")) +// { +// string alias = prop.Element("Alias")?.Value; +// var sp = s.Properties.SingleOrDefault(p => p != null && p.Alias == alias); +// string name = prop.Element("Name")?.Value; +// if (sp == null) +// { +// //sp = StylesheetProperty.MakeNew( +// // name, +// // s, +// // u); + +// sp = new StylesheetProperty(name, "#" + name.ToSafeAlias(), ""); +// s.AddProperty(sp); +// } +// else +// { +// //sp.Text = name; +// //Changing the name requires removing the current property and then adding another new one +// if (sp.Name != name) +// { +// s.RemoveProperty(sp.Name); +// var newProp = new StylesheetProperty(name, sp.Alias, sp.Value); +// s.AddProperty(newProp); +// sp = newProp; +// } +// } +// sp.Alias = alias; +// sp.Value = prop.Element("Value")?.Value; +// } +// //s.saveCssToFile(); +// Current.Services.FileService.SaveStylesheet(s); + + + + +// insPack.Stylesheets.Add(s.Id.ToString(CultureInfo.InvariantCulture)); +// //saveNeeded = true; +// } + +// //if (saveNeeded) { insPack.Save(); saveNeeded = false; } +// #endregion + +// #region Documents +// var documentElement = rootElement.Descendants("DocumentSet").FirstOrDefault(); +// if (documentElement != null) +// { +// var content = packagingService.ImportContent(documentElement, -1, currentUser.Id); +// var firstContentItem = content.First(); +// insPack.ContentNodeId = firstContentItem.Id.ToString(CultureInfo.InvariantCulture); +// } +// #endregion + +// #region Package Actions +// foreach (var n in Config.Root.XPathSelectElements("Actions/Action")) +// { +// var undo = n.AttributeValue("undo"); +// if (undo == null || undo == "true") +// { +// insPack.Actions += n.ToString(); +// } + +// //Run the actions tagged only for 'install' +// var runat = n.AttributeValue("runat"); + +// if (runat != null && runat == "install") +// { +// var alias = n.AttributeValue("alias"); +// if (alias.IsNullOrWhiteSpace() == false) +// { +// Current.PackageActionRunner.RunPackageAction(insPack.Name, alias, n); +// } +// } +// } +// #endregion + +// Current.Services.PackagingService.SaveInstalledPackage(insPack); +// } +// catch (Exception ex) +// { +// Current.Logger.Error(ex, "Error installing businesslogic"); +// throw; +// } + +// OnPackageInstalled(insPack); +// } +// } + +// /// +// /// Remove the temp installation folder +// /// +// /// +// /// +// public void InstallCleanUp(int packageId, string tempDir) +// { +// if (Directory.Exists(tempDir)) +// { +// Directory.Delete(tempDir, true); +// } +// } + +// /// +// /// Reads the configuration of the package from the configuration xmldocument +// /// +// /// The folder to which the contents of the package is extracted +// public void LoadConfig(string tempDir) +// { +// Config = XDocument.Load(tempDir + Path.DirectorySeparatorChar + "package.xml"); + +// var parser = new CompiledPackageXmlParser(); +// var def = parser.ToCompiledPackage(Config); + +// Name = def.Name; +// Version = def.Version; +// Url = def.Url; +// License = def.License; +// LicenseUrl = def.LicenseUrl; + +// RequirementsMajor = def.UmbracoVersion.Major; +// RequirementsMinor = def.UmbracoVersion.Minor; +// RequirementsPatch = def.UmbracoVersion.Build; +// RequirementsType = def.UmbracoVersionRequirementsType; +// IconUrl = def.IconUrl; +// Author = def.Author; +// AuthorUrl = def.AuthorUrl; +// ReadMe = def.Readme; +// Control = def.Control; + +// var basePath = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath; +// var dllBinFiles = new List(); + +// foreach (var f in def.Files) +// { +// var badFile = false; +// var destPath = GetFileName(basePath, f.OriginalPath); +// var orgName = f.OriginalName; +// var destFile = GetFileName(destPath, orgName); + +// if (destPath.ToLower().Contains(IOHelper.DirSepChar + "app_code")) +// { +// badFile = true; +// } + +// if (destPath.ToLower().Contains(IOHelper.DirSepChar + "bin")) +// { +// badFile = true; +// } + +// if (destFile.ToLower().EndsWith(".dll")) +// { +// badFile = true; +// dllBinFiles.Add(Path.Combine(tempDir, orgName)); +// } + +// if (badFile) +// { +// ContainsUnsecureFiles = true; +// UnsecureFiles.Add(f.OriginalName); +// } +// } + + + +// //this will check for existing macros with the same alias +// //since we will not overwrite on import it's a good idea to inform the user what will be overwritten +// foreach (var n in Config.Root.XPathSelectElements("//macro")) +// { +// var alias = n.Element("alias")?.Value; +// if (!string.IsNullOrEmpty(alias)) +// { +// var m = Current.Services.MacroService.GetByAlias(alias); +// if (m != null) +// { +// ContainsMacroConflict = true; +// if (_conflictingMacroAliases.ContainsKey(m.Name) == false) +// { +// _conflictingMacroAliases.Add(m.Name, alias); +// } +// } +// } +// } + +// foreach (var n in Config.Root.XPathSelectElements("Templates/Template")) +// { +// var alias = n.Element("Alias")?.Value; +// if (!string.IsNullOrEmpty(alias)) +// { +// var t = Current.Services.FileService.GetTemplate(alias); +// if (t != null) +// { +// ContainsTemplateConflicts = true; +// if (_conflictingTemplateAliases.ContainsKey(t.Alias) == false) +// { +// _conflictingTemplateAliases.Add(t.Alias, alias); +// } +// } +// } +// } + +// foreach (var n in Config.Root.XPathSelectElements("Stylesheets/Stylesheet")) +// { +// var alias = n.Element("Name")?.Value; +// if (!string.IsNullOrEmpty(alias)) +// { +// var s = Current.Services.FileService.GetStylesheetByName(alias); +// if (s != null) +// { +// ContainsStyleSheeConflicts = true; +// if (_conflictingStyleSheetNames.ContainsKey(s.Alias) == false) +// { +// _conflictingStyleSheetNames.Add(s.Alias, alias); +// } +// } +// } +// } - } +// } - /// - /// This uses the old method of fetching and only supports the packages.umbraco.org repository. - /// - /// - /// - public string Fetch(Guid Package) - { - // Check for package directory - if (Directory.Exists(IOHelper.MapPath(SystemDirectories.Packages)) == false) - Directory.CreateDirectory(IOHelper.MapPath(SystemDirectories.Packages)); +// /// +// /// This uses the old method of fetching and only supports the packages.umbraco.org repository. +// /// +// /// +// /// +// public string Fetch(Guid Package) +// { +// // Check for package directory +// if (Directory.Exists(IOHelper.MapPath(SystemDirectories.Packages)) == false) +// Directory.CreateDirectory(IOHelper.MapPath(SystemDirectories.Packages)); - if (_webClient == null) - _webClient = new WebClient(); +// if (_webClient == null) +// _webClient = new WebClient(); - _webClient.DownloadFile( - "http://" + PackageServer + "/fetch?package=" + Package.ToString(), - IOHelper.MapPath(SystemDirectories.Packages + "/" + Package + ".umb")); +// _webClient.DownloadFile( +// "http://" + PackageServer + "/fetch?package=" + Package.ToString(), +// IOHelper.MapPath(SystemDirectories.Packages + "/" + Package + ".umb")); - return "packages\\" + Package + ".umb"; - } +// return "packages\\" + Package + ".umb"; +// } - #endregion +// #endregion - #region Private Methods +// private void OnPackageInstalled(PackageDefinition insPack) +// { +// // getting an InstallationSummary for sending to the PackagingService.ImportedPackage event +// var fileService = Current.Services.FileService; +// var macroService = Current.Services.MacroService; +// var contentTypeService = Current.Services.ContentTypeService; +// var dataTypeService = Current.Services.DataTypeService; +// var localizationService = Current.Services.LocalizationService; - /// - /// Gets the name of the file in the specified path. - /// Corrects possible problems with slashes that would result from a simple concatenation. - /// Can also be used to concatenate paths. - /// - /// The path. - /// Name of the file. - /// The name of the file in the specified path. - private static string GetFileName(string path, string fileName) - { - // virtual dir support - fileName = IOHelper.FindFile(fileName); +// var installationSummary = InstallationSummary.FromPackageDefinition(insPack, contentTypeService, dataTypeService, fileService, localizationService, macroService); +// installationSummary.PackageInstalled = true; - if (path.Contains("[$")) - { - //this is experimental and undocumented... - path = path.Replace("[$UMBRACO]", SystemDirectories.Umbraco); - path = path.Replace("[$CONFIG]", SystemDirectories.Config); - path = path.Replace("[$DATA]", SystemDirectories.Data); - } - - //to support virtual dirs we try to lookup the file... - path = IOHelper.FindFile(path); - - - - Debug.Assert(path != null && path.Length >= 1); - Debug.Assert(fileName != null && fileName.Length >= 1); - - path = path.Replace('/', '\\'); - fileName = fileName.Replace('/', '\\'); - - // Does filename start with a slash? Does path end with one? - bool fileNameStartsWithSlash = (fileName[0] == Path.DirectorySeparatorChar); - bool pathEndsWithSlash = (path[path.Length - 1] == Path.DirectorySeparatorChar); - - // Path ends with a slash - if (pathEndsWithSlash) - { - if (!fileNameStartsWithSlash) - // No double slash, just concatenate - return path + fileName; - return path + fileName.Substring(1); - } - if (fileNameStartsWithSlash) - // Required slash specified, just concatenate - return path + fileName; - return path + Path.DirectorySeparatorChar + fileName; - } - - private static string UnPack(string zipName, bool deleteFile) - { - // Unzip - - //the temp directory will be the package GUID - this keeps it consistent! - //the zipName is always the package Guid.umb - - var packageFileName = Path.GetFileNameWithoutExtension(zipName); - var packageId = Guid.NewGuid(); - Guid.TryParse(packageFileName, out packageId); - - string tempDir = IOHelper.MapPath(SystemDirectories.Data) + Path.DirectorySeparatorChar + packageId.ToString(); - //clear the directory if it exists - if (Directory.Exists(tempDir)) Directory.Delete(tempDir, true); - Directory.CreateDirectory(tempDir); - - var s = new ZipInputStream(File.OpenRead(zipName)); - - ZipEntry theEntry; - while ((theEntry = s.GetNextEntry()) != null) - { - string fileName = Path.GetFileName(theEntry.Name); - - if (fileName != String.Empty) - { - FileStream streamWriter = File.Create(tempDir + Path.DirectorySeparatorChar + fileName); - - int size = 2048; - byte[] data = new byte[2048]; - while (true) - { - size = s.Read(data, 0, data.Length); - if (size > 0) - { - streamWriter.Write(data, 0, size); - } - else - { - break; - } - } - - streamWriter.Close(); - - } - } - - // Clean up - s.Close(); - - if (deleteFile) - { - File.Delete(zipName); - } - - - return tempDir; - - } - - #endregion - - - private void OnPackageInstalled(PackageDefinition insPack) - { - // getting an InstallationSummary for sending to the PackagingService.ImportedPackage event - var fileService = Current.Services.FileService; - var macroService = Current.Services.MacroService; - var contentTypeService = Current.Services.ContentTypeService; - var dataTypeService = Current.Services.DataTypeService; - var localizationService = Current.Services.LocalizationService; - - var installationSummary = InstallationSummary.FromPackageDefinition(insPack, contentTypeService, dataTypeService, fileService, localizationService, macroService); - installationSummary.PackageInstalled = true; - - var args = new ImportPackageEventArgs(installationSummary, insPack, false); - PackagingService.OnImportedPackage(args); - } - } -} +// var args = new ImportPackageEventArgs(installationSummary, insPack, false); +// PackagingService.OnImportedPackage(args); +// } +// } +//} diff --git a/src/Umbraco.Web/_Legacy/Packager/Settings.cs b/src/Umbraco.Web/_Legacy/Packager/Settings.cs deleted file mode 100644 index e88f18262f..0000000000 --- a/src/Umbraco.Web/_Legacy/Packager/Settings.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.IO; -using Umbraco.Core.IO; - -namespace Umbraco.Web._Legacy.Packager -{ - public class Settings - { - public static string InstalledPackagesSettings => SystemDirectories.Packages + IOHelper.DirSepChar + "installedPackages.config"; - - } - -} From 65e98928d4b5bd96b7ec919978abead1f39d4171 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 14 Jan 2019 17:46:12 +1100 Subject: [PATCH 57/93] Package data installation is now all migrated --- .../Composing/Composers/ServicesComposer.cs | 7 +- src/Umbraco.Core/Composing/Current.cs | 4 +- src/Umbraco.Core/Constants-Packaging.cs | 55 - src/Umbraco.Core/Events/ImportEventArgs.cs | 84 -- .../Models/Packaging/CompiledPackage.cs | 6 +- .../Models/Packaging/InstallationSummary.cs | 2 +- .../Packaging/CompiledPackageXmlParser.cs | 49 +- .../Packaging/ConflictingPackageData.cs | 12 +- .../Packaging/IPackageActionRunner.cs | 23 + .../Packaging/IPackageInstallation.cs | 3 +- .../Packaging/PackageActionRunner.cs | 3 +- .../Packaging/PackageDataInstallation.cs | 1144 +++++++++++++++ .../Packaging/PackageInstallation.cs | 306 +--- .../Services/IPackagingService.cs | 75 - .../Services/Implement/PackagingService.cs | 1303 +---------------- src/Umbraco.Core/Umbraco.Core.csproj | 4 +- .../Packaging/PackageInstallationTest.cs | 12 +- .../Services/Importing/PackageImportTests.cs | 126 +- src/Umbraco.Tests/TestHelpers/TestObjects.cs | 27 +- src/Umbraco.Web/Composing/Current.cs | 2 +- .../Editors/ContentTypeController.cs | 29 +- .../Editors/PackageInstallController.cs | 5 +- src/Umbraco.Web/Umbraco.Web.csproj | 1 - src/Umbraco.Web/_Legacy/Packager/Installer.cs | 688 --------- 24 files changed, 1424 insertions(+), 2546 deletions(-) delete mode 100644 src/Umbraco.Core/Constants-Packaging.cs delete mode 100644 src/Umbraco.Core/Events/ImportEventArgs.cs create mode 100644 src/Umbraco.Core/Packaging/IPackageActionRunner.cs create mode 100644 src/Umbraco.Core/Packaging/PackageDataInstallation.cs delete mode 100644 src/Umbraco.Web/_Legacy/Packager/Installer.cs diff --git a/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs b/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs index ac8f4beeb0..a09462c806 100644 --- a/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs +++ b/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs @@ -59,18 +59,19 @@ namespace Umbraco.Core.Composing.Composers composition.RegisterUnique(); - composition.RegisterUnique(); + composition.RegisterUnique(); composition.RegisterUnique(); composition.RegisterUnique(); composition.RegisterUnique(factory => CreatePackageRepository(factory, "createdPackages.config")); composition.RegisterUnique(factory => CreatePackageRepository(factory, "installedPackages.config")); + composition.RegisterUnique(); composition.RegisterUnique(); var appRoot = new DirectoryInfo(IOHelper.GetRootDirectorySafe()); composition.RegisterUnique(factory => //factory required because we need to pass in a string path new PackageInstallation( - factory.GetInstance(), factory.GetInstance(), - factory.GetInstance(), + factory.GetInstance(), factory.GetInstance(), + factory.GetInstance(), factory.GetInstance(), SystemDirectories.Packages, appRoot, appRoot)); diff --git a/src/Umbraco.Core/Composing/Current.cs b/src/Umbraco.Core/Composing/Current.cs index 4bed76d86f..5c8351924f 100644 --- a/src/Umbraco.Core/Composing/Current.cs +++ b/src/Umbraco.Core/Composing/Current.cs @@ -162,8 +162,8 @@ namespace Umbraco.Core.Composing internal static PackageActionCollection PackageActions => Factory.GetInstance(); - internal static PackageActionRunner PackageActionRunner - => Factory.GetInstance(); + internal static IPackageActionRunner PackageActionRunner + => Factory.GetInstance(); internal static PropertyValueConverterCollection PropertyValueConverters => Factory.GetInstance(); diff --git a/src/Umbraco.Core/Constants-Packaging.cs b/src/Umbraco.Core/Constants-Packaging.cs deleted file mode 100644 index 37f2c23fa3..0000000000 --- a/src/Umbraco.Core/Constants-Packaging.cs +++ /dev/null @@ -1,55 +0,0 @@ -namespace Umbraco.Core -{ - public static partial class Constants - { - /// - /// Defines the constants used for Umbraco packages in the package.config xml - /// - public static class Packaging - { - public const string UmbPackageNodeName = "umbPackage"; - public const string DataTypesNodeName = "DataTypes"; - public const string PackageXmlFileName = "package.xml"; - public const string UmbracoPackageExtention = ".umb"; - public const string DataTypeNodeName = "DataType"; - public const string LanguagesNodeName = "Languages"; - public const string FilesNodeName = "files"; - public const string StylesheetsNodeName = "Stylesheets"; - public const string TemplatesNodeName = "Templates"; - public const string NameNodeName = "Name"; - public const string TemplateNodeName = "Template"; - public const string AliasNodeNameSmall = "alias"; - public const string AliasNodeNameCapital = "Alias"; - public const string DictionaryItemsNodeName = "DictionaryItems"; - public const string DictionaryItemNodeName = "DictionaryItem"; - public const string MacrosNodeName = "Macros"; - public const string DocumentsNodeName = "Documents"; - public const string DocumentSetNodeName = "DocumentSet"; - public const string DocumentTypesNodeName = "DocumentTypes"; - public const string DocumentTypeNodeName = "DocumentType"; - public const string FileNodeName = "file"; - public const string OrgNameNodeName = "orgName"; - public const string OrgPathNodeName = "orgPath"; - public const string GuidNodeName = "guid"; - public const string StylesheetNodeName = "styleSheet"; - public const string MacroNodeName = "macro"; - public const string InfoNodeName = "info"; - public const string PackageRequirementsMajorXpath = "./package/requirements/major"; - public const string PackageRequirementsMinorXpath = "./package/requirements/minor"; - public const string PackageRequirementsPatchXpath = "./package/requirements/patch"; - public const string PackageNameXpath = "./package/name"; - public const string PackageVersionXpath = "./package/version"; - public const string PackageUrlXpath = "./package/url"; - public const string PackageLicenseXpath = "./package/license"; - public const string PackageLicenseXpathUrlAttribute = "url"; - public const string AuthorNameXpath = "./author/name"; - public const string AuthorWebsiteXpath = "./author/website"; - public const string ReadmeXpath = "./readme"; - public const string ControlNodeName = "control"; - public const string ActionNodeName = "Action"; - public const string ActionsNodeName = "Actions"; - public const string UndoNodeAttribute = "undo"; - public const string RunatNodeAttribute = "runat"; - } - } -} diff --git a/src/Umbraco.Core/Events/ImportEventArgs.cs b/src/Umbraco.Core/Events/ImportEventArgs.cs deleted file mode 100644 index fb8f7d4936..0000000000 --- a/src/Umbraco.Core/Events/ImportEventArgs.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Xml.Linq; - -namespace Umbraco.Core.Events -{ - public class ImportEventArgs : CancellableEnumerableObjectEventArgs, IEquatable> - { - /// - /// Constructor accepting an XElement with the xml being imported - /// - /// - public ImportEventArgs(XElement xml) : base(new List(), true) - { - Xml = xml; - } - - /// - /// Constructor accepting a list of entities and an XElement with the imported xml - /// - /// - /// - /// - public ImportEventArgs(IEnumerable eventObject, XElement xml, bool canCancel) - : base(eventObject, canCancel) - { - Xml = xml; - } - - protected ImportEventArgs(IEnumerable eventObject, bool canCancel) : base(eventObject, canCancel) - { - } - - protected ImportEventArgs(IEnumerable eventObject) : base(eventObject) - { - } - - /// - /// Returns all entities that were imported during the operation - /// - public IEnumerable ImportedEntities - { - get { return EventObject; } - } - - /// - /// Returns the xml relating to the import event - /// - public XElement Xml { get; private set; } - - public bool Equals(ImportEventArgs other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return base.Equals(other) && Equals(Xml, other.Xml); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((ImportEventArgs) obj); - } - - public override int GetHashCode() - { - unchecked - { - return (base.GetHashCode() * 397) ^ (Xml != null ? Xml.GetHashCode() : 0); - } - } - - public static bool operator ==(ImportEventArgs left, ImportEventArgs right) - { - return Equals(left, right); - } - - public static bool operator !=(ImportEventArgs left, ImportEventArgs right) - { - return !Equals(left, right); - } - } -} diff --git a/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs b/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs index 96159d1bbf..266c1f5518 100644 --- a/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs +++ b/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs @@ -34,7 +34,11 @@ namespace Umbraco.Core.Models.Packaging public IEnumerable Macros { get; set; } //fixme: make strongly typed public IEnumerable Templates { get; set; } //fixme: make strongly typed public IEnumerable Stylesheets { get; set; } //fixme: make strongly typed - + public IEnumerable DataTypes { get; set; } //fixme: make strongly typed + public IEnumerable Languages { get; set; } //fixme: make strongly typed + public IEnumerable DictionaryItems { get; set; } //fixme: make strongly typed + public IEnumerable DocumentTypes { get; set; } //fixme: make strongly typed + public IEnumerable Documents { get; set; } //fixme: make strongly typed } public class CompiledPackageFile diff --git a/src/Umbraco.Core/Models/Packaging/InstallationSummary.cs b/src/Umbraco.Core/Models/Packaging/InstallationSummary.cs index 1405259da2..b14e3d2a92 100644 --- a/src/Umbraco.Core/Models/Packaging/InstallationSummary.cs +++ b/src/Umbraco.Core/Models/Packaging/InstallationSummary.cs @@ -18,7 +18,7 @@ namespace Umbraco.Core.Models.Packaging public IEnumerable MacrosInstalled { get; set; } = Enumerable.Empty(); public IEnumerable FilesInstalled { get; set; } = Enumerable.Empty(); public IEnumerable TemplatesInstalled { get; set; } = Enumerable.Empty(); - public IEnumerable ContentTypesInstalled { get; set; } = Enumerable.Empty(); + public IEnumerable DocumentTypesInstalled { get; set; } = Enumerable.Empty(); public IEnumerable StylesheetsInstalled { get; set; } = Enumerable.Empty(); public IEnumerable ContentInstalled { get; set; } = Enumerable.Empty(); public IEnumerable Actions { get; set; } = Enumerable.Empty(); diff --git a/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs index 44dbded5d2..76f206f478 100644 --- a/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs +++ b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs @@ -26,7 +26,7 @@ namespace Umbraco.Core.Packaging { if (xml == null) throw new ArgumentNullException(nameof(xml)); if (xml.Root == null) throw new ArgumentException(nameof(xml), "The xml document is invalid"); - if (xml.Root.Name != Constants.Packaging.UmbPackageNodeName) throw new FormatException("The xml document is invalid"); + if (xml.Root.Name != "umbPackage") throw new FormatException("The xml document is invalid"); var info = xml.Root.Element("info"); if (info == null) throw new FormatException("The xml document is invalid"); @@ -53,7 +53,6 @@ namespace Umbraco.Core.Packaging UmbracoVersionRequirementsType = requirements.AttributeValue("type").IsNullOrWhiteSpace() ? RequirementsType.Legacy : Enum.Parse(requirements.AttributeValue("type")), Control = package.Element("control")?.Value, Actions = xml.Element("Actions")?.ToString(SaveOptions.None) ?? "", //take the entire outer xml value - Files = xml.Root.Element("files")?.Elements("file")?.Select(x => new CompiledPackageFile { UniqueFileName = x.Element("guid")?.Value, @@ -64,7 +63,11 @@ namespace Umbraco.Core.Packaging Macros = xml.Element("Macros")?.Elements("macro") ?? Enumerable.Empty(), Templates = xml.Element("Templates")?.Elements("Template") ?? Enumerable.Empty(), Stylesheets = xml.Element("Stylesheets")?.Elements("styleSheet") ?? Enumerable.Empty(), - + DataTypes = xml.Element("DataTypes")?.Elements("DataType") ?? Enumerable.Empty(), + Languages = xml.Element("Languages")?.Elements("Language") ?? Enumerable.Empty(), + DictionaryItems = xml.Element("DictionaryItems")?.Elements("DictionaryItem") ?? Enumerable.Empty(), + DocumentTypes = xml.Element("DocumentTypes")?.Elements("DocumentType") ?? Enumerable.Empty(), + Documents = xml.Element("Documents")?.Elements("DocumentSet") ?? Enumerable.Empty(), }; def.Warnings = GetPreInstallWarnings(def, applicationRootFolder); @@ -133,9 +136,8 @@ namespace Umbraco.Core.Packaging { return pathElement.TrimStart(new[] { '\\', '/', '~' }).Replace("/", "\\"); } - - //fixme: This is duplicated in the parser - public static string UpdatePathPlaceholders(string path) + + private static string UpdatePathPlaceholders(string path) { if (path.Contains("[$")) { @@ -146,5 +148,40 @@ namespace Umbraco.Core.Packaging } return path; } + + public IEnumerable GetPackageActions(XElement actionsElement, string packageName) + { + if (actionsElement == null) { return new PackageAction[0]; } + + if (string.Equals("Actions", actionsElement.Name.LocalName) == false) + throw new ArgumentException($"Must be \"Actions\" as root", nameof(actionsElement)); + + return actionsElement.Elements("Action") + .Select(elemet => + { + var aliasAttr = elemet.Attribute("Alias"); + if (aliasAttr == null) + throw new ArgumentException("missing \"Alias\" atribute in alias element", nameof(actionsElement)); + + var packageAction = new PackageAction + { + XmlData = elemet, + Alias = aliasAttr.Value, + PackageName = packageName, + }; + + + var attr = elemet.Attribute("runat"); + + if (attr != null && Enum.TryParse(attr.Value, true, out ActionRunAt runAt)) { packageAction.RunAt = runAt; } + + attr = elemet.Attribute("undo"); + + if (attr != null && bool.TryParse(attr.Value, out var undo)) { packageAction.Undo = undo; } + + + return packageAction; + }).ToArray(); + } } } diff --git a/src/Umbraco.Core/Packaging/ConflictingPackageData.cs b/src/Umbraco.Core/Packaging/ConflictingPackageData.cs index 401d3b6a85..a37195806e 100644 --- a/src/Umbraco.Core/Packaging/ConflictingPackageData.cs +++ b/src/Umbraco.Core/Packaging/ConflictingPackageData.cs @@ -23,9 +23,9 @@ namespace Umbraco.Core.Packaging return stylesheetNodes .Select(n => { - var xElement = n.Element(Constants.Packaging.NameNodeName); + var xElement = n.Element("Name"); if (xElement == null) - throw new FormatException($"Missing \"{Constants.Packaging.NameNodeName}\" element"); + throw new FormatException("Missing \"Name\" element"); return _fileService.GetStylesheetByName(xElement.Value) as IFile; }) @@ -37,9 +37,9 @@ namespace Umbraco.Core.Packaging return templateNodes .Select(n => { - var xElement = n.Element(Constants.Packaging.AliasNodeNameCapital) ?? n.Element(Constants.Packaging.AliasNodeNameSmall); + var xElement = n.Element("Alias") ?? n.Element("alias"); if (xElement == null) - throw new FormatException($"missing a \"{Constants.Packaging.AliasNodeNameCapital}\" element"); + throw new FormatException("missing a \"Alias\" element"); return _fileService.GetTemplate(xElement.Value); }) @@ -51,9 +51,9 @@ namespace Umbraco.Core.Packaging return macroNodes .Select(n => { - var xElement = n.Element(Constants.Packaging.AliasNodeNameSmall) ?? n.Element(Constants.Packaging.AliasNodeNameCapital); + var xElement = n.Element("alias") ?? n.Element("Alias"); if (xElement == null) - throw new FormatException($"missing a \"{Constants.Packaging.AliasNodeNameSmall}\" element in {Constants.Packaging.AliasNodeNameSmall} element"); + throw new FormatException("missing a \"alias\" element in alias element"); return _macroService.GetByAlias(xElement.Value); }) diff --git a/src/Umbraco.Core/Packaging/IPackageActionRunner.cs b/src/Umbraco.Core/Packaging/IPackageActionRunner.cs new file mode 100644 index 0000000000..d5c6327115 --- /dev/null +++ b/src/Umbraco.Core/Packaging/IPackageActionRunner.cs @@ -0,0 +1,23 @@ +using System.Xml.Linq; + +namespace Umbraco.Core.Packaging +{ + public interface IPackageActionRunner + { + /// + /// Runs the package action with the specified action alias. + /// + /// Name of the package. + /// The action alias. + /// The action XML. + void RunPackageAction(string packageName, string actionAlias, XElement actionXml); + + /// + /// Undos the package action with the specified action alias. + /// + /// Name of the package. + /// The action alias. + /// The action XML. + void UndoPackageAction(string packageName, string actionAlias, XElement actionXml); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Packaging/IPackageInstallation.cs b/src/Umbraco.Core/Packaging/IPackageInstallation.cs index 8ef3546c3d..83a21dcd2f 100644 --- a/src/Umbraco.Core/Packaging/IPackageInstallation.cs +++ b/src/Umbraco.Core/Packaging/IPackageInstallation.cs @@ -4,9 +4,8 @@ using Umbraco.Core.Models.Packaging; namespace Umbraco.Core.Packaging { - internal interface IPackageInstallation + public interface IPackageInstallation { - /// /// Installs a packages data and entities /// diff --git a/src/Umbraco.Core/Packaging/PackageActionRunner.cs b/src/Umbraco.Core/Packaging/PackageActionRunner.cs index 64b913989c..37103b0473 100644 --- a/src/Umbraco.Core/Packaging/PackageActionRunner.cs +++ b/src/Umbraco.Core/Packaging/PackageActionRunner.cs @@ -5,11 +5,10 @@ using Umbraco.Core._Legacy.PackageActions; namespace Umbraco.Core.Packaging { - /// /// Package actions are executed on packge install / uninstall. /// - public sealed class PackageActionRunner + internal class PackageActionRunner : IPackageActionRunner { private readonly ILogger _logger; private readonly PackageActionCollection _packageActions; diff --git a/src/Umbraco.Core/Packaging/PackageDataInstallation.cs b/src/Umbraco.Core/Packaging/PackageDataInstallation.cs new file mode 100644 index 0000000000..8ef99502c8 --- /dev/null +++ b/src/Umbraco.Core/Packaging/PackageDataInstallation.cs @@ -0,0 +1,1144 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using System.Web; +using System.Xml.Linq; +using System.Xml.XPath; +using Umbraco.Core.Collections; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Entities; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; +using Umbraco.Core.Services.Implement; + +namespace Umbraco.Core.Packaging +{ + internal class PackageDataInstallation + { + private readonly ILogger _logger; + private readonly IFileService _fileService; + private readonly IMacroService _macroService; + private readonly ILocalizationService _localizationService; + private readonly IDataTypeService _dataTypeService; + private readonly PropertyEditorCollection _propertyEditors; + private readonly IEntityService _entityService; + private readonly IContentTypeService _contentTypeService; + private readonly IContentService _contentService; + + public PackageDataInstallation(ILogger logger, IFileService fileService, IMacroService macroService, ILocalizationService localizationService, + IDataTypeService dataTypeService, IEntityService entityService, IContentTypeService contentTypeService, + IContentService contentService, PropertyEditorCollection propertyEditors) + { + _logger = logger; + _fileService = fileService; + _macroService = macroService; + _localizationService = localizationService; + _dataTypeService = dataTypeService; + _propertyEditors = propertyEditors; + _entityService = entityService; + _contentTypeService = contentTypeService; + _contentService = contentService; + } + + #region Content + + + public IEnumerable ImportContent(IEnumerable element, IDictionary importedDocumentTypes, int userId) + { + return element.Elements("DocumentSet").SelectMany(x => ImportContent(x, -1, importedDocumentTypes, userId)); + } + + /// + /// Imports and saves package xml as + /// + /// Xml to import + /// Optional parent Id for the content being imported + /// A dictionary of already imported document types (basically used as a cache) + /// Optional Id of the user performing the import + /// An enumrable list of generated content + public IEnumerable ImportContent(XElement element, int parentId, IDictionary importedDocumentTypes, int userId) + { + var name = element.Name.LocalName; + if (name.Equals("DocumentSet")) + { + //This is a regular deep-structured import + var roots = from doc in element.Elements() + where (string)doc.Attribute("isDoc") == "" + select doc; + + var contents = ParseDocumentRootXml(roots, parentId, importedDocumentTypes).ToList(); + if (contents.Any()) + _contentService.Save(contents, userId); + + return contents; + } + + var attribute = element.Attribute("isDoc"); + if (attribute != null) + { + //This is a single doc import + var elements = new List { element }; + var contents = ParseDocumentRootXml(elements, parentId, importedDocumentTypes).ToList(); + if (contents.Any()) + _contentService.Save(contents, userId); + + return contents; + } + + throw new ArgumentException( + "The passed in XElement is not valid! It does not contain a root element called " + + "'DocumentSet' (for structured imports) nor is the first element a Document (for single document import)."); + } + + private IEnumerable ParseDocumentRootXml(IEnumerable roots, int parentId, IDictionary importedContentTypes) + { + var contents = new List(); + foreach (var root in roots) + { + var contentTypeAlias = root.Name.LocalName; + + if (!importedContentTypes.ContainsKey(contentTypeAlias)) + { + var contentType = FindContentTypeByAlias(contentTypeAlias); + importedContentTypes.Add(contentTypeAlias, contentType); + } + + var content = CreateContentFromXml(root, importedContentTypes[contentTypeAlias], null, parentId); + contents.Add(content); + + var children = (from child in root.Elements() + where (string)child.Attribute("isDoc") == "" + select child) + .ToList(); + if (children.Any()) + contents.AddRange(CreateContentFromXml(children, content, importedContentTypes)); + } + return contents; + } + + private IEnumerable CreateContentFromXml(IEnumerable children, IContent parent, IDictionary importedContentTypes) + { + var list = new List(); + foreach (var child in children) + { + string contentTypeAlias = child.Name.LocalName; + + if (importedContentTypes.ContainsKey(contentTypeAlias) == false) + { + var contentType = FindContentTypeByAlias(contentTypeAlias); + importedContentTypes.Add(contentTypeAlias, contentType); + } + + //Create and add the child to the list + var content = CreateContentFromXml(child, importedContentTypes[contentTypeAlias], parent, default); + list.Add(content); + + //Recursive call + XElement child1 = child; + var grandChildren = (from grand in child1.Elements() + where (string)grand.Attribute("isDoc") == "" + select grand).ToList(); + + if (grandChildren.Any()) + list.AddRange(CreateContentFromXml(grandChildren, content, importedContentTypes)); + } + + return list; + } + + private IContent CreateContentFromXml(XElement element, IContentType contentType, IContent parent, int parentId) + { + var id = element.Attribute("id").Value; + var level = element.Attribute("level").Value; + var sortOrder = element.Attribute("sortOrder").Value; + var nodeName = element.Attribute("nodeName").Value; + var path = element.Attribute("path").Value; + //TODO: Shouldn't we be using this value??? + var template = element.Attribute("template").Value; + var key = Guid.Empty; + + var properties = from property in element.Elements() + where property.Attribute("isDoc") == null + select property; + + IContent content = parent == null + ? new Content(nodeName, parentId, contentType) + { + Level = int.Parse(level), + SortOrder = int.Parse(sortOrder) + } + : new Content(nodeName, parent, contentType) + { + Level = int.Parse(level), + SortOrder = int.Parse(sortOrder) + }; + + if (element.Attribute("key") != null && Guid.TryParse(element.Attribute("key").Value, out key)) + { + // update the Guid (for UDI support) + content.Key = key; + } + + foreach (var property in properties) + { + string propertyTypeAlias = property.Name.LocalName; + if (content.HasProperty(propertyTypeAlias)) + { + var propertyValue = property.Value; + + var propertyType = contentType.PropertyTypes.FirstOrDefault(pt => pt.Alias == propertyTypeAlias); + + //set property value + content.SetValue(propertyTypeAlias, propertyValue); + } + } + + return content; + } + + #endregion + + #region ContentTypes + + public IEnumerable ImportDocumentType(XElement docTypeElement, int userId) + { + return ImportDocumentTypes(new []{ docTypeElement }, userId); + } + + /// + /// Imports and saves package xml as + /// + /// Xml to import + /// Optional id of the User performing the operation. Default is zero (admin). + /// An enumrable list of generated ContentTypes + public IEnumerable ImportDocumentTypes(IEnumerable docTypeElements, int userId) + { + return ImportDocumentTypes(docTypeElements.ToList(), true, userId); + } + + /// + /// Imports and saves package xml as + /// + /// Xml to import + /// Boolean indicating whether or not to import the + /// Optional id of the User performing the operation. Default is zero (admin). + /// An enumrable list of generated ContentTypes + public IEnumerable ImportDocumentTypes(IReadOnlyCollection unsortedDocumentTypes, bool importStructure, int userId) + { + var importedContentTypes = new Dictionary(); + + //When you are importing a single doc type we have to assume that the depedencies are already there. + //Otherwise something like uSync won't work. + var graph = new TopoGraph>(x => x.Key, x => x.Dependencies); + var isSingleDocTypeImport = unsortedDocumentTypes.Count == 1; + + var importedFolders = CreateContentTypeFolderStructure(unsortedDocumentTypes); + + if (isSingleDocTypeImport == false) + { + //NOTE Here we sort the doctype XElements based on dependencies + //before creating the doc types - this should also allow for a better structure/inheritance support. + foreach (var documentType in unsortedDocumentTypes) + { + var elementCopy = documentType; + var infoElement = elementCopy.Element("Info"); + var dependencies = new HashSet(); + + //Add the Master as a dependency + if (string.IsNullOrEmpty((string)infoElement.Element("Master")) == false) + { + dependencies.Add(infoElement.Element("Master").Value); + } + + //Add compositions as dependencies + var compositionsElement = infoElement.Element("Compositions"); + if (compositionsElement != null && compositionsElement.HasElements) + { + var compositions = compositionsElement.Elements("Composition"); + if (compositions.Any()) + { + foreach (var composition in compositions) + { + dependencies.Add(composition.Value); + } + } + } + + graph.AddItem(TopoGraph.CreateNode(infoElement.Element("Alias").Value, elementCopy, dependencies.ToArray())); + } + } + + //Sorting the Document Types based on dependencies - if its not a single doc type import ref. #U4-5921 + var documentTypes = isSingleDocTypeImport + ? unsortedDocumentTypes.ToList() + : graph.GetSortedItems().Select(x => x.Item).ToList(); + + //Iterate the sorted document types and create them as IContentType objects + foreach (var documentType in documentTypes) + { + var alias = documentType.Element("Info").Element("Alias").Value; + if (importedContentTypes.ContainsKey(alias) == false) + { + var contentType = _contentTypeService.Get(alias); + importedContentTypes.Add(alias, contentType == null + ? CreateContentTypeFromXml(documentType, importedContentTypes) + : UpdateContentTypeFromXml(documentType, contentType, importedContentTypes)); + } + } + + foreach (var contentType in importedContentTypes) + { + var ct = contentType.Value; + if (importedFolders.ContainsKey(ct.Alias)) + { + ct.ParentId = importedFolders[ct.Alias]; + } + } + + //Save the newly created/updated IContentType objects + var list = importedContentTypes.Select(x => x.Value).ToList(); + _contentTypeService.Save(list, userId); + + //Now we can finish the import by updating the 'structure', + //which requires the doc types to be saved/available in the db + if (importStructure) + { + var updatedContentTypes = new List(); + //Update the structure here - we can't do it untill all DocTypes have been created + foreach (var documentType in documentTypes) + { + var alias = documentType.Element("Info").Element("Alias").Value; + var structureElement = documentType.Element("Structure"); + //Ensure that we only update ContentTypes which has actual structure-elements + if (structureElement == null || structureElement.Elements("DocumentType").Any() == false) continue; + + var updated = UpdateContentTypesStructure(importedContentTypes[alias], structureElement, importedContentTypes); + updatedContentTypes.Add(updated); + } + //Update ContentTypes with a newly added structure/list of allowed children + if (updatedContentTypes.Any()) + _contentTypeService.Save(updatedContentTypes, userId); + } + + return list; + } + + private Dictionary CreateContentTypeFolderStructure(IEnumerable unsortedDocumentTypes) + { + var importedFolders = new Dictionary(); + foreach (var documentType in unsortedDocumentTypes) + { + var foldersAttribute = documentType.Attribute("Folders"); + var infoElement = documentType.Element("Info"); + if (foldersAttribute != null && infoElement != null + //don't import any folder if this is a child doc type - the parent doc type will need to + //exist which contains it's folders + && ((string)infoElement.Element("Master")).IsNullOrWhiteSpace()) + { + var alias = documentType.Element("Info").Element("Alias").Value; + var folders = foldersAttribute.Value.Split('/'); + var rootFolder = HttpUtility.UrlDecode(folders[0]); + //level 1 = root level folders, there can only be one with the same name + var current = _contentTypeService.GetContainers(rootFolder, 1).FirstOrDefault(); + + if (current == null) + { + var tryCreateFolder = _contentTypeService.CreateContainer(-1, rootFolder); + if (tryCreateFolder == false) + { + _logger.Error(tryCreateFolder.Exception, "Could not create folder: {FolderName}", rootFolder); + throw tryCreateFolder.Exception; + } + var rootFolderId = tryCreateFolder.Result.Entity.Id; + current = _contentTypeService.GetContainer(rootFolderId); + } + + importedFolders.Add(alias, current.Id); + + for (var i = 1; i < folders.Length; i++) + { + var folderName = HttpUtility.UrlDecode(folders[i]); + current = CreateContentTypeChildFolder(folderName, current); + importedFolders[alias] = current.Id; + } + } + } + + return importedFolders; + } + + private EntityContainer CreateContentTypeChildFolder(string folderName, IUmbracoEntity current) + { + var children = _entityService.GetChildren(current.Id).ToArray(); + var found = children.Any(x => x.Name.InvariantEquals(folderName)); + if (found) + { + var containerId = children.Single(x => x.Name.InvariantEquals(folderName)).Id; + return _contentTypeService.GetContainer(containerId); + } + + var tryCreateFolder = _contentTypeService.CreateContainer(current.Id, folderName); + if (tryCreateFolder == false) + { + _logger.Error(tryCreateFolder.Exception, "Could not create folder: {FolderName}", folderName); + throw tryCreateFolder.Exception; + } + return _contentTypeService.GetContainer(tryCreateFolder.Result.Entity.Id); + } + + private IContentType CreateContentTypeFromXml(XElement documentType, IReadOnlyDictionary importedContentTypes) + { + var infoElement = documentType.Element("Info"); + + //Name of the master corresponds to the parent + var masterElement = infoElement.Element("Master"); + IContentType parent = null; + if (masterElement != null) + { + var masterAlias = masterElement.Value; + parent = importedContentTypes.ContainsKey(masterAlias) + ? importedContentTypes[masterAlias] + : _contentTypeService.Get(masterAlias); + } + + var alias = infoElement.Element("Alias").Value; + var contentType = parent == null + ? new ContentType(-1) { Alias = alias } + : new ContentType(parent, alias); + + if (parent != null) + contentType.AddContentType(parent); + + return UpdateContentTypeFromXml(documentType, contentType, importedContentTypes); + } + + private IContentType UpdateContentTypeFromXml(XElement documentType, IContentType contentType, IReadOnlyDictionary importedContentTypes) + { + var infoElement = documentType.Element("Info"); + var defaultTemplateElement = infoElement.Element("DefaultTemplate"); + + contentType.Name = infoElement.Element("Name").Value; + contentType.Icon = infoElement.Element("Icon").Value; + contentType.Thumbnail = infoElement.Element("Thumbnail").Value; + contentType.Description = infoElement.Element("Description").Value; + + //NOTE AllowAtRoot is a new property in the package xml so we need to verify it exists before using it. + var allowAtRoot = infoElement.Element("AllowAtRoot"); + if (allowAtRoot != null) + contentType.AllowedAsRoot = allowAtRoot.Value.InvariantEquals("true"); + + //NOTE IsListView is a new property in the package xml so we need to verify it exists before using it. + var isListView = infoElement.Element("IsListView"); + if (isListView != null) + contentType.IsContainer = isListView.Value.InvariantEquals("true"); + + //Name of the master corresponds to the parent and we need to ensure that the Parent Id is set + var masterElement = infoElement.Element("Master"); + if (masterElement != null) + { + var masterAlias = masterElement.Value; + IContentType parent = importedContentTypes.ContainsKey(masterAlias) + ? importedContentTypes[masterAlias] + : _contentTypeService.Get(masterAlias); + + contentType.SetParent(parent); + } + + //Update Compositions on the ContentType to ensure that they are as is defined in the package xml + var compositionsElement = infoElement.Element("Compositions"); + if (compositionsElement != null && compositionsElement.HasElements) + { + var compositions = compositionsElement.Elements("Composition"); + if (compositions.Any()) + { + foreach (var composition in compositions) + { + var compositionAlias = composition.Value; + var compositionContentType = importedContentTypes.ContainsKey(compositionAlias) + ? importedContentTypes[compositionAlias] + : _contentTypeService.Get(compositionAlias); + var added = contentType.AddContentType(compositionContentType); + } + } + } + + UpdateContentTypesAllowedTemplates(contentType, infoElement.Element("AllowedTemplates"), defaultTemplateElement); + UpdateContentTypesTabs(contentType, documentType.Element("Tabs")); + UpdateContentTypesProperties(contentType, documentType.Element("GenericProperties")); + + return contentType; + } + + private void UpdateContentTypesAllowedTemplates(IContentType contentType, + XElement allowedTemplatesElement, XElement defaultTemplateElement) + { + if (allowedTemplatesElement != null && allowedTemplatesElement.Elements("Template").Any()) + { + var allowedTemplates = contentType.AllowedTemplates.ToList(); + foreach (var templateElement in allowedTemplatesElement.Elements("Template")) + { + var alias = templateElement.Value; + var template = _fileService.GetTemplate(alias.ToSafeAlias()); + if (template != null) + { + if (allowedTemplates.Any(x => x.Id == template.Id)) continue; + allowedTemplates.Add(template); + } + else + { + _logger.Warn("Packager: Error handling allowed templates. Template with alias '{TemplateAlias}' could not be found.", alias); + } + } + + contentType.AllowedTemplates = allowedTemplates; + } + + if (string.IsNullOrEmpty((string)defaultTemplateElement) == false) + { + var defaultTemplate = _fileService.GetTemplate(defaultTemplateElement.Value.ToSafeAlias()); + if (defaultTemplate != null) + { + contentType.SetDefaultTemplate(defaultTemplate); + } + else + { + _logger.Warn("Packager: Error handling default template. Default template with alias '{DefaultTemplateAlias}' could not be found.", defaultTemplateElement.Value); + } + } + } + + private void UpdateContentTypesTabs(IContentType contentType, XElement tabElement) + { + if (tabElement == null) + return; + + var tabs = tabElement.Elements("Tab"); + foreach (var tab in tabs) + { + var id = tab.Element("Id").Value;//Do we need to use this for tracking? + var caption = tab.Element("Caption").Value; + + if (contentType.PropertyGroups.Contains(caption) == false) + { + contentType.AddPropertyGroup(caption); + + } + + int sortOrder; + if (tab.Element("SortOrder") != null && int.TryParse(tab.Element("SortOrder").Value, out sortOrder)) + { + // Override the sort order with the imported value + contentType.PropertyGroups[caption].SortOrder = sortOrder; + } + } + } + + private void UpdateContentTypesProperties(IContentType contentType, XElement genericPropertiesElement) + { + var properties = genericPropertiesElement.Elements("GenericProperty"); + foreach (var property in properties) + { + var dataTypeDefinitionId = new Guid(property.Element("Definition").Value);//Unique Id for a DataTypeDefinition + + var dataTypeDefinition = _dataTypeService.GetDataType(dataTypeDefinitionId); + + //If no DataTypeDefinition with the guid from the xml wasn't found OR the ControlId on the DataTypeDefinition didn't match the DataType Id + //We look up a DataTypeDefinition that matches + + + //get the alias as a string for use below + var propertyEditorAlias = property.Element("Type").Value.Trim(); + + //If no DataTypeDefinition with the guid from the xml wasn't found OR the ControlId on the DataTypeDefinition didn't match the DataType Id + //We look up a DataTypeDefinition that matches + + if (dataTypeDefinition == null) + { + var dataTypeDefinitions = _dataTypeService.GetByEditorAlias(propertyEditorAlias); + if (dataTypeDefinitions != null && dataTypeDefinitions.Any()) + { + dataTypeDefinition = dataTypeDefinitions.FirstOrDefault(); + } + } + else if (dataTypeDefinition.EditorAlias != propertyEditorAlias) + { + var dataTypeDefinitions = _dataTypeService.GetByEditorAlias(propertyEditorAlias); + if (dataTypeDefinitions != null && dataTypeDefinitions.Any()) + { + dataTypeDefinition = dataTypeDefinitions.FirstOrDefault(); + } + } + + // For backwards compatibility, if no datatype with that ID can be found, we're letting this fail silently. + // This means that the property will not be created. + if (dataTypeDefinition == null) + { + _logger.Warn("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()); + + //convert to a label! + dataTypeDefinition = _dataTypeService.GetByEditorAlias(Constants.PropertyEditors.Aliases.NoEdit).FirstOrDefault(); + //if for some odd reason this isn't there then ignore + if (dataTypeDefinition == null) continue; + } + + var sortOrder = 0; + var sortOrderElement = property.Element("SortOrder"); + if (sortOrderElement != null) + int.TryParse(sortOrderElement.Value, out sortOrder); + var propertyType = new PropertyType(dataTypeDefinition, property.Element("Alias").Value) + { + Name = property.Element("Name").Value, + Description = (string)property.Element("Description"), + Mandatory = property.Element("Mandatory") != null ? property.Element("Mandatory").Value.ToLowerInvariant().Equals("true") : false, + ValidationRegExp = (string)property.Element("Validation"), + SortOrder = sortOrder + }; + + var tab = (string)property.Element("Tab"); + if (string.IsNullOrEmpty(tab)) + { + contentType.AddPropertyType(propertyType); + } + else + { + contentType.AddPropertyType(propertyType, tab); + } + } + } + + private IContentType UpdateContentTypesStructure(IContentType contentType, XElement structureElement, IReadOnlyDictionary importedContentTypes) + { + var allowedChildren = contentType.AllowedContentTypes.ToList(); + int sortOrder = allowedChildren.Any() ? allowedChildren.Last().SortOrder : 0; + foreach (var element in structureElement.Elements("DocumentType")) + { + var alias = element.Value; + + var allowedChild = importedContentTypes.ContainsKey(alias) ? importedContentTypes[alias] : _contentTypeService.Get(alias); + if (allowedChild == null) + { + _logger.Warn( + "Packager: Error handling DocumentType structure. DocumentType with alias '{DoctypeAlias}' could not be found and was not added to the structure for '{DoctypeStructureAlias}'.", + alias, contentType.Alias); + continue; + } + + if (allowedChildren.Any(x => x.Id.IsValueCreated && x.Id.Value == allowedChild.Id)) continue; + + allowedChildren.Add(new ContentTypeSort(new Lazy(() => allowedChild.Id), sortOrder, allowedChild.Alias)); + sortOrder++; + } + + contentType.AllowedContentTypes = allowedChildren; + return contentType; + } + + /// + /// Used during Content import to ensure that the ContentType of a content item exists + /// + /// + /// + private IContentType FindContentTypeByAlias(string contentTypeAlias) + { + var contentType = _contentTypeService.Get(contentTypeAlias); + + if (contentType == null) + throw new Exception($"ContentType matching the passed in Alias: '{contentTypeAlias}' was null"); + + return contentType; + } + + #endregion + + #region DataTypes + + /// + /// Imports and saves package xml as + /// + /// Xml to import + /// Optional id of the user + /// An enumrable list of generated DataTypeDefinitions + public IEnumerable ImportDataTypes(IReadOnlyCollection dataTypeElements, int userId) + { + var dataTypes = new List(); + + var importedFolders = CreateDataTypeFolderStructure(dataTypeElements); + + foreach (var dataTypeElement in dataTypeElements) + { + var dataTypeDefinitionName = dataTypeElement.AttributeValue("Name"); + + var dataTypeDefinitionId = dataTypeElement.AttributeValue("Definition"); + var databaseTypeAttribute = dataTypeElement.Attribute("DatabaseType"); + + var parentId = -1; + if (importedFolders.ContainsKey(dataTypeDefinitionName)) + parentId = importedFolders[dataTypeDefinitionName]; + + var definition = _dataTypeService.GetDataType(dataTypeDefinitionId); + //If the datatypedefinition doesn't already exist we create a new new according to the one in the package xml + if (definition == null) + { + var databaseType = databaseTypeAttribute?.Value.EnumParse(true) ?? ValueStorageType.Ntext; + + // the Id field is actually the string property editor Alias + // however, the actual editor with this alias could be installed with the package, and + // therefore not yet part of the _propertyEditors collection, so we cannot try and get + // the actual editor - going with a void editor + + var editorAlias = dataTypeElement.Attribute("Id")?.Value?.Trim(); + if (!_propertyEditors.TryGet(editorAlias, out var editor)) + editor = new VoidEditor(_logger) { Alias = editorAlias }; + + var dataType = new DataType(editor) + { + Key = dataTypeDefinitionId, + Name = dataTypeDefinitionName, + DatabaseType = databaseType, + ParentId = parentId + }; + + var configurationAttributeValue = dataTypeElement.Attribute("Configuration")?.Value; + if (!string.IsNullOrWhiteSpace(configurationAttributeValue)) + dataType.Configuration = editor.GetConfigurationEditor().FromDatabase(configurationAttributeValue); + + dataTypes.Add(dataType); + } + else + { + definition.ParentId = parentId; + _dataTypeService.Save(definition, userId); + } + } + + if (dataTypes.Count > 0) + { + _dataTypeService.Save(dataTypes, userId, true); + } + + return dataTypes; + } + + private Dictionary CreateDataTypeFolderStructure(IEnumerable datatypeElements) + { + var importedFolders = new Dictionary(); + foreach (var datatypeElement in datatypeElements) + { + var foldersAttribute = datatypeElement.Attribute("Folders"); + if (foldersAttribute != null) + { + var name = datatypeElement.Attribute("Name").Value; + var folders = foldersAttribute.Value.Split('/'); + var rootFolder = HttpUtility.UrlDecode(folders[0]); + //there will only be a single result by name for level 1 (root) containers + var current = _dataTypeService.GetContainers(rootFolder, 1).FirstOrDefault(); + + if (current == null) + { + var tryCreateFolder = _dataTypeService.CreateContainer(-1, rootFolder); + if (tryCreateFolder == false) + { + _logger.Error(tryCreateFolder.Exception, "Could not create folder: {FolderName}", rootFolder); + throw tryCreateFolder.Exception; + } + current = _dataTypeService.GetContainer(tryCreateFolder.Result.Entity.Id); + } + + importedFolders.Add(name, current.Id); + + for (var i = 1; i < folders.Length; i++) + { + var folderName = HttpUtility.UrlDecode(folders[i]); + current = CreateDataTypeChildFolder(folderName, current); + importedFolders[name] = current.Id; + } + } + } + + return importedFolders; + } + + private EntityContainer CreateDataTypeChildFolder(string folderName, IUmbracoEntity current) + { + var children = _entityService.GetChildren(current.Id).ToArray(); + var found = children.Any(x => x.Name.InvariantEquals(folderName)); + if (found) + { + var containerId = children.Single(x => x.Name.InvariantEquals(folderName)).Id; + return _dataTypeService.GetContainer(containerId); + } + + var tryCreateFolder = _dataTypeService.CreateContainer(current.Id, folderName); + if (tryCreateFolder == false) + { + _logger.Error(tryCreateFolder.Exception, "Could not create folder: {FolderName}", folderName); + throw tryCreateFolder.Exception; + } + return _dataTypeService.GetContainer(tryCreateFolder.Result.Entity.Id); + } + + #endregion + + #region Dictionary Items + + /// + /// Imports and saves the 'DictionaryItems' part of the package xml as a list of + /// + /// Xml to import + /// + /// An enumerable list of dictionary items + public IEnumerable ImportDictionaryItems(IEnumerable dictionaryItemElementList, int userId) + { + var languages = _localizationService.GetAllLanguages().ToList(); + return ImportDictionaryItems(dictionaryItemElementList, languages, null, userId); + } + + private IEnumerable ImportDictionaryItems(IEnumerable dictionaryItemElementList, List languages, Guid? parentId, int userId) + { + var items = new List(); + foreach (var dictionaryItemElement in dictionaryItemElementList) + items.AddRange(ImportDictionaryItem(dictionaryItemElement, languages, parentId, userId)); + + return items; + } + + private IEnumerable ImportDictionaryItem(XElement dictionaryItemElement, List languages, Guid? parentId, int userId) + { + var items = new List(); + + IDictionaryItem dictionaryItem; + var key = dictionaryItemElement.Attribute("Key").Value; + if (_localizationService.DictionaryItemExists(key)) + dictionaryItem = GetAndUpdateDictionaryItem(key, dictionaryItemElement, languages); + else + dictionaryItem = CreateNewDictionaryItem(key, dictionaryItemElement, languages, parentId); + _localizationService.Save(dictionaryItem, userId); + items.Add(dictionaryItem); + + items.AddRange(ImportDictionaryItems(dictionaryItemElement.Elements("DictionaryItem"), languages, dictionaryItem.Key, userId)); + return items; + } + + private IDictionaryItem GetAndUpdateDictionaryItem(string key, XElement dictionaryItemElement, List languages) + { + var dictionaryItem = _localizationService.GetDictionaryItemByKey(key); + var translations = dictionaryItem.Translations.ToList(); + foreach (var valueElement in dictionaryItemElement.Elements("Value").Where(v => DictionaryValueIsNew(translations, v))) + AddDictionaryTranslation(translations, valueElement, languages); + dictionaryItem.Translations = translations; + return dictionaryItem; + } + + private static DictionaryItem CreateNewDictionaryItem(string key, XElement dictionaryItemElement, List languages, Guid? parentId) + { + var dictionaryItem = parentId.HasValue ? new DictionaryItem(parentId.Value, key) : new DictionaryItem(key); + var translations = new List(); + + foreach (var valueElement in dictionaryItemElement.Elements("Value")) + AddDictionaryTranslation(translations, valueElement, languages); + + dictionaryItem.Translations = translations; + return dictionaryItem; + } + + private static bool DictionaryValueIsNew(IEnumerable translations, XElement valueElement) + { + return translations.All(t => + String.Compare(t.Language.IsoCode, valueElement.Attribute("LanguageCultureAlias").Value, + StringComparison.InvariantCultureIgnoreCase) != 0 + ); + } + + private static void AddDictionaryTranslation(ICollection translations, XElement valueElement, IEnumerable languages) + { + var languageId = valueElement.Attribute("LanguageCultureAlias").Value; + var language = languages.SingleOrDefault(l => l.IsoCode == languageId); + if (language == null) + return; + var translation = new DictionaryTranslation(language, valueElement.Value); + translations.Add(translation); + } + + #endregion + + #region Languages + + + /// + /// Imports and saves the 'Languages' part of a package xml as a list of + /// + /// Xml to import + /// Optional id of the User performing the operation + /// An enumerable list of generated languages + public IEnumerable ImportLanguages(IEnumerable languageElements, int userId) + { + var list = new List(); + foreach (var languageElement in languageElements) + { + var isoCode = languageElement.AttributeValue("CultureAlias"); + var existingLanguage = _localizationService.GetLanguageByIsoCode(isoCode); + if (existingLanguage != null) continue; + var langauge = new Language(isoCode) + { + CultureName = languageElement.AttributeValue("FriendlyName") + }; + _localizationService.Save(langauge, userId); + list.Add(langauge); + } + + return list; + } + + #endregion + + #region Macros + + /// + /// Imports and saves the 'Macros' part of a package xml as a list of + /// + /// Xml to import + /// Optional id of the User performing the operation + /// + public IEnumerable ImportMacros(IEnumerable macroElements, int userId) + { + var macros = macroElements.Select(ParseMacroElement).ToList(); + + foreach (var macro in macros) + { + var existing = _macroService.GetByAlias(macro.Alias); + if (existing != null) + macro.Id = existing.Id; + + _macroService.Save(macro, userId); + } + + return macros; + } + + private IMacro ParseMacroElement(XElement macroElement) + { + var macroName = macroElement.Element("name").Value; + var macroAlias = macroElement.Element("alias").Value; + var macroType = Enum.Parse(macroElement.Element("macroType").Value); + var macroSource = macroElement.Element("macroSource").Value; + + //Following xml elements are treated as nullable properties + var useInEditorElement = macroElement.Element("useInEditor"); + var useInEditor = false; + if (useInEditorElement != null && string.IsNullOrEmpty((string)useInEditorElement) == false) + { + useInEditor = bool.Parse(useInEditorElement.Value); + } + var cacheDurationElement = macroElement.Element("refreshRate"); + var cacheDuration = 0; + if (cacheDurationElement != null && string.IsNullOrEmpty((string)cacheDurationElement) == false) + { + cacheDuration = int.Parse(cacheDurationElement.Value); + } + var cacheByMemberElement = macroElement.Element("cacheByMember"); + var cacheByMember = false; + if (cacheByMemberElement != null && string.IsNullOrEmpty((string)cacheByMemberElement) == false) + { + cacheByMember = bool.Parse(cacheByMemberElement.Value); + } + var cacheByPageElement = macroElement.Element("cacheByPage"); + var cacheByPage = false; + if (cacheByPageElement != null && string.IsNullOrEmpty((string)cacheByPageElement) == false) + { + cacheByPage = bool.Parse(cacheByPageElement.Value); + } + var dontRenderElement = macroElement.Element("dontRender"); + var dontRender = true; + if (dontRenderElement != null && string.IsNullOrEmpty((string)dontRenderElement) == false) + { + dontRender = bool.Parse(dontRenderElement.Value); + } + + var existingMacro = _macroService.GetByAlias(macroAlias) as Macro; + var macro = existingMacro ?? new Macro(macroAlias, macroName, macroSource, macroType, + cacheByPage, cacheByMember, dontRender, useInEditor, cacheDuration); + + var properties = macroElement.Element("properties"); + if (properties != null) + { + int sortOrder = 0; + foreach (var property in properties.Elements()) + { + var propertyName = property.Attribute("name").Value; + var propertyAlias = property.Attribute("alias").Value; + var editorAlias = property.Attribute("propertyType").Value; + var sortOrderAttribute = property.Attribute("sortOrder"); + if (sortOrderAttribute != null) + { + sortOrder = int.Parse(sortOrderAttribute.Value); + } + + if (macro.Properties.Values.Any(x => string.Equals(x.Alias, propertyAlias, StringComparison.OrdinalIgnoreCase))) continue; + macro.Properties.Add(new MacroProperty(propertyAlias, propertyName, sortOrder, editorAlias)); + sortOrder++; + } + } + return macro; + } + + + + + + #endregion + + #region Stylesheets + + public IEnumerable ImportStylesheets(IEnumerable stylesheetElements, int userId) + { + var result = new List(); + + foreach (var n in stylesheetElements) + { + var stylesheetName = n.Element("Name")?.Value; + if (stylesheetName.IsNullOrWhiteSpace()) continue; + + var s = _fileService.GetStylesheetByName(stylesheetName); + if (s == null) + { + var fileName = n.Element("FileName")?.Value; + if (fileName == null) continue; + var content = n.Element("Content")?.Value; + if (content == null) continue; + + s = new Stylesheet(fileName) { Content = content }; + _fileService.SaveStylesheet(s); + } + + foreach (var prop in n.XPathSelectElements("Properties/Property")) + { + var alias = prop.Element("Alias")?.Value; + var sp = s.Properties.SingleOrDefault(p => p != null && p.Alias == alias); + var name = prop.Element("Name")?.Value; + if (sp == null) + { + sp = new StylesheetProperty(name, "#" + name.ToSafeAlias(), ""); + s.AddProperty(sp); + } + else + { + //sp.Text = name; + //Changing the name requires removing the current property and then adding another new one + if (sp.Name != name) + { + s.RemoveProperty(sp.Name); + var newProp = new StylesheetProperty(name, sp.Alias, sp.Value); + s.AddProperty(newProp); + sp = newProp; + } + } + sp.Alias = alias; + sp.Value = prop.Element("Value")?.Value; + } + _fileService.SaveStylesheet(s); + result.Add(s); + } + + return result; + } + + #endregion + + #region Templates + + public IEnumerable ImportTemplate(XElement templateElement, int userId) + { + return ImportTemplates(new[] {templateElement}, userId); + } + + /// + /// Imports and saves package xml as + /// + /// Xml to import + /// Optional user id + /// An enumrable list of generated Templates + public IEnumerable ImportTemplates(IReadOnlyCollection templateElements, int userId) + { + var templates = new List(); + + var graph = new TopoGraph>(x => x.Key, x => x.Dependencies); + + foreach (var tempElement in templateElements) + { + var dependencies = new List(); + var elementCopy = tempElement; + //Ensure that the Master of the current template is part of the import, otherwise we ignore this dependency as part of the dependency sorting. + if (string.IsNullOrEmpty((string)elementCopy.Element("Master")) == false && + templateElements.Any(x => (string)x.Element("Alias") == (string)elementCopy.Element("Master"))) + { + dependencies.Add((string)elementCopy.Element("Master")); + } + else if (string.IsNullOrEmpty((string)elementCopy.Element("Master")) == false && + templateElements.Any(x => (string)x.Element("Alias") == (string)elementCopy.Element("Master")) == false) + { + _logger.Info( + "Template '{TemplateAlias}' has an invalid Master '{TemplateMaster}', so the reference has been ignored.", + (string)elementCopy.Element("Alias"), + (string)elementCopy.Element("Master")); + } + + graph.AddItem(TopoGraph.CreateNode((string)elementCopy.Element("Alias"), elementCopy, dependencies)); + } + + //Sort templates by dependencies to a potential master template + var sorted = graph.GetSortedItems(); + foreach (var item in sorted) + { + var templateElement = item.Item; + + var templateName = templateElement.Element("Name").Value; + var alias = templateElement.Element("Alias").Value; + var design = templateElement.Element("Design").Value; + var masterElement = templateElement.Element("Master"); + + var isMasterPage = IsMasterPageSyntax(design); + var path = isMasterPage ? MasterpagePath(alias) : ViewPath(alias); + + var existingTemplate = _fileService.GetTemplate(alias) as Template; + var template = existingTemplate ?? new Template(templateName, alias); + template.Content = design; + if (masterElement != null && string.IsNullOrEmpty((string)masterElement) == false) + { + template.MasterTemplateAlias = masterElement.Value; + var masterTemplate = templates.FirstOrDefault(x => x.Alias == masterElement.Value); + if (masterTemplate != null) + template.MasterTemplateId = new Lazy(() => masterTemplate.Id); + } + templates.Add(template); + } + + if (templates.Any()) + _fileService.SaveTemplate(templates, userId); + + return templates; + } + + + private bool IsMasterPageSyntax(string code) + { + return Regex.IsMatch(code, @"<%@\s*Master", RegexOptions.IgnoreCase) || + code.InvariantContains(" /// Constructor /// - /// + /// /// /// + /// /// /// The relative path of the package storage folder (i.e. ~/App_Data/Packages ) /// @@ -36,13 +38,14 @@ namespace Umbraco.Core.Packaging /// /// The destination root folder to extract the package files (generally the same as applicationRoot) but can be modified for testing /// - public PackageInstallation(IPackagingService packagingService, PackageFileInstallation packageFileInstallation, CompiledPackageXmlParser parser, + public PackageInstallation(PackageDataInstallation packageDataInstallation, PackageFileInstallation packageFileInstallation, CompiledPackageXmlParser parser, IPackageActionRunner packageActionRunner, string packagesFolderPath, DirectoryInfo applicationRootFolder, DirectoryInfo packageExtractionFolder) { _packageExtraction = new PackageExtraction(); - _packageFileInstallation = packageFileInstallation; - _packagingService = packagingService ?? throw new ArgumentNullException(nameof(packagingService)); + _packageFileInstallation = packageFileInstallation ?? throw new ArgumentNullException(nameof(packageFileInstallation)); + _packageDataInstallation = packageDataInstallation ?? throw new ArgumentNullException(nameof(packageDataInstallation)); _parser = parser; + _packageActionRunner = packageActionRunner; _packagesFolderPath = packagesFolderPath; _applicationRootFolder = applicationRootFolder; _packageExtractionFolder = packageExtractionFolder; @@ -61,8 +64,6 @@ namespace Umbraco.Core.Packaging return compiledPackage; } - //fixme: Should we move all of the ImportXXXX methods here instead of on the IPackagingService? we don't want to have cicurlar refs - public IEnumerable InstallPackageFiles(PackageDefinition packageDefinition, CompiledPackage compiledPackage, int userId) { if (packageDefinition == null) throw new ArgumentNullException(nameof(packageDefinition)); @@ -79,118 +80,77 @@ namespace Umbraco.Core.Packaging public InstallationSummary InstallPackageData(PackageDefinition packageDefinition, CompiledPackage compiledPackage, int userId) { - //fixme: fill this in - throw new NotImplementedException(); + //TODO: Update the PackageDefinition! + + var installationSummary = new InstallationSummary + { + DataTypesInstalled = _packageDataInstallation.ImportDataTypes(compiledPackage.DataTypes.ToList(), userId), + LanguagesInstalled = _packageDataInstallation.ImportLanguages(compiledPackage.Languages, userId), + DictionaryItemsInstalled = _packageDataInstallation.ImportDictionaryItems(compiledPackage.DictionaryItems, userId), + MacrosInstalled = _packageDataInstallation.ImportMacros(compiledPackage.Macros, userId), + TemplatesInstalled = _packageDataInstallation.ImportTemplates(compiledPackage.Templates.ToList(), userId), + DocumentTypesInstalled = _packageDataInstallation.ImportDocumentTypes(compiledPackage.DocumentTypes, userId) + }; + + //we need a reference to the imported doc types to continue + var importedDocTypes = installationSummary.DocumentTypesInstalled.ToDictionary(x => x.Alias, x => x); + + installationSummary.StylesheetsInstalled = _packageDataInstallation.ImportStylesheets(compiledPackage.Stylesheets, userId); + installationSummary.ContentInstalled = _packageDataInstallation.ImportContent(compiledPackage.Documents, importedDocTypes, userId); + installationSummary.Actions = _parser.GetPackageActions(XElement.Parse(compiledPackage.Actions), compiledPackage.Name); + 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()); + foreach (var x in installationSummary.MacrosInstalled) packageDefinition.Macros.Add(x.Id.ToInvariantString()); + foreach (var x in installationSummary.TemplatesInstalled) packageDefinition.Templates.Add(x.Id.ToInvariantString()); + foreach (var x in installationSummary.DocumentTypesInstalled) packageDefinition.DocumentTypes.Add(x.Id.ToInvariantString()); + foreach (var x in installationSummary.StylesheetsInstalled) packageDefinition.Stylesheets.Add(x.Id.ToInvariantString()); + var contentInstalled = installationSummary.ContentInstalled.ToList(); + packageDefinition.ContentNodeId = contentInstalled.Count > 0 ? contentInstalled[0].Id.ToInvariantString() : null; + + RunPackageActions(packageDefinition, installationSummary.Actions); + + return installationSummary; } - //public InstallationSummary InstallPackage(FileInfo packageFile, int userId) - //{ - // XElement dataTypes; - // XElement languages; - // XElement dictionaryItems; - // XElement macroes; - // XElement files; - // XElement templates; - // XElement documentTypes; - // XElement styleSheets; - // XElement documentSet; - // XElement documents; - // XElement actions; - // IPackageInfo metaData; - // InstallationSummary installationSummary; + private void RunPackageActions(PackageDefinition packageDefinition, IEnumerable actions) + { + foreach (var n in actions) + { + var undo = n.Undo; + if (undo) + packageDefinition.Actions += n.XmlData.ToString(); - // try - // { - // XElement rootElement = GetConfigXmlElement(packageFile); - // PackageSupportedCheck(rootElement); - // PackageStructureSanityCheck(packageFile, rootElement); - // dataTypes = rootElement.Element(Constants.Packaging.DataTypesNodeName); - // languages = rootElement.Element(Constants.Packaging.LanguagesNodeName); - // dictionaryItems = rootElement.Element(Constants.Packaging.DictionaryItemsNodeName); - // macroes = rootElement.Element(Constants.Packaging.MacrosNodeName); - // files = rootElement.Element(Constants.Packaging.FilesNodeName); - // templates = rootElement.Element(Constants.Packaging.TemplatesNodeName); - // documentTypes = rootElement.Element(Constants.Packaging.DocumentTypesNodeName); - // styleSheets = rootElement.Element(Constants.Packaging.StylesheetsNodeName); - // documentSet = rootElement.Element(Constants.Packaging.DocumentSetNodeName); - // documents = rootElement.Element(Constants.Packaging.DocumentsNodeName); - // actions = rootElement.Element(Constants.Packaging.ActionsNodeName); + //Run the actions tagged only for 'install' + if (n.RunAt != ActionRunAt.Install) continue; - // metaData = GetMetaData(rootElement); - // installationSummary = new InstallationSummary {MetaData = metaData}; - // } - // catch (Exception e) - // { - // throw new Exception("Error reading " + packageFile, e); - // } - - // try - // { - // var dataTypeDefinitions = EmptyEnumerableIfNull(dataTypes) ?? InstallDataTypes(dataTypes, userId); - // installationSummary.DataTypesInstalled = dataTypeDefinitions; - - // var languagesInstalled = EmptyEnumerableIfNull(languages) ?? InstallLanguages(languages, userId); - // installationSummary.LanguagesInstalled = languagesInstalled; - - // var dictionaryInstalled = EmptyEnumerableIfNull(dictionaryItems) ?? InstallDictionaryItems(dictionaryItems); - // installationSummary.DictionaryItemsInstalled = dictionaryInstalled; - - // var macros = EmptyEnumerableIfNull(macroes) ?? InstallMacros(macroes, userId); - // installationSummary.MacrosInstalled = macros; - - // var templatesInstalled = EmptyEnumerableIfNull(templates) ?? InstallTemplats(templates, userId); - // installationSummary.TemplatesInstalled = templatesInstalled; - - // var documentTypesInstalled = EmptyEnumerableIfNull(documentTypes) ?? InstallDocumentTypes(documentTypes, userId); - // installationSummary.ContentTypesInstalled =documentTypesInstalled; - - // var stylesheetsInstalled = EmptyEnumerableIfNull(styleSheets) ?? InstallStylesheets(styleSheets); - // installationSummary.StylesheetsInstalled = stylesheetsInstalled; - - // var documentsInstalled = documents != null ? InstallDocuments(documents, userId) - // : EmptyEnumerableIfNull(documentSet) - // ?? InstallDocuments(documentSet, userId); - // installationSummary.ContentInstalled = documentsInstalled; - - // var packageActions = EmptyEnumerableIfNull(actions) ?? GetPackageActions(actions, metaData.Name); - // installationSummary.Actions = packageActions; - - // installationSummary.PackageInstalled = true; - - // return installationSummary; - // } - // catch (Exception e) - // { - // throw new Exception("Error installing package " + packageFile, e); - // } - //} + if (n.Alias.IsNullOrWhiteSpace() == false) + _packageActionRunner.RunPackageAction(packageDefinition.Name, n.Alias, n.XmlData); + } + } private FileInfo GetPackageZipFile(string packageFileName) => new FileInfo(IOHelper.MapPath(_packagesFolderPath).EnsureEndsWith('\\') + packageFileName); - private static IEnumerable EmptyEnumerableIfNull(object obj) - { - return obj == null ? Enumerable.Empty() : null; - } - private XDocument GetConfigXmlDoc(FileInfo packageFile) { - var configXmlContent = _packageExtraction.ReadTextFileFromArchive(packageFile, Constants.Packaging.PackageXmlFileName, out _); + var configXmlContent = _packageExtraction.ReadTextFileFromArchive(packageFile, "package.xml", out _); var document = XDocument.Parse(configXmlContent); if (document.Root == null || - document.Root.Name.LocalName.Equals(Constants.Packaging.UmbPackageNodeName) == false) + document.Root.Name.LocalName.Equals("umbPackage") == false) throw new FormatException("xml does not have a root node called \"umbPackage\""); return document; } - public XElement GetConfigXmlElement(FileInfo packageFile) - { - var document = GetConfigXmlDoc(packageFile); - return document.Root; - } - private void ValidatePackageFile(FileInfo packageFile, CompiledPackage package) { if (!(package.Files?.Count > 0)) return; @@ -205,8 +165,8 @@ namespace Umbraco.Core.Packaging string.Join(", ", missingFiles.Select( mf => { - var sd = sourceDestination.Single(fi => fi.packageUniqueFile == mf); - return $"source: \"{sd.packageUniqueFile}\" destination: \"{sd.appRelativePath}\""; + var (packageUniqueFile, appRelativePath) = sourceDestination.Single(fi => fi.packageUniqueFile == mf); + return $"source: \"{packageUniqueFile}\" destination: \"{appRelativePath}\""; }))); } @@ -219,143 +179,7 @@ namespace Umbraco.Core.Packaging } } - private static IEnumerable GetPackageActions(XElement actionsElement, string packageName) - { - if (actionsElement == null) { return new PackageAction[0]; } - - if (string.Equals(Constants.Packaging.ActionsNodeName, actionsElement.Name.LocalName) == false) - { - throw new ArgumentException("Must be \"" + Constants.Packaging.ActionsNodeName + "\" as root", - "actionsElement"); - } - - return actionsElement.Elements(Constants.Packaging.ActionNodeName) - .Select(elemet => - { - XAttribute aliasAttr = elemet.Attribute(Constants.Packaging.AliasNodeNameCapital); - if (aliasAttr == null) - throw new ArgumentException( - "missing \"" + Constants.Packaging.AliasNodeNameCapital + "\" atribute in alias element", - "actionsElement"); - - var packageAction = new PackageAction - { - XmlData = elemet, - Alias = aliasAttr.Value, - PackageName = packageName, - }; - - - var attr = elemet.Attribute(Constants.Packaging.RunatNodeAttribute); - - if (attr != null && Enum.TryParse(attr.Value, true, out ActionRunAt runAt)) { packageAction.RunAt = runAt; } - - attr = elemet.Attribute(Constants.Packaging.UndoNodeAttribute); - - if (attr != null && bool.TryParse(attr.Value, out var undo)) { packageAction.Undo = undo; } - - - return packageAction; - }).ToArray(); - } - - private IEnumerable InstallDocuments(XElement documentsElement, int userId = 0) - { - if ((string.Equals(Constants.Packaging.DocumentSetNodeName, documentsElement.Name.LocalName) == false) - && (string.Equals(Constants.Packaging.DocumentsNodeName, documentsElement.Name.LocalName) == false)) - { - throw new ArgumentException("Must be \"" + Constants.Packaging.DocumentsNodeName + "\" as root", - "documentsElement"); - } - - if (string.Equals(Constants.Packaging.DocumentSetNodeName, documentsElement.Name.LocalName)) - return _packagingService.ImportContent(documentsElement, -1, userId); - - return - documentsElement.Elements(Constants.Packaging.DocumentSetNodeName) - .SelectMany(documentSetElement => _packagingService.ImportContent(documentSetElement, -1, userId)) - .ToArray(); - } - - private IEnumerable InstallStylesheets(XElement styleSheetsElement) - { - if (string.Equals(Constants.Packaging.StylesheetsNodeName, styleSheetsElement.Name.LocalName) == false) - { - throw new ArgumentException("Must be \"" + Constants.Packaging.StylesheetsNodeName + "\" as root", - "styleSheetsElement"); - } - - // TODO: Call _packagingService when import stylesheets import has been implimentet - if (styleSheetsElement.HasElements == false) { return new List(); } - - throw new NotImplementedException("The packaging service do not yes have a method for importing stylesheets"); - } - - private IEnumerable InstallDocumentTypes(XElement documentTypes, int userId = 0) - { - if (string.Equals(Constants.Packaging.DocumentTypesNodeName, documentTypes.Name.LocalName) == false) - { - if (string.Equals(Constants.Packaging.DocumentTypeNodeName, documentTypes.Name.LocalName) == false) - throw new ArgumentException( - "Must be \"" + Constants.Packaging.DocumentTypesNodeName + "\" as root", "documentTypes"); - - documentTypes = new XElement(Constants.Packaging.DocumentTypesNodeName, documentTypes); - } - - return _packagingService.ImportContentTypes(documentTypes, userId); - } - - private IEnumerable InstallTemplates(XElement templateElement, int userId = 0) - { - if (string.Equals(Constants.Packaging.TemplatesNodeName, templateElement.Name.LocalName) == false) - { - throw new ArgumentException("Must be \"" + Constants.Packaging.TemplatesNodeName + "\" as root", - "templateElement"); - } - return _packagingService.ImportTemplates(templateElement, userId); - } - - private IEnumerable InstallMacros(XElement macroElements, int userId = 0) - { - if (string.Equals(Constants.Packaging.MacrosNodeName, macroElements.Name.LocalName) == false) - { - throw new ArgumentException("Must be \"" + Constants.Packaging.MacrosNodeName + "\" as root", - "macroElements"); - } - return _packagingService.ImportMacros(macroElements, userId); - } - - private IEnumerable InstallDictionaryItems(XElement dictionaryItemsElement) - { - if (string.Equals(Constants.Packaging.DictionaryItemsNodeName, dictionaryItemsElement.Name.LocalName) == - false) - { - throw new ArgumentException("Must be \"" + Constants.Packaging.DictionaryItemsNodeName + "\" as root", - "dictionaryItemsElement"); - } - return _packagingService.ImportDictionaryItems(dictionaryItemsElement); - } - - private IEnumerable InstallLanguages(XElement languageElement, int userId = 0) - { - if (string.Equals(Constants.Packaging.LanguagesNodeName, languageElement.Name.LocalName) == false) - { - throw new ArgumentException("Must be \"" + Constants.Packaging.LanguagesNodeName + "\" as root", "languageElement"); - } - return _packagingService.ImportLanguages(languageElement, userId); - } - - private IEnumerable InstallDataTypes(XElement dataTypeElements, int userId = 0) - { - if (string.Equals(Constants.Packaging.DataTypesNodeName, dataTypeElements.Name.LocalName) == false) - { - if (string.Equals(Constants.Packaging.DataTypeNodeName, dataTypeElements.Name.LocalName) == false) - { - throw new ArgumentException("Must be \"" + Constants.Packaging.DataTypeNodeName + "\" as root", "dataTypeElements"); - } - } - return _packagingService.ImportDataTypeDefinitions(dataTypeElements, userId); - } + } } diff --git a/src/Umbraco.Core/Services/IPackagingService.cs b/src/Umbraco.Core/Services/IPackagingService.cs index fcb0c66401..8aa936c8ff 100644 --- a/src/Umbraco.Core/Services/IPackagingService.cs +++ b/src/Umbraco.Core/Services/IPackagingService.cs @@ -72,81 +72,6 @@ namespace Umbraco.Core.Services #endregion - #region Importing - /// - /// Imports and saves package xml as - /// - /// Xml to import - /// Optional parent Id for the content being imported - /// Optional Id of the user performing the import - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated content - IEnumerable ImportContent(XElement element, int parentId = -1, int userId = 0, bool raiseEvents = true); - - /// - /// Imports and saves package xml as - /// - /// Xml to import - /// Optional id of the User performing the operation. Default is zero (admin) - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated ContentTypes - IEnumerable ImportContentTypes(XElement element, int userId = 0, bool raiseEvents = true); - - /// - /// Imports and saves package xml as - /// - /// Xml to import - /// Boolean indicating whether or not to import the - /// Optional id of the User performing the operation. Default is zero (admin) - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated ContentTypes - IEnumerable ImportContentTypes(XElement element, bool importStructure, int userId = 0, bool raiseEvents = true); - - /// - /// Imports and saves package xml as - /// - /// Xml to import - /// Optional id of the User performing the operation. Default is zero (admin). - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated DataTypeDefinitions - IEnumerable ImportDataTypeDefinitions(XElement element, int userId = 0, bool raiseEvents = true); - - /// - /// Imports and saves the 'DictionaryItems' part of the package xml as a list of - /// - /// Xml to import - /// Optional parameter indicating whether or not to raise events - /// An enumerable list of dictionary items - IEnumerable ImportDictionaryItems(XElement dictionaryItemElementList, bool raiseEvents = true); - - /// - /// Imports and saves the 'Languages' part of a package xml as a list of - /// - /// Xml to import - /// Optional id of the User performing the operation. Default is zero (admin) - /// Optional parameter indicating whether or not to raise events - /// An enumerable list of generated languages - IEnumerable ImportLanguages(XElement languageElementList, int userId = 0, bool raiseEvents = true); - - /// - /// Imports and saves the 'Macros' part of a package xml as a list of - /// - /// Xml to import - /// Optional id of the User performing the operation - /// Optional parameter indicating whether or not to raise events - /// - IEnumerable ImportMacros(XElement element, int userId = 0, bool raiseEvents = true); - - /// - /// Imports and saves package xml as - /// - /// Xml to import - /// Optional id of the User performing the operation. Default is zero (admin) - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated Templates - IEnumerable ImportTemplates(XElement element, int userId = 0, bool raiseEvents = true); - #endregion - /// /// This will fetch an Umbraco package file from the package repository and return the file name of the downloaded package /// diff --git a/src/Umbraco.Core/Services/Implement/PackagingService.cs b/src/Umbraco.Core/Services/Implement/PackagingService.cs index 7c379dc2dc..ba5d30f8d7 100644 --- a/src/Umbraco.Core/Services/Implement/PackagingService.cs +++ b/src/Umbraco.Core/Services/Implement/PackagingService.cs @@ -31,1097 +31,25 @@ namespace Umbraco.Core.Services.Implement /// public class PackagingService : IPackagingService { - //fixme: inject when ready to use this - private readonly IPackageInstallation _packageInstallation; - private readonly ILogger _logger; - private readonly IContentService _contentService; - private readonly IContentTypeService _contentTypeService; - private readonly IMacroService _macroService; - private readonly IDataTypeService _dataTypeService; - private readonly IFileService _fileService; - private readonly ILocalizationService _localizationService; - private readonly IEntityService _entityService; - private Dictionary _importedContentTypes; + private readonly IPackageInstallation _packageInstallation; private readonly IAuditService _auditService; - private readonly PropertyEditorCollection _propertyEditors; private readonly ICreatedPackagesRepository _createdPackages; private readonly IInstalledPackagesRepository _installedPackages; private static HttpClient _httpClient; public PackagingService( - ILogger logger, - IContentService contentService, - IContentTypeService contentTypeService, - IMacroService macroService, - IDataTypeService dataTypeService, - IFileService fileService, - ILocalizationService localizationService, - IEntityService entityService, IAuditService auditService, - PropertyEditorCollection propertyEditors, ICreatedPackagesRepository createdPackages, - IInstalledPackagesRepository installedPackages) - { - _logger = logger; - _contentService = contentService; - _contentTypeService = contentTypeService; - _macroService = macroService; - _dataTypeService = dataTypeService; - _fileService = fileService; - _localizationService = localizationService; - _entityService = entityService; + IInstalledPackagesRepository installedPackages, + IPackageInstallation packageInstallation) + { _auditService = auditService; - _propertyEditors = propertyEditors; _createdPackages = createdPackages; _installedPackages = installedPackages; - _importedContentTypes = new Dictionary(); + _packageInstallation = packageInstallation; } - #region Content - - - /// - /// Imports and saves package xml as - /// - /// Xml to import - /// Optional parent Id for the content being imported - /// Optional Id of the user performing the import - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated content - public IEnumerable ImportContent(XElement element, int parentId = -1, int userId = 0, bool raiseEvents = true) - { - if (raiseEvents) - { - if (ImportingContent.IsRaisedEventCancelled(new ImportEventArgs(element), this)) - return Enumerable.Empty(); - } - - var name = element.Name.LocalName; - if (name.Equals("DocumentSet")) - { - //This is a regular deep-structured import - var roots = from doc in element.Elements() - where (string)doc.Attribute("isDoc") == "" - select doc; - - var contents = ParseDocumentRootXml(roots, parentId).ToList(); - if (contents.Any()) - _contentService.Save(contents, userId); - - if (raiseEvents) - ImportedContent.RaiseEvent(new ImportEventArgs(contents, element, false), this); - return contents; - } - - var attribute = element.Attribute("isDoc"); - if (attribute != null) - { - //This is a single doc import - var elements = new List { element }; - var contents = ParseDocumentRootXml(elements, parentId).ToList(); - if (contents.Any()) - _contentService.Save(contents, userId); - - if (raiseEvents) - ImportedContent.RaiseEvent(new ImportEventArgs(contents, element, false), this); - return contents; - } - - throw new ArgumentException( - "The passed in XElement is not valid! It does not contain a root element called " + - "'DocumentSet' (for structured imports) nor is the first element a Document (for single document import)."); - } - - private IEnumerable ParseDocumentRootXml(IEnumerable roots, int parentId) - { - var contents = new List(); - foreach (var root in roots) - { - var contentTypeAlias = root.Name.LocalName; - - if (_importedContentTypes.ContainsKey(contentTypeAlias) == false) - { - var contentType = FindContentTypeByAlias(contentTypeAlias); - _importedContentTypes.Add(contentTypeAlias, contentType); - } - - var content = CreateContentFromXml(root, _importedContentTypes[contentTypeAlias], null, parentId); - contents.Add(content); - - var children = (from child in root.Elements() - where (string)child.Attribute("isDoc") == "" - select child) - .ToList(); - if (children.Any()) - contents.AddRange(CreateContentFromXml(children, content)); - } - return contents; - } - - private IEnumerable CreateContentFromXml(IEnumerable children, IContent parent) - { - var list = new List(); - foreach (var child in children) - { - string contentTypeAlias = child.Name.LocalName; - - if (_importedContentTypes.ContainsKey(contentTypeAlias) == false) - { - var contentType = FindContentTypeByAlias(contentTypeAlias); - _importedContentTypes.Add(contentTypeAlias, contentType); - } - - //Create and add the child to the list - var content = CreateContentFromXml(child, _importedContentTypes[contentTypeAlias], parent, default(int)); - list.Add(content); - - //Recursive call - XElement child1 = child; - var grandChildren = (from grand in child1.Elements() - where (string) grand.Attribute("isDoc") == "" - select grand).ToList(); - - if (grandChildren.Any()) - list.AddRange(CreateContentFromXml(grandChildren, content)); - } - - return list; - } - - private IContent CreateContentFromXml(XElement element, IContentType contentType, IContent parent, int parentId) - { - var id = element.Attribute("id").Value; - var level = element.Attribute("level").Value; - var sortOrder = element.Attribute("sortOrder").Value; - var nodeName = element.Attribute("nodeName").Value; - var path = element.Attribute("path").Value; - //TODO: Shouldn't we be using this value??? - var template = element.Attribute("template").Value; - var key = Guid.Empty; - - var properties = from property in element.Elements() - where property.Attribute("isDoc") == null - select property; - - IContent content = parent == null - ? new Content(nodeName, parentId, contentType) - { - Level = int.Parse(level), - SortOrder = int.Parse(sortOrder) - } - : new Content(nodeName, parent, contentType) - { - Level = int.Parse(level), - SortOrder = int.Parse(sortOrder) - }; - - if (element.Attribute("key") != null && Guid.TryParse(element.Attribute("key").Value, out key)) - { - // update the Guid (for UDI support) - content.Key = key; - } - - foreach (var property in properties) - { - string propertyTypeAlias = property.Name.LocalName; - if (content.HasProperty(propertyTypeAlias)) - { - var propertyValue = property.Value; - - var propertyType = contentType.PropertyTypes.FirstOrDefault(pt => pt.Alias == propertyTypeAlias); - - //set property value - content.SetValue(propertyTypeAlias, propertyValue); - } - } - - return content; - } - - #endregion - - #region ContentTypes - - - - /// - /// Imports and saves package xml as - /// - /// Xml to import - /// Optional id of the User performing the operation. Default is zero (admin). - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated ContentTypes - public IEnumerable ImportContentTypes(XElement element, int userId = 0, bool raiseEvents = true) - { - return ImportContentTypes(element, true, userId); - } - - /// - /// Imports and saves package xml as - /// - /// Xml to import - /// Boolean indicating whether or not to import the - /// Optional id of the User performing the operation. Default is zero (admin). - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated ContentTypes - public IEnumerable ImportContentTypes(XElement element, bool importStructure, int userId = 0, bool raiseEvents = true) - { - if (raiseEvents) - { - if (ImportingContentType.IsRaisedEventCancelled(new ImportEventArgs(element), this)) - return Enumerable.Empty(); - } - - var name = element.Name.LocalName; - if (name.Equals("DocumentTypes") == false && name.Equals("DocumentType") == false) - { - throw new ArgumentException("The passed in XElement is not valid! It does not contain a root element called 'DocumentTypes' for multiple imports or 'DocumentType' for a single import."); - } - - _importedContentTypes = new Dictionary(); - var unsortedDocumentTypes = name.Equals("DocumentTypes") - ? (from doc in element.Elements("DocumentType") select doc).ToList() - : new List { element }; - - //When you are importing a single doc type we have to assume that the depedencies are already there. - //Otherwise something like uSync won't work. - var graph = new TopoGraph>(x => x.Key, x => x.Dependencies); - var isSingleDocTypeImport = unsortedDocumentTypes.Count == 1; - - var importedFolders = CreateContentTypeFolderStructure(unsortedDocumentTypes); - - if (isSingleDocTypeImport == false) - { - //NOTE Here we sort the doctype XElements based on dependencies - //before creating the doc types - this should also allow for a better structure/inheritance support. - foreach (var documentType in unsortedDocumentTypes) - { - var elementCopy = documentType; - var infoElement = elementCopy.Element("Info"); - var dependencies = new HashSet(); - - //Add the Master as a dependency - if (string.IsNullOrEmpty((string)infoElement.Element("Master")) == false) - { - dependencies.Add(infoElement.Element("Master").Value); - } - - //Add compositions as dependencies - var compositionsElement = infoElement.Element("Compositions"); - if (compositionsElement != null && compositionsElement.HasElements) - { - var compositions = compositionsElement.Elements("Composition"); - if (compositions.Any()) - { - foreach (var composition in compositions) - { - dependencies.Add(composition.Value); - } - } - } - - graph.AddItem(TopoGraph.CreateNode(infoElement.Element("Alias").Value, elementCopy, dependencies.ToArray())); - } - } - - //Sorting the Document Types based on dependencies - if its not a single doc type import ref. #U4-5921 - var documentTypes = isSingleDocTypeImport - ? unsortedDocumentTypes.ToList() - : graph.GetSortedItems().Select(x => x.Item).ToList(); - - //Iterate the sorted document types and create them as IContentType objects - foreach (var documentType in documentTypes) - { - var alias = documentType.Element("Info").Element("Alias").Value; - if (_importedContentTypes.ContainsKey(alias) == false) - { - var contentType = _contentTypeService.Get(alias); - _importedContentTypes.Add(alias, contentType == null - ? CreateContentTypeFromXml(documentType) - : UpdateContentTypeFromXml(documentType, contentType)); - } - } - - foreach (var contentType in _importedContentTypes) - { - var ct = contentType.Value; - if (importedFolders.ContainsKey(ct.Alias)) - { - ct.ParentId = importedFolders[ct.Alias]; - } - } - - //Save the newly created/updated IContentType objects - var list = _importedContentTypes.Select(x => x.Value).ToList(); - _contentTypeService.Save(list, userId); - - //Now we can finish the import by updating the 'structure', - //which requires the doc types to be saved/available in the db - if (importStructure) - { - var updatedContentTypes = new List(); - //Update the structure here - we can't do it untill all DocTypes have been created - foreach (var documentType in documentTypes) - { - var alias = documentType.Element("Info").Element("Alias").Value; - var structureElement = documentType.Element("Structure"); - //Ensure that we only update ContentTypes which has actual structure-elements - if (structureElement == null || structureElement.Elements("DocumentType").Any() == false) continue; - - var updated = UpdateContentTypesStructure(_importedContentTypes[alias], structureElement); - updatedContentTypes.Add(updated); - } - //Update ContentTypes with a newly added structure/list of allowed children - if (updatedContentTypes.Any()) - _contentTypeService.Save(updatedContentTypes, userId); - } - - if (raiseEvents) - ImportedContentType.RaiseEvent(new ImportEventArgs(list, element, false), this); - - return list; - } - - private Dictionary CreateContentTypeFolderStructure(IEnumerable unsortedDocumentTypes) - { - var importedFolders = new Dictionary(); - foreach (var documentType in unsortedDocumentTypes) - { - var foldersAttribute = documentType.Attribute("Folders"); - var infoElement = documentType.Element("Info"); - if (foldersAttribute != null && infoElement != null - //don't import any folder if this is a child doc type - the parent doc type will need to - //exist which contains it's folders - && ((string)infoElement.Element("Master")).IsNullOrWhiteSpace()) - { - var alias = documentType.Element("Info").Element("Alias").Value; - var folders = foldersAttribute.Value.Split('/'); - var rootFolder = HttpUtility.UrlDecode(folders[0]); - //level 1 = root level folders, there can only be one with the same name - var current = _contentTypeService.GetContainers(rootFolder, 1).FirstOrDefault(); - - if (current == null) - { - var tryCreateFolder = _contentTypeService.CreateContainer(-1, rootFolder); - if (tryCreateFolder == false) - { - _logger.Error(tryCreateFolder.Exception, "Could not create folder: {FolderName}", rootFolder); - throw tryCreateFolder.Exception; - } - var rootFolderId = tryCreateFolder.Result.Entity.Id; - current = _contentTypeService.GetContainer(rootFolderId); - } - - importedFolders.Add(alias, current.Id); - - for (var i = 1; i < folders.Length; i++) - { - var folderName = HttpUtility.UrlDecode(folders[i]); - current = CreateContentTypeChildFolder(folderName, current); - importedFolders[alias] = current.Id; - } - } - } - - return importedFolders; - } - - private EntityContainer CreateContentTypeChildFolder(string folderName, IUmbracoEntity current) - { - var children = _entityService.GetChildren(current.Id).ToArray(); - var found = children.Any(x => x.Name.InvariantEquals(folderName)); - if (found) - { - var containerId = children.Single(x => x.Name.InvariantEquals(folderName)).Id; - return _contentTypeService.GetContainer(containerId); - } - - var tryCreateFolder = _contentTypeService.CreateContainer(current.Id, folderName); - if (tryCreateFolder == false) - { - _logger.Error(tryCreateFolder.Exception, "Could not create folder: {FolderName}", folderName); - throw tryCreateFolder.Exception; - } - return _contentTypeService.GetContainer(tryCreateFolder.Result.Entity.Id); - } - - private IContentType CreateContentTypeFromXml(XElement documentType) - { - var infoElement = documentType.Element("Info"); - - //Name of the master corresponds to the parent - var masterElement = infoElement.Element("Master"); - IContentType parent = null; - if (masterElement != null) - { - var masterAlias = masterElement.Value; - parent = _importedContentTypes.ContainsKey(masterAlias) - ? _importedContentTypes[masterAlias] - : _contentTypeService.Get(masterAlias); - } - - var alias = infoElement.Element("Alias").Value; - var contentType = parent == null - ? new ContentType(-1) { Alias = alias } - : new ContentType(parent, alias); - - if (parent != null) - contentType.AddContentType(parent); - - return UpdateContentTypeFromXml(documentType, contentType); - } - - private IContentType UpdateContentTypeFromXml(XElement documentType, IContentType contentType) - { - var infoElement = documentType.Element("Info"); - var defaultTemplateElement = infoElement.Element("DefaultTemplate"); - - contentType.Name = infoElement.Element("Name").Value; - contentType.Icon = infoElement.Element("Icon").Value; - contentType.Thumbnail = infoElement.Element("Thumbnail").Value; - contentType.Description = infoElement.Element("Description").Value; - - //NOTE AllowAtRoot is a new property in the package xml so we need to verify it exists before using it. - var allowAtRoot = infoElement.Element("AllowAtRoot"); - if (allowAtRoot != null) - contentType.AllowedAsRoot = allowAtRoot.Value.InvariantEquals("true"); - - //NOTE IsListView is a new property in the package xml so we need to verify it exists before using it. - var isListView = infoElement.Element("IsListView"); - if (isListView != null) - contentType.IsContainer = isListView.Value.InvariantEquals("true"); - - //Name of the master corresponds to the parent and we need to ensure that the Parent Id is set - var masterElement = infoElement.Element("Master"); - if (masterElement != null) - { - var masterAlias = masterElement.Value; - IContentType parent = _importedContentTypes.ContainsKey(masterAlias) - ? _importedContentTypes[masterAlias] - : _contentTypeService.Get(masterAlias); - - contentType.SetParent(parent); - } - - //Update Compositions on the ContentType to ensure that they are as is defined in the package xml - var compositionsElement = infoElement.Element("Compositions"); - if (compositionsElement != null && compositionsElement.HasElements) - { - var compositions = compositionsElement.Elements("Composition"); - if (compositions.Any()) - { - foreach (var composition in compositions) - { - var compositionAlias = composition.Value; - var compositionContentType = _importedContentTypes.ContainsKey(compositionAlias) - ? _importedContentTypes[compositionAlias] - : _contentTypeService.Get(compositionAlias); - var added = contentType.AddContentType(compositionContentType); - } - } - } - - UpdateContentTypesAllowedTemplates(contentType, infoElement.Element("AllowedTemplates"), defaultTemplateElement); - UpdateContentTypesTabs(contentType, documentType.Element("Tabs")); - UpdateContentTypesProperties(contentType, documentType.Element("GenericProperties")); - - return contentType; - } - - private void UpdateContentTypesAllowedTemplates(IContentType contentType, - XElement allowedTemplatesElement, XElement defaultTemplateElement) - { - if (allowedTemplatesElement != null && allowedTemplatesElement.Elements("Template").Any()) - { - var allowedTemplates = contentType.AllowedTemplates.ToList(); - foreach (var templateElement in allowedTemplatesElement.Elements("Template")) - { - var alias = templateElement.Value; - var template = _fileService.GetTemplate(alias.ToSafeAlias()); - if (template != null) - { - if (allowedTemplates.Any(x => x.Id == template.Id)) continue; - allowedTemplates.Add(template); - } - else - { - _logger.Warn("Packager: Error handling allowed templates. Template with alias '{TemplateAlias}' could not be found.", alias); - } - } - - contentType.AllowedTemplates = allowedTemplates; - } - - if (string.IsNullOrEmpty((string)defaultTemplateElement) == false) - { - var defaultTemplate = _fileService.GetTemplate(defaultTemplateElement.Value.ToSafeAlias()); - if (defaultTemplate != null) - { - contentType.SetDefaultTemplate(defaultTemplate); - } - else - { - _logger.Warn("Packager: Error handling default template. Default template with alias '{DefaultTemplateAlias}' could not be found.", defaultTemplateElement.Value); - } - } - } - - private void UpdateContentTypesTabs(IContentType contentType, XElement tabElement) - { - if (tabElement == null) - return; - - var tabs = tabElement.Elements("Tab"); - foreach (var tab in tabs) - { - var id = tab.Element("Id").Value;//Do we need to use this for tracking? - var caption = tab.Element("Caption").Value; - - if (contentType.PropertyGroups.Contains(caption) == false) - { - contentType.AddPropertyGroup(caption); - - } - - int sortOrder; - if (tab.Element("SortOrder") != null && int.TryParse(tab.Element("SortOrder").Value, out sortOrder)) - { - // Override the sort order with the imported value - contentType.PropertyGroups[caption].SortOrder = sortOrder; - } - } - } - - private void UpdateContentTypesProperties(IContentType contentType, XElement genericPropertiesElement) - { - var properties = genericPropertiesElement.Elements("GenericProperty"); - foreach (var property in properties) - { - var dataTypeDefinitionId = new Guid(property.Element("Definition").Value);//Unique Id for a DataTypeDefinition - - var dataTypeDefinition = _dataTypeService.GetDataType(dataTypeDefinitionId); - - //If no DataTypeDefinition with the guid from the xml wasn't found OR the ControlId on the DataTypeDefinition didn't match the DataType Id - //We look up a DataTypeDefinition that matches - - - //get the alias as a string for use below - var propertyEditorAlias = property.Element("Type").Value.Trim(); - - //If no DataTypeDefinition with the guid from the xml wasn't found OR the ControlId on the DataTypeDefinition didn't match the DataType Id - //We look up a DataTypeDefinition that matches - - if (dataTypeDefinition == null) - { - var dataTypeDefinitions = _dataTypeService.GetByEditorAlias(propertyEditorAlias); - if (dataTypeDefinitions != null && dataTypeDefinitions.Any()) - { - dataTypeDefinition = dataTypeDefinitions.FirstOrDefault(); - } - } - else if (dataTypeDefinition.EditorAlias != propertyEditorAlias) - { - var dataTypeDefinitions = _dataTypeService.GetByEditorAlias(propertyEditorAlias); - if (dataTypeDefinitions != null && dataTypeDefinitions.Any()) - { - dataTypeDefinition = dataTypeDefinitions.FirstOrDefault(); - } - } - - // For backwards compatibility, if no datatype with that ID can be found, we're letting this fail silently. - // This means that the property will not be created. - if (dataTypeDefinition == null) - { - _logger.Warn("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()); - - //convert to a label! - dataTypeDefinition = _dataTypeService.GetByEditorAlias(Constants.PropertyEditors.Aliases.NoEdit).FirstOrDefault(); - //if for some odd reason this isn't there then ignore - if (dataTypeDefinition == null) continue; - } - - var sortOrder = 0; - var sortOrderElement = property.Element("SortOrder"); - if (sortOrderElement != null) - int.TryParse(sortOrderElement.Value, out sortOrder); - var propertyType = new PropertyType(dataTypeDefinition, property.Element("Alias").Value) - { - Name = property.Element("Name").Value, - Description = (string)property.Element("Description"), - Mandatory = property.Element("Mandatory") != null ? property.Element("Mandatory").Value.ToLowerInvariant().Equals("true") : false, - ValidationRegExp = (string)property.Element("Validation"), - SortOrder = sortOrder - }; - - var tab = (string)property.Element("Tab"); - if (string.IsNullOrEmpty(tab)) - { - contentType.AddPropertyType(propertyType); - } - else - { - contentType.AddPropertyType(propertyType, tab); - } - } - } - - private IContentType UpdateContentTypesStructure(IContentType contentType, XElement structureElement) - { - var allowedChildren = contentType.AllowedContentTypes.ToList(); - int sortOrder = allowedChildren.Any() ? allowedChildren.Last().SortOrder : 0; - foreach (var element in structureElement.Elements("DocumentType")) - { - var alias = element.Value; - - var allowedChild = _importedContentTypes.ContainsKey(alias) ? _importedContentTypes[alias] : _contentTypeService.Get(alias); - if (allowedChild == null) - { - _logger.Warn( - "Packager: Error handling DocumentType structure. DocumentType with alias '{DoctypeAlias}' could not be found and was not added to the structure for '{DoctypeStructureAlias}'.", - alias, contentType.Alias); - continue; - } - - if (allowedChildren.Any(x => x.Id.IsValueCreated && x.Id.Value == allowedChild.Id)) continue; - - allowedChildren.Add(new ContentTypeSort(new Lazy(() => allowedChild.Id), sortOrder, allowedChild.Alias)); - sortOrder++; - } - - contentType.AllowedContentTypes = allowedChildren; - return contentType; - } - - /// - /// Used during Content import to ensure that the ContentType of a content item exists - /// - /// - /// - private IContentType FindContentTypeByAlias(string contentTypeAlias) - { - var contentType = _contentTypeService.Get(contentTypeAlias); - - if (contentType == null) - throw new Exception($"ContentType matching the passed in Alias: '{contentTypeAlias}' was null"); - - return contentType; - } - - #endregion - - #region DataTypes - - - - - - /// - /// Imports and saves package xml as - /// - /// Xml to import - /// Optional id of the user - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated DataTypeDefinitions - public IEnumerable ImportDataTypeDefinitions(XElement element, int userId = 0, bool raiseEvents = true) - { - if (raiseEvents) - { - if (ImportingDataType.IsRaisedEventCancelled(new ImportEventArgs(element), this)) - return Enumerable.Empty(); - } - - var name = element.Name.LocalName; - if (name.Equals("DataTypes") == false && name.Equals("DataType") == false) - { - throw new ArgumentException("The passed in XElement is not valid! It does not contain a root element called 'DataTypes' for multiple imports or 'DataType' for a single import."); - } - - var dataTypes = new List(); - var dataTypeElements = name.Equals("DataTypes") - ? (from doc in element.Elements("DataType") select doc).ToList() - : new List { element }; - - var importedFolders = CreateDataTypeFolderStructure(dataTypeElements); - - foreach (var dataTypeElement in dataTypeElements) - { - var dataTypeDefinitionName = dataTypeElement.Attribute("Name").Value; - - var dataTypeDefinitionId = new Guid(dataTypeElement.Attribute("Definition").Value); - var databaseTypeAttribute = dataTypeElement.Attribute("DatabaseType"); - - var parentId = -1; - if (importedFolders.ContainsKey(dataTypeDefinitionName)) - parentId = importedFolders[dataTypeDefinitionName]; - - var definition = _dataTypeService.GetDataType(dataTypeDefinitionId); - //If the datatypedefinition doesn't already exist we create a new new according to the one in the package xml - if (definition == null) - { - var databaseType = databaseTypeAttribute != null - ? databaseTypeAttribute.Value.EnumParse(true) - : ValueStorageType.Ntext; - - // the Id field is actually the string property editor Alias - // however, the actual editor with this alias could be installed with the package, and - // therefore not yet part of the _propertyEditors collection, so we cannot try and get - // the actual editor - going with a void editor - - var editorAlias = dataTypeElement.Attribute("Id")?.Value?.Trim(); - if (!_propertyEditors.TryGet(editorAlias, out var editor)) - editor = new VoidEditor(_logger) { Alias = editorAlias }; - - var dataType = new DataType(editor) - { - Key = dataTypeDefinitionId, - Name = dataTypeDefinitionName, - DatabaseType = databaseType, - ParentId = parentId - }; - - var configurationAttributeValue = dataTypeElement.Attribute("Configuration")?.Value; - if (!string.IsNullOrWhiteSpace(configurationAttributeValue)) - dataType.Configuration = editor.GetConfigurationEditor().FromDatabase(configurationAttributeValue); - - dataTypes.Add(dataType); - } - else - { - definition.ParentId = parentId; - _dataTypeService.Save(definition, userId); - } - } - - if (dataTypes.Count > 0) - { - _dataTypeService.Save(dataTypes, userId, true); - } - - if (raiseEvents) - ImportedDataType.RaiseEvent(new ImportEventArgs(dataTypes, element, false), this); - - return dataTypes; - } - - private Dictionary CreateDataTypeFolderStructure(IEnumerable datatypeElements) - { - var importedFolders = new Dictionary(); - foreach (var datatypeElement in datatypeElements) - { - var foldersAttribute = datatypeElement.Attribute("Folders"); - if (foldersAttribute != null) - { - var name = datatypeElement.Attribute("Name").Value; - var folders = foldersAttribute.Value.Split('/'); - var rootFolder = HttpUtility.UrlDecode(folders[0]); - //there will only be a single result by name for level 1 (root) containers - var current = _dataTypeService.GetContainers(rootFolder, 1).FirstOrDefault(); - - if (current == null) - { - var tryCreateFolder = _dataTypeService.CreateContainer(-1, rootFolder); - if (tryCreateFolder == false) - { - _logger.Error(tryCreateFolder.Exception, "Could not create folder: {FolderName}", rootFolder); - throw tryCreateFolder.Exception; - } - current = _dataTypeService.GetContainer(tryCreateFolder.Result.Entity.Id); - } - - importedFolders.Add(name, current.Id); - - for (var i = 1; i < folders.Length; i++) - { - var folderName = HttpUtility.UrlDecode(folders[i]); - current = CreateDataTypeChildFolder(folderName, current); - importedFolders[name] = current.Id; - } - } - } - - return importedFolders; - } - - private EntityContainer CreateDataTypeChildFolder(string folderName, IUmbracoEntity current) - { - var children = _entityService.GetChildren(current.Id).ToArray(); - var found = children.Any(x => x.Name.InvariantEquals(folderName)); - if (found) - { - var containerId = children.Single(x => x.Name.InvariantEquals(folderName)).Id; - return _dataTypeService.GetContainer(containerId); - } - - var tryCreateFolder = _dataTypeService.CreateContainer(current.Id, folderName); - if (tryCreateFolder == false) - { - _logger.Error(tryCreateFolder.Exception, "Could not create folder: {FolderName}", folderName); - throw tryCreateFolder.Exception; - } - return _dataTypeService.GetContainer(tryCreateFolder.Result.Entity.Id); - } - - #endregion - - #region Dictionary Items - - - - - - /// - /// Imports and saves the 'DictionaryItems' part of the package xml as a list of - /// - /// Xml to import - /// Optional parameter indicating whether or not to raise events - /// An enumerable list of dictionary items - public IEnumerable ImportDictionaryItems(XElement dictionaryItemElementList, bool raiseEvents = true) - { - if (raiseEvents) - { - if (ImportingDictionaryItem.IsRaisedEventCancelled(new ImportEventArgs(dictionaryItemElementList), this)) - return Enumerable.Empty(); - } - - var languages = _localizationService.GetAllLanguages().ToList(); - return ImportDictionaryItems(dictionaryItemElementList, languages, raiseEvents); - } - - private IEnumerable ImportDictionaryItems(XElement dictionaryItemElementList, List languages, bool raiseEvents, Guid? parentId = null) - { - var items = new List(); - foreach (var dictionaryItemElement in dictionaryItemElementList.Elements("DictionaryItem")) - items.AddRange(ImportDictionaryItem(dictionaryItemElement, languages, raiseEvents, parentId)); - - - if (raiseEvents) - ImportedDictionaryItem.RaiseEvent(new ImportEventArgs(items, dictionaryItemElementList, false), this); - - return items; - } - - private IEnumerable ImportDictionaryItem(XElement dictionaryItemElement, List languages, bool raiseEvents, Guid? parentId) - { - var items = new List(); - - IDictionaryItem dictionaryItem; - var key = dictionaryItemElement.Attribute("Key").Value; - if (_localizationService.DictionaryItemExists(key)) - dictionaryItem = GetAndUpdateDictionaryItem(key, dictionaryItemElement, languages); - else - dictionaryItem = CreateNewDictionaryItem(key, dictionaryItemElement, languages, parentId); - _localizationService.Save(dictionaryItem); - items.Add(dictionaryItem); - - items.AddRange(ImportDictionaryItems(dictionaryItemElement, languages, raiseEvents, dictionaryItem.Key)); - return items; - } - - private IDictionaryItem GetAndUpdateDictionaryItem(string key, XElement dictionaryItemElement, List languages) - { - var dictionaryItem = _localizationService.GetDictionaryItemByKey(key); - var translations = dictionaryItem.Translations.ToList(); - foreach (var valueElement in dictionaryItemElement.Elements("Value").Where(v => DictionaryValueIsNew(translations, v))) - AddDictionaryTranslation(translations, valueElement, languages); - dictionaryItem.Translations = translations; - return dictionaryItem; - } - - private static DictionaryItem CreateNewDictionaryItem(string key, XElement dictionaryItemElement, List languages, Guid? parentId) - { - var dictionaryItem = parentId.HasValue ? new DictionaryItem(parentId.Value, key) : new DictionaryItem(key); - var translations = new List(); - - foreach (var valueElement in dictionaryItemElement.Elements("Value")) - AddDictionaryTranslation(translations, valueElement, languages); - - dictionaryItem.Translations = translations; - return dictionaryItem; - } - - private static bool DictionaryValueIsNew(IEnumerable translations, XElement valueElement) - { - return translations.All(t => - String.Compare(t.Language.IsoCode, valueElement.Attribute("LanguageCultureAlias").Value, - StringComparison.InvariantCultureIgnoreCase) != 0 - ); - } - - private static void AddDictionaryTranslation(ICollection translations, XElement valueElement, IEnumerable languages) - { - var languageId = valueElement.Attribute("LanguageCultureAlias").Value; - var language = languages.SingleOrDefault(l => l.IsoCode == languageId); - if (language == null) - return; - var translation = new DictionaryTranslation(language, valueElement.Value); - translations.Add(translation); - } - - #endregion - - #region Languages - - - - - - /// - /// Imports and saves the 'Languages' part of a package xml as a list of - /// - /// Xml to import - /// Optional id of the User performing the operation - /// Optional parameter indicating whether or not to raise events - /// An enumerable list of generated languages - public IEnumerable ImportLanguages(XElement languageElementList, int userId = 0, bool raiseEvents = true) - { - if (raiseEvents) - { - if (ImportingLanguage.IsRaisedEventCancelled(new ImportEventArgs(languageElementList), this)) - return Enumerable.Empty(); - } - - var list = new List(); - foreach (var languageElement in languageElementList.Elements("Language")) - { - var isoCode = languageElement.Attribute("CultureAlias").Value; - var existingLanguage = _localizationService.GetLanguageByIsoCode(isoCode); - if (existingLanguage == null) - { - var langauge = new Language(isoCode) - { - CultureName = languageElement.Attribute("FriendlyName").Value - }; - _localizationService.Save(langauge); - list.Add(langauge); - } - } - - if (raiseEvents) - ImportedLanguage.RaiseEvent(new ImportEventArgs(list, languageElementList, false), this); - - return list; - } - - #endregion - - #region Macros - - /// - /// Imports and saves the 'Macros' part of a package xml as a list of - /// - /// Xml to import - /// Optional id of the User performing the operation - /// Optional parameter indicating whether or not to raise events - /// - public IEnumerable ImportMacros(XElement element, int userId = 0, bool raiseEvents = true) - { - if (raiseEvents) - { - if (ImportingMacro.IsRaisedEventCancelled(new ImportEventArgs(element), this)) - return Enumerable.Empty(); - } - - var name = element.Name.LocalName; - if (name.Equals("Macros") == false && name.Equals("macro") == false) - { - throw new ArgumentException("The passed in XElement is not valid! It does not contain a root element called 'Macros' for multiple imports or 'macro' for a single import."); - } - - var macroElements = name.Equals("Macros") - ? (from doc in element.Elements("macro") select doc).ToList() - : new List { element }; - - var macros = macroElements.Select(ParseMacroElement).ToList(); - - foreach (var macro in macros) - { - var existing = _macroService.GetByAlias(macro.Alias); - if (existing != null) - macro.Id = existing.Id; - - _macroService.Save(macro, userId); - } - - if (raiseEvents) - ImportedMacro.RaiseEvent(new ImportEventArgs(macros, element, false), this); - - return macros; - } - - private IMacro ParseMacroElement(XElement macroElement) - { - var macroName = macroElement.Element("name").Value; - var macroAlias = macroElement.Element("alias").Value; - var macroType = Enum.Parse(macroElement.Element("macroType").Value); - var macroSource = macroElement.Element("macroSource").Value; - - //Following xml elements are treated as nullable properties - var useInEditorElement = macroElement.Element("useInEditor"); - var useInEditor = false; - if (useInEditorElement != null && string.IsNullOrEmpty((string)useInEditorElement) == false) - { - useInEditor = bool.Parse(useInEditorElement.Value); - } - var cacheDurationElement = macroElement.Element("refreshRate"); - var cacheDuration = 0; - if (cacheDurationElement != null && string.IsNullOrEmpty((string)cacheDurationElement) == false) - { - cacheDuration = int.Parse(cacheDurationElement.Value); - } - var cacheByMemberElement = macroElement.Element("cacheByMember"); - var cacheByMember = false; - if (cacheByMemberElement != null && string.IsNullOrEmpty((string)cacheByMemberElement) == false) - { - cacheByMember = bool.Parse(cacheByMemberElement.Value); - } - var cacheByPageElement = macroElement.Element("cacheByPage"); - var cacheByPage = false; - if (cacheByPageElement != null && string.IsNullOrEmpty((string)cacheByPageElement) == false) - { - cacheByPage = bool.Parse(cacheByPageElement.Value); - } - var dontRenderElement = macroElement.Element("dontRender"); - var dontRender = true; - if (dontRenderElement != null && string.IsNullOrEmpty((string)dontRenderElement) == false) - { - dontRender = bool.Parse(dontRenderElement.Value); - } - - var existingMacro = _macroService.GetByAlias(macroAlias) as Macro; - var macro = existingMacro ?? new Macro(macroAlias, macroName, macroSource, macroType, - cacheByPage, cacheByMember, dontRender, useInEditor, cacheDuration); - - var properties = macroElement.Element("properties"); - if (properties != null) - { - int sortOrder = 0; - foreach (var property in properties.Elements()) - { - var propertyName = property.Attribute("name").Value; - var propertyAlias = property.Attribute("alias").Value; - var editorAlias = property.Attribute("propertyType").Value; - var sortOrderAttribute = property.Attribute("sortOrder"); - if (sortOrderAttribute != null) - { - sortOrder = int.Parse(sortOrderAttribute.Value); - } - - if (macro.Properties.Values.Any(x => string.Equals(x.Alias, propertyAlias, StringComparison.OrdinalIgnoreCase))) continue; - macro.Properties.Add(new MacroProperty(propertyAlias, propertyName, sortOrder, editorAlias)); - sortOrder++; - } - } - return macro; - } - - - - - - #endregion - #region Package Files /// @@ -1167,139 +95,6 @@ namespace Umbraco.Core.Services.Implement #endregion - #region Templates - - /// - /// Imports and saves package xml as - /// - /// Xml to import - /// Optional user id - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated Templates - public IEnumerable ImportTemplates(XElement element, int userId = 0, bool raiseEvents = true) - { - if (raiseEvents) - { - if (ImportingTemplate.IsRaisedEventCancelled(new ImportEventArgs(element), this)) - return Enumerable.Empty(); - } - - var name = element.Name.LocalName; - if (name.Equals("Templates") == false && name.Equals("Template") == false) - { - throw new ArgumentException("The passed in XElement is not valid! It does not contain a root element called 'Templates' for multiple imports or 'Template' for a single import."); - } - - var templates = new List(); - var templateElements = name.Equals("Templates") - ? (from doc in element.Elements("Template") select doc).ToList() - : new List { element }; - - var graph = new TopoGraph>(x => x.Key, x => x.Dependencies); - - foreach (XElement tempElement in templateElements) - { - var dependencies = new List(); - var elementCopy = tempElement; - //Ensure that the Master of the current template is part of the import, otherwise we ignore this dependency as part of the dependency sorting. - if (string.IsNullOrEmpty((string)elementCopy.Element("Master")) == false && - templateElements.Any(x => (string)x.Element("Alias") == (string)elementCopy.Element("Master"))) - { - dependencies.Add((string)elementCopy.Element("Master")); - } - else if (string.IsNullOrEmpty((string)elementCopy.Element("Master")) == false && - templateElements.Any(x => (string)x.Element("Alias") == (string)elementCopy.Element("Master")) == false) - { - _logger.Info( - "Template '{TemplateAlias}' has an invalid Master '{TemplateMaster}', so the reference has been ignored.", - (string) elementCopy.Element("Alias"), - (string) elementCopy.Element("Master")); - } - - graph.AddItem(TopoGraph.CreateNode((string) elementCopy.Element("Alias"), elementCopy, dependencies)); - } - - //Sort templates by dependencies to a potential master template - var sorted = graph.GetSortedItems(); - foreach (var item in sorted) - { - var templateElement = item.Item; - - var templateName = templateElement.Element("Name").Value; - var alias = templateElement.Element("Alias").Value; - var design = templateElement.Element("Design").Value; - var masterElement = templateElement.Element("Master"); - - var isMasterPage = IsMasterPageSyntax(design); - var path = isMasterPage ? MasterpagePath(alias) : ViewPath(alias); - - var existingTemplate = _fileService.GetTemplate(alias) as Template; - var template = existingTemplate ?? new Template(templateName, alias); - template.Content = design; - if (masterElement != null && string.IsNullOrEmpty((string)masterElement) == false) - { - template.MasterTemplateAlias = masterElement.Value; - var masterTemplate = templates.FirstOrDefault(x => x.Alias == masterElement.Value); - if (masterTemplate != null) - template.MasterTemplateId = new Lazy(() => masterTemplate.Id); - } - templates.Add(template); - } - - if (templates.Any()) - _fileService.SaveTemplate(templates, userId); - - if (raiseEvents) - ImportedTemplate.RaiseEvent(new ImportEventArgs(templates, element, false), this); - - return templates; - } - - - private bool IsMasterPageSyntax(string code) - { - return Regex.IsMatch(code, @"<%@\s*Master", RegexOptions.IgnoreCase) || - code.InvariantContains(" ImportStylesheets(XElement element, int userId = 0, bool raiseEvents = true) - { - - if (raiseEvents) - { - if (ImportingStylesheets.IsRaisedEventCancelled(new ImportEventArgs(element), this)) - return Enumerable.Empty(); - } - - IEnumerable styleSheets = Enumerable.Empty(); - - if (element.Elements().Any()) - throw new NotImplementedException("This needs to be implimentet"); - - - if (raiseEvents) - ImportingStylesheets.RaiseEvent(new ImportEventArgs(styleSheets, element, false), this); - - return styleSheets; - - } - - #endregion - #region Installation public CompiledPackage GetCompiledPackageInfo(string packageFileName) => _packageInstallation.ReadPackage(packageFileName); @@ -1317,8 +112,7 @@ namespace Umbraco.Core.Services.Implement SaveInstalledPackage(packageDefinition); - if (userId > -1) - _auditService.Add(AuditType.PackagerInstall, userId, -1, "Package", $"Package files installed for package '{compiledPackage.Name}'."); + _auditService.Add(AuditType.PackagerInstall, userId, -1, "Package", $"Package files installed for package '{compiledPackage.Name}'."); return files; } @@ -1337,6 +131,8 @@ namespace Umbraco.Core.Services.Implement 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(summary, compiledPackage, false), this); @@ -1383,15 +179,6 @@ namespace Umbraco.Core.Services.Implement #endregion - /// - /// This method can be used to trigger the 'ImportedPackage' event when a package is installed by something else but this service. - /// - /// - internal static void OnImportedPackage(ImportPackageEventArgs args) - { - ImportedPackage.RaiseEvent(args, null); - } - /// /// This method can be used to trigger the 'UninstalledPackage' event when a package is uninstalled by something else but this service. /// @@ -1402,80 +189,6 @@ namespace Umbraco.Core.Services.Implement } #region Event Handlers - /// - /// Occurs before Importing Content - /// - public static event TypedEventHandler> ImportingContent; - - /// - /// Occurs after Content is Imported and Saved - /// - public static event TypedEventHandler> ImportedContent; - - /// - /// Occurs before Importing ContentType - /// - public static event TypedEventHandler> ImportingContentType; - - /// - /// Occurs after ContentType is Imported and Saved - /// - public static event TypedEventHandler> ImportedContentType; - - /// - /// Occurs before Importing DataType - /// - public static event TypedEventHandler> ImportingDataType; - - /// - /// Occurs after DataType is Imported and Saved - /// - public static event TypedEventHandler> ImportedDataType; - - /// - /// Occurs before Importing DictionaryItem - /// - public static event TypedEventHandler> ImportingDictionaryItem; - - /// - /// Occurs after DictionaryItem is Imported and Saved - /// - public static event TypedEventHandler> ImportedDictionaryItem; - - /// - /// Occurs before Importing Macro - /// - public static event TypedEventHandler> ImportingMacro; - - /// - /// Occurs after Macro is Imported and Saved - /// - public static event TypedEventHandler> ImportedMacro; - - /// - /// Occurs before Importing Language - /// - public static event TypedEventHandler> ImportingLanguage; - - /// - /// Occurs after Language is Imported and Saved - /// - public static event TypedEventHandler> ImportedLanguage; - - /// - /// Occurs before Importing Template - /// - public static event TypedEventHandler> ImportingTemplate; - - /// - /// Occurs before Importing Stylesheets - /// - public static event TypedEventHandler> ImportingStylesheets; - - /// - /// Occurs after Template is Imported and Saved - /// - public static event TypedEventHandler> ImportedTemplate; /// /// Occurs before Importing umbraco package diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 2ee5d13f6d..b59a5190bd 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -320,7 +320,6 @@ - @@ -447,9 +446,11 @@ + + @@ -573,7 +574,6 @@ - diff --git a/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs b/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs index fe7a5fbe5c..964f0c47de 100644 --- a/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs +++ b/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs @@ -4,9 +4,11 @@ using System.Linq; using Moq; using NUnit.Framework; using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Models.Packaging; using Umbraco.Core.Packaging; +using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; using Umbraco.Tests.TestHelpers; @@ -38,10 +40,16 @@ namespace Umbraco.Tests.Packaging private CompiledPackageXmlParser Parser => new CompiledPackageXmlParser(new ConflictingPackageData(ServiceContext.MacroService, ServiceContext.FileService)); + private PackageDataInstallation PackageDataInstallation => new PackageDataInstallation( + Logger, ServiceContext.FileService, ServiceContext.MacroService, ServiceContext.LocalizationService, + ServiceContext.DataTypeService, ServiceContext.EntityService, + ServiceContext.ContentTypeService, ServiceContext.ContentService, + Factory.GetInstance()); + private IPackageInstallation PackageInstallation => new PackageInstallation( - ServiceContext.PackagingService, + PackageDataInstallation, new PackageFileInstallation(Parser, ProfilingLogger), - Parser, + Parser, Mock.Of(), packagesFolderPath: "~/Packaging/packages",//this is where our test zip file is applicationRootFolder: new DirectoryInfo(IOHelper.GetRootDirectorySafe()), packageExtractionFolder: new DirectoryInfo(IOHelper.MapPath("~/" + _testBaseFolder))); //we don't want to extract package files to the real root, so extract to a test folder diff --git a/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs b/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs index 84eb75a4f7..f29a43c504 100644 --- a/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs +++ b/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs @@ -8,6 +8,7 @@ using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Composing.Composers; using Umbraco.Core.Logging; +using Umbraco.Core.Packaging; using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; @@ -65,6 +66,8 @@ namespace Umbraco.Tests.Services.Importing Composition.ComposeFileSystems(); } + private PackageDataInstallation PackagingService => Factory.GetInstance(); + [Test] public void PackagingService_Can_Import_uBlogsy_ContentTypes_And_Verify_Structure() { @@ -74,12 +77,11 @@ namespace Umbraco.Tests.Services.Importing var dataTypeElement = xml.Descendants("DataTypes").First(); var templateElement = xml.Descendants("Templates").First(); var docTypeElement = xml.Descendants("DocumentTypes").First(); - var packagingService = ServiceContext.PackagingService; // Act - var dataTypes = packagingService.ImportDataTypeDefinitions(dataTypeElement); - var templates = packagingService.ImportTemplates(templateElement); - var contentTypes = packagingService.ImportContentTypes(docTypeElement); + var dataTypes = PackagingService.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); + var templates = PackagingService.ImportTemplates(templateElement.Elements("Template").ToList(), 0); + var contentTypes = PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); var numberOfTemplates = (from doc in templateElement.Elements("Template") select doc).Count(); var numberOfDocTypes = (from doc in docTypeElement.Elements("DocumentType") select doc).Count(); @@ -122,12 +124,11 @@ namespace Umbraco.Tests.Services.Importing var dataTypeElement = xml.Descendants("DataTypes").First(); var templateElement = xml.Descendants("Templates").First(); var docTypeElement = xml.Descendants("DocumentTypes").First(); - var packagingService = ServiceContext.PackagingService; // Act - var dataTypes = packagingService.ImportDataTypeDefinitions(dataTypeElement); - var templates = packagingService.ImportTemplates(templateElement); - var contentTypes = packagingService.ImportContentTypes(docTypeElement); + var dataTypes = PackagingService.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); + var templates = PackagingService.ImportTemplates(templateElement.Elements("Template").ToList(), 0); + var contentTypes = PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); // Assert var mRBasePage = contentTypes.First(x => x.Alias == "MRBasePage"); @@ -148,12 +149,11 @@ namespace Umbraco.Tests.Services.Importing var dataTypeElement = xml.Descendants("DataTypes").First(); var templateElement = xml.Descendants("Templates").First(); var docTypeElement = xml.Descendants("DocumentTypes").First(); - var packagingService = ServiceContext.PackagingService; // Act - var dataTypes = packagingService.ImportDataTypeDefinitions(dataTypeElement); - var templates = packagingService.ImportTemplates(templateElement); - var contentTypes = packagingService.ImportContentTypes(docTypeElement); + var dataTypes = PackagingService.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); + var templates = PackagingService.ImportTemplates(templateElement.Elements("Template").ToList(), 0); + var contentTypes = PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); var numberOfDocTypes = (from doc in docTypeElement.Elements("DocumentType") select doc).Count(); @@ -186,11 +186,11 @@ namespace Umbraco.Tests.Services.Importing string strXml = ImportResources.StandardMvc_Package; var xml = XElement.Parse(strXml); var element = xml.Descendants("Templates").First(); - var packagingService = ServiceContext.PackagingService; + var init = ServiceContext.FileService.GetTemplates().Count(); // Act - var templates = packagingService.ImportTemplates(element); + var templates = PackagingService.ImportTemplates(element.Elements("Template").ToList(), 0); var numberOfTemplates = (from doc in element.Elements("Template") select doc).Count(); var allTemplates = ServiceContext.FileService.GetTemplates(); @@ -209,11 +209,11 @@ namespace Umbraco.Tests.Services.Importing // Arrange string strXml = ImportResources.StandardMvc_Package; var xml = XElement.Parse(strXml); - var element = xml.Descendants("Templates").First().Element("Template"); - var packagingService = ServiceContext.PackagingService; + var element = xml.Descendants("Templates").First(); + // Act - var templates = packagingService.ImportTemplates(element); + var templates = PackagingService.ImportTemplate(element.Elements("Template").First(), 0); // Assert Assert.That(templates, Is.Not.Null); @@ -230,12 +230,12 @@ namespace Umbraco.Tests.Services.Importing var dataTypeElement = xml.Descendants("DataTypes").First(); var templateElement = xml.Descendants("Templates").First(); var docTypeElement = xml.Descendants("DocumentTypes").First(); - var packagingService = ServiceContext.PackagingService; + // Act - var dataTypeDefinitions = packagingService.ImportDataTypeDefinitions(dataTypeElement); - var templates = packagingService.ImportTemplates(templateElement); - var contentTypes = packagingService.ImportContentTypes(docTypeElement); + var dataTypeDefinitions = PackagingService.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); + var templates = PackagingService.ImportTemplates(templateElement.Elements("Template").ToList(), 0); + var contentTypes = PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); var numberOfDocTypes = (from doc in docTypeElement.Elements("DocumentType") select doc).Count(); // Assert @@ -268,13 +268,13 @@ namespace Umbraco.Tests.Services.Importing var docTypeElement = xml.Descendants("DocumentTypes").First(); // Act - var dataTypeDefinitions = ServiceContext.PackagingService.ImportDataTypeDefinitions(dataTypeElement); - var templates = ServiceContext.PackagingService.ImportTemplates(templateElement); - var contentTypes = ServiceContext.PackagingService.ImportContentTypes(docTypeElement); + var dataTypeDefinitions = PackagingService.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); + var templates = PackagingService.ImportTemplates(templateElement.Elements("Template").ToList(), 0); + var contentTypes = PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); var numberOfDocTypes = (from doc in docTypeElement.Elements("DocumentType") select doc).Count(); //Assert - Re-Import contenttypes doesn't throw - Assert.DoesNotThrow(() => ServiceContext.PackagingService.ImportContentTypes(docTypeElement)); + Assert.DoesNotThrow(() => PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0)); Assert.That(contentTypes.Count(), Is.EqualTo(numberOfDocTypes)); Assert.That(dataTypeDefinitions, Is.Not.Null); Assert.That(dataTypeDefinitions.Any(), Is.True); @@ -292,13 +292,13 @@ namespace Umbraco.Tests.Services.Importing var docTypeElement = xml.Descendants("DocumentTypes").First(); // Act - var dataTypeDefinitions = ServiceContext.PackagingService.ImportDataTypeDefinitions(dataTypeElement); - var templates = ServiceContext.PackagingService.ImportTemplates(templateElement); - var contentTypes = ServiceContext.PackagingService.ImportContentTypes(docTypeElement); + var dataTypeDefinitions = PackagingService.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); + var templates = PackagingService.ImportTemplates(templateElement.Elements("Template").ToList(), 0); + var contentTypes = PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); var numberOfDocTypes = (from doc in docTypeElement.Elements("DocumentType") select doc).Count(); //Assert - Re-Import contenttypes doesn't throw - Assert.DoesNotThrow(() => ServiceContext.PackagingService.ImportContentTypes(docTypeElement)); + Assert.DoesNotThrow(() => PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0)); Assert.That(contentTypes.Count(), Is.EqualTo(numberOfDocTypes)); Assert.That(dataTypeDefinitions, Is.Not.Null); Assert.That(dataTypeDefinitions.Any(), Is.True); @@ -314,12 +314,13 @@ namespace Umbraco.Tests.Services.Importing var dataTypeElement = xml.Descendants("DataTypes").First(); var docTypesElement = xml.Descendants("DocumentTypes").First(); var element = xml.Descendants("DocumentSet").First(); - var packagingService = ServiceContext.PackagingService; + // Act - var dataTypeDefinitions = packagingService.ImportDataTypeDefinitions(dataTypeElement); - var contentTypes = packagingService.ImportContentTypes(docTypesElement); - var contents = packagingService.ImportContent(element); + var dataTypeDefinitions = PackagingService.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); + var contentTypes = PackagingService.ImportDocumentTypes(docTypesElement.Elements("DocumentType"), 0); + var importedContentTypes = contentTypes.ToDictionary(x => x.Alias, x => x); + var contents = PackagingService.ImportContent(element, -1, importedContentTypes, 0); var numberOfDocs = (from doc in element.Descendants() where (string) doc.Attribute("isDoc") == "" select doc).Count(); @@ -347,12 +348,13 @@ namespace Umbraco.Tests.Services.Importing var dataTypeElement = xml.Descendants("DataTypes").First(); var docTypesElement = xml.Descendants("DocumentTypes").First(); var element = xml.Descendants("DocumentSet").First(); - var packagingService = ServiceContext.PackagingService; + // Act - var dataTypeDefinitions = packagingService.ImportDataTypeDefinitions(dataTypeElement); - var contentTypes = packagingService.ImportContentTypes(docTypesElement); - var contents = packagingService.ImportContent(element); + var dataTypeDefinitions = PackagingService.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); + var contentTypes = PackagingService.ImportDocumentTypes(docTypesElement.Elements("DocumentType"), 0); + var importedContentTypes = contentTypes.ToDictionary(x => x.Alias, x => x); + var contents = PackagingService.ImportContent(element, -1, importedContentTypes, 0); var numberOfDocs = (from doc in element.Descendants() where (string)doc.Attribute("isDoc") == "" select doc).Count(); @@ -382,10 +384,10 @@ namespace Umbraco.Tests.Services.Importing string strXml = ImportResources.XsltSearch_Package; var xml = XElement.Parse(strXml); var templateElement = xml.Descendants("Templates").First(); - var packagingService = ServiceContext.PackagingService; + // Act - var templates = packagingService.ImportTemplates(templateElement); + var templates = PackagingService.ImportTemplates(templateElement.Elements("Template").ToList(), 0); var numberOfTemplates = (from doc in templateElement.Elements("Template") select doc).Count(); // Assert @@ -399,10 +401,10 @@ namespace Umbraco.Tests.Services.Importing // Arrange string strXml = ImportResources.SingleDocType; var docTypeElement = XElement.Parse(strXml); - var packagingService = ServiceContext.PackagingService; + // Act - var contentTypes = packagingService.ImportContentTypes(docTypeElement); + var contentTypes = PackagingService.ImportDocumentType(docTypeElement, 0); // Assert Assert.That(contentTypes.Any(), Is.True); @@ -416,11 +418,11 @@ namespace Umbraco.Tests.Services.Importing // Arrange string strXml = ImportResources.SingleDocType; var docTypeElement = XElement.Parse(strXml); - var packagingService = ServiceContext.PackagingService; + var serializer = Factory.GetInstance(); // Act - var contentTypes = packagingService.ImportContentTypes(docTypeElement); + var contentTypes = PackagingService.ImportDocumentType(docTypeElement, 0); var contentType = contentTypes.FirstOrDefault(); var element = serializer.Serialize(contentType); @@ -442,8 +444,8 @@ namespace Umbraco.Tests.Services.Importing var docTypeElement = XElement.Parse(strXml); // Act - var contentTypes = ServiceContext.PackagingService.ImportContentTypes(docTypeElement); - var contentTypesUpdated = ServiceContext.PackagingService.ImportContentTypes(docTypeElement); + var contentTypes = PackagingService.ImportDocumentType(docTypeElement, 0); + var contentTypesUpdated = PackagingService.ImportDocumentType(docTypeElement, 0); // Assert Assert.That(contentTypes.Any(), Is.True); @@ -465,7 +467,7 @@ namespace Umbraco.Tests.Services.Importing var templateElement = newPackageXml.Descendants("Templates").First(); var templateElementUpdated = updatedPackageXml.Descendants("Templates").First(); - var packagingService = ServiceContext.PackagingService; + var fileService = ServiceContext.FileService; // kill default test data @@ -473,8 +475,8 @@ namespace Umbraco.Tests.Services.Importing // Act var numberOfTemplates = (from doc in templateElement.Elements("Template") select doc).Count(); - var templates = packagingService.ImportTemplates(templateElement); - var templatesAfterUpdate = packagingService.ImportTemplates(templateElementUpdated); + var templates = PackagingService.ImportTemplates(templateElement.Elements("Template").ToList(), 0); + var templatesAfterUpdate = PackagingService.ImportTemplates(templateElementUpdated.Elements("Template").ToList(), 0); var allTemplates = fileService.GetTemplates(); // Assert @@ -500,7 +502,7 @@ namespace Umbraco.Tests.Services.Importing AddLanguages(); // Act - ServiceContext.PackagingService.ImportDictionaryItems(dictionaryItemsElement); + PackagingService.ImportDictionaryItems(dictionaryItemsElement.Elements("DictionaryItem"), 0); // Assert AssertDictionaryItem("Parent", expectedEnglishParentValue, "en-GB"); @@ -522,7 +524,7 @@ namespace Umbraco.Tests.Services.Importing AddLanguages(); // Act - var dictionaryItems = ServiceContext.PackagingService.ImportDictionaryItems(dictionaryItemsElement); + var dictionaryItems = PackagingService.ImportDictionaryItems(dictionaryItemsElement.Elements("DictionaryItem"), 0); // Assert Assert.That(ServiceContext.LocalizationService.DictionaryItemExists(parentKey), "DictionaryItem parentKey does not exist"); @@ -551,7 +553,7 @@ namespace Umbraco.Tests.Services.Importing AddExistingEnglishAndNorwegianParentDictionaryItem(expectedEnglishParentValue, expectedNorwegianParentValue); // Act - ServiceContext.PackagingService.ImportDictionaryItems(dictionaryItemsElement); + PackagingService.ImportDictionaryItems(dictionaryItemsElement.Elements("DictionaryItem"), 0); // Assert AssertDictionaryItem("Parent", expectedEnglishParentValue, "en-GB"); @@ -576,7 +578,7 @@ namespace Umbraco.Tests.Services.Importing AddExistingEnglishParentDictionaryItem(expectedEnglishParentValue); // Act - ServiceContext.PackagingService.ImportDictionaryItems(dictionaryItemsElement); + PackagingService.ImportDictionaryItems(dictionaryItemsElement.Elements("DictionaryItem"), 0); // Assert AssertDictionaryItem("Parent", expectedEnglishParentValue, "en-GB"); @@ -593,7 +595,7 @@ namespace Umbraco.Tests.Services.Importing var LanguageItemsElement = newPackageXml.Elements("Languages").First(); // Act - var languages = ServiceContext.PackagingService.ImportLanguages(LanguageItemsElement); + var languages = PackagingService.ImportLanguages(LanguageItemsElement.Elements("Language"), 0); var allLanguages = ServiceContext.LocalizationService.GetAllLanguages(); // Assert @@ -611,10 +613,10 @@ namespace Umbraco.Tests.Services.Importing string strXml = ImportResources.uBlogsy_Package; var xml = XElement.Parse(strXml); var macrosElement = xml.Descendants("Macros").First(); - var packagingService = ServiceContext.PackagingService; + // Act - var macros = packagingService.ImportMacros(macrosElement).ToList(); + var macros = PackagingService.ImportMacros(macrosElement.Elements("macro"), 0).ToList(); // Assert Assert.That(macros.Any(), Is.True); @@ -633,10 +635,10 @@ namespace Umbraco.Tests.Services.Importing string strXml = ImportResources.XsltSearch_Package; var xml = XElement.Parse(strXml); var macrosElement = xml.Descendants("Macros").First(); - var packagingService = ServiceContext.PackagingService; + // Act - var macros = packagingService.ImportMacros(macrosElement).ToList(); + var macros = PackagingService.ImportMacros(macrosElement.Elements("macro"), 0).ToList(); // Assert Assert.That(macros.Any(), Is.True); @@ -657,11 +659,11 @@ namespace Umbraco.Tests.Services.Importing var xml = XElement.Parse(strXml); var templateElement = xml.Descendants("Templates").First(); var docTypeElement = xml.Descendants("DocumentTypes").First(); - var packagingService = ServiceContext.PackagingService; + // Act - var templates = packagingService.ImportTemplates(templateElement); - var contentTypes = packagingService.ImportContentTypes(docTypeElement); + var templates = PackagingService.ImportTemplates(templateElement.Elements("Template").ToList(), 0); + var contentTypes = PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); var numberOfDocTypes = (from doc in docTypeElement.Elements("DocumentType") select doc).Count(); // Assert @@ -685,10 +687,10 @@ namespace Umbraco.Tests.Services.Importing string strXml = ImportResources.CompositionsTestPackage_Random; var xml = XElement.Parse(strXml); var docTypeElement = xml.Descendants("DocumentTypes").First(); - var packagingService = ServiceContext.PackagingService; + // Act - var contentTypes = packagingService.ImportContentTypes(docTypeElement); + var contentTypes = PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); var numberOfDocTypes = (from doc in docTypeElement.Elements("DocumentType") select doc).Count(); // Assert diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects.cs b/src/Umbraco.Tests/TestHelpers/TestObjects.cs index f48b72daf9..412a492376 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects.cs @@ -22,6 +22,7 @@ using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; using Umbraco.Core.Strings; +using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Web.Services; namespace Umbraco.Tests.TestHelpers @@ -78,9 +79,11 @@ namespace Umbraco.Tests.TestHelpers /// A cache. /// A logger. /// + /// /// An event messages factory. /// Some url segment providers. - /// A container. + /// + /// A container. /// /// A ServiceContext. /// Should be used sparingly for integration tests only - for unit tests @@ -168,12 +171,22 @@ namespace Umbraco.Tests.TestHelpers GetRepo(c))); var macroService = GetLazyService(factory, c => new MacroService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c))); - var packagingService = GetLazyService(factory, c => new PackagingService( - logger, contentService.Value, contentTypeService.Value, macroService.Value, dataTypeService.Value, fileService.Value, localizationService.Value, entityService.Value, auditService.Value, new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())), - createdPackages: new PackagesRepository(contentService.Value, contentTypeService.Value, dataTypeService.Value, fileService.Value, macroService.Value, localizationService.Value, - new EntityXmlSerializer(contentService.Value, mediaService.Value, dataTypeService.Value, userService.Value, localizationService.Value, contentTypeService.Value, urlSegmentProviders), logger, "createdPackages.config"), - installedPackages: new PackagesRepository(contentService.Value, contentTypeService.Value, dataTypeService.Value, fileService.Value, macroService.Value, localizationService.Value, - new EntityXmlSerializer(contentService.Value, mediaService.Value, dataTypeService.Value, userService.Value, localizationService.Value, contentTypeService.Value, urlSegmentProviders), logger, "installedPackages.config"))); + var packagingService = GetLazyService(factory, c => + { + var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); + var compiledPackageXmlParser = new CompiledPackageXmlParser(new ConflictingPackageData(macroService.Value, fileService.Value)); + return new PackagingService( + auditService.Value, + new PackagesRepository(contentService.Value, contentTypeService.Value, dataTypeService.Value, fileService.Value, macroService.Value, localizationService.Value, + new EntityXmlSerializer(contentService.Value, mediaService.Value, dataTypeService.Value, userService.Value, localizationService.Value, contentTypeService.Value, urlSegmentProviders), logger, "createdPackages.config"), + new PackagesRepository(contentService.Value, contentTypeService.Value, dataTypeService.Value, fileService.Value, macroService.Value, localizationService.Value, + new EntityXmlSerializer(contentService.Value, mediaService.Value, dataTypeService.Value, userService.Value, localizationService.Value, contentTypeService.Value, urlSegmentProviders), logger, "installedPackages.config"), + new PackageInstallation( + new PackageDataInstallation(logger, fileService.Value, macroService.Value, localizationService.Value, dataTypeService.Value, entityService.Value, contentTypeService.Value, contentService.Value, propertyEditorCollection), + new PackageFileInstallation(compiledPackageXmlParser, new ProfilingLogger(logger, new TestProfiler())), + compiledPackageXmlParser, Mock.Of(), + "", null, null)); + }); var relationService = GetLazyService(factory, c => new RelationService(scopeProvider, logger, eventMessagesFactory, entityService.Value, GetRepo(c), GetRepo(c))); var treeService = GetLazyService(factory, c => new ApplicationTreeService(logger, cache, typeLoader)); var tagService = GetLazyService(factory, c => new TagService(scopeProvider, logger, eventMessagesFactory, GetRepo(c))); diff --git a/src/Umbraco.Web/Composing/Current.cs b/src/Umbraco.Web/Composing/Current.cs index daf152c29b..860d3ec88e 100644 --- a/src/Umbraco.Web/Composing/Current.cs +++ b/src/Umbraco.Web/Composing/Current.cs @@ -216,7 +216,7 @@ namespace Umbraco.Web.Composing internal static ManifestValueValidatorCollection ManifestValidators => CoreCurrent.ManifestValidators; - internal static PackageActionRunner PackageActionRunner => CoreCurrent.PackageActionRunner; + internal static IPackageActionRunner PackageActionRunner => CoreCurrent.PackageActionRunner; internal static PackageActionCollection PackageActions => CoreCurrent.PackageActions; diff --git a/src/Umbraco.Web/Editors/ContentTypeController.cs b/src/Umbraco.Web/Editors/ContentTypeController.cs index 0d56b34617..97f7ff3589 100644 --- a/src/Umbraco.Web/Editors/ContentTypeController.cs +++ b/src/Umbraco.Web/Editors/ContentTypeController.cs @@ -17,7 +17,9 @@ using Umbraco.Core.Dictionary; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Packaging; using Umbraco.Core.Persistence; +using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; using Umbraco.Web.Composing; @@ -46,10 +48,19 @@ namespace Umbraco.Web.Editors public class ContentTypeController : ContentTypeControllerBase { private readonly IEntityXmlSerializer _serializer; + private readonly PropertyEditorCollection _propertyEditors; - public ContentTypeController(IEntityXmlSerializer serializer, ICultureDictionaryFactory cultureDictionaryFactory, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, CacheHelper applicationCache, IProfilingLogger logger, IRuntimeState runtimeState) : base(cultureDictionaryFactory, globalSettings, umbracoContextAccessor, sqlContext, services, applicationCache, logger, runtimeState) + public ContentTypeController(IEntityXmlSerializer serializer, + ICultureDictionaryFactory cultureDictionaryFactory, + IGlobalSettings globalSettings, + IUmbracoContextAccessor umbracoContextAccessor, + ISqlContext sqlContext, PropertyEditorCollection propertyEditors, + ServiceContext services, CacheHelper applicationCache, + IProfilingLogger logger, IRuntimeState runtimeState) + : base(cultureDictionaryFactory, globalSettings, umbracoContextAccessor, sqlContext, services, applicationCache, logger, runtimeState) { _serializer = serializer; + _propertyEditors = propertyEditors; } public int GetCount() @@ -162,8 +173,8 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(HttpStatusCode.NotFound); } - var configuration = Current.Services.DataTypeService.GetDataType(id).Configuration; - var editor = Current.PropertyEditors[dataTypeDiff.EditorAlias]; + var configuration = Services.DataTypeService.GetDataType(id).Configuration; + var editor = _propertyEditors[dataTypeDiff.EditorAlias]; return new ContentPropertyDisplay() { @@ -488,14 +499,16 @@ namespace Umbraco.Web.Editors { return Request.CreateResponse(HttpStatusCode.NotFound); } + + var dataInstaller = new PackageDataInstallation(Logger, Services.FileService, Services.MacroService, Services.LocalizationService, + Services.DataTypeService, Services.EntityService, Services.ContentTypeService, Services.ContentService, _propertyEditors); - var xd = new XmlDocument(); - xd.XmlResolver = null; + var xd = new XmlDocument {XmlResolver = null}; xd.Load(filePath); - var userId = Security.GetUserId(); + var userId = Security.GetUserId().ResultOr(0); var element = XElement.Parse(xd.InnerXml); - Current.Services.PackagingService.ImportContentTypes(element, userId); + dataInstaller.ImportDocumentType(element, userId); // Try to clean up the temporary file. try @@ -504,7 +517,7 @@ namespace Umbraco.Web.Editors } catch (Exception ex) { - Current.Logger.Error(ex, "Error cleaning up temporary udt file in App_Data: {File}", filePath); + Logger.Error(ex, "Error cleaning up temporary udt file in App_Data: {File}", filePath); } return Request.CreateResponse(HttpStatusCode.OK); diff --git a/src/Umbraco.Web/Editors/PackageInstallController.cs b/src/Umbraco.Web/Editors/PackageInstallController.cs index 24cc999c6b..bfaec3f82c 100644 --- a/src/Umbraco.Web/Editors/PackageInstallController.cs +++ b/src/Umbraco.Web/Editors/PackageInstallController.cs @@ -42,11 +42,11 @@ namespace Umbraco.Web.Editors [UmbracoApplicationAuthorize(Core.Constants.Applications.Packages)] public class PackageInstallController : UmbracoAuthorizedJsonController { - private readonly PackageActionRunner _packageActionRunner; + private readonly IPackageActionRunner _packageActionRunner; public PackageInstallController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, CacheHelper applicationCache, - IProfilingLogger logger, IRuntimeState runtimeState, PackageActionRunner packageActionRunner) + IProfilingLogger logger, IRuntimeState runtimeState, IPackageActionRunner packageActionRunner) : base(globalSettings, umbracoContextAccessor, sqlContext, services, applicationCache, logger, runtimeState) { _packageActionRunner = packageActionRunner; @@ -259,6 +259,7 @@ namespace Umbraco.Web.Editors }; // trigger the UninstalledPackage event + // fixme: This all needs to be part of the service! PackagingService.OnUninstalledPackage(new UninstallPackageEventArgs(summary, package, false)); } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 64e17af221..89260e59f6 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -1136,7 +1136,6 @@ - diff --git a/src/Umbraco.Web/_Legacy/Packager/Installer.cs b/src/Umbraco.Web/_Legacy/Packager/Installer.cs deleted file mode 100644 index 89dff406b0..0000000000 --- a/src/Umbraco.Web/_Legacy/Packager/Installer.cs +++ /dev/null @@ -1,688 +0,0 @@ -//using System; -//using System.Collections.Generic; -//using System.Diagnostics; -//using System.Globalization; -//using System.IO; -//using System.Linq; -//using System.Net; -//using System.Xml; -//using System.Xml.Linq; -//using System.Xml.XPath; -//using ICSharpCode.SharpZipLib.Zip; -//using Umbraco.Core; -//using Umbraco.Core.Composing; -//using Umbraco.Core.Events; -//using Umbraco.Core.IO; -//using Umbraco.Core.Logging; -//using Umbraco.Core.Models; -//using Umbraco.Core.Models.Packaging; -//using Umbraco.Core.Packaging; -//using Umbraco.Core.Services.Implement; -//using File = System.IO.File; - -//namespace Umbraco.Web._Legacy.Packager -//{ -// /// -// /// The packager is a component which enables sharing of both data and functionality components between different umbraco installations. -// /// -// /// The output is a .umb (a zip compressed file) which contains the exported documents/medias/macroes/documentTypes (etc.) -// /// in a Xml document, along with the physical files used (images/usercontrols/xsl documents etc.) -// /// -// /// Partly implemented, import of packages is done, the export is *under construction*. -// /// -// /// -// /// Ruben Verborgh 31/12/2007: I had to change some code, I marked my changes with "DATALAYER". -// /// Reason: @@IDENTITY can't be used with the new datalayer. -// /// I wasn't able to test the code, since I'm not aware how the code functions. -// /// -// public class Installer -// { -// private const string PackageServer = "packages.umbraco.org"; - -// private readonly Dictionary _conflictingMacroAliases = new Dictionary(); -// private readonly Dictionary _conflictingTemplateAliases = new Dictionary(); -// private readonly Dictionary _conflictingStyleSheetNames = new Dictionary(); - -// private readonly int _currentUserId = -1; -// private static WebClient _webClient; - - -// public string Name { get; private set; } -// public string Version { get; private set; } -// public string Url { get; private set; } -// public string License { get; private set; } -// public string LicenseUrl { get; private set; } -// public string Author { get; private set; } -// public string AuthorUrl { get; private set; } -// public string ReadMe { get; private set; } -// public string Control { get; private set; } - -// public bool ContainsMacroConflict { get; private set; } -// public IDictionary ConflictingMacroAliases => _conflictingMacroAliases; - -// public bool ContainsUnsecureFiles { get; private set; } -// public List UnsecureFiles { get; } = new List(); - -// public bool ContainsTemplateConflicts { get; private set; } -// public IDictionary ConflictingTemplateAliases => _conflictingTemplateAliases; - -// public bool ContainsStyleSheeConflicts { get; private set; } -// public IDictionary ConflictingStyleSheetNames => _conflictingStyleSheetNames; - -// public int RequirementsMajor { get; private set; } -// public int RequirementsMinor { get; private set; } -// public int RequirementsPatch { get; private set; } - -// public RequirementsType RequirementsType { get; private set; } - -// public string IconUrl { get; private set; } - -// /// -// /// The xml of the compiled package -// /// -// public XDocument Config { get; private set; } - -// /// -// /// Constructor -// /// -// public Installer() -// { -// Initialize(); -// } - -// public Installer(int currentUserId) -// { -// Initialize(); -// _currentUserId = currentUserId; -// } - -// private void Initialize() -// { -// ContainsTemplateConflicts = false; -// ContainsUnsecureFiles = false; -// ContainsMacroConflict = false; -// ContainsStyleSheeConflicts = false; -// } - - - -// /// -// /// Constructor -// /// -// /// The name of the package -// /// The version of the package -// /// The url to a descriptionpage -// /// The license under which the package is released (preferably GPL ;)) -// /// The url to a licensedescription -// /// The original author of the package -// /// The url to the Authors website -// /// Umbraco version major -// /// Umbraco version minor -// /// Umbraco version patch -// /// The readme text -// /// The name of the usercontrol used to configure the package after install -// /// -// /// -// public Installer(string name, string version, string url, string license, string licenseUrl, string author, string authorUrl, int requirementsMajor, int requirementsMinor, int requirementsPatch, string readme, string control, RequirementsType requirementsType, string iconUrl) -// { -// ContainsTemplateConflicts = false; -// ContainsUnsecureFiles = false; -// ContainsMacroConflict = false; -// ContainsStyleSheeConflicts = false; -// this.Name = name; -// this.Version = version; -// this.Url = url; -// this.License = license; -// this.LicenseUrl = licenseUrl; -// this.RequirementsMajor = requirementsMajor; -// this.RequirementsMinor = requirementsMinor; -// this.RequirementsPatch = requirementsPatch; -// this.RequirementsType = requirementsType; -// this.Author = author; -// this.AuthorUrl = authorUrl; -// this.IconUrl = iconUrl; -// ReadMe = readme; -// this.Control = control; -// } - -// #region Public Methods - -// /// -// /// Imports the specified package -// /// -// /// Filename of the umbracopackage -// /// true if the input file should be deleted after import -// /// -// public string Import(string inputFile, bool deleteFile) -// { -// using (Current.ProfilingLogger.DebugDuration( -// $"Importing package file {inputFile}.", -// $"Package file {inputFile} imported.")) -// { -// var tempDir = ""; -// if (File.Exists(IOHelper.MapPath(SystemDirectories.Data + "/" + inputFile))) -// { -// var fi = new FileInfo(IOHelper.MapPath(SystemDirectories.Data + "/" + inputFile)); -// // Check if the file is a valid package -// if (fi.Extension.ToLower() == ".umb") -// { -// try -// { -// tempDir = UnPack(fi.FullName, deleteFile); -// LoadConfig(tempDir); -// } -// catch (Exception ex) -// { -// Current.Logger.Error(ex, "Error importing file {FileName}", fi.FullName); -// throw; -// } -// } -// else -// throw new Exception("Error - file isn't a package (doesn't have a .umb extension). Check if the file automatically got named '.zip' upon download."); -// } -// else -// throw new Exception("Error - file not found. Could find file named '" + IOHelper.MapPath(SystemDirectories.Data + Path.DirectorySeparatorChar + inputFile) + "'"); -// return tempDir; -// } - -// } - -// /// -// /// Imports the specified package -// /// -// /// Filename of the umbracopackage -// /// -// public string Import(string inputFile) -// { -// return Import(inputFile, true); -// } - -// public int CreateManifest(Guid guid) -// { -// //This is the new improved install rutine, which chops up the process into 3 steps, creating the manifest, moving files, and finally handling umb objects - -// var parser = new CompiledPackageXmlParser(); -// var def = parser.ToCompiledPackage(Config); - -// //create a new entry in the installedPackages.config -// var installedPackage = new PackageDefinition -// { -// Author = def.Author, -// AuthorUrl = def.AuthorUrl, -// Control = def.Control, -// IconUrl = def.IconUrl, -// License = def.License, -// LicenseUrl = def.LicenseUrl, -// Name = def.Name, -// Readme = def.Readme, -// UmbracoVersion = def.UmbracoVersion, -// Url = def.Url, -// Version = def.Version, -// PackageId = guid -// }; - -// if (!Current.Services.PackagingService.SaveInstalledPackage(installedPackage)) -// throw new InvalidOperationException("Could not save package definition"); - -// return installedPackage.Id; -// } - -// public void InstallFiles(int packageId, string tempDir) -// { -// var parser = new CompiledPackageXmlParser(); - -// using (Current.ProfilingLogger.DebugDuration( -// "Installing package files for package id " + packageId + " into temp folder " + tempDir, -// "Package file installation complete for package id " + packageId)) -// { -// //retrieve the manifest to continue installation -// var insPack = Current.Services.PackagingService.GetInstalledPackageById(packageId); - -// //TODO: Depending on some files, some files should be installed differently. -// //i.e. if stylsheets should probably be installed via business logic, media items should probably use the media IFileSystem! - -// // Move files -// //string virtualBasePath = System.Web.HttpContext.Current.Request.ApplicationPath; -// string basePath = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath; - -// var def = parser.ToCompiledPackage(Config); - -// try -// { -// foreach (var f in def.Files) -// { -// var destPath = GetFileName(basePath, f.OriginalPath); -// var sourceFile = GetFileName(tempDir, f.UniqueFileName); -// var destFile = GetFileName(destPath, f.OriginalName); - -// // Create the destination directory if it doesn't exist -// if (Directory.Exists(destPath) == false) -// Directory.CreateDirectory(destPath); -// //If a file with this name exists, delete it -// else if (File.Exists(destFile)) -// File.Delete(destFile); - -// // Copy the file -// // SJ: Note - this used to do a move but some packages included the same file to be -// // copied to multiple locations like so: -// // -// // -// // my-icon.png -// // /umbraco/Images/ -// // my-icon.png -// // -// // -// // my-icon.png -// // /App_Plugins/MyPlugin/Images -// // my-icon.png -// // -// // -// // Since this file unzips as a flat list of files, moving the file the first time means -// // that when you try to do that a second time, it would result in a FileNotFoundException -// File.Copy(sourceFile, destFile); - -// //PPH log file install -// insPack.Files.Add(f.OriginalPath.EnsureEndsWith('/') + f.OriginalName); - -// } - -// // Once we're done copying, remove all the files -// foreach (var f in def.Files) -// { -// var sourceFile = GetFileName(tempDir, f.UniqueFileName); -// if (File.Exists(sourceFile)) -// File.Delete(sourceFile); -// } -// } -// catch (Exception ex) -// { -// Current.Logger.Error(ex, "Package install error"); -// throw; -// } - -// // log that a user has install files -// if (_currentUserId > -1) -// { -// Current.Services.AuditService.Add(AuditType.PackagerInstall, -// _currentUserId, -// -1, "Package", string.Format("Package '{0}' installed. Package guid: {1}", insPack.Name, insPack.PackageId)); -// } - -// Current.Services.PackagingService.SaveInstalledPackage(insPack); -// } -// } - -// public void InstallBusinessLogic(int packageId, string tempDir) -// { -// using (Current.ProfilingLogger.DebugDuration( -// "Installing business logic for package id " + packageId + " into temp folder " + tempDir, -// "Package business logic installation complete for package id " + packageId)) -// { -// PackageDefinition insPack; -// try -// { -// //retrieve the manifest to continue installation -// insPack = Current.Services.PackagingService.GetInstalledPackageById(packageId); -// //bool saveNeeded = false; - -// // Get current user, with a fallback -// var currentUser = Current.Services.UserService.GetUserById(Constants.Security.SuperUserId); - -// //TODO: Get rid of this entire class! Until then all packages will be installed by the admin user - -// var rootElement = Config.Root; -// var packagingService = Current.Services.PackagingService; - -// //Perhaps it would have been a good idea to put the following into methods eh?!? - -// #region DataTypes -// var dataTypeElement = rootElement.Descendants("DataTypes").FirstOrDefault(); -// if (dataTypeElement != null) -// { -// var dataTypeDefinitions = packagingService.ImportDataTypeDefinitions(dataTypeElement, currentUser.Id); -// foreach (var dataTypeDefinition in dataTypeDefinitions) -// { -// insPack.DataTypes.Add(dataTypeDefinition.Id.ToString(CultureInfo.InvariantCulture)); -// } -// } -// #endregion - -// #region Languages -// var languageItemsElement = rootElement.Descendants("Languages").FirstOrDefault(); -// if (languageItemsElement != null) -// { -// var insertedLanguages = packagingService.ImportLanguages(languageItemsElement); -// foreach(var x in insertedLanguages.Select(l => l.Id.ToString(CultureInfo.InvariantCulture))) -// insPack.Languages.Add(x); -// } - -// #endregion - -// #region Dictionary items -// var dictionaryItemsElement = rootElement.Descendants("DictionaryItems").FirstOrDefault(); -// if (dictionaryItemsElement != null) -// { -// var insertedDictionaryItems = packagingService.ImportDictionaryItems(dictionaryItemsElement); -// foreach (var x in insertedDictionaryItems.Select(d => d.Id.ToString(CultureInfo.InvariantCulture))) -// insPack.DictionaryItems.Add(x); -// } -// #endregion - -// #region Macros -// var macroItemsElement = rootElement.Descendants("Macros").FirstOrDefault(); -// if (macroItemsElement != null) -// { -// var insertedMacros = packagingService.ImportMacros(macroItemsElement); -// foreach (var x in insertedMacros.Select(m => m.Id.ToString(CultureInfo.InvariantCulture))) -// insPack.Macros.Add(x); - -// } -// #endregion - -// #region Templates -// var templateElement = rootElement.Descendants("Templates").FirstOrDefault(); -// if (templateElement != null) -// { -// var templates = packagingService.ImportTemplates(templateElement, currentUser.Id); -// foreach (var template in templates) -// { -// insPack.Templates.Add(template.Id.ToString(CultureInfo.InvariantCulture)); -// } -// } -// #endregion - -// #region DocumentTypes -// //Check whether the root element is a doc type rather then a complete package -// var docTypeElement = rootElement.Name.LocalName.Equals("DocumentType") || -// rootElement.Name.LocalName.Equals("DocumentTypes") -// ? rootElement -// : rootElement.Descendants("DocumentTypes").FirstOrDefault(); - -// if (docTypeElement != null) -// { -// var contentTypes = packagingService.ImportContentTypes(docTypeElement, currentUser.Id); -// foreach (var contentType in contentTypes) -// { -// insPack.DocumentTypes.Add(contentType.Id.ToString(CultureInfo.InvariantCulture)); -// //saveNeeded = true; -// } -// } -// #endregion - -// #region Stylesheets -// foreach (var n in Config.Root.XPathSelectElements("Stylesheets/Stylesheet")) -// { -// string stylesheetName = n.Element("Name")?.Value; -// if (stylesheetName.IsNullOrWhiteSpace()) continue; - -// var s = Current.Services.FileService.GetStylesheetByName(stylesheetName); -// if (s == null) -// { -// var fileName = n.Element("FileName")?.Value; -// if (fileName == null) continue; -// var content = n.Element("Content")?.Value; -// if (content == null) continue; - -// s = new Stylesheet(fileName) { Content = content }; -// Current.Services.FileService.SaveStylesheet(s); -// } - -// foreach (var prop in n.XPathSelectElements("Properties/Property")) -// { -// string alias = prop.Element("Alias")?.Value; -// var sp = s.Properties.SingleOrDefault(p => p != null && p.Alias == alias); -// string name = prop.Element("Name")?.Value; -// if (sp == null) -// { -// //sp = StylesheetProperty.MakeNew( -// // name, -// // s, -// // u); - -// sp = new StylesheetProperty(name, "#" + name.ToSafeAlias(), ""); -// s.AddProperty(sp); -// } -// else -// { -// //sp.Text = name; -// //Changing the name requires removing the current property and then adding another new one -// if (sp.Name != name) -// { -// s.RemoveProperty(sp.Name); -// var newProp = new StylesheetProperty(name, sp.Alias, sp.Value); -// s.AddProperty(newProp); -// sp = newProp; -// } -// } -// sp.Alias = alias; -// sp.Value = prop.Element("Value")?.Value; -// } -// //s.saveCssToFile(); -// Current.Services.FileService.SaveStylesheet(s); - - - - -// insPack.Stylesheets.Add(s.Id.ToString(CultureInfo.InvariantCulture)); -// //saveNeeded = true; -// } - -// //if (saveNeeded) { insPack.Save(); saveNeeded = false; } -// #endregion - -// #region Documents -// var documentElement = rootElement.Descendants("DocumentSet").FirstOrDefault(); -// if (documentElement != null) -// { -// var content = packagingService.ImportContent(documentElement, -1, currentUser.Id); -// var firstContentItem = content.First(); -// insPack.ContentNodeId = firstContentItem.Id.ToString(CultureInfo.InvariantCulture); -// } -// #endregion - -// #region Package Actions -// foreach (var n in Config.Root.XPathSelectElements("Actions/Action")) -// { -// var undo = n.AttributeValue("undo"); -// if (undo == null || undo == "true") -// { -// insPack.Actions += n.ToString(); -// } - -// //Run the actions tagged only for 'install' -// var runat = n.AttributeValue("runat"); - -// if (runat != null && runat == "install") -// { -// var alias = n.AttributeValue("alias"); -// if (alias.IsNullOrWhiteSpace() == false) -// { -// Current.PackageActionRunner.RunPackageAction(insPack.Name, alias, n); -// } -// } -// } -// #endregion - -// Current.Services.PackagingService.SaveInstalledPackage(insPack); -// } -// catch (Exception ex) -// { -// Current.Logger.Error(ex, "Error installing businesslogic"); -// throw; -// } - -// OnPackageInstalled(insPack); -// } -// } - -// /// -// /// Remove the temp installation folder -// /// -// /// -// /// -// public void InstallCleanUp(int packageId, string tempDir) -// { -// if (Directory.Exists(tempDir)) -// { -// Directory.Delete(tempDir, true); -// } -// } - -// /// -// /// Reads the configuration of the package from the configuration xmldocument -// /// -// /// The folder to which the contents of the package is extracted -// public void LoadConfig(string tempDir) -// { -// Config = XDocument.Load(tempDir + Path.DirectorySeparatorChar + "package.xml"); - -// var parser = new CompiledPackageXmlParser(); -// var def = parser.ToCompiledPackage(Config); - -// Name = def.Name; -// Version = def.Version; -// Url = def.Url; -// License = def.License; -// LicenseUrl = def.LicenseUrl; - -// RequirementsMajor = def.UmbracoVersion.Major; -// RequirementsMinor = def.UmbracoVersion.Minor; -// RequirementsPatch = def.UmbracoVersion.Build; -// RequirementsType = def.UmbracoVersionRequirementsType; -// IconUrl = def.IconUrl; -// Author = def.Author; -// AuthorUrl = def.AuthorUrl; -// ReadMe = def.Readme; -// Control = def.Control; - -// var basePath = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath; -// var dllBinFiles = new List(); - -// foreach (var f in def.Files) -// { -// var badFile = false; -// var destPath = GetFileName(basePath, f.OriginalPath); -// var orgName = f.OriginalName; -// var destFile = GetFileName(destPath, orgName); - -// if (destPath.ToLower().Contains(IOHelper.DirSepChar + "app_code")) -// { -// badFile = true; -// } - -// if (destPath.ToLower().Contains(IOHelper.DirSepChar + "bin")) -// { -// badFile = true; -// } - -// if (destFile.ToLower().EndsWith(".dll")) -// { -// badFile = true; -// dllBinFiles.Add(Path.Combine(tempDir, orgName)); -// } - -// if (badFile) -// { -// ContainsUnsecureFiles = true; -// UnsecureFiles.Add(f.OriginalName); -// } -// } - - - -// //this will check for existing macros with the same alias -// //since we will not overwrite on import it's a good idea to inform the user what will be overwritten -// foreach (var n in Config.Root.XPathSelectElements("//macro")) -// { -// var alias = n.Element("alias")?.Value; -// if (!string.IsNullOrEmpty(alias)) -// { -// var m = Current.Services.MacroService.GetByAlias(alias); -// if (m != null) -// { -// ContainsMacroConflict = true; -// if (_conflictingMacroAliases.ContainsKey(m.Name) == false) -// { -// _conflictingMacroAliases.Add(m.Name, alias); -// } -// } -// } -// } - -// foreach (var n in Config.Root.XPathSelectElements("Templates/Template")) -// { -// var alias = n.Element("Alias")?.Value; -// if (!string.IsNullOrEmpty(alias)) -// { -// var t = Current.Services.FileService.GetTemplate(alias); -// if (t != null) -// { -// ContainsTemplateConflicts = true; -// if (_conflictingTemplateAliases.ContainsKey(t.Alias) == false) -// { -// _conflictingTemplateAliases.Add(t.Alias, alias); -// } -// } -// } -// } - -// foreach (var n in Config.Root.XPathSelectElements("Stylesheets/Stylesheet")) -// { -// var alias = n.Element("Name")?.Value; -// if (!string.IsNullOrEmpty(alias)) -// { -// var s = Current.Services.FileService.GetStylesheetByName(alias); -// if (s != null) -// { -// ContainsStyleSheeConflicts = true; -// if (_conflictingStyleSheetNames.ContainsKey(s.Alias) == false) -// { -// _conflictingStyleSheetNames.Add(s.Alias, alias); -// } -// } -// } -// } - - -// } - -// /// -// /// This uses the old method of fetching and only supports the packages.umbraco.org repository. -// /// -// /// -// /// -// public string Fetch(Guid Package) -// { -// // Check for package directory -// if (Directory.Exists(IOHelper.MapPath(SystemDirectories.Packages)) == false) -// Directory.CreateDirectory(IOHelper.MapPath(SystemDirectories.Packages)); - -// if (_webClient == null) -// _webClient = new WebClient(); - -// _webClient.DownloadFile( -// "http://" + PackageServer + "/fetch?package=" + Package.ToString(), -// IOHelper.MapPath(SystemDirectories.Packages + "/" + Package + ".umb")); - -// return "packages\\" + Package + ".umb"; -// } - -// #endregion - -// private void OnPackageInstalled(PackageDefinition insPack) -// { -// // getting an InstallationSummary for sending to the PackagingService.ImportedPackage event -// var fileService = Current.Services.FileService; -// var macroService = Current.Services.MacroService; -// var contentTypeService = Current.Services.ContentTypeService; -// var dataTypeService = Current.Services.DataTypeService; -// var localizationService = Current.Services.LocalizationService; - -// var installationSummary = InstallationSummary.FromPackageDefinition(insPack, contentTypeService, dataTypeService, fileService, localizationService, macroService); -// installationSummary.PackageInstalled = true; - -// var args = new ImportPackageEventArgs(installationSummary, insPack, false); -// PackagingService.OnImportedPackage(args); -// } -// } -//} From aa6342cc30d2d0d13a72881ae63dd25e4ba68c83 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 14 Jan 2019 17:56:41 +1100 Subject: [PATCH 58/93] adds test --- .../Packaging/CompiledPackageXmlParser.cs | 18 +++++++-------- .../Packaging/PackageInstallationTest.cs | 22 ++++++++++++++++++- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs index 76f206f478..0f3119f697 100644 --- a/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs +++ b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs @@ -52,7 +52,7 @@ namespace Umbraco.Core.Packaging UmbracoVersion = new Version((int)requirements.Element("major"), (int)requirements.Element("minor"), (int)requirements.Element("patch")), UmbracoVersionRequirementsType = requirements.AttributeValue("type").IsNullOrWhiteSpace() ? RequirementsType.Legacy : Enum.Parse(requirements.AttributeValue("type")), Control = package.Element("control")?.Value, - Actions = xml.Element("Actions")?.ToString(SaveOptions.None) ?? "", //take the entire outer xml value + Actions = xml.Root.Element("Actions")?.ToString(SaveOptions.None) ?? "", //take the entire outer xml value Files = xml.Root.Element("files")?.Elements("file")?.Select(x => new CompiledPackageFile { UniqueFileName = x.Element("guid")?.Value, @@ -60,14 +60,14 @@ namespace Umbraco.Core.Packaging OriginalPath = x.Element("orgPath")?.Value }).ToList() ?? new List(), - Macros = xml.Element("Macros")?.Elements("macro") ?? Enumerable.Empty(), - Templates = xml.Element("Templates")?.Elements("Template") ?? Enumerable.Empty(), - Stylesheets = xml.Element("Stylesheets")?.Elements("styleSheet") ?? Enumerable.Empty(), - DataTypes = xml.Element("DataTypes")?.Elements("DataType") ?? Enumerable.Empty(), - Languages = xml.Element("Languages")?.Elements("Language") ?? Enumerable.Empty(), - DictionaryItems = xml.Element("DictionaryItems")?.Elements("DictionaryItem") ?? Enumerable.Empty(), - DocumentTypes = xml.Element("DocumentTypes")?.Elements("DocumentType") ?? Enumerable.Empty(), - Documents = xml.Element("Documents")?.Elements("DocumentSet") ?? Enumerable.Empty(), + Macros = xml.Root.Element("Macros")?.Elements("macro") ?? Enumerable.Empty(), + Templates = xml.Root.Element("Templates")?.Elements("Template") ?? Enumerable.Empty(), + Stylesheets = xml.Root.Element("Stylesheets")?.Elements("styleSheet") ?? Enumerable.Empty(), + DataTypes = xml.Root.Element("DataTypes")?.Elements("DataType") ?? Enumerable.Empty(), + Languages = xml.Root.Element("Languages")?.Elements("Language") ?? Enumerable.Empty(), + DictionaryItems = xml.Root.Element("DictionaryItems")?.Elements("DictionaryItem") ?? Enumerable.Empty(), + DocumentTypes = xml.Root.Element("DocumentTypes")?.Elements("DocumentType") ?? Enumerable.Empty(), + Documents = xml.Root.Element("Documents")?.Elements("DocumentSet") ?? Enumerable.Empty(), }; def.Warnings = GetPreInstallWarnings(def, applicationRootFolder); diff --git a/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs b/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs index 964f0c47de..609d26aec9 100644 --- a/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs +++ b/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs @@ -87,7 +87,7 @@ namespace Umbraco.Tests.Packaging Assert.AreEqual("@tentonipete", package.Author); Assert.AreEqual("auros.co.uk", package.AuthorUrl); Assert.AreEqual("Document Type Picker datatype that enables back office user to select one or many document types.", package.Readme); - + Assert.AreEqual(1, package.DataTypes.Count()); } [Test] @@ -114,6 +114,26 @@ namespace Umbraco.Tests.Packaging Assert.AreEqual(1, result.Count); Assert.AreEqual("bin\\Auros.DocumentTypePicker.dll", result[0]); Assert.IsTrue(File.Exists(Path.Combine(IOHelper.MapPath("~/" + _testBaseFolder), result[0]))); + + //make sure the def is updated too + Assert.AreEqual(result.Count(), def.Files.Count); + } + + [Test] + public void Install_Data() + { + var package = PackageInstallation.ReadPackage(documentTypePickerUmb); + var def = PackageDefinition.FromCompiledPackage(package); + def.Id = 1; + def.PackageId = Guid.NewGuid(); + + var summary = PackageInstallation.InstallPackageData(def, package, -1); + + Assert.AreEqual(1, summary.DataTypesInstalled.Count()); + + + //make sure the def is updated too + Assert.AreEqual(summary.DataTypesInstalled.Count(), def.DataTypes.Count); } From b3585b008395e28bcdeeaae02c0a42ea5255c24e Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 14 Jan 2019 18:03:06 +1100 Subject: [PATCH 59/93] some cleanup --- .../Models/Packaging/InstallationSummary.cs | 42 +------------------ .../Models/Packaging/PackageDefinition.cs | 8 ---- .../Packaging/PackageDefinitionXmlParser.cs | 2 - .../Packaging/PackagesRepository.cs | 4 +- .../CreatedPackagesRepositoryTests.cs | 2 - 5 files changed, 2 insertions(+), 56 deletions(-) diff --git a/src/Umbraco.Core/Models/Packaging/InstallationSummary.cs b/src/Umbraco.Core/Models/Packaging/InstallationSummary.cs index b14e3d2a92..7d81f48dd2 100644 --- a/src/Umbraco.Core/Models/Packaging/InstallationSummary.cs +++ b/src/Umbraco.Core/Models/Packaging/InstallationSummary.cs @@ -2,8 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; -using Umbraco.Core.IO; -using Umbraco.Core.Services; namespace Umbraco.Core.Models.Packaging { @@ -23,45 +21,7 @@ namespace Umbraco.Core.Models.Packaging public IEnumerable ContentInstalled { get; set; } = Enumerable.Empty(); public IEnumerable Actions { get; set; } = Enumerable.Empty(); public bool PackageInstalled { get; set; } - - //public static InstallationSummary FromPackageDefinition(PackageDefinition def, IContentTypeService contentTypeService, IDataTypeService dataTypeService, IFileService fileService, ILocalizationService localizationService, IMacroService macroService) - //{ - // var macros = TryGetIntegerIds(def.Macros).Select(macroService.GetById).ToList(); - // var templates = TryGetIntegerIds(def.Templates).Select(fileService.GetTemplate).ToList(); - // var contentTypes = TryGetIntegerIds(def.DocumentTypes).Select(contentTypeService.Get).ToList(); // fixme - media types? - // var dataTypes = TryGetIntegerIds(def.DataTypes).Select(dataTypeService.GetDataType).ToList(); - // var dictionaryItems = TryGetIntegerIds(def.DictionaryItems).Select(localizationService.GetDictionaryItemById).ToList(); - // var languages = TryGetIntegerIds(def.Languages).Select(localizationService.GetLanguageById).ToList(); - - // for (var i = 0; i < def.Files.Count; i++) - // { - // var filePath = def.Files[i]; - // def.Files[i] = filePath.GetRelativePath(); - // } - - // return new InstallationSummary - // { - // ContentTypesInstalled = contentTypes, - // DataTypesInstalled = dataTypes, - // DictionaryItemsInstalled = dictionaryItems, - // FilesInstalled = def.Files, - // LanguagesInstalled = languages, - // MacrosInstalled = macros, - // MetaData = def, - // TemplatesInstalled = templates, - // }; - //} - - //private static IEnumerable TryGetIntegerIds(IEnumerable ids) - //{ - // var intIds = new List(); - // foreach (var id in ids) - // { - // if (int.TryParse(id, out var parsed)) - // intIds.Add(parsed); - // } - // return intIds; - //} + } } diff --git a/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs b/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs index 56a316ff81..de8415ddd2 100644 --- a/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs +++ b/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs @@ -34,7 +34,6 @@ namespace Umbraco.Core.Models.Packaging UmbracoVersion = compiled.UmbracoVersion, Url = compiled.Url, Version = compiled.Version, - //fixme: Is OriginalPath correct here? Files = compiled.Files.Select(x => x.OriginalPath).ToList() }; } @@ -54,13 +53,6 @@ namespace Umbraco.Core.Models.Packaging [Url] public string Url { get; set; } = string.Empty; - //fixme: remove this - /// - /// This is a generated GUID which is used to determine a temporary folder name for processing the package - /// - [DataMember(Name = "folder")] - public Guid FolderId { get; set; } - [ReadOnly(true)] [DataMember(Name = "packagePath")] public string PackagePath { get; set; } = string.Empty; diff --git a/src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs b/src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs index 9a996d201f..1f0e5ec92e 100644 --- a/src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs +++ b/src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs @@ -28,7 +28,6 @@ namespace Umbraco.Core.Packaging { Id = xml.AttributeValue("id"), Name = xml.AttributeValue("name") ?? string.Empty, - FolderId = xml.AttributeValue("folder"), PackagePath = xml.AttributeValue("packagePath") ?? string.Empty, Version = xml.AttributeValue("version") ?? string.Empty, Url = xml.AttributeValue("url") ?? string.Empty, @@ -74,7 +73,6 @@ namespace Umbraco.Core.Packaging new XAttribute("version", def.Version ?? string.Empty), new XAttribute("url", def.Url ?? string.Empty), new XAttribute("name", def.Name ?? string.Empty), - new XAttribute("folder", def.FolderId), new XAttribute("packagePath", def.PackagePath ?? string.Empty), new XAttribute("iconUrl", def.IconUrl ?? string.Empty), new XAttribute("umbVersion", def.UmbracoVersion), diff --git a/src/Umbraco.Core/Packaging/PackagesRepository.cs b/src/Umbraco.Core/Packaging/PackagesRepository.cs index 9f37f8a546..c0620820a2 100644 --- a/src/Umbraco.Core/Packaging/PackagesRepository.cs +++ b/src/Umbraco.Core/Packaging/PackagesRepository.cs @@ -131,7 +131,6 @@ namespace Umbraco.Core.Packaging var newId = maxId + 1; definition.Id = newId; definition.PackageId = definition.PackageId == default ? Guid.NewGuid() : definition.PackageId; - definition.FolderId = Guid.NewGuid(); var packageXml = _parser.ToXml(definition); packagesXml.Root.Add(packageXml); } @@ -155,13 +154,12 @@ namespace Umbraco.Core.Packaging { if (definition.Id == default) throw new ArgumentException("The package definition does not have an ID, it must be saved before being exported"); if (definition.PackageId == default) throw new ArgumentException("the package definition does not have a GUID, it must be saved before being exported"); - if (definition.FolderId == default) throw new ArgumentException("the package definition does not have a folder GUID, it must be saved before being exported"); //ensure it's valid ValidatePackage(definition); //Create a folder for building this package - var temporaryPath = IOHelper.MapPath(_tempFolderPath.EnsureEndsWith('/') + definition.FolderId); + var temporaryPath = IOHelper.MapPath(_tempFolderPath.EnsureEndsWith('/') + Guid.NewGuid()); if (Directory.Exists(temporaryPath) == false) Directory.CreateDirectory(temporaryPath); diff --git a/src/Umbraco.Tests/Packaging/CreatedPackagesRepositoryTests.cs b/src/Umbraco.Tests/Packaging/CreatedPackagesRepositoryTests.cs index e059d87524..010572abec 100644 --- a/src/Umbraco.Tests/Packaging/CreatedPackagesRepositoryTests.cs +++ b/src/Umbraco.Tests/Packaging/CreatedPackagesRepositoryTests.cs @@ -83,7 +83,6 @@ namespace Umbraco.Tests.Packaging Assert.IsTrue(result); Assert.AreEqual(1, def1.Id); Assert.AreNotEqual(default(Guid).ToString(), def1.PackageId); - Assert.AreNotEqual(default(Guid).ToString(), def1.FolderId); var def2 = new PackageDefinition { @@ -98,7 +97,6 @@ namespace Umbraco.Tests.Packaging Assert.IsTrue(result); Assert.AreEqual(2, def2.Id); Assert.AreNotEqual(default(Guid).ToString(), def2.PackageId); - Assert.AreNotEqual(default(Guid).ToString(), def2.FolderId); } [Test] From fbf60fbac47eb0fe19074bab6e96c87330f6f220 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Mon, 22 Oct 2018 09:39:22 +0200 Subject: [PATCH 60/93] Don't add the "Notification" tree menu item if Umbraco can't send emails --- src/Umbraco.Web/Trees/ContentTreeController.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Trees/ContentTreeController.cs b/src/Umbraco.Web/Trees/ContentTreeController.cs index 293cc3d36a..54adb24b74 100644 --- a/src/Umbraco.Web/Trees/ContentTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentTreeController.cs @@ -238,7 +238,10 @@ namespace Umbraco.Web.Trees menu.Items.Add(ui.Text("actions", ActionRights.Instance.Alias), true); menu.Items.Add(ui.Text("actions", ActionProtect.Instance.Alias), true).ConvertLegacyMenuItem(item, "content", "content"); - menu.Items.Add(ui.Text("actions", ActionNotify.Instance.Alias), true); + if (EmailSender.CanSendRequiredEmail) + { + menu.Items.Add(ui.Text("actions", ActionNotify.Instance.Alias), true); + } menu.Items.Add(ui.Text("actions", ActionSendToTranslate.Instance.Alias)).ConvertLegacyMenuItem(item, "content", "content"); menu.Items.Add(ui.Text("actions", ActionRefresh.Instance.Alias), true); From b08e9226001f59d144fca943c59c5486964569b0 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 7 Nov 2018 12:27:50 +0100 Subject: [PATCH 61/93] Only allow setting up scheduled publishing if the user has the correct permissions to do so --- .../components/content/umbcontentnodeinfo.directive.js | 3 +++ .../src/views/components/content/umb-content-node-info.html | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js index 8f613469a3..c753ac67d2 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js @@ -53,6 +53,9 @@ if (scope.documentType !== null) { scope.previewOpenUrl = '#/settings/documenttypes/edit/' + scope.documentType.id; } + + // only allow configuring scheduled publishing if the user has publish ("U") and unpublish ("Z") permissions on this node + scope.allowScheduledPublishing = _.contains(scope.node.allowedActions, "U") && _.contains(scope.node.allowedActions, "Z"); } scope.auditTrailPageChange = function (pageNumber) { diff --git a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html index 7085babf59..0ed3986fe3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html @@ -107,7 +107,7 @@
- + From 05f1a24caff54e2ae9b6ec3f5289b9eb95410df9 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Mon, 14 Jan 2019 08:28:58 +0100 Subject: [PATCH 62/93] Fix duplicate IIS Express settings --- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 81ca9c5a37..fc24d2b40f 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -1041,9 +1041,6 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\" 7140 / http://localhost:7140 - 7131 - / - http://localhost:7131 False False From db8954e0a5d532943e9885581ef154cb21872cc4 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Mon, 14 Jan 2019 08:52:02 +0100 Subject: [PATCH 63/93] Verify that the current member is approved and not locked out (#3588) --- .../Routing/PublishedContentRequestEngine.cs | 853 +++++++++--------- 1 file changed, 438 insertions(+), 415 deletions(-) diff --git a/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs b/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs index 217f407f02..0341c083f9 100644 --- a/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs +++ b/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs @@ -26,61 +26,61 @@ using RenderingEngine = Umbraco.Core.RenderingEngine; namespace Umbraco.Web.Routing { internal class PublishedContentRequestEngine - { - private readonly PublishedContentRequest _pcr; - private readonly RoutingContext _routingContext; - private readonly IWebRoutingSection _webRoutingSection; + { + private readonly PublishedContentRequest _pcr; + private readonly RoutingContext _routingContext; + private readonly IWebRoutingSection _webRoutingSection; - /// - /// Initializes a new instance of the class with a content request. - /// - /// - /// The content request. - public PublishedContentRequestEngine( + /// + /// Initializes a new instance of the class with a content request. + /// + /// + /// The content request. + public PublishedContentRequestEngine( IWebRoutingSection webRoutingSection, PublishedContentRequest pcr) - { - if (pcr == null) throw new ArgumentException("pcr is null."); - if (webRoutingSection == null) throw new ArgumentNullException("webRoutingSection"); - - _pcr = pcr; - _webRoutingSection = webRoutingSection; + { + if (pcr == null) throw new ArgumentException("pcr is null."); + if (webRoutingSection == null) throw new ArgumentNullException("webRoutingSection"); - _routingContext = pcr.RoutingContext; - if (_routingContext == null) throw new ArgumentException("pcr.RoutingContext is null."); - - var umbracoContext = _routingContext.UmbracoContext; - if (umbracoContext == null) throw new ArgumentException("pcr.RoutingContext.UmbracoContext is null."); - if (umbracoContext.RoutingContext != _routingContext) throw new ArgumentException("RoutingContext confusion."); - // no! not set yet. - //if (umbracoContext.PublishedContentRequest != _pcr) throw new ArgumentException("PublishedContentRequest confusion."); - } + _pcr = pcr; + _webRoutingSection = webRoutingSection; + + _routingContext = pcr.RoutingContext; + if (_routingContext == null) throw new ArgumentException("pcr.RoutingContext is null."); + + var umbracoContext = _routingContext.UmbracoContext; + if (umbracoContext == null) throw new ArgumentException("pcr.RoutingContext.UmbracoContext is null."); + if (umbracoContext.RoutingContext != _routingContext) throw new ArgumentException("RoutingContext confusion."); + // no! not set yet. + //if (umbracoContext.PublishedContentRequest != _pcr) throw new ArgumentException("PublishedContentRequest confusion."); + } protected ProfilingLogger ProfilingLogger - { + { get { return _routingContext.UmbracoContext.Application.ProfilingLogger; } - } + } - protected ServiceContext Services - { + protected ServiceContext Services + { get { return _routingContext.UmbracoContext.Application.Services; } - } + } - #region Public + #region Public /// /// Tries to route the request. /// - internal bool TryRouteRequest() - { + internal bool TryRouteRequest() + { // disabled - is it going to change the routing? //_pcr.OnPreparing(); FindDomain(); - if (_pcr.IsRedirect) return false; - if (_pcr.HasPublishedContent) return true; - FindPublishedContent(); + if (_pcr.IsRedirect) return false; + if (_pcr.HasPublishedContent) return true; + FindPublishedContent(); if (_pcr.IsRedirect) return false; // don't handle anything - we just want to ensure that we find the content @@ -93,51 +93,51 @@ namespace Umbraco.Web.Routing //_pcr.OnPrepared(); return _pcr.HasPublishedContent; - } + } - /// - /// Prepares the request. - /// - /// - /// Returns false if the request was not successfully prepared - /// - public bool PrepareRequest() - { - // note - at that point the original legacy module did something do handle IIS custom 404 errors - // ie pages looking like /anything.aspx?404;/path/to/document - I guess the reason was to support - // "directory urls" without having to do wildcard mapping to ASP.NET on old IIS. This is a pain - // to maintain and probably not used anymore - removed as of 06/2012. @zpqrtbnk. - // - // to trigger Umbraco's not-found, one should configure IIS and/or ASP.NET custom 404 errors - // so that they point to a non-existing page eg /redirect-404.aspx - // TODO: SD: We need more information on this for when we release 4.10.0 as I'm not sure what this means. + /// + /// Prepares the request. + /// + /// + /// Returns false if the request was not successfully prepared + /// + public bool PrepareRequest() + { + // note - at that point the original legacy module did something do handle IIS custom 404 errors + // ie pages looking like /anything.aspx?404;/path/to/document - I guess the reason was to support + // "directory urls" without having to do wildcard mapping to ASP.NET on old IIS. This is a pain + // to maintain and probably not used anymore - removed as of 06/2012. @zpqrtbnk. + // + // to trigger Umbraco's not-found, one should configure IIS and/or ASP.NET custom 404 errors + // so that they point to a non-existing page eg /redirect-404.aspx + // TODO: SD: We need more information on this for when we release 4.10.0 as I'm not sure what this means. // trigger the Preparing event - at that point anything can still be changed // the idea is that it is possible to change the uri // - _pcr.OnPreparing(); + _pcr.OnPreparing(); - //find domain - FindDomain(); + //find domain + FindDomain(); - // if request has been flagged to redirect then return - // whoever called us is in charge of actually redirecting - if (_pcr.IsRedirect) - { - return false; - } + // if request has been flagged to redirect then return + // whoever called us is in charge of actually redirecting + if (_pcr.IsRedirect) + { + return false; + } - // set the culture on the thread - once, so it's set when running document lookups - Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture = _pcr.Culture; + // set the culture on the thread - once, so it's set when running document lookups + Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture = _pcr.Culture; //find the published content if it's not assigned. This could be manually assigned with a custom route handler, or // with something like EnsurePublishedContentRequestAttribute or UmbracoVirtualNodeRouteHandler. Those in turn call this method // to setup the rest of the pipeline but we don't want to run the finders since there's one assigned. - if (_pcr.PublishedContent == null) - { + if (_pcr.PublishedContent == null) + { // find the document & template FindPublishedContentAndTemplate(); - } + } // handle wildcard domains HandleWildcardDomains(); @@ -145,19 +145,19 @@ namespace Umbraco.Web.Routing // set the culture on the thread -- again, 'cos it might have changed due to a finder or wildcard domain Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture = _pcr.Culture; - // trigger the Prepared event - at that point it is still possible to change about anything + // trigger the Prepared event - at that point it is still possible to change about anything // even though the request might be flagged for redirection - we'll redirect _after_ the event // // also, OnPrepared() will make the PublishedContentRequest readonly, so nothing can change // - _pcr.OnPrepared(); + _pcr.OnPrepared(); // we don't take care of anything so if the content has changed, it's up to the user // to find out the appropriate template //complete the PCR and assign the remaining values - return ConfigureRequest(); - } + return ConfigureRequest(); + } /// /// Called by PrepareRequest once everything has been discovered, resolved and assigned to the PCR. This method @@ -210,73 +210,73 @@ namespace Umbraco.Web.Routing return true; } - /// - /// Updates the request when there is no template to render the content. - /// - /// This is called from Mvc when there's a document to render but no template. - public void UpdateRequestOnMissingTemplate() - { - // clear content - var content = _pcr.PublishedContent; - _pcr.PublishedContent = null; + /// + /// Updates the request when there is no template to render the content. + /// + /// This is called from Mvc when there's a document to render but no template. + public void UpdateRequestOnMissingTemplate() + { + // clear content + var content = _pcr.PublishedContent; + _pcr.PublishedContent = null; - HandlePublishedContent(); // will go 404 - FindTemplate(); + HandlePublishedContent(); // will go 404 + FindTemplate(); - // if request has been flagged to redirect then return - // whoever called us is in charge of redirecting - if (_pcr.IsRedirect) - return; + // if request has been flagged to redirect then return + // whoever called us is in charge of redirecting + if (_pcr.IsRedirect) + return; - if (_pcr.HasPublishedContent == false) - { - // means the engine could not find a proper document to handle 404 - // restore the saved content so we know it exists - _pcr.PublishedContent = content; - return; - } + if (_pcr.HasPublishedContent == false) + { + // means the engine could not find a proper document to handle 404 + // restore the saved content so we know it exists + _pcr.PublishedContent = content; + return; + } - if (_pcr.HasTemplate == false) - { - // means we may have a document, but we have no template - // at that point there isn't much we can do and there is no point returning - // to Mvc since Mvc can't do much either - return; - } + if (_pcr.HasTemplate == false) + { + // means we may have a document, but we have no template + // at that point there isn't much we can do and there is no point returning + // to Mvc since Mvc can't do much either + return; + } // see note in PrepareRequest() - // assign the legacy page back to the docrequest - // handlers like default.aspx will want it and most macros currently need it - _pcr.UmbracoPage = new page(_pcr); + // assign the legacy page back to the docrequest + // handlers like default.aspx will want it and most macros currently need it + _pcr.UmbracoPage = new page(_pcr); - // these two are used by many legacy objects - _routingContext.UmbracoContext.HttpContext.Items["pageID"] = _pcr.PublishedContent.Id; - _routingContext.UmbracoContext.HttpContext.Items["pageElements"] = _pcr.UmbracoPage.Elements; - } + // these two are used by many legacy objects + _routingContext.UmbracoContext.HttpContext.Items["pageID"] = _pcr.PublishedContent.Id; + _routingContext.UmbracoContext.HttpContext.Items["pageElements"] = _pcr.UmbracoPage.Elements; + } - #endregion + #endregion - #region Domain + #region Domain - /// - /// Finds the site root (if any) matching the http request, and updates the PublishedContentRequest accordingly. - /// - /// A value indicating whether a domain was found. - internal bool FindDomain() - { - const string tracePrefix = "FindDomain: "; + /// + /// Finds the site root (if any) matching the http request, and updates the PublishedContentRequest accordingly. + /// + /// A value indicating whether a domain was found. + internal bool FindDomain() + { + const string tracePrefix = "FindDomain: "; - // note - we are not handling schemes nor ports here. + // note - we are not handling schemes nor ports here. - ProfilingLogger.Logger.Debug("{0}Uri=\"{1}\"", () => tracePrefix, () => _pcr.Uri); + ProfilingLogger.Logger.Debug("{0}Uri=\"{1}\"", () => tracePrefix, () => _pcr.Uri); - // try to find a domain matching the current request + // try to find a domain matching the current request var domainAndUri = DomainHelper.DomainForUri(Services.DomainService.GetAll(false), _pcr.Uri); - // handle domain - if (domainAndUri != null && domainAndUri.UmbracoDomain.LanguageIsoCode.IsNullOrWhiteSpace() == false) - { + // handle domain + if (domainAndUri != null && domainAndUri.UmbracoDomain.LanguageIsoCode.IsNullOrWhiteSpace() == false) + { // matching an existing domain ProfilingLogger.Logger.Debug("{0}Matches domain=\"{1}\", rootId={2}, culture=\"{3}\"", () => tracePrefix, @@ -296,49 +296,49 @@ namespace Umbraco.Web.Routing // } } else - { - // not matching any existing domain - ProfilingLogger.Logger.Debug("{0}Matches no domain", () => tracePrefix); + { + // not matching any existing domain + ProfilingLogger.Logger.Debug("{0}Matches no domain", () => tracePrefix); var defaultLanguage = Services.LocalizationService.GetAllLanguages().FirstOrDefault(); - _pcr.Culture = defaultLanguage == null ? CultureInfo.CurrentUICulture : new CultureInfo(defaultLanguage.IsoCode); - } + _pcr.Culture = defaultLanguage == null ? CultureInfo.CurrentUICulture : new CultureInfo(defaultLanguage.IsoCode); + } - ProfilingLogger.Logger.Debug("{0}Culture=\"{1}\"", () => tracePrefix, () => _pcr.Culture.Name); + ProfilingLogger.Logger.Debug("{0}Culture=\"{1}\"", () => tracePrefix, () => _pcr.Culture.Name); - return _pcr.UmbracoDomain != null; - } + return _pcr.UmbracoDomain != null; + } - /// - /// Looks for wildcard domains in the path and updates Culture accordingly. - /// - internal void HandleWildcardDomains() - { - const string tracePrefix = "HandleWildcardDomains: "; + /// + /// Looks for wildcard domains in the path and updates Culture accordingly. + /// + internal void HandleWildcardDomains() + { + const string tracePrefix = "HandleWildcardDomains: "; - if (_pcr.HasPublishedContent == false) - return; + if (_pcr.HasPublishedContent == false) + return; - var nodePath = _pcr.PublishedContent.Path; - ProfilingLogger.Logger.Debug("{0}Path=\"{1}\"", () => tracePrefix, () => nodePath); + var nodePath = _pcr.PublishedContent.Path; + ProfilingLogger.Logger.Debug("{0}Path=\"{1}\"", () => tracePrefix, () => nodePath); var rootNodeId = _pcr.HasDomain ? _pcr.UmbracoDomain.RootContentId : (int?)null; var domain = DomainHelper.FindWildcardDomainInPath(Services.DomainService.GetAll(true), nodePath, rootNodeId); - if (domain != null && domain.LanguageIsoCode.IsNullOrWhiteSpace() == false) - { + if (domain != null && domain.LanguageIsoCode.IsNullOrWhiteSpace() == false) + { _pcr.Culture = new CultureInfo(domain.LanguageIsoCode); ProfilingLogger.Logger.Debug("{0}Got domain on node {1}, set culture to \"{2}\".", () => tracePrefix, () => domain.RootContentId, () => _pcr.Culture.Name); } - else - { - ProfilingLogger.Logger.Debug("{0}No match.", () => tracePrefix); - } - } + else + { + ProfilingLogger.Logger.Debug("{0}No match.", () => tracePrefix); + } + } - #endregion + #endregion - #region Rendering engine + #region Rendering engine /// /// Finds the rendering engine to use to render a template specified by its alias. @@ -383,264 +383,287 @@ namespace Umbraco.Web.Routing return directory.GetFiles().Any(f => extensions.Any(e => f.Name.InvariantEquals(alias + e))); } - #endregion + #endregion - #region Document and template + #region Document and template - /// - /// Finds the Umbraco document (if any) matching the request, and updates the PublishedContentRequest accordingly. - /// - /// A value indicating whether a document and template were found. - private void FindPublishedContentAndTemplate() - { - const string tracePrefix = "FindPublishedContentAndTemplate: "; - ProfilingLogger.Logger.Debug("{0}Path=\"{1}\"", () => tracePrefix, () => _pcr.Uri.AbsolutePath); + /// + /// Finds the Umbraco document (if any) matching the request, and updates the PublishedContentRequest accordingly. + /// + /// A value indicating whether a document and template were found. + private void FindPublishedContentAndTemplate() + { + const string tracePrefix = "FindPublishedContentAndTemplate: "; + ProfilingLogger.Logger.Debug("{0}Path=\"{1}\"", () => tracePrefix, () => _pcr.Uri.AbsolutePath); - // run the document finders - FindPublishedContent(); + // run the document finders + FindPublishedContent(); // if request has been flagged to redirect then return // whoever called us is in charge of actually redirecting // -- do not process anything any further -- if (_pcr.IsRedirect) - return; + return; - // not handling umbracoRedirect here but after LookupDocument2 - // so internal redirect, 404, etc has precedence over redirect + // not handling umbracoRedirect here but after LookupDocument2 + // so internal redirect, 404, etc has precedence over redirect - // handle not-found, redirects, access... - HandlePublishedContent(); + // handle not-found, redirects, access... + HandlePublishedContent(); - // find a template - FindTemplate(); + // find a template + FindTemplate(); - // handle umbracoRedirect - FollowExternalRedirect(); - } + // handle umbracoRedirect + FollowExternalRedirect(); + } - /// - /// Tries to find the document matching the request, by running the IPublishedContentFinder instances. - /// + /// + /// Tries to find the document matching the request, by running the IPublishedContentFinder instances. + /// /// There is no finder collection. - internal void FindPublishedContent() - { - const string tracePrefix = "FindPublishedContent: "; + internal void FindPublishedContent() + { + const string tracePrefix = "FindPublishedContent: "; - // look for the document - // the first successful finder, if any, will set this.PublishedContent, and may also set this.Template - // some finders may implement caching + // look for the document + // the first successful finder, if any, will set this.PublishedContent, and may also set this.Template + // some finders may implement caching using (ProfilingLogger.DebugDuration( - string.Format("{0}Begin finders", tracePrefix), - string.Format("{0}End finders, {1}", tracePrefix, (_pcr.HasPublishedContent ? "a document was found" : "no document was found")))) - { - if (_routingContext.PublishedContentFinders == null) + string.Format("{0}Begin finders", tracePrefix), + string.Format("{0}End finders, {1}", tracePrefix, (_pcr.HasPublishedContent ? "a document was found" : "no document was found")))) + { + if (_routingContext.PublishedContentFinders == null) throw new InvalidOperationException("There is no finder collection."); //iterate but return on first one that finds it - var found = _routingContext.PublishedContentFinders.Any(finder => finder.TryFindContent(_pcr)); - } + var found = _routingContext.PublishedContentFinders.Any(finder => finder.TryFindContent(_pcr)); + } - // indicate that the published content (if any) we have at the moment is the - // one that was found by the standard finders before anything else took place. - _pcr.SetIsInitialPublishedContent(); - } + // indicate that the published content (if any) we have at the moment is the + // one that was found by the standard finders before anything else took place. + _pcr.SetIsInitialPublishedContent(); + } - /// - /// Handles the published content (if any). - /// - /// - /// Handles "not found", internal redirects, access validation... - /// things that must be handled in one place because they can create loops - /// - private void HandlePublishedContent() - { - const string tracePrefix = "HandlePublishedContent: "; + /// + /// Handles the published content (if any). + /// + /// + /// Handles "not found", internal redirects, access validation... + /// things that must be handled in one place because they can create loops + /// + private void HandlePublishedContent() + { + const string tracePrefix = "HandlePublishedContent: "; - // because these might loop, we have to have some sort of infinite loop detection - int i = 0, j = 0; - const int maxLoop = 8; - do - { - ProfilingLogger.Logger.Debug("{0}{1}", () => tracePrefix, () => (i == 0 ? "Begin" : "Loop")); + // because these might loop, we have to have some sort of infinite loop detection + int i = 0, j = 0; + const int maxLoop = 8; + do + { + ProfilingLogger.Logger.Debug("{0}{1}", () => tracePrefix, () => (i == 0 ? "Begin" : "Loop")); - // handle not found - if (_pcr.HasPublishedContent == false) - { - _pcr.Is404 = true; - ProfilingLogger.Logger.Debug("{0}No document, try last chance lookup", () => tracePrefix); + // handle not found + if (_pcr.HasPublishedContent == false) + { + _pcr.Is404 = true; + ProfilingLogger.Logger.Debug("{0}No document, try last chance lookup", () => tracePrefix); - // if it fails then give up, there isn't much more that we can do - var lastChance = _routingContext.PublishedContentLastChanceFinder; - if (lastChance == null || lastChance.TryFindContent(_pcr) == false) - { - ProfilingLogger.Logger.Debug("{0}Failed to find a document, give up", () => tracePrefix); - break; - } + // if it fails then give up, there isn't much more that we can do + var lastChance = _routingContext.PublishedContentLastChanceFinder; + if (lastChance == null || lastChance.TryFindContent(_pcr) == false) + { + ProfilingLogger.Logger.Debug("{0}Failed to find a document, give up", () => tracePrefix); + break; + } - ProfilingLogger.Logger.Debug("{0}Found a document", () => tracePrefix); - } + ProfilingLogger.Logger.Debug("{0}Found a document", () => tracePrefix); + } - // follow internal redirects as long as it's not running out of control ie infinite loop of some sort - j = 0; - while (FollowInternalRedirects() && j++ < maxLoop) - { } - if (j == maxLoop) // we're running out of control - break; + // follow internal redirects as long as it's not running out of control ie infinite loop of some sort + j = 0; + while (FollowInternalRedirects() && j++ < maxLoop) + { } + if (j == maxLoop) // we're running out of control + break; - // ensure access - if (_pcr.HasPublishedContent) - EnsurePublishedContentAccess(); + // ensure access + if (_pcr.HasPublishedContent) + EnsurePublishedContentAccess(); - // loop while we don't have page, ie the redirect or access - // got us to nowhere and now we need to run the notFoundLookup again - // as long as it's not running out of control ie infinite loop of some sort + // loop while we don't have page, ie the redirect or access + // got us to nowhere and now we need to run the notFoundLookup again + // as long as it's not running out of control ie infinite loop of some sort - } while (_pcr.HasPublishedContent == false && i++ < maxLoop); + } while (_pcr.HasPublishedContent == false && i++ < maxLoop); - if (i == maxLoop || j == maxLoop) - { - ProfilingLogger.Logger.Debug("{0}Looks like we're running into an infinite loop, abort", () => tracePrefix); - _pcr.PublishedContent = null; - } + if (i == maxLoop || j == maxLoop) + { + ProfilingLogger.Logger.Debug("{0}Looks like we're running into an infinite loop, abort", () => tracePrefix); + _pcr.PublishedContent = null; + } - ProfilingLogger.Logger.Debug("{0}End", () => tracePrefix); - } + ProfilingLogger.Logger.Debug("{0}End", () => tracePrefix); + } - /// - /// Follows internal redirections through the umbracoInternalRedirectId document property. - /// - /// A value indicating whether redirection took place and led to a new published document. - /// - /// Redirecting to a different site root and/or culture will not pick the new site root nor the new culture. - /// As per legacy, if the redirect does not work, we just ignore it. - /// - private bool FollowInternalRedirects() - { - const string tracePrefix = "FollowInternalRedirects: "; + /// + /// Follows internal redirections through the umbracoInternalRedirectId document property. + /// + /// A value indicating whether redirection took place and led to a new published document. + /// + /// Redirecting to a different site root and/or culture will not pick the new site root nor the new culture. + /// As per legacy, if the redirect does not work, we just ignore it. + /// + private bool FollowInternalRedirects() + { + const string tracePrefix = "FollowInternalRedirects: "; + + if (_pcr.PublishedContent == null) + throw new InvalidOperationException("There is no PublishedContent."); - if (_pcr.PublishedContent == null) - throw new InvalidOperationException("There is no PublishedContent."); - // don't try to find a redirect if the property doesn't exist - if (_pcr.PublishedContent.HasProperty(Constants.Conventions.Content.InternalRedirectId) == false) - return false; + if (_pcr.PublishedContent.HasProperty(Constants.Conventions.Content.InternalRedirectId) == false) + return false; var redirect = false; - IPublishedContent internalRedirectNode = null; - var internalRedirectId = - _pcr.PublishedContent.GetPropertyValue(Constants.Conventions.Content.InternalRedirectId, -1); - var valueValid = false; - if (internalRedirectId > 0) - { - valueValid = true; - // Try and get the redirect node from a legacy integer ID - internalRedirectNode = _routingContext.UmbracoContext.ContentCache.GetById(internalRedirectId); - } - else - { - var udiInternalRedirectId = - _pcr.PublishedContent.GetPropertyValue(Constants.Conventions.Content.InternalRedirectId); + IPublishedContent internalRedirectNode = null; + var internalRedirectId = + _pcr.PublishedContent.GetPropertyValue(Constants.Conventions.Content.InternalRedirectId, -1); + var valueValid = false; + if (internalRedirectId > 0) + { + valueValid = true; + // Try and get the redirect node from a legacy integer ID + internalRedirectNode = _routingContext.UmbracoContext.ContentCache.GetById(internalRedirectId); + } + else + { + var udiInternalRedirectId = + _pcr.PublishedContent.GetPropertyValue(Constants.Conventions.Content.InternalRedirectId); if (udiInternalRedirectId != null) - { - valueValid = true; - // Try and get the redirect node from a UDI Guid - internalRedirectNode = - _routingContext.UmbracoContext.ContentCache.GetById(udiInternalRedirectId.Guid); - } - } + { + valueValid = true; + // Try and get the redirect node from a UDI Guid + internalRedirectNode = + _routingContext.UmbracoContext.ContentCache.GetById(udiInternalRedirectId.Guid); + } + } if (valueValid == false) - { + { // bad redirect - log and display the current page (legacy behavior) ProfilingLogger - .Logger.Debug( - "{0}Failed to redirect, value of '{1}' is not an int nor a GuidUdi", - () => tracePrefix, () => Constants.Conventions.Content.InternalRedirectId); - } + .Logger.Debug( + "{0}Failed to redirect, value of '{1}' is not an int nor a GuidUdi", + () => tracePrefix, () => Constants.Conventions.Content.InternalRedirectId); + } - if (internalRedirectNode == null) - { - ProfilingLogger.Logger.Debug( - "{0}Failed to redirect, value of '{1}' does not lead to a published document", () => tracePrefix, - () => Constants.Conventions.Content.InternalRedirectId); - } - else if (internalRedirectNode.Id == _pcr.PublishedContent.Id) - { - // redirect to self - ProfilingLogger.Logger.Debug("{0}Redirecting to self, ignore", - () => tracePrefix); - } - else - { - // Redirect to another page - _pcr.SetInternalRedirectPublishedContent(internalRedirectNode); - redirect = true; - ProfilingLogger.Logger.Debug("{0}Redirecting to id={1}", () => tracePrefix, - () => internalRedirectNode.Id); - } + if (internalRedirectNode == null) + { + ProfilingLogger.Logger.Debug( + "{0}Failed to redirect, value of '{1}' does not lead to a published document", () => tracePrefix, + () => Constants.Conventions.Content.InternalRedirectId); + } + else if (internalRedirectNode.Id == _pcr.PublishedContent.Id) + { + // redirect to self + ProfilingLogger.Logger.Debug("{0}Redirecting to self, ignore", + () => tracePrefix); + } + else + { + // Redirect to another page + _pcr.SetInternalRedirectPublishedContent(internalRedirectNode); + redirect = true; + ProfilingLogger.Logger.Debug("{0}Redirecting to id={1}", () => tracePrefix, + () => internalRedirectNode.Id); + } - return redirect; - } + return redirect; + } - /// - /// Ensures that access to current node is permitted. - /// - /// Redirecting to a different site root and/or culture will not pick the new site root nor the new culture. - private void EnsurePublishedContentAccess() - { - const string tracePrefix = "EnsurePublishedContentAccess: "; + /// + /// Ensures that access to current node is permitted. + /// + /// Redirecting to a different site root and/or culture will not pick the new site root nor the new culture. + private void EnsurePublishedContentAccess() + { + const string tracePrefix = "EnsurePublishedContentAccess: "; - if (_pcr.PublishedContent == null) - throw new InvalidOperationException("There is no PublishedContent."); + if (_pcr.PublishedContent == null) + throw new InvalidOperationException("There is no PublishedContent."); - var path = _pcr.PublishedContent.Path; + var path = _pcr.PublishedContent.Path; - var publicAccessAttempt = Services.PublicAccessService.IsProtected(path); + var publicAccessAttempt = Services.PublicAccessService.IsProtected(path); if (publicAccessAttempt) - { - ProfilingLogger.Logger.Debug("{0}Page is protected, check for access", () => tracePrefix); + { + ProfilingLogger.Logger.Debug("{0}Page is protected, check for access", () => tracePrefix); - var membershipHelper = new MembershipHelper(_routingContext.UmbracoContext); + var membershipHelper = new MembershipHelper(_routingContext.UmbracoContext); - if (membershipHelper.IsLoggedIn() == false) - { - ProfilingLogger.Logger.Debug("{0}Not logged in, redirect to login page", () => tracePrefix); + if (membershipHelper.IsLoggedIn() == false) + { + ProfilingLogger.Logger.Debug("{0}Not logged in, redirect to login page", () => tracePrefix); var loginPageId = publicAccessAttempt.Result.LoginNodeId; - if (loginPageId != _pcr.PublishedContent.Id) + if (loginPageId != _pcr.PublishedContent.Id) _pcr.PublishedContent = _routingContext.UmbracoContext.ContentCache.GetById(loginPageId); - } + } else if (Services.PublicAccessService.HasAccess(_pcr.PublishedContent.Id, Services.ContentService, _pcr.GetRolesForLogin(membershipHelper.CurrentUserName)) == false) - { - ProfilingLogger.Logger.Debug("{0}Current member has not access, redirect to error page", () => tracePrefix); - var errorPageId = publicAccessAttempt.Result.NoAccessNodeId; - if (errorPageId != _pcr.PublishedContent.Id) + { + ProfilingLogger.Logger.Debug("{0}Current member has not access, redirect to error page", () => tracePrefix); + var errorPageId = publicAccessAttempt.Result.NoAccessNodeId; + if (errorPageId != _pcr.PublishedContent.Id) _pcr.PublishedContent = _routingContext.UmbracoContext.ContentCache.GetById(errorPageId); - } - else - { - ProfilingLogger.Logger.Debug("{0}Current member has access", () => tracePrefix); - } - } - else - { - ProfilingLogger.Logger.Debug("{0}Page is not protected", () => tracePrefix); - } - } + } + else + { + // grab the current member + var member = membershipHelper.GetCurrentMember(); + // if the member has the "approved" and/or "locked out" properties, make sure they're correctly set before allowing access + var memberIsActive = true; + if (member != null) + { + if (member.HasProperty(Constants.Conventions.Member.IsApproved) == false) + memberIsActive = member.GetPropertyValue(Constants.Conventions.Member.IsApproved); - /// - /// Finds a template for the current node, if any. - /// - private void FindTemplate() - { - // NOTE: at the moment there is only 1 way to find a template, and then ppl must - // use the Prepared event to change the template if they wish. Should we also - // implement an ITemplateFinder logic? + if (member.HasProperty(Constants.Conventions.Member.IsLockedOut) == false) + memberIsActive = member.GetPropertyValue(Constants.Conventions.Member.IsLockedOut) == false; + } - const string tracePrefix = "FindTemplate: "; + if (memberIsActive == false) + { + ProfilingLogger.Logger.Debug("{0}Current member is either unapproved or locked out, redirect to error page", () => tracePrefix); + var errorPageId = publicAccessAttempt.Result.NoAccessNodeId; + if (errorPageId != _pcr.PublishedContent.Id) + _pcr.PublishedContent = _routingContext.UmbracoContext.ContentCache.GetById(errorPageId); + } + else + { + ProfilingLogger.Logger.Debug("{0}Current member has access", () => tracePrefix); + } + } + } + else + { + ProfilingLogger.Logger.Debug("{0}Page is not protected", () => tracePrefix); + } + } + + /// + /// Finds a template for the current node, if any. + /// + private void FindTemplate() + { + // NOTE: at the moment there is only 1 way to find a template, and then ppl must + // use the Prepared event to change the template if they wish. Should we also + // implement an ITemplateFinder logic? + + const string tracePrefix = "FindTemplate: "; if (_pcr.PublishedContent == null) { @@ -648,49 +671,49 @@ namespace Umbraco.Web.Routing return; } - // read the alternate template alias, from querystring, form, cookie or server vars, - // only if the published content is the initial once, else the alternate template - // does not apply + // read the alternate template alias, from querystring, form, cookie or server vars, + // only if the published content is the initial once, else the alternate template + // does not apply // + optionnally, apply the alternate template on internal redirects var useAltTemplate = _pcr.IsInitialPublishedContent || (_webRoutingSection.InternalRedirectPreservesTemplate && _pcr.IsInternalRedirectPublishedContent); string altTemplate = useAltTemplate ? _routingContext.UmbracoContext.HttpContext.Request[Constants.Conventions.Url.AltTemplate] - : null; + : null; - if (string.IsNullOrWhiteSpace(altTemplate)) - { - // we don't have an alternate template specified. use the current one if there's one already, - // which can happen if a content lookup also set the template (LookupByNiceUrlAndTemplate...), - // else lookup the template id on the document then lookup the template with that id. + if (string.IsNullOrWhiteSpace(altTemplate)) + { + // we don't have an alternate template specified. use the current one if there's one already, + // which can happen if a content lookup also set the template (LookupByNiceUrlAndTemplate...), + // else lookup the template id on the document then lookup the template with that id. - if (_pcr.HasTemplate) - { - ProfilingLogger.Logger.Debug("{0}Has a template already, and no alternate template.", () => tracePrefix); - return; - } + if (_pcr.HasTemplate) + { + ProfilingLogger.Logger.Debug("{0}Has a template already, and no alternate template.", () => tracePrefix); + return; + } - // TODO: When we remove the need for a database for templates, then this id should be irrelavent, - // not sure how were going to do this nicely. + // TODO: When we remove the need for a database for templates, then this id should be irrelavent, + // not sure how were going to do this nicely. - var templateId = _pcr.PublishedContent.TemplateId; + var templateId = _pcr.PublishedContent.TemplateId; // This code was moved to GetTemplateModel to allow the same functionality on a failed altTemplate (U4-8550) // The only change is a diffent logger prefix and null will be set to _pcr.TemplateModel if the templateId is <= 0 // rather than no set taking place _pcr.TemplateModel = GetTemplateModel(_pcr.PublishedContent.TemplateId); } - else - { - // we have an alternate template specified. lookup the template with that alias - // this means the we override any template that a content lookup might have set - // so /path/to/page/template1?altTemplate=template2 will use template2 + else + { + // we have an alternate template specified. lookup the template with that alias + // this means the we override any template that a content lookup might have set + // so /path/to/page/template1?altTemplate=template2 will use template2 - // ignore if the alias does not match - just trace + // ignore if the alias does not match - just trace - if (_pcr.HasTemplate) - ProfilingLogger.Logger.Debug("{0}Has a template already, but also an alternate template.", () => tracePrefix); - ProfilingLogger.Logger.Debug("{0}Look for alternate template alias=\"{1}\"", () => tracePrefix, () => altTemplate); + if (_pcr.HasTemplate) + ProfilingLogger.Logger.Debug("{0}Has a template already, but also an alternate template.", () => tracePrefix); + ProfilingLogger.Logger.Debug("{0}Look for alternate template alias=\"{1}\"", () => tracePrefix, () => altTemplate); if (_pcr.PublishedContent.IsAllowedTemplate(altTemplate)) { @@ -705,26 +728,26 @@ namespace Umbraco.Web.Routing LogHelper.Warn("{0}Configuration settings prevent template \"{1}\" from showing for node \"{2}\"", () => tracePrefix, () => altTemplate, () => _pcr.PublishedContent.Id); _pcr.TemplateModel = GetTemplateModel(_pcr.PublishedContent.TemplateId); } - } + } - if (_pcr.HasTemplate == false) - { - ProfilingLogger.Logger.Debug("{0}No template was found.", () => tracePrefix); + if (_pcr.HasTemplate == false) + { + ProfilingLogger.Logger.Debug("{0}No template was found.", () => tracePrefix); - // initial idea was: if we're not already 404 and UmbracoSettings.HandleMissingTemplateAs404 is true - // then reset _pcr.Document to null to force a 404. - // - // but: because we want to let MVC hijack routes even though no template is defined, we decide that - // a missing template is OK but the request will then be forwarded to MVC, which will need to take - // care of everything. - // - // so, don't set _pcr.Document to null here - } - else - { - ProfilingLogger.Logger.Debug("{0}Running with template id={1} alias=\"{2}\"", () => tracePrefix, () => _pcr.TemplateModel.Id, () => _pcr.TemplateModel.Alias); - } - } + // initial idea was: if we're not already 404 and UmbracoSettings.HandleMissingTemplateAs404 is true + // then reset _pcr.Document to null to force a 404. + // + // but: because we want to let MVC hijack routes even though no template is defined, we decide that + // a missing template is OK but the request will then be forwarded to MVC, which will need to take + // care of everything. + // + // so, don't set _pcr.Document to null here + } + else + { + ProfilingLogger.Logger.Debug("{0}Running with template id={1} alias=\"{2}\"", () => tracePrefix, () => _pcr.TemplateModel.Id, () => _pcr.TemplateModel.Alias); + } + } private ITemplate GetTemplateModel(int templateId) { @@ -751,32 +774,32 @@ namespace Umbraco.Web.Routing /// /// As per legacy, if the redirect does not work, we just ignore it. private void FollowExternalRedirect() - { - if (_pcr.HasPublishedContent == false) return; + { + if (_pcr.HasPublishedContent == false) return; - // don't try to find a redirect if the property doesn't exist + // don't try to find a redirect if the property doesn't exist if (_pcr.PublishedContent.HasProperty(Constants.Conventions.Content.Redirect) == false) - return; - - var redirectId = _pcr.PublishedContent.GetPropertyValue(Constants.Conventions.Content.Redirect, -1); + return; - var redirectUrl = "#"; - if (redirectId > 0) - { - redirectUrl = _routingContext.UrlProvider.GetUrl(redirectId); - } - else - { + var redirectId = _pcr.PublishedContent.GetPropertyValue(Constants.Conventions.Content.Redirect, -1); + + var redirectUrl = "#"; + if (redirectId > 0) + { + redirectUrl = _routingContext.UrlProvider.GetUrl(redirectId); + } + else + { // might be a UDI instead of an int Id - var redirectUdi = _pcr.PublishedContent.GetPropertyValue(Constants.Conventions.Content.Redirect); - if (redirectUdi != null) - redirectUrl = _routingContext.UrlProvider.GetUrl(redirectUdi.Guid); - } - if (redirectUrl != "#") - { - _pcr.SetRedirect(redirectUrl); - } - } - #endregion - } + var redirectUdi = _pcr.PublishedContent.GetPropertyValue(Constants.Conventions.Content.Redirect); + if (redirectUdi != null) + redirectUrl = _routingContext.UrlProvider.GetUrl(redirectUdi.Guid); + } + if (redirectUrl != "#") + { + _pcr.SetRedirect(redirectUrl); + } + } + #endregion + } } From b9430158cedb0bf5b1c0b93f543ec5383d8bec41 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Mon, 14 Jan 2019 09:25:59 +0100 Subject: [PATCH 64/93] Move member group picker property data store from NVarchar to NText (#3712) --- .../UpdateMemberGroupPickerData.cs | 36 +++++++++++++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../MemberGroupPickerPropertyEditor.cs | 2 +- 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourteenZero/UpdateMemberGroupPickerData.cs diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourteenZero/UpdateMemberGroupPickerData.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourteenZero/UpdateMemberGroupPickerData.cs new file mode 100644 index 0000000000..2dbc69e58a --- /dev/null +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourteenZero/UpdateMemberGroupPickerData.cs @@ -0,0 +1,36 @@ +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence.SqlSyntax; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFourteenZero +{ + /// + /// Migrates member group picker properties from NVarchar to NText. See https://github.com/umbraco/Umbraco-CMS/issues/3268. + /// + [Migration("7.14.0", 1, Constants.System.UmbracoMigrationName)] + public class UpdateMemberGroupPickerData : MigrationBase + { + public UpdateMemberGroupPickerData(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) + { + } + + public override void Up() + { + // move the data for all member group properties from the NVarchar to the NText column and clear the NVarchar column + Execute.Sql(@"UPDATE cmsPropertyData SET dataNtext = dataNvarchar, dataNvarchar = NULL + WHERE dataNtext IS NULL AND id IN ( + SELECT id FROM cmsPropertyData WHERE propertyTypeId in ( + SELECT id from cmsPropertyType where dataTypeID IN ( + SELECT nodeId FROM cmsDataType WHERE propertyEditorAlias = 'Umbraco.MemberGroupPicker' + ) + ) + )"); + + // ensure that all exising member group properties are defined as NText + Execute.Sql("UPDATE cmsDataType SET dbType = 'Ntext' WHERE propertyEditorAlias = 'Umbraco.MemberGroupPicker'"); + } + + public override void Down() + { + } + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index c7393e524f..8ee29e777f 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -572,6 +572,7 @@ + diff --git a/src/Umbraco.Web/PropertyEditors/MemberGroupPickerPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/MemberGroupPickerPropertyEditor.cs index 433199c536..6d7dd9e344 100644 --- a/src/Umbraco.Web/PropertyEditors/MemberGroupPickerPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/MemberGroupPickerPropertyEditor.cs @@ -8,7 +8,7 @@ using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.MemberGroupPickerAlias, "Member Group Picker", "membergrouppicker", Group="People", Icon="icon-users")] + [PropertyEditor(Constants.PropertyEditors.MemberGroupPickerAlias, "Member Group Picker", PropertyEditorValueTypes.Text, "membergrouppicker", Group="People", Icon="icon-users")] public class MemberGroupPickerPropertyEditor : PropertyEditor { } From f7671ec9a7653082d432352a7801e5bedb621c5c Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 15 Jan 2019 08:39:43 +0100 Subject: [PATCH 65/93] Fixes #4071 - Error executing scheduled task - This instance has already started one or more requests. --- src/Umbraco.Web/Scheduling/ScheduledTasks.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web/Scheduling/ScheduledTasks.cs b/src/Umbraco.Web/Scheduling/ScheduledTasks.cs index 4278b82c34..7387bca000 100644 --- a/src/Umbraco.Web/Scheduling/ScheduledTasks.cs +++ b/src/Umbraco.Web/Scheduling/ScheduledTasks.cs @@ -63,10 +63,11 @@ namespace Umbraco.Web.Scheduling private async Task GetTaskByHttpAync(string url, CancellationToken token) { if (_httpClient == null) - _httpClient = new HttpClient(); - - if (Uri.TryCreate(_appContext.UmbracoApplicationUrl, UriKind.Absolute, out var baseUri)) - _httpClient.BaseAddress = baseUri; + { + _httpClient = Uri.TryCreate(_appContext.UmbracoApplicationUrl, UriKind.Absolute, out var baseUri) + ? new HttpClient { BaseAddress = baseUri } + : new HttpClient(); + } var request = new HttpRequestMessage(HttpMethod.Get, url); From 44ab0991ceaa653befbd0eede1277902d047fff0 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 15 Jan 2019 22:08:08 +1100 Subject: [PATCH 66/93] Package installing is working --- .../Composing/Composers/ServicesComposer.cs | 1 - src/Umbraco.Core/Composing/TypeLoader.cs | 2 +- src/Umbraco.Core/IO/IOHelper.cs | 54 --- src/Umbraco.Core/IO/ShadowWrapper.cs | 2 +- src/Umbraco.Core/IO/SystemDirectories.cs | 4 + .../Models/Packaging/CompiledPackage.cs | 3 +- .../Models/Packaging/InstallationSummary.cs | 2 +- .../Models/Packaging/PackageDefinition.cs | 3 + .../Models/Packaging/UninstallationSummary.cs | 6 +- .../Packaging/CompiledPackageXmlParser.cs | 4 +- .../Packaging/IPackageActionRunner.cs | 11 +- .../Packaging/IPackageInstallation.cs | 13 +- .../Packaging/PackageActionRunner.cs | 29 +- .../Packaging/PackageDataInstallation.cs | 127 ++++++- .../Packaging/PackageDefinitionXmlParser.cs | 2 +- .../Packaging/PackageFileInstallation.cs | 26 ++ .../Packaging/PackageInstallation.cs | 77 ++-- .../Packaging/PackagesRepository.cs | 2 +- .../Services/IPackagingService.cs | 16 +- .../Services/Implement/PackagingService.cs | 48 +-- .../Sync/DatabaseServerMessenger.cs | 2 +- src/Umbraco.Examine/LuceneIndexCreator.cs | 2 +- .../Composing/TypeLoaderTests.cs | 2 +- src/Umbraco.Tests/IO/ShadowFileSystemTests.cs | 8 +- .../Packaging/PackageInstallationTest.cs | 20 +- .../Scoping/ScopeFileSystemsTests.cs | 2 +- src/Umbraco.Tests/TestHelpers/TestObjects.cs | 3 +- .../src/views/packages/edit.controller.js | 2 + .../src/views/packages/edit.html | 2 +- .../views/install-local.controller.js | 131 ++++--- .../Binders/ContentModelBinderHelper.cs | 3 +- .../Editors/ContentTypeController.cs | 19 +- src/Umbraco.Web/Editors/MediaController.cs | 2 +- src/Umbraco.Web/Editors/PackageController.cs | 2 +- .../Editors/PackageInstallController.cs | 258 +++---------- src/Umbraco.Web/Editors/UsersController.cs | 2 +- .../Controllers/InstallPackageController.cs | 346 +++++++++--------- src/Umbraco.Web/Install/InstallHelper.cs | 25 -- .../Install/InstallStatusTracker.cs | 4 +- .../InstallSteps/StarterKitDownloadStep.cs | 24 +- .../InstallSteps/StarterKitInstallStep.cs | 10 +- .../Models/ContentTypeImportModel.cs | 12 +- .../Models/LocalPackageInstallModel.cs | 10 +- src/Umbraco.Web/Models/PackageInstallModel.cs | 10 +- .../Runtime/WebRuntimeComponent.cs | 5 +- src/Umbraco.Web/Umbraco.Web.csproj | 1 - .../FileUploadCleanupFilterAttribute.cs | 16 +- .../_Legacy/PackageActions/addApplication.cs | 2 +- .../PackageActions/addDashboardSection.cs | 98 ----- .../PackageActions/addProxyFeedHost.cs | 2 +- .../_Legacy/PackageActions/allowDoctype.cs | 3 +- .../PackageActions/publishRootDocument.cs | 4 +- 52 files changed, 669 insertions(+), 795 deletions(-) delete mode 100644 src/Umbraco.Web/_Legacy/PackageActions/addDashboardSection.cs diff --git a/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs b/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs index a09462c806..bd07123fb2 100644 --- a/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs +++ b/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs @@ -72,7 +72,6 @@ namespace Umbraco.Core.Composing.Composers new PackageInstallation( factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), - SystemDirectories.Packages, appRoot, appRoot)); //TODO: These are replaced in the web project - we need to declare them so that diff --git a/src/Umbraco.Core/Composing/TypeLoader.cs b/src/Umbraco.Core/Composing/TypeLoader.cs index 3121e869c3..acb12ab575 100644 --- a/src/Umbraco.Core/Composing/TypeLoader.cs +++ b/src/Umbraco.Core/Composing/TypeLoader.cs @@ -405,7 +405,7 @@ namespace Umbraco.Core.Composing break; case LocalTempStorage.Default: default: - var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/TypesCache"); + var tempFolder = IOHelper.MapPath(SystemDirectories.TempData.EnsureEndsWith('/') + "TypesCache"); _fileBasePath = Path.Combine(tempFolder, "umbraco-types." + NetworkHelper.FileSafeMachineName); break; } diff --git a/src/Umbraco.Core/IO/IOHelper.cs b/src/Umbraco.Core/IO/IOHelper.cs index 7773f378a5..76e7631482 100644 --- a/src/Umbraco.Core/IO/IOHelper.cs +++ b/src/Umbraco.Core/IO/IOHelper.cs @@ -31,16 +31,6 @@ namespace Umbraco.Core.IO public static char DirSepChar => Path.DirectorySeparatorChar; - internal static void UnZip(string zipFilePath, string unPackDirectory, bool deleteZipFile) - { - // Unzip - var tempDir = unPackDirectory; - Directory.CreateDirectory(tempDir); - ZipFile.ExtractToDirectory(zipFilePath, unPackDirectory); - if (deleteZipFile) - File.Delete(zipFilePath); - } - //helper to try and match the old path to a new virtual one public static string FindFile(string virtualPath) { @@ -123,11 +113,6 @@ namespace Umbraco.Core.IO return MapPath(path, true); } - public static string MapPathIfVirtual(string path) - { - return path.StartsWith("~/") ? MapPath(path) : path; - } - //use a tilde character instead of the complete path internal static string ReturnPath(string settingsKey, string standardPath, bool useTilde) { @@ -156,20 +141,6 @@ namespace Umbraco.Core.IO return VerifyEditPath(filePath, new[] { validDir }); } - /// - /// Validates that the current filepath matches a directory where the user is allowed to edit a file. - /// - /// The filepath to validate. - /// The valid directory. - /// True, if the filepath is valid, else an exception is thrown. - /// The filepath is invalid. - internal static bool ValidateEditPath(string filePath, string validDir) - { - if (VerifyEditPath(filePath, validDir) == false) - throw new FileSecurityException(String.Format("The filepath '{0}' is not within an allowed directory for this type of files", filePath.Replace(MapPath(SystemDirectories.Root), ""))); - return true; - } - /// /// Verifies that the current filepath matches one of several directories where the user is allowed to edit a file. /// @@ -221,20 +192,6 @@ namespace Umbraco.Core.IO return ext != null && validFileExtensions.Contains(ext.TrimStart('.')); } - /// - /// Validates that the current filepath has one of several authorized extensions. - /// - /// The filepath to validate. - /// The valid extensions. - /// True, if the filepath is valid, else an exception is thrown. - /// The filepath is invalid. - internal static bool ValidateFileExtension(string filePath, List validFileExtensions) - { - if (VerifyFileExtension(filePath, validFileExtensions) == false) - throw new FileSecurityException(String.Format("The extension for the current file '{0}' is not of an allowed type for this editor. This is typically controlled from either the installed MacroEngines or based on configuration in /config/umbracoSettings.config", filePath.Replace(MapPath(SystemDirectories.Root), ""))); - return true; - } - public static bool PathStartsWith(string path, string root, char separator) { // either it is identical to root, @@ -329,17 +286,6 @@ namespace Umbraco.Core.IO Directory.CreateDirectory(absolutePath); } - public static void EnsureFileExists(string path, string contents) - { - var absolutePath = IOHelper.MapPath(path); - if (File.Exists(absolutePath)) return; - - using (var writer = File.CreateText(absolutePath)) - { - writer.Write(contents); - } - } - /// /// Checks if a given path is a full path including drive letter /// diff --git a/src/Umbraco.Core/IO/ShadowWrapper.cs b/src/Umbraco.Core/IO/ShadowWrapper.cs index 6493238391..d71f328713 100644 --- a/src/Umbraco.Core/IO/ShadowWrapper.cs +++ b/src/Umbraco.Core/IO/ShadowWrapper.cs @@ -7,7 +7,7 @@ namespace Umbraco.Core.IO { internal class ShadowWrapper : IFileSystem { - private const string ShadowFsPath = "~/App_Data/TEMP/ShadowFs"; + private static readonly string ShadowFsPath = SystemDirectories.TempData.EnsureEndsWith('/') + "ShadowFs"; private readonly Func _isScoped; private readonly IFileSystem _innerFileSystem; diff --git a/src/Umbraco.Core/IO/SystemDirectories.cs b/src/Umbraco.Core/IO/SystemDirectories.cs index 94aa7b16cc..4ea3ed64d5 100644 --- a/src/Umbraco.Core/IO/SystemDirectories.cs +++ b/src/Umbraco.Core/IO/SystemDirectories.cs @@ -12,6 +12,10 @@ namespace Umbraco.Core.IO public static string Data => "~/App_Data"; + public static string TempData => Data + "/TEMP"; + + public static string TempFileUploads => TempData + "/FileUploads"; + public static string Install => "~/install"; //fixme: remove this diff --git a/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs b/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs index 266c1f5518..d458337bf9 100644 --- a/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs +++ b/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Xml.Linq; @@ -10,7 +11,7 @@ namespace Umbraco.Core.Models.Packaging /// public class CompiledPackage : IPackageInfo { - public string PackageFileName { get; set; } + public FileInfo PackageFile { get; set; } public string Name { get; set; } public string Version { get; set; } diff --git a/src/Umbraco.Core/Models/Packaging/InstallationSummary.cs b/src/Umbraco.Core/Models/Packaging/InstallationSummary.cs index 7d81f48dd2..1cab17e220 100644 --- a/src/Umbraco.Core/Models/Packaging/InstallationSummary.cs +++ b/src/Umbraco.Core/Models/Packaging/InstallationSummary.cs @@ -20,7 +20,7 @@ namespace Umbraco.Core.Models.Packaging public IEnumerable StylesheetsInstalled { get; set; } = Enumerable.Empty(); public IEnumerable ContentInstalled { get; set; } = Enumerable.Empty(); public IEnumerable Actions { get; set; } = Enumerable.Empty(); - public bool PackageInstalled { get; set; } + public IEnumerable ActionErrors { get; set; } = Enumerable.Empty(); } diff --git a/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs b/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs index de8415ddd2..bab52c63f6 100644 --- a/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs +++ b/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs @@ -53,6 +53,9 @@ namespace Umbraco.Core.Models.Packaging [Url] public string Url { get; set; } = string.Empty; + /// + /// The full path to the package's zip file when it was installed (or is being installed) + /// [ReadOnly(true)] [DataMember(Name = "packagePath")] public string PackagePath { get; set; } = string.Empty; diff --git a/src/Umbraco.Core/Models/Packaging/UninstallationSummary.cs b/src/Umbraco.Core/Models/Packaging/UninstallationSummary.cs index 20cbedbcf0..fd39954f6b 100644 --- a/src/Umbraco.Core/Models/Packaging/UninstallationSummary.cs +++ b/src/Umbraco.Core/Models/Packaging/UninstallationSummary.cs @@ -16,9 +16,9 @@ namespace Umbraco.Core.Models.Packaging public IEnumerable MacrosUninstalled { get; set; } = Enumerable.Empty(); public IEnumerable FilesUninstalled { get; set; } = Enumerable.Empty(); public IEnumerable TemplatesUninstalled { get; set; } = Enumerable.Empty(); - public IEnumerable ContentTypesUninstalled { get; set; } = Enumerable.Empty(); + public IEnumerable DocumentTypesUninstalled { get; set; } = Enumerable.Empty(); public IEnumerable StylesheetsUninstalled { get; set; } = Enumerable.Empty(); - public IEnumerable ContentUninstalled { get; set; } = Enumerable.Empty(); - public bool PackageUninstalled { get; set; } + public IEnumerable Actions { get; set; } = Enumerable.Empty(); + public IEnumerable ActionErrors { get; set; } = Enumerable.Empty(); } } diff --git a/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs index 0f3119f697..eb9cf46008 100644 --- a/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs +++ b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs @@ -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, diff --git a/src/Umbraco.Core/Packaging/IPackageActionRunner.cs b/src/Umbraco.Core/Packaging/IPackageActionRunner.cs index d5c6327115..1f8c134364 100644 --- a/src/Umbraco.Core/Packaging/IPackageActionRunner.cs +++ b/src/Umbraco.Core/Packaging/IPackageActionRunner.cs @@ -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 /// Name of the package. /// The action alias. /// The action XML. - void RunPackageAction(string packageName, string actionAlias, XElement actionXml); + /// + bool RunPackageAction(string packageName, string actionAlias, XElement actionXml, out IEnumerable errors); /// /// Undos the package action with the specified action alias. @@ -18,6 +20,7 @@ namespace Umbraco.Core.Packaging /// Name of the package. /// The action alias. /// The action XML. - void UndoPackageAction(string packageName, string actionAlias, XElement actionXml); + /// + bool UndoPackageAction(string packageName, string actionAlias, XElement actionXml, out IEnumerable errors); } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Packaging/IPackageInstallation.cs b/src/Umbraco.Core/Packaging/IPackageInstallation.cs index 83a21dcd2f..5dae76674d 100644 --- a/src/Umbraco.Core/Packaging/IPackageInstallation.cs +++ b/src/Umbraco.Core/Packaging/IPackageInstallation.cs @@ -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 { + /// + /// Uninstalls a package including all data, entities and files + /// + /// + /// + /// + UninstallationSummary UninstallPackage(PackageDefinition packageDefinition, int userId); + /// /// Installs a packages data and entities /// @@ -27,8 +36,8 @@ namespace Umbraco.Core.Packaging /// /// Reads the package (zip) file and returns the model /// - /// + /// /// - CompiledPackage ReadPackage(string packageFileName); + CompiledPackage ReadPackage(FileInfo packageFile); } } diff --git a/src/Umbraco.Core/Packaging/PackageActionRunner.cs b/src/Umbraco.Core/Packaging/PackageActionRunner.cs index 37103b0473..38275d5f0a 100644 --- a/src/Umbraco.Core/Packaging/PackageActionRunner.cs +++ b/src/Umbraco.Core/Packaging/PackageActionRunner.cs @@ -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; } - /// - /// Runs the package action with the specified action alias. - /// - /// Name of the package. - /// The action alias. - /// The action XML. - public void RunPackageAction(string packageName, string actionAlias, XElement actionXml) + /// + public bool RunPackageAction(string packageName, string actionAlias, XElement actionXml, out IEnumerable errors) { - + var e = new List(); 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(ex, "Error loading package action '{PackageActionAlias}' for package {PackageName}", ipa.Alias(), packageName); } } + + errors = e; + return e.Count == 0; } - /// - /// Undos the package action with the specified action alias. - /// - /// Name of the package. - /// The action alias. - /// The action XML. - public void UndoPackageAction(string packageName, string actionAlias, XElement actionXml) + /// + public bool UndoPackageAction(string packageName, string actionAlias, XElement actionXml, out IEnumerable errors) { + var e = new List(); 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(ex, "Error undoing package action '{PackageActionAlias}' for package {PackageName}", ipa.Alias(), packageName); } } + errors = e; + return e.Count == 0; } } diff --git a/src/Umbraco.Core/Packaging/PackageDataInstallation.cs b/src/Umbraco.Core/Packaging/PackageDataInstallation.cs index 8ef99502c8..1743bb0041 100644 --- a/src/Umbraco.Core/Packaging/PackageDataInstallation.cs +++ b/src/Umbraco.Core/Packaging/PackageDataInstallation.cs @@ -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(); + var removedMacros = new List(); + var removedContentTypes = new List(); + var removedDictionaryItems = new List(); + var removedDataTypes = new List(); + var removedLanguages = new List(); + + + //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(); + 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 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("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()); diff --git a/src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs b/src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs index 1f0e5ec92e..0d7adf8ece 100644 --- a/src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs +++ b/src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs @@ -91,7 +91,7 @@ namespace Umbraco.Core.Packaging new XElement("datatypes", string.Join(",", def.DataTypes ?? Array.Empty())), 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())), diff --git a/src/Umbraco.Core/Packaging/PackageFileInstallation.cs b/src/Umbraco.Core/Packaging/PackageFileInstallation.cs index 55b8bdc63e..7c0891175b 100644 --- a/src/Umbraco.Core/Packaging/PackageFileInstallation.cs +++ b/src/Umbraco.Core/Packaging/PackageFileInstallation.cs @@ -53,6 +53,32 @@ namespace Umbraco.Core.Packaging } } + public IEnumerable UninstallFiles(PackageDefinition package) + { + var removedFiles = new List(); + + 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 diff --git a/src/Umbraco.Core/Packaging/PackageInstallation.cs b/src/Umbraco.Core/Packaging/PackageInstallation.cs index 12d4769235..4563d31560 100644 --- a/src/Umbraco.Core/Packaging/PackageInstallation.cs +++ b/src/Umbraco.Core/Packaging/PackageInstallation.cs @@ -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 /// /// /// - /// - /// The relative path of the package storage folder (i.e. ~/App_Data/Packages ) - /// /// /// The root folder of the application /// @@ -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 /// 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 actions) + private IEnumerable RunPackageActions(PackageDefinition packageDefinition, IEnumerable 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 UndoPackageActions(IPackageInfo packageDefinition, IEnumerable 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) { diff --git a/src/Umbraco.Core/Packaging/PackagesRepository.cs b/src/Umbraco.Core/Packaging/PackagesRepository.cs index c0620820a2..af5c7ffded 100644 --- a/src/Umbraco.Core/Packaging/PackagesRepository.cs +++ b/src/Umbraco.Core/Packaging/PackagesRepository.cs @@ -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"; diff --git a/src/Umbraco.Core/Services/IPackagingService.cs b/src/Umbraco.Core/Services/IPackagingService.cs index 8aa936c8ff..a31fc2d6cf 100644 --- a/src/Umbraco.Core/Services/IPackagingService.cs +++ b/src/Umbraco.Core/Services/IPackagingService.cs @@ -16,25 +16,27 @@ namespace Umbraco.Core.Services /// /// Returns a result from an umbraco package file (zip) /// - /// + /// /// - CompiledPackage GetCompiledPackageInfo(string packageFileName); + CompiledPackage GetCompiledPackageInfo(FileInfo packageFile); /// /// Installs the package files contained in an umbraco package file (zip) /// /// - /// + /// /// - IEnumerable InstallCompiledPackageFiles(PackageDefinition packageDefinition, string packageFileName, int userId = 0); + IEnumerable InstallCompiledPackageFiles(PackageDefinition packageDefinition, FileInfo packageFile, int userId = 0); /// /// Installs the data, entities, objects contained in an umbraco package file (zip) /// /// - /// + /// /// - InstallationSummary InstallCompiledPackageData(PackageDefinition packageDefinition, string packageFileName, int userId = 0); + InstallationSummary InstallCompiledPackageData(PackageDefinition packageDefinition, FileInfo packageFile, int userId = 0); + + UninstallationSummary UninstallPackage(PackageDefinition packageDefinition, int userId = 0); #endregion @@ -81,6 +83,6 @@ namespace Umbraco.Core.Services /// /// The file name of the downloaded package which will exist in ~/App_Data/packages /// - Task FetchPackageFileAsync(Guid packageId, Version umbracoVersion, int userId); + Task FetchPackageFileAsync(Guid packageId, Version umbracoVersion, int userId); } } diff --git a/src/Umbraco.Core/Services/Implement/PackagingService.cs b/src/Umbraco.Core/Services/Implement/PackagingService.cs index ba5d30f8d7..362b1fc67b 100644 --- a/src/Umbraco.Core/Services/Implement/PackagingService.cs +++ b/src/Umbraco.Core/Services/Implement/PackagingService.cs @@ -53,7 +53,7 @@ namespace Umbraco.Core.Services.Implement #region Package Files /// - public async Task FetchPackageFileAsync(Guid packageId, Version umbracoVersion, int userId) + public async Task FetchPackageFileAsync(Guid packageId, Version umbracoVersion, int userId) { //includeHidden = true because we don't care if it's hidden we want to get the file regardless var url = $"{Constants.PackageRepository.RestApiBaseUrl}/{packageId}?version={umbracoVersion.ToString(3)}&includeHidden=true&asFile=true"; @@ -85,7 +85,7 @@ namespace Umbraco.Core.Services.Implement using (var fs1 = new FileStream(packageFilePath, FileMode.Create)) { fs1.Write(bytes, 0, bytes.Length); - return packageId + ".umb"; + return new FileInfo(packageFilePath); } } @@ -97,18 +97,19 @@ namespace Umbraco.Core.Services.Implement #region Installation - public CompiledPackage GetCompiledPackageInfo(string packageFileName) => _packageInstallation.ReadPackage(packageFileName); + public CompiledPackage GetCompiledPackageInfo(FileInfo packageFile) => _packageInstallation.ReadPackage(packageFile); - public IEnumerable InstallCompiledPackageFiles(PackageDefinition packageDefinition, string packageFileName, int userId = 0) + public IEnumerable InstallCompiledPackageFiles(PackageDefinition packageDefinition, FileInfo packageFile, int userId = 0) { 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"); - var compiledPackage = GetCompiledPackageInfo(packageFileName); - if (compiledPackage == null) throw new InvalidOperationException("Could not read the package file " + packageFileName); + var compiledPackage = GetCompiledPackageInfo(packageFile); + if (compiledPackage == null) throw new InvalidOperationException("Could not read the package file " + packageFile); - var files = _packageInstallation.InstallPackageFiles(packageDefinition, compiledPackage, userId); + var files = _packageInstallation.InstallPackageFiles(packageDefinition, compiledPackage, userId).ToList(); + packageDefinition.Files = files; SaveInstalledPackage(packageDefinition); @@ -117,16 +118,16 @@ namespace Umbraco.Core.Services.Implement return files; } - public InstallationSummary InstallCompiledPackageData(PackageDefinition packageDefinition, string packageFileName, int userId = 0) + public InstallationSummary InstallCompiledPackageData(PackageDefinition packageDefinition, FileInfo packageFile, int userId = 0) { 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"); - var compiledPackage = GetCompiledPackageInfo(packageFileName); - if (compiledPackage == null) throw new InvalidOperationException("Could not read the package file " + packageFileName); + var compiledPackage = GetCompiledPackageInfo(packageFile); + if (compiledPackage == null) throw new InvalidOperationException("Could not read the package file " + packageFile); - if (ImportingPackage.IsRaisedEventCancelled(new ImportPackageEventArgs(packageFileName, compiledPackage), this)) + if (ImportingPackage.IsRaisedEventCancelled(new ImportPackageEventArgs(packageFile.Name, compiledPackage), this)) return new InstallationSummary { MetaData = compiledPackage }; var summary = _packageInstallation.InstallPackageData(packageDefinition, compiledPackage, userId); @@ -140,6 +141,20 @@ namespace Umbraco.Core.Services.Implement return summary; } + public UninstallationSummary UninstallPackage(PackageDefinition package, int userId = 0) + { + var summary = _packageInstallation.UninstallPackage(package, userId); + + SaveInstalledPackage(package); + + DeleteInstalledPackage(package.Id, userId); + + // trigger the UninstalledPackage event + UninstalledPackage.RaiseEvent(new UninstallPackageEventArgs(summary, package, false), this); + + return summary; + } + #endregion #region Created/Installed Package Repositories @@ -179,21 +194,12 @@ namespace Umbraco.Core.Services.Implement #endregion - /// - /// This method can be used to trigger the 'UninstalledPackage' event when a package is uninstalled by something else but this service. - /// - /// - internal static void OnUninstalledPackage(UninstallPackageEventArgs args) - { - UninstalledPackage.RaiseEvent(args, null); - } - #region Event Handlers /// /// Occurs before Importing umbraco package /// - internal static event TypedEventHandler> ImportingPackage; + public static event TypedEventHandler> ImportingPackage; /// /// Occurs after a package is imported diff --git a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs index 5cfcb501e5..6844e6e75c 100644 --- a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs +++ b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs @@ -551,7 +551,7 @@ namespace Umbraco.Core.Sync break; case LocalTempStorage.Default: default: - var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/DistCache"); + var tempFolder = IOHelper.MapPath(SystemDirectories.TempData.EnsureEndsWith('/') + "DistCache"); distCacheFilePath = Path.Combine(tempFolder, fileName); break; } diff --git a/src/Umbraco.Examine/LuceneIndexCreator.cs b/src/Umbraco.Examine/LuceneIndexCreator.cs index 0e83b37dc5..806d0edc7a 100644 --- a/src/Umbraco.Examine/LuceneIndexCreator.cs +++ b/src/Umbraco.Examine/LuceneIndexCreator.cs @@ -29,7 +29,7 @@ namespace Umbraco.Examine public virtual Lucene.Net.Store.Directory CreateFileSystemLuceneDirectory(string folderName) { - var dirInfo = new DirectoryInfo(Path.Combine(IOHelper.MapPath(SystemDirectories.Data), "TEMP", "ExamineIndexes", folderName)); + var dirInfo = new DirectoryInfo(Path.Combine(IOHelper.MapPath(SystemDirectories.TempData), "ExamineIndexes", folderName)); if (!dirInfo.Exists) System.IO.Directory.CreateDirectory(dirInfo.FullName); diff --git a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs index 5148c7eb1b..1649f3675d 100644 --- a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs @@ -30,7 +30,7 @@ namespace Umbraco.Tests.Composing // this ensures it's reset _typeLoader = new TypeLoader(NullCacheProvider.Instance, LocalTempStorage.Default, new ProfilingLogger(Mock.Of(), Mock.Of())); - foreach (var file in Directory.GetFiles(IOHelper.MapPath("~/App_Data/TEMP/TypesCache"))) + foreach (var file in Directory.GetFiles(IOHelper.MapPath(SystemDirectories.TempData.EnsureEndsWith('/') + "TypesCache"))) File.Delete(file); // for testing, we'll specify which assemblies are scanned for the PluginTypeResolver diff --git a/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs b/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs index 262b026cae..2244f9085d 100644 --- a/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs +++ b/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs @@ -41,7 +41,7 @@ namespace Umbraco.Tests.IO private static void ClearFiles() { TestHelper.DeleteDirectory(IOHelper.MapPath("FileSysTests")); - TestHelper.DeleteDirectory(IOHelper.MapPath("App_Data/TEMP/ShadowFs")); + TestHelper.DeleteDirectory(IOHelper.MapPath(SystemDirectories.TempData.EnsureEndsWith('/') + "ShadowFs")); } private static string NormPath(string path) @@ -388,7 +388,7 @@ namespace Umbraco.Tests.IO var logger = Mock.Of(); var path = IOHelper.MapPath("FileSysTests"); - var shadowfs = IOHelper.MapPath("App_Data/TEMP/ShadowFs"); + var shadowfs = IOHelper.MapPath(SystemDirectories.TempData.EnsureEndsWith('/') + "ShadowFs"); Directory.CreateDirectory(path); Directory.CreateDirectory(shadowfs); @@ -482,7 +482,7 @@ namespace Umbraco.Tests.IO var logger = Mock.Of(); var path = IOHelper.MapPath("FileSysTests"); - var shadowfs = IOHelper.MapPath("App_Data/TEMP/ShadowFs"); + var shadowfs = IOHelper.MapPath(SystemDirectories.TempData.EnsureEndsWith('/') + "ShadowFs"); Directory.CreateDirectory(path); var scopedFileSystems = false; @@ -535,7 +535,7 @@ namespace Umbraco.Tests.IO var logger = Mock.Of(); var path = IOHelper.MapPath("FileSysTests"); - var shadowfs = IOHelper.MapPath("App_Data/TEMP/ShadowFs"); + var shadowfs = IOHelper.MapPath(SystemDirectories.TempData.EnsureEndsWith('/') + "ShadowFs"); Directory.CreateDirectory(path); var scopedFileSystems = false; diff --git a/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs b/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs index 609d26aec9..7514eea00b 100644 --- a/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs +++ b/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs @@ -50,11 +50,10 @@ namespace Umbraco.Tests.Packaging PackageDataInstallation, new PackageFileInstallation(Parser, ProfilingLogger), Parser, Mock.Of(), - packagesFolderPath: "~/Packaging/packages",//this is where our test zip file is applicationRootFolder: new DirectoryInfo(IOHelper.GetRootDirectorySafe()), packageExtractionFolder: new DirectoryInfo(IOHelper.MapPath("~/" + _testBaseFolder))); //we don't want to extract package files to the real root, so extract to a test folder - const string documentTypePickerUmb = "Document_Type_Picker_1.1.umb"; + private const string DocumentTypePickerUmb = "Document_Type_Picker_1.1.umb"; //[Test] //public void PackagingService_Can_ImportPackage() @@ -72,7 +71,9 @@ namespace Umbraco.Tests.Packaging [Test] public void Can_Read_Compiled_Package() { - var package = PackageInstallation.ReadPackage(documentTypePickerUmb); + var package = PackageInstallation.ReadPackage( + //this is where our test zip file is + new FileInfo(Path.Combine(IOHelper.MapPath("~/Packaging/packages"), DocumentTypePickerUmb))); Assert.IsNotNull(package); Assert.AreEqual(1, package.Files.Count); Assert.AreEqual("095e064b-ba4d-442d-9006-3050983c13d8.dll", package.Files[0].UniqueFileName); @@ -95,7 +96,10 @@ namespace Umbraco.Tests.Packaging { - var preInstallWarnings = PackageInstallation.ReadPackage(documentTypePickerUmb).Warnings; + var preInstallWarnings = PackageInstallation.ReadPackage( + //this is where our test zip file is + new FileInfo(Path.Combine(IOHelper.MapPath("~/Packaging/packages"), DocumentTypePickerUmb))) + .Warnings; Assert.IsNotNull(preInstallWarnings); //TODO: Assert! @@ -104,7 +108,9 @@ namespace Umbraco.Tests.Packaging [Test] public void Install_Files() { - var package = PackageInstallation.ReadPackage(documentTypePickerUmb); + var package = PackageInstallation.ReadPackage( + //this is where our test zip file is + new FileInfo(Path.Combine(IOHelper.MapPath("~/Packaging/packages"), DocumentTypePickerUmb))); var def = PackageDefinition.FromCompiledPackage(package); def.Id = 1; def.PackageId = Guid.NewGuid(); @@ -122,7 +128,9 @@ namespace Umbraco.Tests.Packaging [Test] public void Install_Data() { - var package = PackageInstallation.ReadPackage(documentTypePickerUmb); + var package = PackageInstallation.ReadPackage( + //this is where our test zip file is + new FileInfo(Path.Combine(IOHelper.MapPath("~/Packaging/packages"), DocumentTypePickerUmb))); var def = PackageDefinition.FromCompiledPackage(package); def.Id = 1; def.PackageId = Guid.NewGuid(); diff --git a/src/Umbraco.Tests/Scoping/ScopeFileSystemsTests.cs b/src/Umbraco.Tests/Scoping/ScopeFileSystemsTests.cs index 0ef0e9362b..0a23e4a1b9 100644 --- a/src/Umbraco.Tests/Scoping/ScopeFileSystemsTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopeFileSystemsTests.cs @@ -45,7 +45,7 @@ namespace Umbraco.Tests.Scoping { TestHelper.DeleteDirectory(IOHelper.MapPath("media")); TestHelper.DeleteDirectory(IOHelper.MapPath("FileSysTests")); - TestHelper.DeleteDirectory(IOHelper.MapPath("App_Data/TEMP/ShadowFs")); + TestHelper.DeleteDirectory(IOHelper.MapPath(SystemDirectories.TempData.EnsureEndsWith('/') + "ShadowFs")); } [TestCase(true)] diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects.cs b/src/Umbraco.Tests/TestHelpers/TestObjects.cs index 412a492376..684da2599e 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects.cs @@ -185,7 +185,8 @@ namespace Umbraco.Tests.TestHelpers new PackageDataInstallation(logger, fileService.Value, macroService.Value, localizationService.Value, dataTypeService.Value, entityService.Value, contentTypeService.Value, contentService.Value, propertyEditorCollection), new PackageFileInstallation(compiledPackageXmlParser, new ProfilingLogger(logger, new TestProfiler())), compiledPackageXmlParser, Mock.Of(), - "", null, null)); + new DirectoryInfo(IOHelper.GetRootDirectorySafe()), + new DirectoryInfo(IOHelper.GetRootDirectorySafe()))); }); var relationService = GetLazyService(factory, c => new RelationService(scopeProvider, logger, eventMessagesFactory, entityService.Value, GetRepo(c), GetRepo(c))); var treeService = GetLazyService(factory, c => new ApplicationTreeService(logger, cache, typeLoader)); diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js index 057f1160c2..4afe62786e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js @@ -144,6 +144,8 @@ vm.package = updatedPackage; vm.buttonState = "success"; + formHelper.resetForm({ scope: $scope }); + if (create) { //if we are creating, then redirect to the correct url and reload $location.path("packages/packages/edit/" + vm.package.id).search("subview", "created").search("create", null); diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html index 955676b806..a66eb94746 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html @@ -138,7 +138,7 @@ {{doctype.name}} diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.controller.js index 8be7d1b17a..9c6c2bb7c5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.controller.js @@ -10,7 +10,7 @@ vm.installPackage = installPackage; vm.installState = { status: "", - progress:0 + progress: 0 }; vm.installCompleted = false; vm.zipFile = { @@ -27,6 +27,23 @@ } }; + var labels = {}; + var labelKeys = [ + "packager_installStateImporting", + "packager_installStateInstalling", + "packager_installStateRestarting", + "packager_installStateComplete", + "packager_installStateCompleted" + ]; + + localizationService.localizeMany(labelKeys).then(function (values) { + labels.installStateImporting = values[0]; + labels.installStateInstalling = values[1]; + labels.installStateRestarting = values[2]; + labels.installStateComplete = values[3]; + labels.installStateCompleted = values[4]; + }); + function upload(file) { Upload.upload({ @@ -34,10 +51,10 @@ fields: {}, file: file }).progress(function (evt) { - + // hack: in some browsers the progress event is called after success // this prevents the UI from going back to a uploading state - if(vm.zipFile.uploadStatus !== "done" && vm.zipFile.uploadStatus !== "error") { + if (vm.zipFile.uploadStatus !== "done" && vm.zipFile.uploadStatus !== "error") { // set view state to uploading vm.state = 'uploading'; @@ -110,83 +127,85 @@ } function installPackage() { - vm.installState.status = localizationService.localize("packager_installStateImporting"); + + vm.installState.status = labels.installStateImporting; vm.installState.progress = "0"; packageResource - .import(vm.localPackage) - .then(function(pack) { - vm.installState.progress = "25"; - vm.installState.status = localizationService.localize("packager_installStateInstalling"); - return packageResource.installFiles(pack); - }, + .import(vm.localPackage) + .then(function (pack) { + vm.installState.progress = "25"; + vm.installState.status = labels.installStateInstalling; + return packageResource.installFiles(pack); + }, installError) - .then(function(pack) { - vm.installState.status = localizationService.localize("packager_installStateRestarting"); - vm.installState.progress = "50"; - var deferred = $q.defer(); + .then(function (pack) { + vm.installState.status = labels.installStateRestarting; + vm.installState.progress = "50"; + var deferred = $q.defer(); - //check if the app domain is restarted ever 2 seconds - var count = 0; - function checkRestart() { - $timeout(function () { + //check if the app domain is restarted ever 2 seconds + var count = 0; + + function checkRestart() { + $timeout(function () { packageResource.checkRestart(pack).then(function (d) { count++; //if there is an id it means it's not restarted yet but we'll limit it to only check 10 times if (d.isRestarting && count < 10) { - checkRestart(); + checkRestart(); } else { - //it's restarted! - deferred.resolve(d); + //it's restarted! + deferred.resolve(d); } - }, - installError); - }, 2000); - } + }, + installError); + }, + 2000); + } - checkRestart(); - - return deferred.promise; - }, installError) - .then(function(pack) { - vm.installState.status = localizationService.localize("packager_installStateRestarting"); - vm.installState.progress = "75"; - return packageResource.installData(pack); - }, + checkRestart(); + + return deferred.promise; + }, installError) - .then(function(pack) { - vm.installState.status = localizationService.localize("packager_installStateComplete"); - vm.installState.progress = "100"; - return packageResource.cleanUp(pack); - }, + .then(function (pack) { + vm.installState.status = labels.installStateRestarting; + vm.installState.progress = "75"; + return packageResource.installData(pack); + }, installError) - .then(function(result) { + .then(function (pack) { + vm.installState.status = labels.installStateComplete; + vm.installState.progress = "100"; + return packageResource.cleanUp(pack); + }, + installError) + .then(function (result) { - if (result.postInstallationPath) { - //Put the redirect Uri in a cookie so we can use after reloading - localStorageService.set("packageInstallUri", result.postInstallationPath); - } - else { - //set to a constant value so it knows to just go to the installed view - localStorageService.set("packageInstallUri", "installed"); - } + if (result.postInstallationPath) { + //Put the redirect Uri in a cookie so we can use after reloading + localStorageService.set("packageInstallUri", result.postInstallationPath); + } + else { + //set to a constant value so it knows to just go to the installed view + localStorageService.set("packageInstallUri", "installed"); + } - vm.installState.status = localizationService.localize("packager_installStateCompleted"); - vm.installCompleted = true; - - + vm.installState.status = labels.installStateCompleted; + vm.installCompleted = true; - }, - installError); + + }, installError); } - + function installError() { //This will return a rejection meaning that the promise change above will stop return $q.reject(); } - vm.reloadPage = function() { + vm.reloadPage = function () { //reload on next digest (after cookie) $timeout(function () { $window.location.reload(true); diff --git a/src/Umbraco.Web/Editors/Binders/ContentModelBinderHelper.cs b/src/Umbraco.Web/Editors/Binders/ContentModelBinderHelper.cs index 8c3edd915d..dd6d22a967 100644 --- a/src/Umbraco.Web/Editors/Binders/ContentModelBinderHelper.cs +++ b/src/Umbraco.Web/Editors/Binders/ContentModelBinderHelper.cs @@ -3,6 +3,7 @@ using System.Net.Http; using System.Web.Http; using System.Web.Http.Controllers; using Umbraco.Core; +using Umbraco.Core.IO; using Umbraco.Core.Models; using Umbraco.Core.Models.Editors; using Umbraco.Web.Models.ContentEditing; @@ -19,7 +20,7 @@ namespace Umbraco.Web.Editors.Binders public TModelSave BindModelFromMultipartRequest(HttpActionContext actionContext, ModelBindingContext bindingContext) where TModelSave : IHaveUploadedFiles { - var result = actionContext.ReadAsMultipart("~/App_Data/TEMP/FileUploads"); + var result = actionContext.ReadAsMultipart(SystemDirectories.TempFileUploads); var model = actionContext.GetModelFromMultipartRequest(result, "contentItem"); diff --git a/src/Umbraco.Web/Editors/ContentTypeController.cs b/src/Umbraco.Web/Editors/ContentTypeController.cs index 97f7ff3589..670d37e7a7 100644 --- a/src/Umbraco.Web/Editors/ContentTypeController.cs +++ b/src/Umbraco.Web/Editors/ContentTypeController.cs @@ -17,6 +17,7 @@ using Umbraco.Core.Dictionary; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Models.Editors; using Umbraco.Core.Packaging; using Umbraco.Core.Persistence; using Umbraco.Core.PropertyEditors; @@ -532,7 +533,7 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); } - var root = IOHelper.MapPath("~/App_Data/TEMP/FileUploads"); + var root = IOHelper.MapPath(SystemDirectories.TempData.EnsureEndsWith('/') + "FileUploads"); //ensure it exists Directory.CreateDirectory(root); var provider = new MultipartFormDataStreamProvider(root); @@ -545,26 +546,24 @@ namespace Umbraco.Web.Editors } var model = new ContentTypeImportModel(); + var file = result.FileData[0]; var fileName = file.Headers.ContentDisposition.FileName.Trim('\"'); var ext = fileName.Substring(fileName.LastIndexOf('.') + 1).ToLower(); if (ext.InvariantEquals("udt")) { - //TODO: Currently it has to be here, it's not ideal but that's the way it is right now - var tempDir = IOHelper.MapPath(SystemDirectories.Data); + model.TempFileName = Path.Combine(root, model.TempFileName); - //ensure it's there - Directory.CreateDirectory(tempDir); - - model.TempFileName = "justDelete_" + Guid.NewGuid() + ".udt"; - var tempFileLocation = Path.Combine(tempDir, model.TempFileName); - System.IO.File.Copy(file.LocalFileName, tempFileLocation, true); + model.UploadedFiles.Add(new ContentPropertyFile + { + TempFilePath = model.TempFileName + }); var xd = new XmlDocument { XmlResolver = null }; - xd.Load(tempFileLocation); + xd.Load(model.TempFileName); model.Alias = xd.DocumentElement?.SelectSingleNode("//DocumentType/Info/Alias")?.FirstChild.Value; model.Name = xd.DocumentElement?.SelectSingleNode("//DocumentType/Info/Name")?.FirstChild.Value; diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index 2c6ba06dc2..ce088e0caa 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -626,7 +626,7 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); } - var root = IOHelper.MapPath("~/App_Data/TEMP/FileUploads"); + var root = IOHelper.MapPath(SystemDirectories.TempFileUploads); //ensure it exists Directory.CreateDirectory(root); var provider = new MultipartFormDataStreamProvider(root); diff --git a/src/Umbraco.Web/Editors/PackageController.cs b/src/Umbraco.Web/Editors/PackageController.cs index 4b452ac6a6..063dcf483e 100644 --- a/src/Umbraco.Web/Editors/PackageController.cs +++ b/src/Umbraco.Web/Editors/PackageController.cs @@ -13,7 +13,7 @@ using Umbraco.Web.WebApi.Filters; namespace Umbraco.Web.Editors { /// - /// A controller used for installing packages and managing all of the data in the packages section in the back office + /// A controller used for managing packages in the back office /// [PluginController("UmbracoApi")] [SerializeVersion] diff --git a/src/Umbraco.Web/Editors/PackageInstallController.cs b/src/Umbraco.Web/Editors/PackageInstallController.cs index bfaec3f82c..38b75a27f8 100644 --- a/src/Umbraco.Web/Editors/PackageInstallController.cs +++ b/src/Umbraco.Web/Editors/PackageInstallController.cs @@ -16,6 +16,7 @@ using Umbraco.Core.Events; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Models.Editors; using Umbraco.Core.Models.Packaging; using Umbraco.Core.Packaging; using Umbraco.Core.Persistence; @@ -42,14 +43,11 @@ namespace Umbraco.Web.Editors [UmbracoApplicationAuthorize(Core.Constants.Applications.Packages)] public class PackageInstallController : UmbracoAuthorizedJsonController { - private readonly IPackageActionRunner _packageActionRunner; - public PackageInstallController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, CacheHelper applicationCache, - IProfilingLogger logger, IRuntimeState runtimeState, IPackageActionRunner packageActionRunner) + IProfilingLogger logger, IRuntimeState runtimeState) : base(globalSettings, umbracoContextAccessor, sqlContext, services, applicationCache, logger, runtimeState) { - _packageActionRunner = packageActionRunner; } /// @@ -75,7 +73,7 @@ namespace Umbraco.Web.Editors var package = Services.PackagingService.GetInstalledPackageById(packageId); if (package == null) return NotFound(); - PerformUninstall(package); + var summary = Services.PackagingService.UninstallPackage(package); //now get all other packages by this name since we'll uninstall all versions foreach (var installed in Services.PackagingService.GetAllInstalledPackages() @@ -94,176 +92,6 @@ namespace Umbraco.Web.Editors return Ok(); } - /// - /// SORRY :( I didn't have time to put this in a service somewhere - the old packager did this all manually too - /// - /// - protected void PerformUninstall(PackageDefinition package) - { - if (package == null) throw new ArgumentNullException("package"); - - var removedTemplates = new List(); - var removedMacros = new List(); - var removedContentTypes = new List(); - var removedDictionaryItems = new List(); - var removedDataTypes = new List(); - var removedFiles = new List(); - - //Uninstall templates - foreach (var item in package.Templates.ToArray()) - { - int nId; - if (int.TryParse(item, out nId) == false) continue; - var found = Services.FileService.GetTemplate(nId); - if (found != null) - { - removedTemplates.Add(found); - Services.FileService.DeleteTemplate(found.Alias, Security.GetUserId().ResultOr(0)); - } - package.Templates.Remove(nId.ToString()); - } - - //Uninstall macros - foreach (var item in package.Macros.ToArray()) - { - int nId; - if (int.TryParse(item, out nId) == false) continue; - var macro = Services.MacroService.GetById(nId); - if (macro != null) - { - removedMacros.Add(macro); - Services.MacroService.Delete(macro); - } - package.Macros.Remove(nId.ToString()); - } - - //Remove Document Types - var contentTypes = new List(); - var contentTypeService = Services.ContentTypeService; - foreach (var item in package.DocumentTypes.ToArray()) - { - int nId; - if (int.TryParse(item, out 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; - removedContentTypes.AddRange(orderedTypes); - contentTypeService.Delete(orderedTypes); - } - - //Remove Dictionary items - foreach (var item in package.DictionaryItems.ToArray()) - { - int nId; - if (int.TryParse(item, out nId) == false) continue; - var di = Services.LocalizationService.GetDictionaryItemById(nId); - if (di != null) - { - removedDictionaryItems.Add(di); - Services.LocalizationService.Delete(di); - } - package.DictionaryItems.Remove(nId.ToString()); - } - - //Remove Data types - foreach (var item in package.DataTypes.ToArray()) - { - int nId; - if (int.TryParse(item, out nId) == false) continue; - var dtd = Services.DataTypeService.GetDataType(nId); - if (dtd != null) - { - removedDataTypes.Add(dtd); - Services.DataTypeService.Delete(dtd); - } - package.DataTypes.Remove(nId.ToString()); - } - - Services.PackagingService.SaveInstalledPackage(package); - - // uninstall actions - //TODO: We should probably report errors to the UI!! - // This never happened before though, but we should do something now - if (package.Actions.IsNullOrWhiteSpace() == false) - { - try - { - var actionsXml = XDocument.Parse("" + package.Actions + ""); - - Logger.Debug("Executing undo actions: {UndoActionsXml}", actionsXml.ToString(SaveOptions.DisableFormatting)); - - foreach (var n in actionsXml.Root.Elements("Action")) - { - try - { - _packageActionRunner.UndoPackageAction(package.Name, n.AttributeValue("alias"), n); - } - catch (Exception ex) - { - Logger.Error(ex, "An error occurred running undo actions"); - } - } - } - catch (Exception ex) - { - Logger.Error(ex, "An error occurred running undo actions"); - } - } - - //moved remove of files here so custom package actions can still undo - //Remove files - 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) - { - if (file.StartsWith("/") == false) - file = string.Format("/{0}", file); - var filePath = IOHelper.MapPath(file); - - if (File.Exists(filePath)) - File.Delete(filePath); - - } - package.Files.Remove(file); - } - - Services.PackagingService.SaveInstalledPackage(package); - - Services.PackagingService.DeleteInstalledPackage(package.Id, Security.GetUserId().ResultOr(0)); - - // create a summary of what was actually removed, for PackagingService.UninstalledPackage - var summary = new UninstallationSummary - { - MetaData = package, - TemplatesUninstalled = removedTemplates, - MacrosUninstalled = removedMacros, - ContentTypesUninstalled = removedContentTypes, - DictionaryItemsUninstalled = removedDictionaryItems, - DataTypesUninstalled = removedDataTypes, - FilesUninstalled = removedFiles, - PackageUninstalled = true - }; - - // trigger the UninstalledPackage event - // fixme: This all needs to be part of the service! - PackagingService.OnUninstalledPackage(new UninstallPackageEventArgs(summary, package, false)); - - } - /// /// Returns all installed packages - only shows their latest versions /// @@ -302,7 +130,9 @@ namespace Umbraco.Web.Editors private void PopulateFromPackageData(LocalPackageInstallModel model) { - var ins = Services.PackagingService.GetCompiledPackageInfo(model.ZipFilePath); + var zipFile = new FileInfo(Path.Combine(IOHelper.MapPath(SystemDirectories.Packages), model.ZipFileName)); + + var ins = Services.PackagingService.GetCompiledPackageInfo(zipFile); model.Name = ins.Name; model.Author = ins.Author; @@ -365,11 +195,9 @@ namespace Umbraco.Web.Editors public async Task UploadLocalPackage() { if (Request.Content.IsMimeMultipartContent() == false) - { throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); - } - var root = IOHelper.MapPath("~/App_Data/TEMP/FileUploads"); + var root = IOHelper.MapPath(SystemDirectories.TempFileUploads); //ensure it exists Directory.CreateDirectory(root); var provider = new MultipartFormDataStreamProvider(root); @@ -378,38 +206,36 @@ namespace Umbraco.Web.Editors //must have a file if (result.FileData.Count == 0) - { throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound)); - } - //TODO: App/Tree Permissions? var model = new LocalPackageInstallModel { + //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) { - var fileName = file.Headers.ContentDisposition.FileName.Trim(new[] { '\"' }); + var fileName = file.Headers.ContentDisposition.FileName.Trim('\"'); var ext = fileName.Substring(fileName.LastIndexOf('.') + 1).ToLower(); - //TODO: Only allow .zip if (ext.InvariantEquals("zip") || ext.InvariantEquals("umb")) { - //TODO: Currently it has to be here, it's not ideal but that's the way it is right now - var packageTempDir = IOHelper.MapPath(SystemDirectories.Data); + //we always save package files to /App_Data/packages/package-guid.umb for processing as a standard so lets copy. + + var packagesFolder = IOHelper.MapPath(SystemDirectories.Packages); + Directory.CreateDirectory(packagesFolder); + var packageFile = Path.Combine(packagesFolder, model.PackageGuid + ".umb"); + File.Copy(file.LocalFileName, packageFile); - //ensure it's there - Directory.CreateDirectory(packageTempDir); + model.ZipFileName = Path.GetFileName(packageFile); - //copy it - must always be '.umb' for the installer thing to work - //the file name must be a GUID - this is what the packager expects (strange yes) - //because essentially this is creating a temporary package Id that will be used - //for unpacking/installing/etc... - model.ZipFilePath = model.PackageGuid + ".umb"; - var packageTempFileLocation = Path.Combine(packageTempDir, model.ZipFilePath); - File.Copy(file.LocalFileName, packageTempFileLocation, true); + //add to the outgoing model so that all temp files are cleaned up + model.UploadedFiles.Add(new ContentPropertyFile + { + TempFilePath = file.LocalFileName + }); //Populate the model from the metadata in the package file (zip file) PopulateFromPackageData(model); @@ -447,17 +273,22 @@ namespace Umbraco.Web.Editors public async Task Fetch(string packageGuid) { //Default path - string path = Path.Combine("packages", packageGuid + ".umb"); - if (File.Exists(IOHelper.MapPath(Path.Combine(SystemDirectories.Data, path))) == false) + string fileName = packageGuid + ".umb"; + if (File.Exists(Path.Combine(IOHelper.MapPath(SystemDirectories.Packages), fileName)) == false) { - path = await Services.PackagingService.FetchPackageFileAsync(Guid.Parse(packageGuid), UmbracoVersion.Current, Security.GetUserId().ResultOr(0)); + var packageFile = await Services.PackagingService.FetchPackageFileAsync( + Guid.Parse(packageGuid), + UmbracoVersion.Current, + Security.GetUserId().ResultOr(0)); + + fileName = packageFile.Name; } var model = new LocalPackageInstallModel { PackageGuid = Guid.Parse(packageGuid), - RepositoryGuid = Guid.Parse("65194810-1f85-11dd-bd0b-0800200c9a66"), - ZipFilePath = path + //RepositoryGuid = Guid.Parse("65194810-1f85-11dd-bd0b-0800200c9a66"), + ZipFileName = fileName }; //Populate the model from the metadata in the package file (zip file) @@ -483,7 +314,9 @@ namespace Umbraco.Web.Editors [HttpPost] public PackageInstallModel Import(PackageInstallModel model) { - var packageInfo = Services.PackagingService.GetCompiledPackageInfo(model.ZipFilePath); + var zipFile = new FileInfo(Path.Combine(IOHelper.MapPath(SystemDirectories.Packages), model.ZipFileName)); + + var packageInfo = Services.PackagingService.GetCompiledPackageInfo(zipFile); //now we need to check for version comparison if (packageInfo.UmbracoVersionRequirementsType == RequirementsType.Strict) @@ -497,9 +330,10 @@ namespace Umbraco.Web.Editors } var packageDefinition = PackageDefinition.FromCompiledPackage(packageInfo); + packageDefinition.PackageId = model.PackageGuid; //We must re-map the original package GUID that was generated + packageDefinition.PackagePath = zipFile.FullName; //save to the installedPackages.config - packageDefinition.PackageId = model.PackageGuid; //fixme: why are we doing this? Services.PackagingService.SaveInstalledPackage(packageDefinition); model.Id = packageDefinition.Id; @@ -518,7 +352,9 @@ namespace Umbraco.Web.Editors var definition = Services.PackagingService.GetInstalledPackageById(model.Id); if (definition == null) throw new InvalidOperationException("Not package definition found with id " + model.Id); - Services.PackagingService.InstallCompiledPackageFiles(definition, model.ZipFilePath, Security.GetUserId().ResultOr(0)); + var zipFile = new FileInfo(definition.PackagePath); + + var installedFiles = Services.PackagingService.InstallCompiledPackageFiles(definition, zipFile, Security.GetUserId().ResultOr(0)); //set a restarting marker and reset the app pool Current.RestartAppPool(Request.TryGetHttpContext().Result); @@ -553,7 +389,10 @@ namespace Umbraco.Web.Editors var definition = Services.PackagingService.GetInstalledPackageById(model.Id); if (definition == null) throw new InvalidOperationException("Not package definition found with id " + model.Id); - Services.PackagingService.InstallCompiledPackageData(definition, model.ZipFilePath, Security.GetUserId().ResultOr(0)); + var zipFile = new FileInfo(definition.PackagePath); + + var installSummary = Services.PackagingService.InstallCompiledPackageData(definition, zipFile, Security.GetUserId().ResultOr(0)); + return model; } @@ -565,7 +404,12 @@ namespace Umbraco.Web.Editors [HttpPost] public PackageInstallResult CleanUp(PackageInstallModel model) { - var packageInfo = Services.PackagingService.GetCompiledPackageInfo(model.ZipFilePath); + 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); var clientDependencyConfig = new ClientDependencyConfiguration(Logger); var clientDependencyUpdated = clientDependencyConfig.UpdateVersionNumber( @@ -585,9 +429,9 @@ namespace Umbraco.Web.Editors return new PackageInstallResult { Id = model.Id, - ZipFilePath = model.ZipFilePath, + ZipFileName = model.ZipFileName, PackageGuid = model.PackageGuid, - RepositoryGuid = model.RepositoryGuid, + //RepositoryGuid = model.RepositoryGuid, PostInstallationPath = redirectUrl }; diff --git a/src/Umbraco.Web/Editors/UsersController.cs b/src/Umbraco.Web/Editors/UsersController.cs index 7577624621..e46e83c6e4 100644 --- a/src/Umbraco.Web/Editors/UsersController.cs +++ b/src/Umbraco.Web/Editors/UsersController.cs @@ -70,7 +70,7 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); } - var root = IOHelper.MapPath("~/App_Data/TEMP/FileUploads"); + var root = IOHelper.MapPath(SystemDirectories.TempFileUploads); //ensure it exists Directory.CreateDirectory(root); var provider = new MultipartFormDataStreamProvider(root); diff --git a/src/Umbraco.Web/Install/Controllers/InstallPackageController.cs b/src/Umbraco.Web/Install/Controllers/InstallPackageController.cs index dd9b0a42d0..81cb1e2e39 100644 --- a/src/Umbraco.Web/Install/Controllers/InstallPackageController.cs +++ b/src/Umbraco.Web/Install/Controllers/InstallPackageController.cs @@ -1,196 +1,196 @@ -using System; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Text; -using System.Threading.Tasks; -using System.Web; -using System.Web.Http; -using Newtonsoft.Json.Linq; -using umbraco; -using Umbraco.Core; -using Umbraco.Web.Cache; -using Umbraco.Core.Configuration; -using Umbraco.Core.Models.Packaging; -using Umbraco.Core.Services; -using Umbraco.Web.Composing; -using Umbraco.Web.Install.Models; -using Umbraco.Web.WebApi; +//using System; +//using System.Linq; +//using System.Net; +//using System.Net.Http; +//using System.Text; +//using System.Threading.Tasks; +//using System.Web; +//using System.Web.Http; +//using Newtonsoft.Json.Linq; +//using umbraco; +//using Umbraco.Core; +//using Umbraco.Web.Cache; +//using Umbraco.Core.Configuration; +//using Umbraco.Core.Models.Packaging; +//using Umbraco.Core.Services; +//using Umbraco.Web.Composing; +//using Umbraco.Web.Install.Models; +//using Umbraco.Web.WebApi; -namespace Umbraco.Web.Install.Controllers -{ - /// - /// A controller for the installation process regarding packages - /// - /// - /// Currently this is used for web services however we should/could eventually migrate the whole installer to MVC as it - /// is a bit of a mess currently. - /// - [HttpInstallAuthorize] - [AngularJsonOnlyConfiguration] - [Obsolete("This is only used for the legacy way of installing starter kits in the back office")] - //fixme: Is this used anymore?? - public class InstallPackageController : ApiController - { - private readonly IPackagingService _packagingService; - private readonly UmbracoContext _umbracoContext; +//namespace Umbraco.Web.Install.Controllers +//{ +// /// +// /// A controller for the installation process regarding packages +// /// +// /// +// /// Currently this is used for web services however we should/could eventually migrate the whole installer to MVC as it +// /// is a bit of a mess currently. +// /// +// [HttpInstallAuthorize] +// [AngularJsonOnlyConfiguration] +// [Obsolete("This is only used for the legacy way of installing starter kits in the back office")] +// //fixme: Is this used anymore?? +// public class InstallPackageController : ApiController +// { +// private readonly IPackagingService _packagingService; +// private readonly UmbracoContext _umbracoContext; - public InstallPackageController(IPackagingService packagingService, UmbracoContext umbracoContext) - { - _packagingService = packagingService; - _umbracoContext = umbracoContext; - } +// public InstallPackageController(IPackagingService packagingService, UmbracoContext umbracoContext) +// { +// _packagingService = packagingService; +// _umbracoContext = umbracoContext; +// } - /// - /// Empty action, useful for retrieving the base url for this controller - /// - /// - [HttpGet] - public HttpResponseMessage Index() - { - throw new NotImplementedException(); - } +// /// +// /// Empty action, useful for retrieving the base url for this controller +// /// +// /// +// [HttpGet] +// public HttpResponseMessage Index() +// { +// throw new NotImplementedException(); +// } - /// - /// Connects to the repo, downloads the package and creates the definition - /// - /// - /// - [HttpPost] - public async Task DownloadPackageFiles(InstallPackageModel model) - { - var packageFile = await _packagingService.FetchPackageFileAsync( - model.KitGuid, - UmbracoVersion.Current, - UmbracoContext.Current.Security.CurrentUser.Id); +// /// +// /// Connects to the repo, downloads the package and creates the definition +// /// +// /// +// /// +// [HttpPost] +// public async Task DownloadPackageFiles(InstallPackageModel model) +// { +// var packageFile = await _packagingService.FetchPackageFileAsync( +// model.KitGuid, +// UmbracoVersion.Current, +// UmbracoContext.Current.Security.CurrentUser.Id); - var packageInfo = _packagingService.GetCompiledPackageInfo(packageFile); - if (packageInfo == null) throw new InvalidOperationException("Could not read package file " + packageFile); +// var packageInfo = _packagingService.GetCompiledPackageInfo(packageFile); +// if (packageInfo == null) throw new InvalidOperationException("Could not read package file " + packageFile); - //save to the installedPackages.config - var packageDefinition = PackageDefinition.FromCompiledPackage(packageInfo); - _packagingService.SaveInstalledPackage(packageDefinition); +// //save to the installedPackages.config +// var packageDefinition = PackageDefinition.FromCompiledPackage(packageInfo); +// _packagingService.SaveInstalledPackage(packageDefinition); - return Json(new - { - success = true, - packageId = packageDefinition.Id, - packageFile = packageInfo.PackageFileName, - percentage = 10, - message = "Downloading starter kit files..." - }, HttpStatusCode.OK); - } +// return Json(new +// { +// success = true, +// packageId = packageDefinition.Id, +// packageFile = packageInfo.PackageFileName, +// percentage = 10, +// message = "Downloading starter kit files..." +// }, HttpStatusCode.OK); +// } - /// - /// Installs the files in the package - /// - /// - [HttpPost] - public HttpResponseMessage InstallPackageFiles(InstallPackageModel model) - { - model.PackageFile = HttpUtility.UrlDecode(model.PackageFile); +// /// +// /// Installs the files in the package +// /// +// /// +// [HttpPost] +// public HttpResponseMessage InstallPackageFiles(InstallPackageModel model) +// { +// model.PackageFile = HttpUtility.UrlDecode(model.PackageFile); - var definition = _packagingService.GetInstalledPackageById(model.PackageId); - if (definition == null) throw new InvalidOperationException("Not package definition found with id " + model.PackageId); +// var definition = _packagingService.GetInstalledPackageById(model.PackageId); +// if (definition == null) throw new InvalidOperationException("Not package definition found with id " + model.PackageId); - _packagingService.InstallCompiledPackageFiles(definition, model.PackageFile, _umbracoContext.Security.GetUserId().ResultOr(0)); +// _packagingService.InstallCompiledPackageFiles(definition, model.PackageFile, _umbracoContext.Security.GetUserId().ResultOr(0)); - return Json(new - { - success = true, - ManifestId = model.PackageId, - model.PackageFile, - percentage = 20, - message = "Installing starter kit files" - }, HttpStatusCode.OK); - } +// return Json(new +// { +// success = true, +// ManifestId = model.PackageId, +// model.PackageFile, +// percentage = 20, +// message = "Installing starter kit files" +// }, HttpStatusCode.OK); +// } - /// - /// Ensures the app pool is restarted - /// - /// - [HttpPost] - public HttpResponseMessage RestartAppPool() - { - Current.RestartAppPool(Request.TryGetHttpContext().Result); - return Json(new - { - success = true, - percentage = 25, - message = "Installing starter kit files" - }, HttpStatusCode.OK); - } +// /// +// /// Ensures the app pool is restarted +// /// +// /// +// [HttpPost] +// public HttpResponseMessage RestartAppPool() +// { +// Current.RestartAppPool(Request.TryGetHttpContext().Result); +// return Json(new +// { +// success = true, +// percentage = 25, +// message = "Installing starter kit files" +// }, HttpStatusCode.OK); +// } - /// - /// Checks if the app pool has completed restarted - /// - /// - [HttpPost] - public HttpResponseMessage CheckAppPoolRestart() - { - if (Request.TryGetHttpContext().Result.Application.AllKeys.Contains("AppPoolRestarting")) - { - return Request.CreateResponse(HttpStatusCode.BadRequest); - } +// /// +// /// Checks if the app pool has completed restarted +// /// +// /// +// [HttpPost] +// public HttpResponseMessage CheckAppPoolRestart() +// { +// if (Request.TryGetHttpContext().Result.Application.AllKeys.Contains("AppPoolRestarting")) +// { +// return Request.CreateResponse(HttpStatusCode.BadRequest); +// } - return Json(new - { - percentage = 30, - success = true, - }, HttpStatusCode.OK); - } +// return Json(new +// { +// percentage = 30, +// success = true, +// }, HttpStatusCode.OK); +// } - /// - /// Installs the business logic portion of the package after app restart - /// - /// - [HttpPost] - public HttpResponseMessage InstallBusinessLogic(InstallPackageModel model) - { - model.PackageFile = HttpUtility.UrlDecode(model.PackageFile); +// /// +// /// Installs the business logic portion of the package after app restart +// /// +// /// +// [HttpPost] +// public HttpResponseMessage InstallBusinessLogic(InstallPackageModel model) +// { +// model.PackageFile = HttpUtility.UrlDecode(model.PackageFile); - var definition = _packagingService.GetInstalledPackageById(model.PackageId); - if (definition == null) throw new InvalidOperationException("Not package definition found with id " + model.PackageId); +// var definition = _packagingService.GetInstalledPackageById(model.PackageId); +// if (definition == null) throw new InvalidOperationException("Not package definition found with id " + model.PackageId); - _packagingService.InstallCompiledPackageData(definition, model.PackageFile, _umbracoContext.Security.GetUserId().ResultOr(0)); +// _packagingService.InstallCompiledPackageData(definition, model.PackageFile, _umbracoContext.Security.GetUserId().ResultOr(0)); - return Json(new - { - success = true, - ManifestId = model.PackageId, - model.PackageFile, - percentage = 70, - message = "Installing starter kit files" - }, HttpStatusCode.OK); - } +// return Json(new +// { +// success = true, +// ManifestId = model.PackageId, +// model.PackageFile, +// percentage = 70, +// message = "Installing starter kit files" +// }, HttpStatusCode.OK); +// } - /// - /// Cleans up the package installation - /// - /// - [HttpPost] - public HttpResponseMessage CleanupInstallation(InstallPackageModel model) - { - model.PackageFile = HttpUtility.UrlDecode(model.PackageFile); +// /// +// /// Cleans up the package installation +// /// +// /// +// [HttpPost] +// public HttpResponseMessage CleanupInstallation(InstallPackageModel model) +// { +// model.PackageFile = HttpUtility.UrlDecode(model.PackageFile); - return Json(new - { - success = true, - ManifestId = model.PackageId, - model.PackageFile, - percentage = 100, - message = "Starter kit has been installed" - }, HttpStatusCode.OK); - } +// return Json(new +// { +// success = true, +// ManifestId = model.PackageId, +// model.PackageFile, +// percentage = 100, +// message = "Starter kit has been installed" +// }, HttpStatusCode.OK); +// } - private HttpResponseMessage Json(object jsonObject, HttpStatusCode status) - { - var response = Request.CreateResponse(status); - var json = JObject.FromObject(jsonObject); - response.Content = new StringContent(json.ToString(), Encoding.UTF8, "application/json"); - return response; - } - } +// private HttpResponseMessage Json(object jsonObject, HttpStatusCode status) +// { +// var response = Request.CreateResponse(status); +// var json = JObject.FromObject(jsonObject); +// response.Content = new StringContent(json.ToString(), Encoding.UTF8, "application/json"); +// return response; +// } +// } -} +//} diff --git a/src/Umbraco.Web/Install/InstallHelper.cs b/src/Umbraco.Web/Install/InstallHelper.cs index 08a552d4d4..36fb384655 100644 --- a/src/Umbraco.Web/Install/InstallHelper.cs +++ b/src/Umbraco.Web/Install/InstallHelper.cs @@ -41,31 +41,6 @@ namespace Umbraco.Web.Install return _installationType ?? (_installationType = IsBrandNewInstall ? InstallationType.NewInstall : InstallationType.Upgrade).Value; } - internal static void DeleteLegacyInstaller() - { - if (Directory.Exists(IOHelper.MapPath(SystemDirectories.Install))) - { - if (Directory.Exists(IOHelper.MapPath("~/app_data/temp/install_backup"))) - { - //this means the backup already exists with files but there's no files in it, so we'll delete the backup and re-run it - if (Directory.GetFiles(IOHelper.MapPath("~/app_data/temp/install_backup")).Any() == false) - { - Directory.Delete(IOHelper.MapPath("~/app_data/temp/install_backup"), true); - Directory.Move(IOHelper.MapPath(SystemDirectories.Install), IOHelper.MapPath("~/app_data/temp/install_backup")); - } - } - else - { - Directory.Move(IOHelper.MapPath(SystemDirectories.Install), IOHelper.MapPath("~/app_data/temp/install_backup")); - } - } - - if (Directory.Exists(IOHelper.MapPath("~/Areas/UmbracoInstall"))) - { - Directory.Delete(IOHelper.MapPath("~/Areas/UmbracoInstall"), true); - } - } - internal void InstallStatus(bool isCompleted, string errorMsg) { try diff --git a/src/Umbraco.Web/Install/InstallStatusTracker.cs b/src/Umbraco.Web/Install/InstallStatusTracker.cs index 7944100648..d93eb9a06c 100644 --- a/src/Umbraco.Web/Install/InstallStatusTracker.cs +++ b/src/Umbraco.Web/Install/InstallStatusTracker.cs @@ -24,7 +24,7 @@ namespace Umbraco.Web.Install private static string GetFile(Guid installId) { - var file = IOHelper.MapPath("~/App_Data/TEMP/Install/" + var file = IOHelper.MapPath(SystemDirectories.TempData.EnsureEndsWith('/') + "Install/" + "install_" + installId.ToString("N") + ".txt"); @@ -39,7 +39,7 @@ namespace Umbraco.Web.Install public static void ClearFiles() { - var dir = IOHelper.MapPath("~/App_Data/TEMP/Install/"); + var dir = IOHelper.MapPath(SystemDirectories.TempData.EnsureEndsWith('/') + "Install/"); if (Directory.Exists(dir)) { var files = Directory.GetFiles(dir); diff --git a/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs b/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs index 0fe1e333d3..0349bb4ec7 100644 --- a/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading.Tasks; using System.Web; @@ -49,39 +50,38 @@ namespace Umbraco.Web.Install.InstallSteps return null; } - var result = await DownloadPackageFilesAsync(starterKitId.Value); + var (packageFile, packageId) = await DownloadPackageFilesAsync(starterKitId.Value); Current.RestartAppPool(); return new InstallSetupResult(new Dictionary { - {"packageId", result.Item2}, - {"packageFile", result.Item1} + {"packageId", packageId}, + {"packageFile", packageFile} }); } - private async Task> DownloadPackageFilesAsync(Guid kitGuid) + private async Task<(string packageFile, int packageId)> DownloadPackageFilesAsync(Guid kitGuid) { //Go get the package file from the package repo - var packageFileName = await _packageService.FetchPackageFileAsync(kitGuid, UmbracoVersion.Current, _umbracoContext.Security.GetUserId().ResultOr(0)); - if (packageFileName == null) throw new InvalidOperationException("Could not fetch package file " + kitGuid); + var packageFile = await _packageService.FetchPackageFileAsync(kitGuid, UmbracoVersion.Current, _umbracoContext.Security.GetUserId().ResultOr(0)); + if (packageFile == null) throw new InvalidOperationException("Could not fetch package file " + kitGuid); //add an entry to the installedPackages.config - var compiledPackage = _packageService.GetCompiledPackageInfo(packageFileName); + var compiledPackage = _packageService.GetCompiledPackageInfo(packageFile); var packageDefinition = PackageDefinition.FromCompiledPackage(compiledPackage); _packageService.SaveInstalledPackage(packageDefinition); - InstallPackageFiles(packageDefinition, compiledPackage.PackageFileName); + InstallPackageFiles(packageDefinition, compiledPackage.PackageFile); - return new Tuple(compiledPackage.PackageFileName, packageDefinition.Id); + return (compiledPackage.PackageFile.Name, packageDefinition.Id); } - private void InstallPackageFiles(PackageDefinition packageDefinition, string packageFileName) + private void InstallPackageFiles(PackageDefinition packageDefinition, FileInfo packageFile) { if (packageDefinition == null) throw new ArgumentNullException(nameof(packageDefinition)); - packageFileName = HttpUtility.UrlDecode(packageFileName); - _packageService.InstallCompiledPackageData(packageDefinition, packageFileName, _umbracoContext.Security.GetUserId().ResultOr(0)); + _packageService.InstallCompiledPackageData(packageDefinition, packageFile, _umbracoContext.Security.GetUserId().ResultOr(0)); } public override string View => _packageService.GetAllInstalledPackages().Any() ? string.Empty : base.View; diff --git a/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs b/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs index 9d3f38b061..805041391f 100644 --- a/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Linq; using System.Threading.Tasks; using System.Web; @@ -30,22 +31,21 @@ namespace Umbraco.Web.Install.InstallSteps var installSteps = InstallStatusTracker.GetStatus().ToArray(); var previousStep = installSteps.Single(x => x.Name == "StarterKitDownload"); var packageId = Convert.ToInt32(previousStep.AdditionalData["packageId"]); - var packageFile = (string)previousStep.AdditionalData["packageFile"]; - InstallBusinessLogic(packageId, packageFile); + InstallBusinessLogic(packageId); Current.RestartAppPool(_httContext); return Task.FromResult(null); } - private void InstallBusinessLogic(int packageId, string packageFile) + private void InstallBusinessLogic(int packageId) { - packageFile = HttpUtility.UrlDecode(packageFile); - var definition = _packagingService.GetInstalledPackageById(packageId); if (definition == null) throw new InvalidOperationException("Not package definition found with id " + packageId); + var packageFile = new FileInfo(definition.PackagePath); + _packagingService.InstallCompiledPackageData(definition, packageFile, _umbracoContext.Security.GetUserId().ResultOr(0)); } diff --git a/src/Umbraco.Web/Models/ContentTypeImportModel.cs b/src/Umbraco.Web/Models/ContentTypeImportModel.cs index 961476e5f0..f6f9a5926d 100644 --- a/src/Umbraco.Web/Models/ContentTypeImportModel.cs +++ b/src/Umbraco.Web/Models/ContentTypeImportModel.cs @@ -1,17 +1,13 @@ using System.Collections.Generic; using System.Runtime.Serialization; +using Umbraco.Core.Models.Editors; using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models { [DataContract(Name = "contentTypeImportModel")] - public class ContentTypeImportModel : INotificationModel + public class ContentTypeImportModel : INotificationModel, IHaveUploadedFiles { - public ContentTypeImportModel() - { - Notifications = new List(); - } - [DataMember(Name = "alias")] public string Alias { get; set; } @@ -19,9 +15,11 @@ namespace Umbraco.Web.Models public string Name { get; set; } [DataMember(Name = "notifications")] - public List Notifications { get; } + public List Notifications { get; } = new List(); [DataMember(Name = "tempFileName")] public string TempFileName { get; set; } + + public List UploadedFiles => new List(); } } diff --git a/src/Umbraco.Web/Models/LocalPackageInstallModel.cs b/src/Umbraco.Web/Models/LocalPackageInstallModel.cs index d1d51e1fcc..8adc9acf30 100644 --- a/src/Umbraco.Web/Models/LocalPackageInstallModel.cs +++ b/src/Umbraco.Web/Models/LocalPackageInstallModel.cs @@ -11,16 +11,10 @@ namespace Umbraco.Web.Models [DataContract(Name = "localPackageInstallModel")] public class LocalPackageInstallModel : PackageInstallModel, IHaveUploadedFiles, INotificationModel { - public LocalPackageInstallModel() - { - UploadedFiles = new List(); - Notifications = new List(); - } - - public List UploadedFiles { get; } + public List UploadedFiles { get; } = new List(); [DataMember(Name = "notifications")] - public List Notifications { get; } + public List Notifications { get; } = new List(); /// /// A flag to determine if this package is compatible to be installed diff --git a/src/Umbraco.Web/Models/PackageInstallModel.cs b/src/Umbraco.Web/Models/PackageInstallModel.cs index 1cf4a483ae..2decaeb098 100644 --- a/src/Umbraco.Web/Models/PackageInstallModel.cs +++ b/src/Umbraco.Web/Models/PackageInstallModel.cs @@ -15,13 +15,13 @@ namespace Umbraco.Web.Models [DataMember(Name = "packageGuid")] public Guid PackageGuid { get; set; } - //TODO: Do we need this? - [DataMember(Name = "repositoryGuid")] - public Guid RepositoryGuid { get; set; } + ////TODO: Do we need this? + //[DataMember(Name = "repositoryGuid")] + //public Guid RepositoryGuid { get; set; } - [DataMember(Name = "zipFilePath")] - public string ZipFilePath { get; set; } + [DataMember(Name = "zipFileName")] + public string ZipFileName { get; set; } /// /// During installation this can be used to track any pending appdomain restarts diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs index 0199a34579..422b502f80 100644 --- a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs +++ b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs @@ -17,6 +17,7 @@ using Umbraco.Core.Components; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.IO; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Profiling; using Umbraco.Core.Services; @@ -77,8 +78,6 @@ namespace Umbraco.Web.Runtime // Disable the X-AspNetMvc-Version HTTP Header MvcHandler.DisableMvcResponseHeader = true; - InstallHelper.DeleteLegacyInstaller(); - // wrap view engines in the profiling engine WrapViewEngines(ViewEngines.Engines); @@ -245,7 +244,7 @@ namespace Umbraco.Web.Runtime private static void ConfigureClientDependency(IGlobalSettings globalSettings) { // Backwards compatibility - set the path and URL type for ClientDependency 1.5.1 [LK] - XmlFileMapper.FileMapDefaultFolder = "~/App_Data/TEMP/ClientDependency"; + XmlFileMapper.FileMapDefaultFolder = SystemDirectories.TempData.EnsureEndsWith('/') + "ClientDependency"; BaseCompositeFileProcessingProvider.UrlTypeDefault = CompositeUrlType.Base64QueryStrings; // Now we need to detect if we are running umbracoLocalTempStorage as EnvironmentTemp and in that case we want to change the CDF file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 89260e59f6..36550f9b54 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -1132,7 +1132,6 @@ - diff --git a/src/Umbraco.Web/WebApi/Filters/FileUploadCleanupFilterAttribute.cs b/src/Umbraco.Web/WebApi/Filters/FileUploadCleanupFilterAttribute.cs index b60545e3cb..233ce39e52 100644 --- a/src/Umbraco.Web/WebApi/Filters/FileUploadCleanupFilterAttribute.cs +++ b/src/Umbraco.Web/WebApi/Filters/FileUploadCleanupFilterAttribute.cs @@ -14,7 +14,7 @@ using File = System.IO.File; namespace Umbraco.Web.WebApi.Filters { /// - /// Checks if the parameter is ContentItemSave and then deletes any temporary saved files from file uploads associated with the request + /// Checks if the parameter is IHaveUploadedFiles and then deletes any temporary saved files from file uploads associated with the request /// internal sealed class FileUploadCleanupFilterAttribute : ActionFilterAttribute { @@ -29,14 +29,6 @@ namespace Umbraco.Web.WebApi.Filters _incomingModel = incomingModel; } - /// - /// Returns true so that other filters can execute along with this one - /// - public override bool AllowMultiple - { - get { return true; } - } - public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) { base.OnActionExecuted(actionExecutedContext); @@ -47,8 +39,7 @@ namespace Umbraco.Web.WebApi.Filters { if (actionExecutedContext.ActionContext.ActionArguments.Any()) { - var contentItem = actionExecutedContext.ActionContext.ActionArguments.First().Value as IHaveUploadedFiles; - if (contentItem != null) + if (actionExecutedContext.ActionContext.ActionArguments.First().Value is IHaveUploadedFiles contentItem) { //cleanup any files associated foreach (var f in contentItem.UploadedFiles) @@ -104,8 +95,7 @@ namespace Umbraco.Web.WebApi.Filters if (objectContent != null) { - var uploadedFiles = objectContent.Value as IHaveUploadedFiles; - if (uploadedFiles != null) + if (objectContent.Value is IHaveUploadedFiles uploadedFiles) { if (uploadedFiles.UploadedFiles != null) { diff --git a/src/Umbraco.Web/_Legacy/PackageActions/addApplication.cs b/src/Umbraco.Web/_Legacy/PackageActions/addApplication.cs index 75614f68aa..26116820e6 100644 --- a/src/Umbraco.Web/_Legacy/PackageActions/addApplication.cs +++ b/src/Umbraco.Web/_Legacy/PackageActions/addApplication.cs @@ -11,7 +11,7 @@ namespace Umbraco.Web._Legacy.PackageActions /// This class implements the IPackageAction Interface, used to execute code when packages are installed. /// All IPackageActions only takes a PackageName and a XmlNode as input, and executes based on the data in the xmlnode. /// - public class addApplication : IPackageAction + public class AddApplication : IPackageAction { #region IPackageAction Members diff --git a/src/Umbraco.Web/_Legacy/PackageActions/addDashboardSection.cs b/src/Umbraco.Web/_Legacy/PackageActions/addDashboardSection.cs deleted file mode 100644 index 1e2e396d2c..0000000000 --- a/src/Umbraco.Web/_Legacy/PackageActions/addDashboardSection.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System; -using System.Linq; -using System.Xml; -using System.Xml.Linq; -using System.Xml.XPath; -using Umbraco.Core; -using Umbraco.Core.IO; -using Umbraco.Core.Xml; -using Umbraco.Core._Legacy.PackageActions; - -namespace Umbraco.Web._Legacy.PackageActions -{ - /// - /// - /// - public class addDashboardSection : IPackageAction - { - #region IPackageAction Members - - /// - /// Installs a dashboard section. This action reuses the action XML, so it has to be valid dashboard markup. - /// - /// Name of the package. - /// The XML data. - /// true if successfull - /// - /// - /// - ///
- /// - /// default - /// content - /// - /// - /// /usercontrols/dashboard/latestEdits.ascx - /// /usercontrols/umbracoBlog/dashboardBlogPostCreate.ascx - /// - /// - /// /usercontrols/umbracoBlog/dashboardBlogPostCreate.ascx - /// - ///
- ///
- ///
- ///
- public bool Execute(string packageName, XElement xmlData) - { - //this will need a complete section node to work... - - if (xmlData.HasElements) - { - string sectionAlias = xmlData.AttributeValue("dashboardAlias"); - string dbConfig = SystemFiles.DashboardConfig; - - var section = xmlData.Element("section"); - var dashboardFile = XDocument.Load(IOHelper.MapPath(dbConfig)); - - //don't continue if it already exists - var found = dashboardFile.XPathSelectElements("//section[@alias='" + sectionAlias + "']"); - if (!found.Any()) - { - dashboardFile.Root.Add(section); - dashboardFile.Save(IOHelper.MapPath(dbConfig)); - } - - return true; - } - - return false; - } - - - public string Alias() - { - return "addDashboardSection"; - } - - public bool Undo(string packageName, XElement xmlData) - { - - string sectionAlias = xmlData.AttributeValue("dashboardAlias"); - string dbConfig = SystemFiles.DashboardConfig; - var dashboardFile = XDocument.Load(IOHelper.MapPath(dbConfig)); - - var section = dashboardFile.XPathSelectElement("//section [@alias = '" + sectionAlias + "']"); - - if (section != null) - { - section.Remove(); - dashboardFile.Save(IOHelper.MapPath(dbConfig)); - } - - return true; - } - - #endregion - - } -} diff --git a/src/Umbraco.Web/_Legacy/PackageActions/addProxyFeedHost.cs b/src/Umbraco.Web/_Legacy/PackageActions/addProxyFeedHost.cs index bcd7d7c52f..f53adb3be0 100644 --- a/src/Umbraco.Web/_Legacy/PackageActions/addProxyFeedHost.cs +++ b/src/Umbraco.Web/_Legacy/PackageActions/addProxyFeedHost.cs @@ -7,7 +7,7 @@ using Umbraco.Core._Legacy.PackageActions; namespace Umbraco.Web._Legacy.PackageActions { - public class addProxyFeedHost : IPackageAction + public class AddProxyFeedHost : IPackageAction { #region IPackageAction Members diff --git a/src/Umbraco.Web/_Legacy/PackageActions/allowDoctype.cs b/src/Umbraco.Web/_Legacy/PackageActions/allowDoctype.cs index 2ed16e3842..f4b206a9ad 100644 --- a/src/Umbraco.Web/_Legacy/PackageActions/allowDoctype.cs +++ b/src/Umbraco.Web/_Legacy/PackageActions/allowDoctype.cs @@ -14,7 +14,7 @@ namespace Umbraco.Web._Legacy.PackageActions /// This class implements the IPackageAction Interface, used to execute code when packages are installed. /// All IPackageActions only takes a PackageName and a XmlNode as input, and executes based on the data in the xmlnode. ///
- public class allowDoctype : IPackageAction + public class AllowDoctype : IPackageAction { #region IPackageAction Members @@ -68,7 +68,6 @@ namespace Umbraco.Web._Legacy.PackageActions return false; } - //this has no undo. /// /// This action has no undo. /// diff --git a/src/Umbraco.Web/_Legacy/PackageActions/publishRootDocument.cs b/src/Umbraco.Web/_Legacy/PackageActions/publishRootDocument.cs index f4fe17e999..f4bd7dd4fc 100644 --- a/src/Umbraco.Web/_Legacy/PackageActions/publishRootDocument.cs +++ b/src/Umbraco.Web/_Legacy/PackageActions/publishRootDocument.cs @@ -11,7 +11,7 @@ namespace Umbraco.Web._Legacy.PackageActions /// This class implements the IPackageAction Interface, used to execute code when packages are installed. /// All IPackageActions only takes a PackageName and a XmlNode as input, and executes based on the data in the xmlnode. ///
- public class publishRootDocument : IPackageAction + public class PublishRootDocument : IPackageAction { #region IPackageAction Members @@ -29,7 +29,6 @@ namespace Umbraco.Web._Legacy.PackageActions string documentName = xmlData.AttributeValue("documentName"); - //global::umbraco.cms.businesslogic.web.Document[] rootDocs = global::umbraco.cms.businesslogic.web.Document.GetRootDocuments(); var rootDocs = Current.Services.ContentService.GetRootContent(); foreach (var rootDoc in rootDocs) @@ -44,7 +43,6 @@ namespace Umbraco.Web._Legacy.PackageActions return true; } - //this has no undo. /// /// This action has no undo. /// From a5ff29ccc7457ecb7ea82744e405daf58cdc2d57 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 15 Jan 2019 22:59:13 +1100 Subject: [PATCH 67/93] Gets package uninstall working, fixes other UI issues with packages --- .../Packaging/CompiledPackageXmlParser.cs | 37 ++-- .../Packaging/PackageDefinitionXmlParser.cs | 2 + .../Packaging/PackageInstallation.cs | 1 + .../src/views/packages/overview.controller.js | 15 +- .../src/views/packages/overview.html | 34 ++- .../views/install-local.controller.js | 2 +- .../packages/views/installed.controller.js | 16 +- .../Controllers/InstallPackageController.cs | 196 ------------------ src/Umbraco.Web/Install/UmbracoInstallArea.cs | 8 - 9 files changed, 67 insertions(+), 244 deletions(-) delete mode 100644 src/Umbraco.Web/Install/Controllers/InstallPackageController.cs diff --git a/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs index eb9cf46008..be9c69667a 100644 --- a/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs +++ b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs @@ -149,37 +149,50 @@ namespace Umbraco.Core.Packaging return path; } + /// + /// Parses the package actions stored in the package definition + /// + /// + /// + /// public IEnumerable GetPackageActions(XElement actionsElement, string packageName) { - if (actionsElement == null) { return new PackageAction[0]; } + if (actionsElement == null) return Enumerable.Empty(); - if (string.Equals("Actions", actionsElement.Name.LocalName) == false) - throw new ArgumentException($"Must be \"Actions\" as root", nameof(actionsElement)); + //invariant check ... because people can realy enter anything :/ + if (!string.Equals("actions", actionsElement.Name.LocalName, StringComparison.InvariantCultureIgnoreCase)) + throw new FormatException("Must be \"\" as root"); - return actionsElement.Elements("Action") - .Select(elemet => + if (!actionsElement.HasElements) return Enumerable.Empty(); + + var actionElementName = actionsElement.Elements().First().Name.LocalName; + + //invariant check ... because people can realy enter anything :/ + if (!string.Equals("action", actionElementName, StringComparison.InvariantCultureIgnoreCase)) + throw new FormatException("Must be \" { - var aliasAttr = elemet.Attribute("Alias"); + var aliasAttr = e.Attribute("alias") ?? e.Attribute("Alias"); //allow both ... because people can really enter anything :/ if (aliasAttr == null) - throw new ArgumentException("missing \"Alias\" atribute in alias element", nameof(actionsElement)); + throw new ArgumentException("missing \"alias\" atribute in alias element", nameof(actionsElement)); var packageAction = new PackageAction { - XmlData = elemet, + XmlData = e, Alias = aliasAttr.Value, PackageName = packageName, }; - - var attr = elemet.Attribute("runat"); + var attr = e.Attribute("runat") ?? e.Attribute("Runat"); //allow both ... because people can really enter anything :/ if (attr != null && Enum.TryParse(attr.Value, true, out ActionRunAt runAt)) { packageAction.RunAt = runAt; } - attr = elemet.Attribute("undo"); + attr = e.Attribute("undo") ?? e.Attribute("Undo"); //allow both ... because people can really enter anything :/ if (attr != null && bool.TryParse(attr.Value, out var undo)) { packageAction.Undo = undo; } - return packageAction; }).ToArray(); } diff --git a/src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs b/src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs index 0d7adf8ece..45e6fd9845 100644 --- a/src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs +++ b/src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs @@ -105,5 +105,7 @@ namespace Umbraco.Core.Packaging return packageXml; } + + } } diff --git a/src/Umbraco.Core/Packaging/PackageInstallation.cs b/src/Umbraco.Core/Packaging/PackageInstallation.cs index 4563d31560..7abf96b266 100644 --- a/src/Umbraco.Core/Packaging/PackageInstallation.cs +++ b/src/Umbraco.Core/Packaging/PackageInstallation.cs @@ -76,6 +76,7 @@ namespace Umbraco.Core.Packaging { //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 diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/overview.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/overview.controller.js index 4ca0015aa5..ccd86c6f6c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/overview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/overview.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function PackagesOverviewController($scope, $route, $location, navigationService, $timeout, localStorageService) { + function PackagesOverviewController($scope, $location, localStorageService) { //Hack! // if there is a cookie value for packageInstallUri then we need to redirect there, @@ -9,12 +9,13 @@ // because it will double load it. // we will refresh and then navigate there. - var installPackageUri = localStorageService.get("packageInstallUri"); - const packageUri = $location.search().subview; + let installPackageUri = localStorageService.get("packageInstallUri"); + let packageUri = $location.search().subview; if (installPackageUri) { localStorageService.remove("packageInstallUri"); } + if (installPackageUri && installPackageUri !== "installed") { //navigate to the custom installer screen, if it is just "installed", then we'll //show the installed view @@ -23,6 +24,8 @@ else { var vm = this; + packageUri = installPackageUri ? installPackageUri : packageUri; //use the path stored in storage over the one in the current path + vm.page = {}; vm.page.name = "Packages"; vm.page.navigation = [ @@ -30,7 +33,7 @@ "name": "Packages", "icon": "icon-cloud", "view": "views/packages/views/repo.html", - "active": !packageUri || installPackageUri === "navigation" || packageUri === "navigation", + "active": !packageUri || packageUri === "navigation", "alias": "umbPackages", "action": function() { $location.search("subview", "navigation"); @@ -40,7 +43,7 @@ "name": "Installed", "icon": "icon-box", "view": "views/packages/views/installed.html", - "active": installPackageUri === "installed" || packageUri === "installed", + "active": packageUri === "installed", "alias": "umbInstalled", "action": function() { $location.search("subview", "installed"); @@ -50,7 +53,7 @@ "name": "Install local", "icon": "icon-add", "view": "views/packages/views/install-local.html", - "active": installPackageUri === "local" || packageUri === "local", + "active": packageUri === "local", "alias": "umbInstallLocal", "action": function() { $location.search("subview", "local"); diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/overview.html b/src/Umbraco.Web.UI.Client/src/views/packages/overview.html index 250dc889d6..84f594b48d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/overview.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/overview.html @@ -1,28 +1,24 @@
-
+ - + + - - + - + + - - + - - - - - +
diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.controller.js index 9c6c2bb7c5..6753fad942 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function PackagesInstallLocalController($scope, $route, $location, Upload, umbRequestHelper, packageResource, localStorageService, $timeout, $window, localizationService, $q) { + function PackagesInstallLocalController($scope, Upload, umbRequestHelper, packageResource, localStorageService, $timeout, $window, localizationService, $q) { var vm = this; vm.state = "upload"; diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/views/installed.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/views/installed.controller.js index ce1d2cca0f..6e0e162e86 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/views/installed.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/views/installed.controller.js @@ -13,6 +13,8 @@ }; vm.package = {}; + var labels = {}; + function init() { packageResource.getInstalled() .then(function (packs) { @@ -20,6 +22,16 @@ }); vm.installState.status = ""; vm.state = "list"; + + var labelKeys = [ + "packager_installStateUninstalling", + "packager_installStateComplete", + ]; + + localizationService.localizeMany(labelKeys).then(function (values) { + labels.installStateUninstalling = values[0]; + labels.installStateComplete = values[1]; + }); } function confirmUninstall(pck) { @@ -28,14 +40,14 @@ } function uninstallPackage(installedPackage) { - vm.installState.status = localizationService.localize("packager_installStateUninstalling"); + vm.installState.status = labels.installStateUninstalling; vm.installState.progress = "0"; packageResource.uninstall(installedPackage.id) .then(function () { if (installedPackage.files.length > 0) { - vm.installState.status = localizationService.localize("packager_installStateComplete"); + vm.installState.status = labels.installStateComplete; vm.installState.progress = "100"; //set this flag so that on refresh it shows the installed packages list diff --git a/src/Umbraco.Web/Install/Controllers/InstallPackageController.cs b/src/Umbraco.Web/Install/Controllers/InstallPackageController.cs deleted file mode 100644 index 81cb1e2e39..0000000000 --- a/src/Umbraco.Web/Install/Controllers/InstallPackageController.cs +++ /dev/null @@ -1,196 +0,0 @@ -//using System; -//using System.Linq; -//using System.Net; -//using System.Net.Http; -//using System.Text; -//using System.Threading.Tasks; -//using System.Web; -//using System.Web.Http; -//using Newtonsoft.Json.Linq; -//using umbraco; -//using Umbraco.Core; -//using Umbraco.Web.Cache; -//using Umbraco.Core.Configuration; -//using Umbraco.Core.Models.Packaging; -//using Umbraco.Core.Services; -//using Umbraco.Web.Composing; -//using Umbraco.Web.Install.Models; -//using Umbraco.Web.WebApi; - -//namespace Umbraco.Web.Install.Controllers -//{ -// /// -// /// A controller for the installation process regarding packages -// /// -// /// -// /// Currently this is used for web services however we should/could eventually migrate the whole installer to MVC as it -// /// is a bit of a mess currently. -// /// -// [HttpInstallAuthorize] -// [AngularJsonOnlyConfiguration] -// [Obsolete("This is only used for the legacy way of installing starter kits in the back office")] -// //fixme: Is this used anymore?? -// public class InstallPackageController : ApiController -// { -// private readonly IPackagingService _packagingService; -// private readonly UmbracoContext _umbracoContext; - -// public InstallPackageController(IPackagingService packagingService, UmbracoContext umbracoContext) -// { -// _packagingService = packagingService; -// _umbracoContext = umbracoContext; -// } - -// /// -// /// Empty action, useful for retrieving the base url for this controller -// /// -// /// -// [HttpGet] -// public HttpResponseMessage Index() -// { -// throw new NotImplementedException(); -// } - -// /// -// /// Connects to the repo, downloads the package and creates the definition -// /// -// /// -// /// -// [HttpPost] -// public async Task DownloadPackageFiles(InstallPackageModel model) -// { -// var packageFile = await _packagingService.FetchPackageFileAsync( -// model.KitGuid, -// UmbracoVersion.Current, -// UmbracoContext.Current.Security.CurrentUser.Id); - - -// var packageInfo = _packagingService.GetCompiledPackageInfo(packageFile); -// if (packageInfo == null) throw new InvalidOperationException("Could not read package file " + packageFile); - -// //save to the installedPackages.config -// var packageDefinition = PackageDefinition.FromCompiledPackage(packageInfo); -// _packagingService.SaveInstalledPackage(packageDefinition); - -// return Json(new -// { -// success = true, -// packageId = packageDefinition.Id, -// packageFile = packageInfo.PackageFileName, -// percentage = 10, -// message = "Downloading starter kit files..." -// }, HttpStatusCode.OK); -// } - -// /// -// /// Installs the files in the package -// /// -// /// -// [HttpPost] -// public HttpResponseMessage InstallPackageFiles(InstallPackageModel model) -// { -// model.PackageFile = HttpUtility.UrlDecode(model.PackageFile); - -// var definition = _packagingService.GetInstalledPackageById(model.PackageId); -// if (definition == null) throw new InvalidOperationException("Not package definition found with id " + model.PackageId); - -// _packagingService.InstallCompiledPackageFiles(definition, model.PackageFile, _umbracoContext.Security.GetUserId().ResultOr(0)); - -// return Json(new -// { -// success = true, -// ManifestId = model.PackageId, -// model.PackageFile, -// percentage = 20, -// message = "Installing starter kit files" -// }, HttpStatusCode.OK); -// } - -// /// -// /// Ensures the app pool is restarted -// /// -// /// -// [HttpPost] -// public HttpResponseMessage RestartAppPool() -// { -// Current.RestartAppPool(Request.TryGetHttpContext().Result); -// return Json(new -// { -// success = true, -// percentage = 25, -// message = "Installing starter kit files" -// }, HttpStatusCode.OK); -// } - -// /// -// /// Checks if the app pool has completed restarted -// /// -// /// -// [HttpPost] -// public HttpResponseMessage CheckAppPoolRestart() -// { -// if (Request.TryGetHttpContext().Result.Application.AllKeys.Contains("AppPoolRestarting")) -// { -// return Request.CreateResponse(HttpStatusCode.BadRequest); -// } - -// return Json(new -// { -// percentage = 30, -// success = true, -// }, HttpStatusCode.OK); -// } - -// /// -// /// Installs the business logic portion of the package after app restart -// /// -// /// -// [HttpPost] -// public HttpResponseMessage InstallBusinessLogic(InstallPackageModel model) -// { -// model.PackageFile = HttpUtility.UrlDecode(model.PackageFile); - -// var definition = _packagingService.GetInstalledPackageById(model.PackageId); -// if (definition == null) throw new InvalidOperationException("Not package definition found with id " + model.PackageId); - -// _packagingService.InstallCompiledPackageData(definition, model.PackageFile, _umbracoContext.Security.GetUserId().ResultOr(0)); - -// return Json(new -// { -// success = true, -// ManifestId = model.PackageId, -// model.PackageFile, -// percentage = 70, -// message = "Installing starter kit files" -// }, HttpStatusCode.OK); -// } - -// /// -// /// Cleans up the package installation -// /// -// /// -// [HttpPost] -// public HttpResponseMessage CleanupInstallation(InstallPackageModel model) -// { -// model.PackageFile = HttpUtility.UrlDecode(model.PackageFile); - -// return Json(new -// { -// success = true, -// ManifestId = model.PackageId, -// model.PackageFile, -// percentage = 100, -// message = "Starter kit has been installed" -// }, HttpStatusCode.OK); -// } - -// private HttpResponseMessage Json(object jsonObject, HttpStatusCode status) -// { -// var response = Request.CreateResponse(status); -// var json = JObject.FromObject(jsonObject); -// response.Content = new StringContent(json.ToString(), Encoding.UTF8, "application/json"); -// return response; -// } -// } - -//} diff --git a/src/Umbraco.Web/Install/UmbracoInstallArea.cs b/src/Umbraco.Web/Install/UmbracoInstallArea.cs index 2b44abd4ab..398ed7b245 100644 --- a/src/Umbraco.Web/Install/UmbracoInstallArea.cs +++ b/src/Umbraco.Web/Install/UmbracoInstallArea.cs @@ -32,14 +32,6 @@ namespace Umbraco.Web.Install new { controller = "Install", action = "Index", id = UrlParameter.Optional }, new[] { typeof(InstallController).Namespace }); - //TODO: We can remove this when we re-build the back office package installer - //Create the install routes - context.MapHttpRoute( - "Umbraco_install_packages", - "Install/PackageInstaller/{action}/{id}", - new { controller = "InstallPackage", action = "Index", id = UrlParameter.Optional }, - new[] { typeof(InstallPackageController).Namespace }); - context.MapHttpRoute( "umbraco-install-api", "install/api/{action}/{id}", From 8c7d29bd020a62a2d87c2a34763ab4b865a01080 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 15 Jan 2019 23:03:23 +1100 Subject: [PATCH 68/93] file update missing --- src/Umbraco.Web/Umbraco.Web.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index f8f33ca0fa..b0142771d2 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -730,7 +730,6 @@ - From 56ab88983a9c95d0803c348e989c23b0c2fc9124 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 15 Jan 2019 23:10:08 +1100 Subject: [PATCH 69/93] updates a test --- .../Packaging/PackageInstallation.cs | 6 +++++- .../Services/Implement/PackagingService.cs | 3 +-- .../Packaging/PackageInstallationTest.cs | 21 ++++--------------- 3 files changed, 10 insertions(+), 20 deletions(-) diff --git a/src/Umbraco.Core/Packaging/PackageInstallation.cs b/src/Umbraco.Core/Packaging/PackageInstallation.cs index 7abf96b266..ae9b8173f5 100644 --- a/src/Umbraco.Core/Packaging/PackageInstallation.cs +++ b/src/Umbraco.Core/Packaging/PackageInstallation.cs @@ -69,7 +69,11 @@ namespace Umbraco.Core.Packaging var packageZipFile = compiledPackage.PackageFile; - return _packageFileInstallation.InstallFiles(compiledPackage, packageZipFile, _packageExtractionFolder.FullName); + var files = _packageFileInstallation.InstallFiles(compiledPackage, packageZipFile, _packageExtractionFolder.FullName).ToList(); + + packageDefinition.Files = files; + + return files; } public UninstallationSummary UninstallPackage(PackageDefinition package, int userId) diff --git a/src/Umbraco.Core/Services/Implement/PackagingService.cs b/src/Umbraco.Core/Services/Implement/PackagingService.cs index 362b1fc67b..a5c279fa21 100644 --- a/src/Umbraco.Core/Services/Implement/PackagingService.cs +++ b/src/Umbraco.Core/Services/Implement/PackagingService.cs @@ -109,8 +109,7 @@ namespace Umbraco.Core.Services.Implement if (compiledPackage == null) throw new InvalidOperationException("Could not read the package file " + packageFile); var files = _packageInstallation.InstallPackageFiles(packageDefinition, compiledPackage, userId).ToList(); - packageDefinition.Files = files; - + SaveInstalledPackage(packageDefinition); _auditService.Add(AuditType.PackagerInstall, userId, -1, "Package", $"Package files installed for package '{compiledPackage.Name}'."); diff --git a/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs b/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs index 7514eea00b..62feb64c10 100644 --- a/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs +++ b/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs @@ -1,16 +1,14 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using Moq; using NUnit.Framework; -using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Models.Packaging; using Umbraco.Core.Packaging; using Umbraco.Core.PropertyEditors; -using Umbraco.Core.Services; -using Umbraco.Core.Services.Implement; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; @@ -55,19 +53,6 @@ namespace Umbraco.Tests.Packaging private const string DocumentTypePickerUmb = "Document_Type_Picker_1.1.umb"; - //[Test] - //public void PackagingService_Can_ImportPackage() - //{ - // const string documentTypePickerUmb = "Document_Type_Picker_1.1.umb"; - - // string testPackagePath = GetTestPackagePath(documentTypePickerUmb); - - // InstallationSummary installationSummary = packagingService.InstallPackage(testPackagePath); - - // Assert.IsNotNull(installationSummary); - //} - - [Test] public void Can_Read_Compiled_Package() { @@ -111,9 +96,11 @@ namespace Umbraco.Tests.Packaging var package = PackageInstallation.ReadPackage( //this is where our test zip file is new FileInfo(Path.Combine(IOHelper.MapPath("~/Packaging/packages"), DocumentTypePickerUmb))); + var def = PackageDefinition.FromCompiledPackage(package); def.Id = 1; def.PackageId = Guid.NewGuid(); + def.Files = new List(); //clear out the files of the def for testing, this should be populated by the install var result = PackageInstallation.InstallPackageFiles(def, package, -1).ToList(); @@ -122,7 +109,7 @@ namespace Umbraco.Tests.Packaging Assert.IsTrue(File.Exists(Path.Combine(IOHelper.MapPath("~/" + _testBaseFolder), result[0]))); //make sure the def is updated too - Assert.AreEqual(result.Count(), def.Files.Count); + Assert.AreEqual(result.Count, def.Files.Count); } [Test] From 62fcbd8e84c8e6c09e1f4a7cef70d469ac6f43d3 Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 15 Jan 2019 13:43:56 +0100 Subject: [PATCH 70/93] Handle max runtime level for components --- src/Umbraco.Core/Components/Composers.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Core/Components/Composers.cs b/src/Umbraco.Core/Components/Composers.cs index 1c836e9e5c..89deed934e 100644 --- a/src/Umbraco.Core/Components/Composers.cs +++ b/src/Umbraco.Core/Components/Composers.cs @@ -76,11 +76,13 @@ namespace Umbraco.Core.Components var composerTypeList = _composerTypes .Where(x => { - // use the min level specified by the attribute if any - // otherwise, user composers have Run min level, anything else is Unknown (always run) + // use the min/max levels specified by the attribute if any + // otherwise, min: user composers are Run, anything else is Unknown (always run) + // max: everything is Run (always run) var attr = x.GetCustomAttribute(); var minLevel = attr?.MinLevel ?? (x.Implements() ? RuntimeLevel.Run : RuntimeLevel.Unknown); - return _composition.RuntimeState.Level >= minLevel; + var maxLevel = attr?.MaxLevel ?? RuntimeLevel.Run; + return _composition.RuntimeState.Level >= minLevel && _composition.RuntimeState.Level <= maxLevel; }) .ToList(); From f6fdc9ae2d33b38d41d4c6a6626d11a6b5f83849 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 15 Jan 2019 23:46:58 +1100 Subject: [PATCH 71/93] Updates test, fixes some other issues during testing --- .../Models/Packaging/PreInstallWarnings.cs | 2 ++ .../Packaging/ConflictingPackageData.cs | 2 +- .../Packaging/PackageInstallationTest.cs | 13 +++++++++++-- .../views/packages/views/installed.controller.js | 2 +- src/Umbraco.Web/Search/ExamineComponent.cs | 2 +- 5 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Core/Models/Packaging/PreInstallWarnings.cs b/src/Umbraco.Core/Models/Packaging/PreInstallWarnings.cs index 18b63ced88..f0acb2a46b 100644 --- a/src/Umbraco.Core/Models/Packaging/PreInstallWarnings.cs +++ b/src/Umbraco.Core/Models/Packaging/PreInstallWarnings.cs @@ -9,6 +9,8 @@ namespace Umbraco.Core.Models.Packaging { public IEnumerable UnsecureFiles { get; set; } = Enumerable.Empty(); public IEnumerable FilesReplaced { get; set; } = Enumerable.Empty(); + + //TODO: Shouldn't we detect other conflicting entities too ? public IEnumerable ConflictingMacros { get; set; } = Enumerable.Empty(); public IEnumerable ConflictingTemplates { get; set; } = Enumerable.Empty(); public IEnumerable ConflictingStylesheets { get; set; } = Enumerable.Empty(); diff --git a/src/Umbraco.Core/Packaging/ConflictingPackageData.cs b/src/Umbraco.Core/Packaging/ConflictingPackageData.cs index a37195806e..82693677fb 100644 --- a/src/Umbraco.Core/Packaging/ConflictingPackageData.cs +++ b/src/Umbraco.Core/Packaging/ConflictingPackageData.cs @@ -23,7 +23,7 @@ namespace Umbraco.Core.Packaging return stylesheetNodes .Select(n => { - var xElement = n.Element("Name"); + var xElement = n.Element("Name") ?? n.Element("name"); ; if (xElement == null) throw new FormatException("Missing \"Name\" element"); diff --git a/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs b/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs index 62feb64c10..8fcacbeff7 100644 --- a/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs +++ b/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs @@ -6,11 +6,13 @@ using Moq; using NUnit.Framework; using Umbraco.Core.Composing; using Umbraco.Core.IO; +using Umbraco.Core.Models; using Umbraco.Core.Models.Packaging; using Umbraco.Core.Packaging; using Umbraco.Core.PropertyEditors; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; +using File = System.IO.File; namespace Umbraco.Tests.Packaging { @@ -79,7 +81,11 @@ namespace Umbraco.Tests.Packaging [Test] public void Can_Read_Compiled_Package_Warnings() { - + //copy a file to the same path that the package will install so we can detect file conflicts + var path = IOHelper.MapPath("~/" + _testBaseFolder); + var filePath = Path.Combine(path, "bin", "Auros.DocumentTypePicker.dll"); + Directory.CreateDirectory(Path.GetDirectoryName(filePath)); + File.WriteAllText(filePath, "test"); var preInstallWarnings = PackageInstallation.ReadPackage( //this is where our test zip file is @@ -87,7 +93,10 @@ namespace Umbraco.Tests.Packaging .Warnings; Assert.IsNotNull(preInstallWarnings); - //TODO: Assert! + Assert.AreEqual(preInstallWarnings.FilesReplaced.Count(), 1); + Assert.AreEqual(preInstallWarnings.FilesReplaced.First(), "bin\\Auros.DocumentTypePicker.dll"); + + //TODO: More Asserts } [Test] diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/views/installed.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/views/installed.controller.js index 6e0e162e86..5265897708 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/views/installed.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/views/installed.controller.js @@ -25,7 +25,7 @@ var labelKeys = [ "packager_installStateUninstalling", - "packager_installStateComplete", + "packager_installStateComplete" ]; localizationService.localizeMany(labelKeys).then(function (values) { diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index 09d0c555da..336fcc7aec 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -446,7 +446,7 @@ namespace Umbraco.Web.Search while (page * pageSize < total) { //paging with examine, see https://shazwazza.com/post/paging-with-examine/ - var results = searcher.CreateQuery().Field("nodeType", id).Execute(maxResults: pageSize * (page + 1)); + var results = searcher.CreateQuery().Field("nodeType", id.ToInvariantString()).Execute(maxResults: pageSize * (page + 1)); total = results.TotalItemCount; var paged = results.Skip(page * pageSize); From 709df804e5ac3069e768eba7b153fc82f3889890 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 16 Jan 2019 00:13:40 +1100 Subject: [PATCH 72/93] Updates tests and better type support for importing content --- .../Models/Packaging/CompiledPackage.cs | 12 +- .../Packaging/CompiledPackageDocument.cs | 26 +++++ .../Models/Packaging/CompiledPackageFile.cs | 25 +++++ .../Packaging/CompiledPackageXmlParser.cs | 10 +- .../Packaging/PackageDataInstallation.cs | 59 +++++----- .../Packaging/PackageInstallation.cs | 1 - src/Umbraco.Core/Umbraco.Core.csproj | 2 + .../PackageDataInstallationTests.cs} | 105 +++++++++--------- .../Packaging/PackageInstallationTest.cs | 39 ++++++- .../Packaging/Packages/Hello_1.0.0.zip | Bin 0 -> 1529 bytes src/Umbraco.Tests/Umbraco.Tests.csproj | 2 +- 11 files changed, 172 insertions(+), 109 deletions(-) create mode 100644 src/Umbraco.Core/Models/Packaging/CompiledPackageDocument.cs create mode 100644 src/Umbraco.Core/Models/Packaging/CompiledPackageFile.cs rename src/Umbraco.Tests/{Services/Importing/PackageImportTests.cs => Packaging/PackageDataInstallationTests.cs} (84%) create mode 100644 src/Umbraco.Tests/Packaging/Packages/Hello_1.0.0.zip diff --git a/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs b/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs index d458337bf9..a852fcc997 100644 --- a/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs +++ b/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs @@ -39,16 +39,6 @@ namespace Umbraco.Core.Models.Packaging public IEnumerable Languages { get; set; } //fixme: make strongly typed public IEnumerable DictionaryItems { get; set; } //fixme: make strongly typed public IEnumerable DocumentTypes { get; set; } //fixme: make strongly typed - public IEnumerable Documents { get; set; } //fixme: make strongly typed + public IEnumerable Documents { get; set; } } - - public class CompiledPackageFile - { - public string OriginalPath { get; set; } - public string UniqueFileName { get; set; } - public string OriginalName { get; set; } - - } - - } diff --git a/src/Umbraco.Core/Models/Packaging/CompiledPackageDocument.cs b/src/Umbraco.Core/Models/Packaging/CompiledPackageDocument.cs new file mode 100644 index 0000000000..c41966dfe1 --- /dev/null +++ b/src/Umbraco.Core/Models/Packaging/CompiledPackageDocument.cs @@ -0,0 +1,26 @@ +using System; +using System.Xml.Linq; + +namespace Umbraco.Core.Models.Packaging +{ + public class CompiledPackageDocument + { + public static CompiledPackageDocument Create(XElement xml) + { + if (xml.Name.LocalName != "DocumentSet") + throw new ArgumentException("The xml isn't formatted correctly, a document element is defined by ", nameof(xml)); + return new CompiledPackageDocument + { + XmlData = xml, + ImportMode = xml.AttributeValue("importMode") + }; + } + + public string ImportMode { get; set; } //this is never used + + /// + /// The serialized version of the content + /// + public XElement XmlData { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Packaging/CompiledPackageFile.cs b/src/Umbraco.Core/Models/Packaging/CompiledPackageFile.cs new file mode 100644 index 0000000000..2cb989b42b --- /dev/null +++ b/src/Umbraco.Core/Models/Packaging/CompiledPackageFile.cs @@ -0,0 +1,25 @@ +using System; +using System.Xml.Linq; + +namespace Umbraco.Core.Models.Packaging +{ + public class CompiledPackageFile + { + public static CompiledPackageFile Create(XElement xml) + { + if (xml.Name.LocalName != "file") + throw new ArgumentException("The xml isn't formatted correctly, a file element is defined by ", nameof(xml)); + return new CompiledPackageFile + { + UniqueFileName = xml.Element("guid")?.Value, + OriginalName = xml.Element("orgName")?.Value, + OriginalPath = xml.Element("orgPath")?.Value + }; + } + + public string OriginalPath { get; set; } + public string UniqueFileName { get; set; } + public string OriginalName { get; set; } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs index be9c69667a..4695ccb673 100644 --- a/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs +++ b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs @@ -53,13 +53,7 @@ namespace Umbraco.Core.Packaging UmbracoVersionRequirementsType = requirements.AttributeValue("type").IsNullOrWhiteSpace() ? RequirementsType.Legacy : Enum.Parse(requirements.AttributeValue("type")), Control = package.Element("control")?.Value, Actions = xml.Root.Element("Actions")?.ToString(SaveOptions.None) ?? "", //take the entire outer xml value - Files = xml.Root.Element("files")?.Elements("file")?.Select(x => new CompiledPackageFile - { - UniqueFileName = x.Element("guid")?.Value, - OriginalName = x.Element("orgName")?.Value, - OriginalPath = x.Element("orgPath")?.Value - }).ToList() ?? new List(), - + Files = xml.Root.Element("files")?.Elements("file")?.Select(CompiledPackageFile.Create).ToList() ?? new List(), Macros = xml.Root.Element("Macros")?.Elements("macro") ?? Enumerable.Empty(), Templates = xml.Root.Element("Templates")?.Elements("Template") ?? Enumerable.Empty(), Stylesheets = xml.Root.Element("Stylesheets")?.Elements("styleSheet") ?? Enumerable.Empty(), @@ -67,7 +61,7 @@ namespace Umbraco.Core.Packaging Languages = xml.Root.Element("Languages")?.Elements("Language") ?? Enumerable.Empty(), DictionaryItems = xml.Root.Element("DictionaryItems")?.Elements("DictionaryItem") ?? Enumerable.Empty(), DocumentTypes = xml.Root.Element("DocumentTypes")?.Elements("DocumentType") ?? Enumerable.Empty(), - Documents = xml.Root.Element("Documents")?.Elements("DocumentSet") ?? Enumerable.Empty(), + Documents = xml.Root.Element("Documents")?.Elements("DocumentSet")?.Select(CompiledPackageDocument.Create) ?? Enumerable.Empty(), }; def.Warnings = GetPreInstallWarnings(def, applicationRootFolder); diff --git a/src/Umbraco.Core/Packaging/PackageDataInstallation.cs b/src/Umbraco.Core/Packaging/PackageDataInstallation.cs index 1743bb0041..2212a8957d 100644 --- a/src/Umbraco.Core/Packaging/PackageDataInstallation.cs +++ b/src/Umbraco.Core/Packaging/PackageDataInstallation.cs @@ -170,51 +170,48 @@ namespace Umbraco.Core.Packaging #region Content - public IEnumerable ImportContent(IEnumerable element, IDictionary importedDocumentTypes, int userId) + public IEnumerable ImportContent(IEnumerable docs, IDictionary importedDocumentTypes, int userId) { - return element.Elements("DocumentSet").SelectMany(x => ImportContent(x, -1, importedDocumentTypes, userId)); + return docs.SelectMany(x => ImportContent(x, -1, importedDocumentTypes, userId)); } /// /// Imports and saves package xml as /// - /// Xml to import + /// Xml to import /// Optional parent Id for the content being imported /// A dictionary of already imported document types (basically used as a cache) /// Optional Id of the user performing the import /// An enumrable list of generated content - public IEnumerable ImportContent(XElement element, int parentId, IDictionary importedDocumentTypes, int userId) + public IEnumerable ImportContent(CompiledPackageDocument packageDocument, int parentId, IDictionary importedDocumentTypes, int userId) { - var name = element.Name.LocalName; - if (name.Equals("DocumentSet")) - { - //This is a regular deep-structured import - var roots = from doc in element.Elements() - where (string)doc.Attribute("isDoc") == "" - select doc; + var element = packageDocument.XmlData; - var contents = ParseDocumentRootXml(roots, parentId, importedDocumentTypes).ToList(); - if (contents.Any()) - _contentService.Save(contents, userId); - - return contents; - } + var roots = from doc in element.Elements() + where (string)doc.Attribute("isDoc") == "" + select doc; - var attribute = element.Attribute("isDoc"); - if (attribute != null) - { - //This is a single doc import - var elements = new List { element }; - var contents = ParseDocumentRootXml(elements, parentId, importedDocumentTypes).ToList(); - if (contents.Any()) - _contentService.Save(contents, userId); - - return contents; - } + var contents = ParseDocumentRootXml(roots, parentId, importedDocumentTypes).ToList(); + if (contents.Any()) + _contentService.Save(contents, userId); - throw new ArgumentException( - "The passed in XElement is not valid! It does not contain a root element called " + - "'DocumentSet' (for structured imports) nor is the first element a Document (for single document import)."); + return contents; + + //var attribute = element.Attribute("isDoc"); + //if (attribute != null) + //{ + // //This is a single doc import + // var elements = new List { element }; + // var contents = ParseDocumentRootXml(elements, parentId, importedDocumentTypes).ToList(); + // if (contents.Any()) + // _contentService.Save(contents, userId); + + // return contents; + //} + + //throw new ArgumentException( + // "The passed in XElement is not valid! It does not contain a root element called " + + // "'DocumentSet' (for structured imports) nor is the first element a Document (for single document import)."); } private IEnumerable ParseDocumentRootXml(IEnumerable roots, int parentId, IDictionary importedContentTypes) diff --git a/src/Umbraco.Core/Packaging/PackageInstallation.cs b/src/Umbraco.Core/Packaging/PackageInstallation.cs index ae9b8173f5..d7bb72836e 100644 --- a/src/Umbraco.Core/Packaging/PackageInstallation.cs +++ b/src/Umbraco.Core/Packaging/PackageInstallation.cs @@ -111,7 +111,6 @@ namespace Umbraco.Core.Packaging installationSummary.ContentInstalled = _packageDataInstallation.ImportContent(compiledPackage.Documents, importedDocTypes, userId); installationSummary.Actions = _parser.GetPackageActions(XElement.Parse(compiledPackage.Actions), compiledPackage.Name); installationSummary.MetaData = compiledPackage; - //fixme: Verify that this will work! installationSummary.FilesInstalled = packageDefinition.Files; //make sure the definition is up to date with everything diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 6b334b234b..cd974613eb 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -432,6 +432,8 @@ + + diff --git a/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs b/src/Umbraco.Tests/Packaging/PackageDataInstallationTests.cs similarity index 84% rename from src/Umbraco.Tests/Services/Importing/PackageImportTests.cs rename to src/Umbraco.Tests/Packaging/PackageDataInstallationTests.cs index f29a43c504..aecef34f4f 100644 --- a/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs +++ b/src/Umbraco.Tests/Packaging/PackageDataInstallationTests.cs @@ -3,24 +3,27 @@ using System.Linq; using System.Threading; using System.Xml.Linq; using NUnit.Framework; -using Umbraco.Core.Models; using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Composing.Composers; using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Packaging; using Umbraco.Core.Packaging; using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; +using Umbraco.Tests.Services; +using Umbraco.Tests.Services.Importing; using Umbraco.Tests.Testing; -namespace Umbraco.Tests.Services.Importing +namespace Umbraco.Tests.Packaging { [TestFixture] [Category("Slow")] [Apartment(ApartmentState.STA)] [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] - public class PackageImportTests : TestWithSomeContentBase + public class PackageDataInstallationTests : TestWithSomeContentBase { [HideFromTypeFinder] public class Editor1 : DataEditor @@ -66,7 +69,7 @@ namespace Umbraco.Tests.Services.Importing Composition.ComposeFileSystems(); } - private PackageDataInstallation PackagingService => Factory.GetInstance(); + private PackageDataInstallation PackageDataInstallation => Factory.GetInstance(); [Test] public void PackagingService_Can_Import_uBlogsy_ContentTypes_And_Verify_Structure() @@ -79,9 +82,9 @@ namespace Umbraco.Tests.Services.Importing var docTypeElement = xml.Descendants("DocumentTypes").First(); // Act - var dataTypes = PackagingService.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); - var templates = PackagingService.ImportTemplates(templateElement.Elements("Template").ToList(), 0); - var contentTypes = PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); + var dataTypes = PackageDataInstallation.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); + var templates = PackageDataInstallation.ImportTemplates(templateElement.Elements("Template").ToList(), 0); + var contentTypes = PackageDataInstallation.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); var numberOfTemplates = (from doc in templateElement.Elements("Template") select doc).Count(); var numberOfDocTypes = (from doc in docTypeElement.Elements("DocumentType") select doc).Count(); @@ -126,9 +129,9 @@ namespace Umbraco.Tests.Services.Importing var docTypeElement = xml.Descendants("DocumentTypes").First(); // Act - var dataTypes = PackagingService.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); - var templates = PackagingService.ImportTemplates(templateElement.Elements("Template").ToList(), 0); - var contentTypes = PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); + var dataTypes = PackageDataInstallation.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); + var templates = PackageDataInstallation.ImportTemplates(templateElement.Elements("Template").ToList(), 0); + var contentTypes = PackageDataInstallation.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); // Assert var mRBasePage = contentTypes.First(x => x.Alias == "MRBasePage"); @@ -151,9 +154,9 @@ namespace Umbraco.Tests.Services.Importing var docTypeElement = xml.Descendants("DocumentTypes").First(); // Act - var dataTypes = PackagingService.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); - var templates = PackagingService.ImportTemplates(templateElement.Elements("Template").ToList(), 0); - var contentTypes = PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); + var dataTypes = PackageDataInstallation.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); + var templates = PackageDataInstallation.ImportTemplates(templateElement.Elements("Template").ToList(), 0); + var contentTypes = PackageDataInstallation.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); var numberOfDocTypes = (from doc in docTypeElement.Elements("DocumentType") select doc).Count(); @@ -190,7 +193,7 @@ namespace Umbraco.Tests.Services.Importing var init = ServiceContext.FileService.GetTemplates().Count(); // Act - var templates = PackagingService.ImportTemplates(element.Elements("Template").ToList(), 0); + var templates = PackageDataInstallation.ImportTemplates(element.Elements("Template").ToList(), 0); var numberOfTemplates = (from doc in element.Elements("Template") select doc).Count(); var allTemplates = ServiceContext.FileService.GetTemplates(); @@ -213,7 +216,7 @@ namespace Umbraco.Tests.Services.Importing // Act - var templates = PackagingService.ImportTemplate(element.Elements("Template").First(), 0); + var templates = PackageDataInstallation.ImportTemplate(element.Elements("Template").First(), 0); // Assert Assert.That(templates, Is.Not.Null); @@ -233,9 +236,9 @@ namespace Umbraco.Tests.Services.Importing // Act - var dataTypeDefinitions = PackagingService.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); - var templates = PackagingService.ImportTemplates(templateElement.Elements("Template").ToList(), 0); - var contentTypes = PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); + var dataTypeDefinitions = PackageDataInstallation.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); + var templates = PackageDataInstallation.ImportTemplates(templateElement.Elements("Template").ToList(), 0); + var contentTypes = PackageDataInstallation.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); var numberOfDocTypes = (from doc in docTypeElement.Elements("DocumentType") select doc).Count(); // Assert @@ -268,13 +271,13 @@ namespace Umbraco.Tests.Services.Importing var docTypeElement = xml.Descendants("DocumentTypes").First(); // Act - var dataTypeDefinitions = PackagingService.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); - var templates = PackagingService.ImportTemplates(templateElement.Elements("Template").ToList(), 0); - var contentTypes = PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); + var dataTypeDefinitions = PackageDataInstallation.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); + var templates = PackageDataInstallation.ImportTemplates(templateElement.Elements("Template").ToList(), 0); + var contentTypes = PackageDataInstallation.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); var numberOfDocTypes = (from doc in docTypeElement.Elements("DocumentType") select doc).Count(); //Assert - Re-Import contenttypes doesn't throw - Assert.DoesNotThrow(() => PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0)); + Assert.DoesNotThrow(() => PackageDataInstallation.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0)); Assert.That(contentTypes.Count(), Is.EqualTo(numberOfDocTypes)); Assert.That(dataTypeDefinitions, Is.Not.Null); Assert.That(dataTypeDefinitions.Any(), Is.True); @@ -292,13 +295,13 @@ namespace Umbraco.Tests.Services.Importing var docTypeElement = xml.Descendants("DocumentTypes").First(); // Act - var dataTypeDefinitions = PackagingService.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); - var templates = PackagingService.ImportTemplates(templateElement.Elements("Template").ToList(), 0); - var contentTypes = PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); + var dataTypeDefinitions = PackageDataInstallation.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); + var templates = PackageDataInstallation.ImportTemplates(templateElement.Elements("Template").ToList(), 0); + var contentTypes = PackageDataInstallation.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); var numberOfDocTypes = (from doc in docTypeElement.Elements("DocumentType") select doc).Count(); //Assert - Re-Import contenttypes doesn't throw - Assert.DoesNotThrow(() => PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0)); + Assert.DoesNotThrow(() => PackageDataInstallation.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0)); Assert.That(contentTypes.Count(), Is.EqualTo(numberOfDocTypes)); Assert.That(dataTypeDefinitions, Is.Not.Null); Assert.That(dataTypeDefinitions.Any(), Is.True); @@ -314,13 +317,13 @@ namespace Umbraco.Tests.Services.Importing var dataTypeElement = xml.Descendants("DataTypes").First(); var docTypesElement = xml.Descendants("DocumentTypes").First(); var element = xml.Descendants("DocumentSet").First(); - + var packageDocument = CompiledPackageDocument.Create(element); // Act - var dataTypeDefinitions = PackagingService.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); - var contentTypes = PackagingService.ImportDocumentTypes(docTypesElement.Elements("DocumentType"), 0); + var dataTypeDefinitions = PackageDataInstallation.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); + var contentTypes = PackageDataInstallation.ImportDocumentTypes(docTypesElement.Elements("DocumentType"), 0); var importedContentTypes = contentTypes.ToDictionary(x => x.Alias, x => x); - var contents = PackagingService.ImportContent(element, -1, importedContentTypes, 0); + var contents = PackageDataInstallation.ImportContent(packageDocument, -1, importedContentTypes, 0); var numberOfDocs = (from doc in element.Descendants() where (string) doc.Attribute("isDoc") == "" select doc).Count(); @@ -348,13 +351,13 @@ namespace Umbraco.Tests.Services.Importing var dataTypeElement = xml.Descendants("DataTypes").First(); var docTypesElement = xml.Descendants("DocumentTypes").First(); var element = xml.Descendants("DocumentSet").First(); - + var packageDocument = CompiledPackageDocument.Create(element); // Act - var dataTypeDefinitions = PackagingService.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); - var contentTypes = PackagingService.ImportDocumentTypes(docTypesElement.Elements("DocumentType"), 0); + var dataTypeDefinitions = PackageDataInstallation.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0); + var contentTypes = PackageDataInstallation.ImportDocumentTypes(docTypesElement.Elements("DocumentType"), 0); var importedContentTypes = contentTypes.ToDictionary(x => x.Alias, x => x); - var contents = PackagingService.ImportContent(element, -1, importedContentTypes, 0); + var contents = PackageDataInstallation.ImportContent(packageDocument, -1, importedContentTypes, 0); var numberOfDocs = (from doc in element.Descendants() where (string)doc.Attribute("isDoc") == "" select doc).Count(); @@ -387,7 +390,7 @@ namespace Umbraco.Tests.Services.Importing // Act - var templates = PackagingService.ImportTemplates(templateElement.Elements("Template").ToList(), 0); + var templates = PackageDataInstallation.ImportTemplates(templateElement.Elements("Template").ToList(), 0); var numberOfTemplates = (from doc in templateElement.Elements("Template") select doc).Count(); // Assert @@ -404,7 +407,7 @@ namespace Umbraco.Tests.Services.Importing // Act - var contentTypes = PackagingService.ImportDocumentType(docTypeElement, 0); + var contentTypes = PackageDataInstallation.ImportDocumentType(docTypeElement, 0); // Assert Assert.That(contentTypes.Any(), Is.True); @@ -422,7 +425,7 @@ namespace Umbraco.Tests.Services.Importing var serializer = Factory.GetInstance(); // Act - var contentTypes = PackagingService.ImportDocumentType(docTypeElement, 0); + var contentTypes = PackageDataInstallation.ImportDocumentType(docTypeElement, 0); var contentType = contentTypes.FirstOrDefault(); var element = serializer.Serialize(contentType); @@ -444,8 +447,8 @@ namespace Umbraco.Tests.Services.Importing var docTypeElement = XElement.Parse(strXml); // Act - var contentTypes = PackagingService.ImportDocumentType(docTypeElement, 0); - var contentTypesUpdated = PackagingService.ImportDocumentType(docTypeElement, 0); + var contentTypes = PackageDataInstallation.ImportDocumentType(docTypeElement, 0); + var contentTypesUpdated = PackageDataInstallation.ImportDocumentType(docTypeElement, 0); // Assert Assert.That(contentTypes.Any(), Is.True); @@ -475,8 +478,8 @@ namespace Umbraco.Tests.Services.Importing // Act var numberOfTemplates = (from doc in templateElement.Elements("Template") select doc).Count(); - var templates = PackagingService.ImportTemplates(templateElement.Elements("Template").ToList(), 0); - var templatesAfterUpdate = PackagingService.ImportTemplates(templateElementUpdated.Elements("Template").ToList(), 0); + var templates = PackageDataInstallation.ImportTemplates(templateElement.Elements("Template").ToList(), 0); + var templatesAfterUpdate = PackageDataInstallation.ImportTemplates(templateElementUpdated.Elements("Template").ToList(), 0); var allTemplates = fileService.GetTemplates(); // Assert @@ -502,7 +505,7 @@ namespace Umbraco.Tests.Services.Importing AddLanguages(); // Act - PackagingService.ImportDictionaryItems(dictionaryItemsElement.Elements("DictionaryItem"), 0); + PackageDataInstallation.ImportDictionaryItems(dictionaryItemsElement.Elements("DictionaryItem"), 0); // Assert AssertDictionaryItem("Parent", expectedEnglishParentValue, "en-GB"); @@ -524,7 +527,7 @@ namespace Umbraco.Tests.Services.Importing AddLanguages(); // Act - var dictionaryItems = PackagingService.ImportDictionaryItems(dictionaryItemsElement.Elements("DictionaryItem"), 0); + var dictionaryItems = PackageDataInstallation.ImportDictionaryItems(dictionaryItemsElement.Elements("DictionaryItem"), 0); // Assert Assert.That(ServiceContext.LocalizationService.DictionaryItemExists(parentKey), "DictionaryItem parentKey does not exist"); @@ -553,7 +556,7 @@ namespace Umbraco.Tests.Services.Importing AddExistingEnglishAndNorwegianParentDictionaryItem(expectedEnglishParentValue, expectedNorwegianParentValue); // Act - PackagingService.ImportDictionaryItems(dictionaryItemsElement.Elements("DictionaryItem"), 0); + PackageDataInstallation.ImportDictionaryItems(dictionaryItemsElement.Elements("DictionaryItem"), 0); // Assert AssertDictionaryItem("Parent", expectedEnglishParentValue, "en-GB"); @@ -578,7 +581,7 @@ namespace Umbraco.Tests.Services.Importing AddExistingEnglishParentDictionaryItem(expectedEnglishParentValue); // Act - PackagingService.ImportDictionaryItems(dictionaryItemsElement.Elements("DictionaryItem"), 0); + PackageDataInstallation.ImportDictionaryItems(dictionaryItemsElement.Elements("DictionaryItem"), 0); // Assert AssertDictionaryItem("Parent", expectedEnglishParentValue, "en-GB"); @@ -595,7 +598,7 @@ namespace Umbraco.Tests.Services.Importing var LanguageItemsElement = newPackageXml.Elements("Languages").First(); // Act - var languages = PackagingService.ImportLanguages(LanguageItemsElement.Elements("Language"), 0); + var languages = PackageDataInstallation.ImportLanguages(LanguageItemsElement.Elements("Language"), 0); var allLanguages = ServiceContext.LocalizationService.GetAllLanguages(); // Assert @@ -616,7 +619,7 @@ namespace Umbraco.Tests.Services.Importing // Act - var macros = PackagingService.ImportMacros(macrosElement.Elements("macro"), 0).ToList(); + var macros = PackageDataInstallation.ImportMacros(macrosElement.Elements("macro"), 0).ToList(); // Assert Assert.That(macros.Any(), Is.True); @@ -638,7 +641,7 @@ namespace Umbraco.Tests.Services.Importing // Act - var macros = PackagingService.ImportMacros(macrosElement.Elements("macro"), 0).ToList(); + var macros = PackageDataInstallation.ImportMacros(macrosElement.Elements("macro"), 0).ToList(); // Assert Assert.That(macros.Any(), Is.True); @@ -662,8 +665,8 @@ namespace Umbraco.Tests.Services.Importing // Act - var templates = PackagingService.ImportTemplates(templateElement.Elements("Template").ToList(), 0); - var contentTypes = PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); + var templates = PackageDataInstallation.ImportTemplates(templateElement.Elements("Template").ToList(), 0); + var contentTypes = PackageDataInstallation.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); var numberOfDocTypes = (from doc in docTypeElement.Elements("DocumentType") select doc).Count(); // Assert @@ -690,7 +693,7 @@ namespace Umbraco.Tests.Services.Importing // Act - var contentTypes = PackagingService.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); + var contentTypes = PackageDataInstallation.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0); var numberOfDocTypes = (from doc in docTypeElement.Elements("DocumentType") select doc).Count(); // Assert diff --git a/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs b/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs index 8fcacbeff7..d944b638ad 100644 --- a/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs +++ b/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs @@ -53,14 +53,15 @@ namespace Umbraco.Tests.Packaging applicationRootFolder: new DirectoryInfo(IOHelper.GetRootDirectorySafe()), packageExtractionFolder: new DirectoryInfo(IOHelper.MapPath("~/" + _testBaseFolder))); //we don't want to extract package files to the real root, so extract to a test folder - private const string DocumentTypePickerUmb = "Document_Type_Picker_1.1.umb"; + private const string DocumentTypePickerPackage = "Document_Type_Picker_1.1.umb"; + private const string HelloPackage = "Hello_1.0.0.zip"; [Test] - public void Can_Read_Compiled_Package() + public void Can_Read_Compiled_Package_1() { var package = PackageInstallation.ReadPackage( //this is where our test zip file is - new FileInfo(Path.Combine(IOHelper.MapPath("~/Packaging/packages"), DocumentTypePickerUmb))); + new FileInfo(Path.Combine(IOHelper.MapPath("~/Packaging/packages"), DocumentTypePickerPackage))); Assert.IsNotNull(package); Assert.AreEqual(1, package.Files.Count); Assert.AreEqual("095e064b-ba4d-442d-9006-3050983c13d8.dll", package.Files[0].UniqueFileName); @@ -78,6 +79,32 @@ namespace Umbraco.Tests.Packaging Assert.AreEqual(1, package.DataTypes.Count()); } + [Test] + public void Can_Read_Compiled_Package_2() + { + var package = PackageInstallation.ReadPackage( + //this is where our test zip file is + new FileInfo(Path.Combine(IOHelper.MapPath("~/Packaging/packages"), HelloPackage))); + Assert.IsNotNull(package); + Assert.AreEqual(0, package.Files.Count); + Assert.AreEqual("Hello", package.Name); + Assert.AreEqual("1.0.0", package.Version); + Assert.AreEqual("http://opensource.org/licenses/MIT", package.LicenseUrl); + Assert.AreEqual("MIT License", package.License); + Assert.AreEqual(8, package.UmbracoVersion.Major); + Assert.AreEqual(0, package.UmbracoVersion.Minor); + Assert.AreEqual(0, package.UmbracoVersion.Build); + Assert.AreEqual(RequirementsType.Strict, package.UmbracoVersionRequirementsType); + Assert.AreEqual("asdf", package.Author); + Assert.AreEqual("http://hello.com", package.AuthorUrl); + Assert.AreEqual("asdf", package.Readme); + Assert.AreEqual(1, package.Documents.Count()); + Assert.AreEqual("root", package.Documents.First().ImportMode); + Assert.AreEqual(1, package.DocumentTypes.Count()); + Assert.AreEqual(1, package.Templates.Count()); + Assert.AreEqual(1, package.DataTypes.Count()); + } + [Test] public void Can_Read_Compiled_Package_Warnings() { @@ -89,7 +116,7 @@ namespace Umbraco.Tests.Packaging var preInstallWarnings = PackageInstallation.ReadPackage( //this is where our test zip file is - new FileInfo(Path.Combine(IOHelper.MapPath("~/Packaging/packages"), DocumentTypePickerUmb))) + new FileInfo(Path.Combine(IOHelper.MapPath("~/Packaging/packages"), DocumentTypePickerPackage))) .Warnings; Assert.IsNotNull(preInstallWarnings); @@ -104,7 +131,7 @@ namespace Umbraco.Tests.Packaging { var package = PackageInstallation.ReadPackage( //this is where our test zip file is - new FileInfo(Path.Combine(IOHelper.MapPath("~/Packaging/packages"), DocumentTypePickerUmb))); + new FileInfo(Path.Combine(IOHelper.MapPath("~/Packaging/packages"), DocumentTypePickerPackage))); var def = PackageDefinition.FromCompiledPackage(package); def.Id = 1; @@ -126,7 +153,7 @@ namespace Umbraco.Tests.Packaging { var package = PackageInstallation.ReadPackage( //this is where our test zip file is - new FileInfo(Path.Combine(IOHelper.MapPath("~/Packaging/packages"), DocumentTypePickerUmb))); + new FileInfo(Path.Combine(IOHelper.MapPath("~/Packaging/packages"), DocumentTypePickerPackage))); var def = PackageDefinition.FromCompiledPackage(package); def.Id = 1; def.PackageId = Guid.NewGuid(); diff --git a/src/Umbraco.Tests/Packaging/Packages/Hello_1.0.0.zip b/src/Umbraco.Tests/Packaging/Packages/Hello_1.0.0.zip new file mode 100644 index 0000000000000000000000000000000000000000..c95cb282d14065396851362194fd37007cb632fe GIT binary patch literal 1529 zcmVeh01E&B0B~VrYhh<)E_iKh%~wlv z+cpqBH#7MUEDt@I;=|N46cg8)q?)l(kENssPYxgnN@yrT0YTfEOnzAp{Ym``U63FN z=#iw8OAofN59}_!UF?GY{PlP6@@bnH5118%=2O${Ii`X0m?k7&PtB62*2sJrK7SUJ z+vpa>AJG~EWf(z9GF%vTO^`gL)e6=-MXO@uXp6(&aF)@)uGXzYx(&f=&k5{?Y~>`T z`8$?{f!(ZHQAT2%7uYCSHZ?b#?`A^bCLYEICII&)wq zlvXw((q2rrfnBN|8U9ofhPOE9g~9hbJT;e`k(isU4rp!BpOl58z^)gngyg`0W7Z2r zvO_%H2uZzYJ=&_v23}joH$WxdDCTOkQIVvFU_auhARM1KW+Q1lXeQy@C;>ac&-d4} ztJT%Lu>IjfD59$bRRP#-1q3r1ms-RPV~M#zwmZuBf+m0-qg25z*zT7g&G0Q8&L9cY zqT>yH)A)$@Q!_#s`QtvahJNf={%GJ>k)L9#?Tqzq16hPKamlH#I%S9a*ktxuccmOqfY~kVhf~U@|r9ovW&p2A(tS=&OvN0=6rVw5O;oFE=R9X>J;i z3{rQR*_@YSFj9pNUPi+GZ5d^x*kC}Jvl43+%n8qwbXP~J@?8rQ&4jMk1Q(s|5|ssn zT4^^z^#+{Ea9?J)kE4Ek>3G&)FdmBxCl*RBJ!?2{5D)S0+_HOY4r8kLe{R?~WtFXY+&PpM8(R%p3C&USUSli5C%?>96{ zaBkdT&M;{7dmn5;0U?acns6g_u{b?rQ^sNIN0qwk?)OB4QaDMcZj0! zx-Mk!1B51BHqBJLQaVD@S)e=-O>5s&TgTA!-lL2FUqlet3ZWLvd1Su>^ch_j=P{S< z>D#E!z{89_=DpV$ej<@b)K~F$BiHZa(WNzV(gg4yMHU+MVU#(kI~=B|8@ut3D8P9V!qgnX#Y}X;HL6-zceaM{-|9=pq@ubP@zHhGS#oR~cAwz5 zt$brV$O<|?8bs43>%1F~uSmWDOyPr}sj5G4)LT5nJxPmRTv?L%h?j9z^t7?~sx07R z%+NH9sb;Yq^0o8@Z2J8C*-tlUPfHHEd6{Lv{89~Q8DxW=217?m)Rue?-+3F1dHb=q zKry2&HG?l8$R}d^IXpgE_6-cc65{Xl5J&pNmJFkQ(1=@W8gqE2oV|di(cK<+0Di}P zgB{={LWO*Ky?sC|-XM6b0h%dUmkia$KL7Hmr2NGKrVRn)Yic6i#uxRvI-F$ROKp-! zhAWjGP*yg3>mT_du8A)fhZ`@?ZajQiowMMbwQz$Xx#`0fv&n+3ry;nCyRi@7yWKbb zzfem70u%rg000080Jn)RPO_5QS%C!r02>eh01E&B0000000000000000001RVPk7y fXJsyUZER3W1qJ{B000310RTAw0062500000ehA61 literal 0 HcmV?d00001 diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 8d3900088c..e49ba250fa 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -444,7 +444,7 @@ - + True True From db2a003444664d12935fed139d94ff0dc2cb76af Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 16 Jan 2019 00:54:25 +1100 Subject: [PATCH 73/93] change how the install status works, fixes infinite loop --- .../src/views/packages/views/install-local.controller.js | 2 +- src/Umbraco.Web/Search/ExamineComponent.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.controller.js index 6753fad942..17b417de48 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.controller.js @@ -171,7 +171,7 @@ }, installError) .then(function (pack) { - vm.installState.status = labels.installStateRestarting; + vm.installState.status = labels.installStateInstalling; vm.installState.progress = "75"; return packageResource.installData(pack); }, diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index 336fcc7aec..05a1258441 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -453,6 +453,7 @@ namespace Umbraco.Web.Search foreach (var item in paged) if (int.TryParse(item.Id, out var contentId)) DeleteIndexForEntity(contentId, false); + page++; } } } From 20d6a3b4b496dfd61ca2e10b35faa5daa46d6eb3 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 16 Jan 2019 01:02:21 +1100 Subject: [PATCH 74/93] some cleanup and ensure the package file is deleted after install --- src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs | 2 +- src/Umbraco.Web/Editors/PackageInstallController.cs | 4 +--- src/Umbraco.Web/Models/PackageInstallModel.cs | 5 ----- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs index 4695ccb673..3dee5686ff 100644 --- a/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs +++ b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs @@ -50,7 +50,7 @@ namespace Umbraco.Core.Packaging Url = package.Element("url")?.Value, IconUrl = package.Element("iconUrl")?.Value, UmbracoVersion = new Version((int)requirements.Element("major"), (int)requirements.Element("minor"), (int)requirements.Element("patch")), - UmbracoVersionRequirementsType = requirements.AttributeValue("type").IsNullOrWhiteSpace() ? RequirementsType.Legacy : Enum.Parse(requirements.AttributeValue("type")), + UmbracoVersionRequirementsType = requirements.AttributeValue("type").IsNullOrWhiteSpace() ? RequirementsType.Legacy : Enum.Parse(requirements.AttributeValue("type"), true), Control = package.Element("control")?.Value, Actions = xml.Root.Element("Actions")?.ToString(SaveOptions.None) ?? "", //take the entire outer xml value Files = xml.Root.Element("files")?.Elements("file")?.Select(CompiledPackageFile.Create).ToList() ?? new List(), diff --git a/src/Umbraco.Web/Editors/PackageInstallController.cs b/src/Umbraco.Web/Editors/PackageInstallController.cs index 38b75a27f8..2dfb5e12e4 100644 --- a/src/Umbraco.Web/Editors/PackageInstallController.cs +++ b/src/Umbraco.Web/Editors/PackageInstallController.cs @@ -287,7 +287,6 @@ namespace Umbraco.Web.Editors var model = new LocalPackageInstallModel { PackageGuid = Guid.Parse(packageGuid), - //RepositoryGuid = Guid.Parse("65194810-1f85-11dd-bd0b-0800200c9a66"), ZipFileName = fileName }; @@ -415,7 +414,7 @@ namespace Umbraco.Web.Editors var clientDependencyUpdated = clientDependencyConfig.UpdateVersionNumber( UmbracoVersion.SemanticVersion, DateTime.UtcNow, "yyyyMMdd"); - //fixme: when do we delete the zip file? + zipFile.Delete(); var redirectUrl = ""; if (packageInfo.Control.IsNullOrWhiteSpace() == false) @@ -431,7 +430,6 @@ namespace Umbraco.Web.Editors Id = model.Id, ZipFileName = model.ZipFileName, PackageGuid = model.PackageGuid, - //RepositoryGuid = model.RepositoryGuid, PostInstallationPath = redirectUrl }; diff --git a/src/Umbraco.Web/Models/PackageInstallModel.cs b/src/Umbraco.Web/Models/PackageInstallModel.cs index 2decaeb098..1ec7ddd434 100644 --- a/src/Umbraco.Web/Models/PackageInstallModel.cs +++ b/src/Umbraco.Web/Models/PackageInstallModel.cs @@ -15,11 +15,6 @@ namespace Umbraco.Web.Models [DataMember(Name = "packageGuid")] public Guid PackageGuid { get; set; } - ////TODO: Do we need this? - //[DataMember(Name = "repositoryGuid")] - //public Guid RepositoryGuid { get; set; } - - [DataMember(Name = "zipFileName")] public string ZipFileName { get; set; } From b7c15dc393d43a40a33670b836033411ea0e0f72 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 16 Jan 2019 01:53:08 +1100 Subject: [PATCH 75/93] gets readme and our package install working --- .../src/common/resources/package.resource.js | 2 +- .../views/packages/views/repo.controller.js | 162 ++++++++++-------- src/Umbraco.Web/Editors/PackageController.cs | 41 ++++- .../Editors/PackageInstallController.cs | 36 +--- .../ContentEditing/InstalledPackageModel.cs | 3 + 5 files changed, 135 insertions(+), 109 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 ce2a557390..0d74d0fdd3 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 @@ -19,7 +19,7 @@ function packageResource($q, $http, umbDataFormatter, umbRequestHelper) { return umbRequestHelper.resourcePromise( $http.get( umbRequestHelper.getApiUrl( - "packageInstallApiBaseUrl", + "packageApiBaseUrl", "GetInstalled")), 'Failed to get installed packages'); }, diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/views/repo.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/views/repo.controller.js index 395800d329..b77326d2fc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/views/repo.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/views/repo.controller.js @@ -32,6 +32,8 @@ vm.search = search; vm.installCompleted = false; + var labels = {}; + var currSort = "Latest"; //used to cancel any request in progress if another one needs to take it's place var canceler = null; @@ -52,22 +54,38 @@ vm.loading = true; + var labelKeys = [ + "packager_installStateImporting", + "packager_installStateInstalling", + "packager_installStateRestarting", + "packager_installStateComplete", + "packager_installStateCompleted" + ]; + + localizationService.localizeMany(labelKeys).then(function (values) { + labels.installStateImporting = values[0]; + labels.installStateInstalling = values[1]; + labels.installStateRestarting = values[2]; + labels.installStateComplete = values[3]; + labels.installStateCompleted = values[4]; + }); + $q.all([ - ourPackageRepositoryResource.getCategories() - .then(function(cats) { + ourPackageRepositoryResource.getCategories() + .then(function (cats) { vm.categories = cats; }), - ourPackageRepositoryResource.getPopular(8) - .then(function(pack) { + ourPackageRepositoryResource.getPopular(8) + .then(function (pack) { vm.popular = pack.packages; }), - ourPackageRepositoryResource.search(vm.pagination.pageNumber - 1, vm.pagination.pageSize, currSort) - .then(function(pack) { + ourPackageRepositoryResource.search(vm.pagination.pageNumber - 1, vm.pagination.pageSize, currSort) + .then(function (pack) { vm.packages = pack.packages; vm.pagination.totalPages = Math.ceil(pack.total / vm.pagination.pageSize); }) - ]) - .then(function() { + ]) + .then(function () { vm.loading = false; }); @@ -94,18 +112,18 @@ currSort = "Latest"; $q.all([ - ourPackageRepositoryResource.getPopular(8, searchCategory) - .then(function(pack) { + ourPackageRepositoryResource.getPopular(8, searchCategory) + .then(function (pack) { vm.popular = pack.packages; }), - ourPackageRepositoryResource.search(vm.pagination.pageNumber - 1, vm.pagination.pageSize, currSort, searchCategory, vm.searchQuery) - .then(function(pack) { + ourPackageRepositoryResource.search(vm.pagination.pageNumber - 1, vm.pagination.pageSize, currSort, searchCategory, vm.searchQuery) + .then(function (pack) { vm.packages = pack.packages; vm.pagination.totalPages = Math.ceil(pack.total / vm.pagination.pageSize); vm.pagination.pageNumber = 1; }) - ]) - .then(function() { + ]) + .then(function () { vm.loading = false; selectedCategory.active = reset === false; }); @@ -115,12 +133,12 @@ ourPackageRepositoryResource.getDetails(selectedPackage.id) .then(function (pack) { packageResource.validateInstalled(pack.name, pack.latestVersion) - .then(function() { + .then(function () { //ok, can install vm.package = pack; vm.package.isValid = true; vm.packageViewState = "packageDetails"; - }, function() { + }, function () { //nope, cannot install vm.package = pack; vm.package.isValid = false; @@ -130,7 +148,7 @@ } function setPackageViewState(state) { - if(state) { + if (state) { vm.packageViewState = state; } } @@ -164,13 +182,13 @@ packageResource .fetch(selectedPackage.id) - .then(function(pack) { - vm.packageViewState = "packageInstall"; - vm.loading = false; - vm.localPackage = pack; - vm.localPackage.allowed = true; + .then(function (pack) { + vm.packageViewState = "packageInstall"; + vm.loading = false; + vm.localPackage = pack; + vm.localPackage.allowed = true; }, function (evt, status, headers, config) { - + if (status == 400) { //it's a validation error vm.installState.type = "error"; @@ -186,68 +204,68 @@ function installPackage(selectedPackage) { - vm.installState.status = localizationService.localize("packager_installStateImporting"); + vm.installState.status = labels.installStateImporting; vm.installState.progress = "0"; packageResource .import(selectedPackage) - .then(function(pack) { - vm.installState.status = localizationService.localize("packager_installStateInstalling"); - vm.installState.progress = "25"; - return packageResource.installFiles(pack); - }, + .then(function (pack) { + vm.installState.status = labels.installStateInstalling; + vm.installState.progress = "25"; + return packageResource.installFiles(pack); + }, error) - .then(function(pack) { - vm.installState.status = localizationService.localize("packager_installStateRestarting"); - vm.installState.progress = "50"; - var deferred = $q.defer(); + .then(function (pack) { + vm.installState.status = labels.installStateRestarting; + vm.installState.progress = "50"; + var deferred = $q.defer(); - //check if the app domain is restarted ever 2 seconds - var count = 0; - function checkRestart() { - $timeout(function () { + //check if the app domain is restarted ever 2 seconds + var count = 0; + function checkRestart() { + $timeout(function () { packageResource.checkRestart(pack).then(function (d) { count++; //if there is an id it means it's not restarted yet but we'll limit it to only check 10 times if (d.isRestarting && count < 10) { - checkRestart(); + checkRestart(); } else { - //it's restarted! - deferred.resolve(d); + //it's restarted! + deferred.resolve(d); } - }, - error); - }, 2000); - } + }, + error); + }, 2000); + } - checkRestart(); - - return deferred.promise; - }, error) + checkRestart(); + + return deferred.promise; + }, error) .then(function (pack) { - vm.installState.status = localizationService.localize("packager_installStateRestarting"); - vm.installState.progress = "75"; - return packageResource.installData(pack); - }, + vm.installState.status = labels.installStateInstalling; + vm.installState.progress = "75"; + return packageResource.installData(pack); + }, error) - .then(function(pack) { - vm.installState.status = localizationService.localize("packager_installStateComplete"); - vm.installState.progress = "100"; - return packageResource.cleanUp(pack); - }, + .then(function (pack) { + vm.installState.status = labels.installStateComplete; + vm.installState.progress = "100"; + return packageResource.cleanUp(pack); + }, error) - .then(function(result) { + .then(function (result) { - if (result.postInstallationPath) { - //Put the redirect Uri in a cookie so we can use after reloading - localStorageService.set("packageInstallUri", result.postInstallationPath); - } + if (result.postInstallationPath) { + //Put the redirect Uri in a cookie so we can use after reloading + localStorageService.set("packageInstallUri", result.postInstallationPath); + } - vm.installState.status = localizationService.localize("packager_installStateCompleted"); - vm.installCompleted = true; + vm.installState.status = labels.installStateCompleted; + vm.installCompleted = true; - }, + }, error); } @@ -265,7 +283,7 @@ } - var searchDebounced = _.debounce(function(e) { + var searchDebounced = _.debounce(function (e) { $scope.$apply(function () { @@ -281,12 +299,12 @@ currSort = vm.searchQuery ? "Default" : "Latest"; ourPackageRepositoryResource.search(vm.pagination.pageNumber - 1, - vm.pagination.pageSize, - currSort, - "", - vm.searchQuery, - canceler) - .then(function(pack) { + vm.pagination.pageSize, + currSort, + "", + vm.searchQuery, + canceler) + .then(function (pack) { vm.packages = pack.packages; vm.pagination.totalPages = Math.ceil(pack.total / vm.pagination.pageSize); vm.pagination.pageNumber = 1; diff --git a/src/Umbraco.Web/Editors/PackageController.cs b/src/Umbraco.Web/Editors/PackageController.cs index 063dcf483e..2d1d124a2c 100644 --- a/src/Umbraco.Web/Editors/PackageController.cs +++ b/src/Umbraco.Web/Editors/PackageController.cs @@ -1,11 +1,14 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; +using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Web.Http; using Umbraco.Core.IO; using Umbraco.Core.Models.Packaging; +using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; @@ -111,5 +114,41 @@ namespace Umbraco.Web.Editors return response; } + /// + /// Returns all installed packages - only shows their latest versions + /// + /// + public IEnumerable GetInstalled() + { + return Services.PackagingService.GetAllInstalledPackages() + .GroupBy( + //group by name + x => x.Name, + //select the package with a parsed version + pck => Version.TryParse(pck.Version, out var pckVersion) + ? new { package = pck, version = pckVersion } + : new { package = pck, version = new Version(0, 0, 0) }) + .Select(grouping => + { + //get the max version for the package + var maxVersion = grouping.Max(x => x.version); + //only return the first package with this version + return grouping.First(x => x.version == maxVersion).package; + }) + .Select(pack => new InstalledPackageModel + { + Name = pack.Name, + Id = pack.Id, + Author = pack.Author, + Version = pack.Version, + Url = pack.Url, + License = pack.License, + LicenseUrl = pack.LicenseUrl, + Files = pack.Files, + IconUrl = pack.IconUrl, + Readme = pack.Readme + }) + .ToList(); + } } } diff --git a/src/Umbraco.Web/Editors/PackageInstallController.cs b/src/Umbraco.Web/Editors/PackageInstallController.cs index 2dfb5e12e4..bc6db1b552 100644 --- a/src/Umbraco.Web/Editors/PackageInstallController.cs +++ b/src/Umbraco.Web/Editors/PackageInstallController.cs @@ -92,41 +92,7 @@ namespace Umbraco.Web.Editors return Ok(); } - /// - /// Returns all installed packages - only shows their latest versions - /// - /// - public IEnumerable GetInstalled() - { - return Services.PackagingService.GetAllInstalledPackages() - .GroupBy( - //group by name - x => x.Name, - //select the package with a parsed version - pck => Version.TryParse(pck.Version, out var pckVersion) - ? new { package = pck, version = pckVersion } - : new { package = pck, version = new Version(0, 0, 0) }) - .Select(grouping => - { - //get the max version for the package - var maxVersion = grouping.Max(x => x.version); - //only return the first package with this version - return grouping.First(x => x.version == maxVersion).package; - }) - .Select(pack => new InstalledPackageModel - { - Name = pack.Name, - Id = pack.Id, - Author = pack.Author, - Version = pack.Version, - Url = pack.Url, - License = pack.License, - LicenseUrl = pack.LicenseUrl, - Files = pack.Files, - IconUrl = pack.IconUrl - }) - .ToList(); - } + private void PopulateFromPackageData(LocalPackageInstallModel model) { diff --git a/src/Umbraco.Web/Models/ContentEditing/InstalledPackageModel.cs b/src/Umbraco.Web/Models/ContentEditing/InstalledPackageModel.cs index 9161f972bd..67bfe6fe53 100644 --- a/src/Umbraco.Web/Models/ContentEditing/InstalledPackageModel.cs +++ b/src/Umbraco.Web/Models/ContentEditing/InstalledPackageModel.cs @@ -32,5 +32,8 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "iconUrl")] public string IconUrl { get; set; } + + [DataMember(Name = "readme")] + public string Readme { get; set; } } } From d97b100bab5ff66b2bc0f99a7ceaf89a8e2c22ce Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 16 Jan 2019 02:18:02 +1100 Subject: [PATCH 76/93] fixes merge --- src/Umbraco.Core/Packaging/PackageDataInstallation.cs | 4 ++++ src/Umbraco.Core/Services/Implement/PackagingService.cs | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Core/Packaging/PackageDataInstallation.cs b/src/Umbraco.Core/Packaging/PackageDataInstallation.cs index 2212a8957d..36066b104f 100644 --- a/src/Umbraco.Core/Packaging/PackageDataInstallation.cs +++ b/src/Umbraco.Core/Packaging/PackageDataInstallation.cs @@ -556,6 +556,10 @@ namespace Umbraco.Core.Packaging if (isListView != null) contentType.IsContainer = isListView.Value.InvariantEquals("true"); + var isElement = infoElement.Element("IsElement"); + if (isElement != null) + contentType.IsElement = isElement.Value.InvariantEquals("true"); + //Name of the master corresponds to the parent and we need to ensure that the Parent Id is set var masterElement = infoElement.Element("Master"); if (masterElement != null) diff --git a/src/Umbraco.Core/Services/Implement/PackagingService.cs b/src/Umbraco.Core/Services/Implement/PackagingService.cs index 55608e605e..a5c279fa21 100644 --- a/src/Umbraco.Core/Services/Implement/PackagingService.cs +++ b/src/Umbraco.Core/Services/Implement/PackagingService.cs @@ -43,10 +43,6 @@ namespace Umbraco.Core.Services.Implement ICreatedPackagesRepository createdPackages, IInstalledPackagesRepository installedPackages, IPackageInstallation packageInstallation) - var isElement = infoElement.Element("IsElement"); - if (isListView != null) - contentType.IsElement = isElement.Value.InvariantEquals("true"); - { _auditService = auditService; _createdPackages = createdPackages; From 00e951c3949b015c06ce3d6576734fcffb9d7dde Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 16 Jan 2019 02:35:21 +1100 Subject: [PATCH 77/93] fixes installer with async --- .../Install/Controllers/InstallApiController.cs | 15 ++++++++------- .../Install/Models/InstallSetupStep.cs | 4 +--- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web/Install/Controllers/InstallApiController.cs b/src/Umbraco.Web/Install/Controllers/InstallApiController.cs index a63484fb39..c8e862abcb 100644 --- a/src/Umbraco.Web/Install/Controllers/InstallApiController.cs +++ b/src/Umbraco.Web/Install/Controllers/InstallApiController.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Threading.Tasks; using System.Web.Http; using Newtonsoft.Json.Linq; using Umbraco.Core; @@ -75,7 +76,7 @@ namespace Umbraco.Web.Install.Controllers /// /// Installs. /// - public InstallProgressResultModel PostPerformInstall(InstallInstructions installModel) + public async Task PostPerformInstall(InstallInstructions installModel) { if (installModel == null) throw new ArgumentNullException(nameof(installModel)); @@ -94,8 +95,7 @@ namespace Umbraco.Web.Install.Controllers var step = _installSteps.GetAllSteps().Single(x => x.Name == item.Name); // if this step has any instructions then extract them - JToken instruction; - installModel.Instructions.TryGetValue(item.Name, out instruction); // else null + installModel.Instructions.TryGetValue(item.Name, out var instruction); // else null // if this step doesn't require execution then continue to the next one, this is just a fail-safe check. if (StepRequiresExecution(step, instruction) == false) @@ -107,7 +107,7 @@ namespace Umbraco.Web.Install.Controllers try { - var setupData = ExecuteStep(step, instruction); + var setupData = await ExecuteStepAsync(step, instruction); // update the status InstallStatusTracker.SetComplete(installModel.InstallId, step.Name, setupData?.SavedStepData); @@ -222,7 +222,7 @@ namespace Umbraco.Web.Install.Controllers } // executes the step - internal InstallSetupResult ExecuteStep(InstallSetupStep step, JToken instruction) + internal async Task ExecuteStepAsync(InstallSetupStep step, JToken instruction) { using (_proflog.TraceDuration($"Executing installation step: '{step.Name}'.", "Step completed")) { @@ -232,8 +232,9 @@ namespace Umbraco.Web.Install.Controllers var typedStepType = genericStepType.MakeGenericType(typeArgs); try { - var method = typedStepType.GetMethods().Single(x => x.Name == "Execute"); - return (InstallSetupResult) method.Invoke(step, new[] { model }); + var method = typedStepType.GetMethods().Single(x => x.Name == "ExecuteAsync"); + var task = (Task) method.Invoke(step, new[] { model }); + return await task; } catch (Exception ex) { diff --git a/src/Umbraco.Web/Install/Models/InstallSetupStep.cs b/src/Umbraco.Web/Install/Models/InstallSetupStep.cs index 3b017368f9..fd50d7855c 100644 --- a/src/Umbraco.Web/Install/Models/InstallSetupStep.cs +++ b/src/Umbraco.Web/Install/Models/InstallSetupStep.cs @@ -82,8 +82,6 @@ namespace Umbraco.Web.Install.Models ///
[IgnoreDataMember] public abstract Type StepType { get; } - - [IgnoreDataMember] - public bool HasUIElement => View.IsNullOrWhiteSpace() == false; + } } From 90be93d94842b84ed86358e0d812fdd50e0afc66 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 16 Jan 2019 16:27:51 +1100 Subject: [PATCH 78/93] Got upgrade status showing in the ui, started implementing merging the package definitions for upgrades but have decided to have an element for each install like we currenlty have in case there are problems installing. --- .../Models/Packaging/PackageDefinition.cs | 33 ++++++++ .../Packaging/CompiledPackageXmlParser.cs | 2 +- .../Packaging/PackageInstallType.cs | 9 ++ .../Services/IPackagingService.cs | 19 +++++ .../Services/Implement/PackagingService.cs | 81 ++++++++++++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../mocks/services/localization.mocks.js | 1 - .../views/packages/views/install-local.html | 31 ++++--- src/Umbraco.Web.UI/Umbraco/config/lang/cs.xml | 1 - src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 1 - src/Umbraco.Web.UI/Umbraco/config/lang/de.xml | 1 - src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 1 - .../Umbraco/config/lang/en_us.xml | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml | 1 - src/Umbraco.Web.UI/Umbraco/config/lang/ja.xml | 1 - src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml | 1 - src/Umbraco.Web.UI/Umbraco/config/lang/nl.xml | 1 - src/Umbraco.Web.UI/Umbraco/config/lang/pl.xml | 1 - src/Umbraco.Web.UI/Umbraco/config/lang/ru.xml | 1 - src/Umbraco.Web.UI/Umbraco/config/lang/sv.xml | 1 - src/Umbraco.Web.UI/Umbraco/config/lang/tr.xml | 1 - src/Umbraco.Web.UI/Umbraco/config/lang/zh.xml | 1 - .../Umbraco/config/lang/zh_tw.xml | 1 - src/Umbraco.Web.UI/umbraco/config/lang/nb.xml | 1 - .../umbraco/config/lang/zh_tw.xml | 1 - .../Editors/PackageInstallController.cs | 82 ++++++++++--------- .../Models/LocalPackageInstallModel.cs | 6 ++ src/Umbraco.Web/Models/PackageInstallModel.cs | 1 + 28 files changed, 212 insertions(+), 72 deletions(-) create mode 100644 src/Umbraco.Core/Packaging/PackageInstallType.cs diff --git a/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs b/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs index bab52c63f6..c068c57b08 100644 --- a/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs +++ b/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs @@ -128,5 +128,38 @@ namespace Umbraco.Core.Models.Packaging [DataMember(Name = "iconUrl")] public string IconUrl { get; set; } = string.Empty; + public PackageDefinition Clone() + { + return new PackageDefinition + { + Id = Id, + PackagePath = PackagePath, + Name = Name, + Files = new List(Files), + UmbracoVersion = (Version) UmbracoVersion.Clone(), + Version = Version, + Url = Url, + Readme = Readme, + AuthorUrl = AuthorUrl, + Author = Author, + LicenseUrl = LicenseUrl, + Actions = Actions, + PackageId = PackageId, + Control = Control, + DataTypes = new List(DataTypes), + IconUrl = IconUrl, + License = License, + Templates = new List(Templates), + Languages = new List(Languages), + Macros = new List(Macros), + Stylesheets = new List(Stylesheets), + DocumentTypes = new List(DocumentTypes), + DictionaryItems = new List(DictionaryItems), + ContentNodeId = ContentNodeId, + ContentLoadChildNodes = ContentLoadChildNodes + }; + } + } + } diff --git a/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs index 3dee5686ff..0d533cfbc2 100644 --- a/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs +++ b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs @@ -149,7 +149,7 @@ namespace Umbraco.Core.Packaging /// /// /// - public IEnumerable GetPackageActions(XElement actionsElement, string packageName) + public static IEnumerable GetPackageActions(XElement actionsElement, string packageName) { if (actionsElement == null) return Enumerable.Empty(); diff --git a/src/Umbraco.Core/Packaging/PackageInstallType.cs b/src/Umbraco.Core/Packaging/PackageInstallType.cs new file mode 100644 index 0000000000..015b994aec --- /dev/null +++ b/src/Umbraco.Core/Packaging/PackageInstallType.cs @@ -0,0 +1,9 @@ +namespace Umbraco.Core.Packaging +{ + public enum PackageInstallType + { + AlreadyInstalled, + NewInstall, + Upgrade + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Services/IPackagingService.cs b/src/Umbraco.Core/Services/IPackagingService.cs index a31fc2d6cf..7d4d70196d 100644 --- a/src/Umbraco.Core/Services/IPackagingService.cs +++ b/src/Umbraco.Core/Services/IPackagingService.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Threading.Tasks; using System.Xml.Linq; +using Semver; using Umbraco.Core.Models; using Umbraco.Core.Models.Packaging; using Umbraco.Core.Packaging; @@ -44,8 +45,26 @@ namespace Umbraco.Core.Services IEnumerable GetAllInstalledPackages(); PackageDefinition GetInstalledPackageById(int id); + PackageDefinition GetInstalledPackageByName(string name); + + /// + /// Returns a for a given package name and version + /// + /// + /// + /// If the package is an upgrade, the original/current PackageDefinition is returned + /// + PackageInstallType GetPackageInstallType(string packageName, SemVersion packageVersion, out PackageDefinition alreadyInstalled); void DeleteInstalledPackage(int packageId, int userId = 0); + /// + /// Merges the package definition information from the upgrade on to the original and returns the merged definition + /// + /// + /// + /// + PackageDefinition MergePackageDefinition(PackageDefinition original, PackageDefinition upgrade); + /// /// Persists a package definition to storage /// diff --git a/src/Umbraco.Core/Services/Implement/PackagingService.cs b/src/Umbraco.Core/Services/Implement/PackagingService.cs index a5c279fa21..1da53c5e11 100644 --- a/src/Umbraco.Core/Services/Implement/PackagingService.cs +++ b/src/Umbraco.Core/Services/Implement/PackagingService.cs @@ -7,6 +7,7 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Web; using System.Xml.Linq; +using Semver; using Umbraco.Core.Collections; using Umbraco.Core.Events; using Umbraco.Core.Exceptions; @@ -180,6 +181,86 @@ namespace Umbraco.Core.Services.Implement public PackageDefinition GetInstalledPackageById(int id) => _installedPackages.GetById(id); + public PackageDefinition GetInstalledPackageByName(string name) + { + var found = _installedPackages.GetAll().FirstOrDefault(x => x.Name.InvariantEquals(name)); + 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)); + + alreadyInstalled = GetInstalledPackageByName(packageName); + 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 PackageDefinition MergePackageDefinition(PackageDefinition original, PackageDefinition upgrade) + { + if (!SemVersion.TryParse(original.Version, out var originalVersion)) + throw new InvalidOperationException("Could not parse the original version"); + if(!SemVersion.TryParse(upgrade.Version, out var upgradeVersion)) + throw new InvalidOperationException("Could not parse the upgrade version"); + if (originalVersion >= upgradeVersion) + throw new InvalidOperationException("The upgrade version must be higher than the original version"); + if (!original.Name.InvariantEquals(upgrade.Name)) + throw new InvalidOperationException("Cannot merge the package definitions, the package name doesn't match"); + + var result = original.Clone(); + + result.PackagePath = upgrade.PackagePath; + result.PackageId = upgrade.PackageId; + + result.UmbracoVersion = upgrade.UmbracoVersion; + result.Version = upgrade.Version; + result.Url = upgrade.Url; + result.Readme = upgrade.Readme; + result.AuthorUrl = upgrade.AuthorUrl; + result.Author = upgrade.Author; + result.LicenseUrl = upgrade.LicenseUrl; + result.PackageId = upgrade.PackageId; + result.Control = upgrade.Control; + result.IconUrl = upgrade.IconUrl; + result.License = upgrade.License; + result.ContentNodeId = upgrade.ContentNodeId; + result.ContentLoadChildNodes = upgrade.ContentLoadChildNodes; + + result.Files = original.Files.Concat(upgrade.Files).Distinct(StringComparer.InvariantCultureIgnoreCase).ToList(); + result.DataTypes = original.DataTypes.Concat(upgrade.DataTypes).Distinct(StringComparer.InvariantCultureIgnoreCase).ToList(); + result.Templates = original.Templates.Concat(upgrade.Templates).Distinct(StringComparer.InvariantCultureIgnoreCase).ToList(); + result.Languages = original.Languages.Concat(upgrade.Languages).Distinct(StringComparer.InvariantCultureIgnoreCase).ToList(); + result.Macros = original.Macros.Concat(upgrade.Macros).Distinct(StringComparer.InvariantCultureIgnoreCase).ToList(); + result.Stylesheets = original.Stylesheets.Concat(upgrade.Stylesheets).Distinct(StringComparer.InvariantCultureIgnoreCase).ToList(); + result.DocumentTypes = original.DocumentTypes.Concat(upgrade.DocumentTypes).Distinct(StringComparer.InvariantCultureIgnoreCase).ToList(); + result.DictionaryItems = original.DictionaryItems.Concat(upgrade.DictionaryItems).Distinct(StringComparer.InvariantCultureIgnoreCase).ToList(); + + var originalActions = CompiledPackageXmlParser.GetPackageActions(XElement.Parse(result.Actions), result.Name); + var upgradeActions = CompiledPackageXmlParser.GetPackageActions(XElement.Parse(upgrade.Actions), result.Name).ToList(); + var upgradeActionsLookup = upgradeActions.ToLookup(x => x.Alias, x => x.XmlData.ToString()); + + foreach (var originalAction in originalActions) + { + var upgradeActionsWithAlias = upgradeActionsLookup[originalAction.Alias]; + + //check if the original action does not exist already in our upgrade actions + if(upgradeActionsWithAlias.All(upgradeActionXml => upgradeActionXml != originalAction.XmlData.ToString())) + upgradeActions.Add(originalAction); + } + result.Actions = new XElement("actions", upgradeActions.Select(x => x.XmlData)).ToString(); + + return result; + } + public bool SaveInstalledPackage(PackageDefinition definition) => _installedPackages.SavePackage(definition); public void DeleteInstalledPackage(int packageId, int userId = 0) diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 251ecd00c1..b385e63d0c 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -458,6 +458,7 @@ + diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/services/localization.mocks.js b/src/Umbraco.Web.UI.Client/src/common/mocks/services/localization.mocks.js index b6c87027b1..ec1175ab6c 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/services/localization.mocks.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/services/localization.mocks.js @@ -473,7 +473,6 @@ angular.module('umbraco.mocks'). "packager_packageUpgradeInstructions": "Upgrade instructions", "packager_packageUpgradeText": " There's an upgrade available for this package. You can download it directly from the Umbraco package repository.", "packager_packageVersion": "Package version", - "packager_packageVersionHistory": "Package version history", "packager_viewPackageWebsite": "View package website", "paste_doNothing": "Paste with full formatting (Not recommended)", "paste_errorMessage": "The text you're trying to paste contains special characters or formatting. This could be caused by copying text from Microsoft Word. Umbraco can remove special characters or formatting automatically, so the pasted content will be more suitable for the web.", diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.html b/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.html index 5e3ef6ad1c..b657c42877 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.html @@ -118,6 +118,14 @@
Version {{ vm.localPackage.version }} +

+ + + Upgrading from version + {{ vm.localPackage.originalVersion }} + + +

@@ -136,21 +144,19 @@ I accept terms of use - +
- +
@@ -165,9 +171,8 @@
- +
[DataMember(Name = "isRestarting")] public bool IsRestarting { get; set; } + } } From 94da9a66816734292471f7b600ff4d348e2954eb Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 16 Jan 2019 17:25:46 +1100 Subject: [PATCH 79/93] Gets package upgrading working along with package uninstallation with multiple versions --- .../Events/UninstallPackageEventArgs.cs | 11 +- .../Packaging/IPackageInstallation.cs | 2 +- .../Packaging/PackageDataInstallation.cs | 39 +++--- .../Packaging/PackageInstallation.cs | 5 +- .../Packaging/PackagesRepository.cs | 5 - .../Services/IPackagingService.cs | 32 +++-- .../Services/Implement/PackagingService.cs | 111 +++++++----------- .../src/views/packages/edit.html | 14 +-- src/Umbraco.Web/Editors/PackageController.cs | 5 +- .../Editors/PackageInstallController.cs | 17 +-- 10 files changed, 110 insertions(+), 131 deletions(-) diff --git a/src/Umbraco.Core/Events/UninstallPackageEventArgs.cs b/src/Umbraco.Core/Events/UninstallPackageEventArgs.cs index 2618d04e17..63c5ceaba0 100644 --- a/src/Umbraco.Core/Events/UninstallPackageEventArgs.cs +++ b/src/Umbraco.Core/Events/UninstallPackageEventArgs.cs @@ -3,16 +3,13 @@ using Umbraco.Core.Models.Packaging; namespace Umbraco.Core.Events { - public class UninstallPackageEventArgs : CancellableObjectEventArgs> + public class UninstallPackageEventArgs: CancellableObjectEventArgs> { - public UninstallPackageEventArgs(TEntity eventObject, IPackageInfo packageMetaData, bool canCancel) - : base(new[] { eventObject }, canCancel) + public UninstallPackageEventArgs(IEnumerable eventObject, bool canCancel) + : base(eventObject, canCancel) { - PackageMetaData = packageMetaData; } - public IPackageInfo PackageMetaData { get; } - - public IEnumerable UninstallationSummary => EventObject; + public IEnumerable UninstallationSummary => EventObject; } } diff --git a/src/Umbraco.Core/Packaging/IPackageInstallation.cs b/src/Umbraco.Core/Packaging/IPackageInstallation.cs index 5dae76674d..c85a4ccd5f 100644 --- a/src/Umbraco.Core/Packaging/IPackageInstallation.cs +++ b/src/Umbraco.Core/Packaging/IPackageInstallation.cs @@ -8,7 +8,7 @@ namespace Umbraco.Core.Packaging public interface IPackageInstallation { /// - /// Uninstalls a package including all data, entities and files + /// This will run the uninstallation sequence for this /// /// /// diff --git a/src/Umbraco.Core/Packaging/PackageDataInstallation.cs b/src/Umbraco.Core/Packaging/PackageDataInstallation.cs index 36066b104f..2e9fee9595 100644 --- a/src/Umbraco.Core/Packaging/PackageDataInstallation.cs +++ b/src/Umbraco.Core/Packaging/PackageDataInstallation.cs @@ -228,14 +228,17 @@ namespace Umbraco.Core.Packaging } var content = CreateContentFromXml(root, importedContentTypes[contentTypeAlias], null, parentId); + if (content == null) continue; + contents.Add(content); var children = (from child in root.Elements() where (string)child.Attribute("isDoc") == "" select child) .ToList(); - if (children.Any()) - contents.AddRange(CreateContentFromXml(children, content, importedContentTypes)); + + if (children.Count > 0) + contents.AddRange(CreateContentFromXml(children, content, importedContentTypes).WhereNotNull()); } return contents; } @@ -258,7 +261,7 @@ namespace Umbraco.Core.Packaging list.Add(content); //Recursive call - XElement child1 = child; + var child1 = child; var grandChildren = (from grand in child1.Elements() where (string)grand.Attribute("isDoc") == "" select grand).ToList(); @@ -272,37 +275,43 @@ namespace Umbraco.Core.Packaging private IContent CreateContentFromXml(XElement element, IContentType contentType, IContent parent, int parentId) { + var key = Guid.Empty; + if (element.Attribute("key") != null && Guid.TryParse(element.Attribute("key").Value, out key)) + { + //if a Key is supplied, then we need to check if the content already exists and if so we ignore the installation for this item + if (_contentService.GetById(key) != null) + return null; + } + var id = element.Attribute("id").Value; var level = element.Attribute("level").Value; var sortOrder = element.Attribute("sortOrder").Value; var nodeName = element.Attribute("nodeName").Value; var path = element.Attribute("path").Value; - //TODO: Shouldn't we be using this value??? - var template = element.Attribute("template").Value; - var key = Guid.Empty; - + var templateId = element.AttributeValue("template"); + var properties = from property in element.Elements() where property.Attribute("isDoc") == null select property; + var template = templateId.HasValue ? _fileService.GetTemplate(templateId.Value) : null; + IContent content = parent == null ? new Content(nodeName, parentId, contentType) { Level = int.Parse(level), - SortOrder = int.Parse(sortOrder) + SortOrder = int.Parse(sortOrder), + TemplateId = template?.Id, + Key = key } : new Content(nodeName, parent, contentType) { Level = int.Parse(level), - SortOrder = int.Parse(sortOrder) + SortOrder = int.Parse(sortOrder), + TemplateId = template?.Id, + Key = key }; - if (element.Attribute("key") != null && Guid.TryParse(element.Attribute("key").Value, out key)) - { - // update the Guid (for UDI support) - content.Key = key; - } - foreach (var property in properties) { string propertyTypeAlias = property.Name.LocalName; diff --git a/src/Umbraco.Core/Packaging/PackageInstallation.cs b/src/Umbraco.Core/Packaging/PackageInstallation.cs index d7bb72836e..955662f3fe 100644 --- a/src/Umbraco.Core/Packaging/PackageInstallation.cs +++ b/src/Umbraco.Core/Packaging/PackageInstallation.cs @@ -76,12 +76,13 @@ namespace Umbraco.Core.Packaging return files; } + /// 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); + summary.Actions = CompiledPackageXmlParser.GetPackageActions(XElement.Parse(package.Actions), package.Name); //run actions before files are removed summary.ActionErrors = UndoPackageActions(package, summary.Actions).ToList(); @@ -109,7 +110,7 @@ namespace Umbraco.Core.Packaging installationSummary.StylesheetsInstalled = _packageDataInstallation.ImportStylesheets(compiledPackage.Stylesheets, userId); installationSummary.ContentInstalled = _packageDataInstallation.ImportContent(compiledPackage.Documents, importedDocTypes, userId); - installationSummary.Actions = _parser.GetPackageActions(XElement.Parse(compiledPackage.Actions), compiledPackage.Name); + installationSummary.Actions = CompiledPackageXmlParser.GetPackageActions(XElement.Parse(compiledPackage.Actions), compiledPackage.Name); installationSummary.MetaData = compiledPackage; installationSummary.FilesInstalled = packageDefinition.Files; diff --git a/src/Umbraco.Core/Packaging/PackagesRepository.cs b/src/Umbraco.Core/Packaging/PackagesRepository.cs index af5c7ffded..249d02a320 100644 --- a/src/Umbraco.Core/Packaging/PackagesRepository.cs +++ b/src/Umbraco.Core/Packaging/PackagesRepository.cs @@ -120,11 +120,6 @@ namespace Umbraco.Core.Packaging if (definition.Id == default) { - //check if the name already exists - var existsByName = packagesXml.Root.Elements("package").Any(x => x.AttributeValue("name") == definition.Name); - if (existsByName) - return false; - //need to gen an id and persist // Find max id var maxId = packagesXml.Root.Elements("package").Max(x => x.AttributeValue("id")) ?? 0; diff --git a/src/Umbraco.Core/Services/IPackagingService.cs b/src/Umbraco.Core/Services/IPackagingService.cs index 7d4d70196d..c14882fe7a 100644 --- a/src/Umbraco.Core/Services/IPackagingService.cs +++ b/src/Umbraco.Core/Services/IPackagingService.cs @@ -37,15 +37,35 @@ namespace Umbraco.Core.Services /// InstallationSummary InstallCompiledPackageData(PackageDefinition packageDefinition, FileInfo packageFile, int userId = 0); - UninstallationSummary UninstallPackage(PackageDefinition packageDefinition, int userId = 0); + /// + /// Uninstalls all versions of the package by name + /// + /// + /// + /// + UninstallationSummary UninstallPackage(string packageName, int userId = 0); #endregion #region Installed Packages IEnumerable GetAllInstalledPackages(); + + /// + /// Returns the for the installation id + /// + /// + /// PackageDefinition GetInstalledPackageById(int id); - PackageDefinition GetInstalledPackageByName(string name); + + /// + /// Returns all for the package by name + /// + /// + /// + /// A list of all package definitions installed for this package (i.e. original install and any upgrades) + /// + IEnumerable GetInstalledPackageByName(string name); /// /// Returns a for a given package name and version @@ -57,14 +77,6 @@ namespace Umbraco.Core.Services PackageInstallType GetPackageInstallType(string packageName, SemVersion packageVersion, out PackageDefinition alreadyInstalled); void DeleteInstalledPackage(int packageId, int userId = 0); - /// - /// Merges the package definition information from the upgrade on to the original and returns the merged definition - /// - /// - /// - /// - PackageDefinition MergePackageDefinition(PackageDefinition original, PackageDefinition upgrade); - /// /// Persists a package definition to storage /// diff --git a/src/Umbraco.Core/Services/Implement/PackagingService.cs b/src/Umbraco.Core/Services/Implement/PackagingService.cs index 1da53c5e11..24ef818624 100644 --- a/src/Umbraco.Core/Services/Implement/PackagingService.cs +++ b/src/Umbraco.Core/Services/Implement/PackagingService.cs @@ -44,7 +44,7 @@ namespace Umbraco.Core.Services.Implement ICreatedPackagesRepository createdPackages, IInstalledPackagesRepository installedPackages, IPackageInstallation packageInstallation) - { + { _auditService = auditService; _createdPackages = createdPackages; _installedPackages = installedPackages; @@ -110,7 +110,7 @@ namespace Umbraco.Core.Services.Implement if (compiledPackage == null) throw new InvalidOperationException("Could not read the package file " + packageFile); var files = _packageInstallation.InstallPackageFiles(packageDefinition, compiledPackage, userId).ToList(); - + SaveInstalledPackage(packageDefinition); _auditService.Add(AuditType.PackagerInstall, userId, -1, "Package", $"Package files installed for package '{compiledPackage.Name}'."); @@ -141,16 +141,44 @@ namespace Umbraco.Core.Services.Implement return summary; } - public UninstallationSummary UninstallPackage(PackageDefinition package, int userId = 0) + public UninstallationSummary UninstallPackage(string packageName, int userId = 0) { - var summary = _packageInstallation.UninstallPackage(package, userId); - - SaveInstalledPackage(package); + //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); - DeleteInstalledPackage(package.Id, userId); + var summary = new UninstallationSummary + { + MetaData = allPackageVersions[0] + }; + + var allSummaries = new List(); + + 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); + } // trigger the UninstalledPackage event - UninstalledPackage.RaiseEvent(new UninstallPackageEventArgs(summary, package, false), this); + UninstalledPackage.RaiseEvent(new UninstallPackageEventArgs(allSummaries, false), this); return summary; } @@ -181,9 +209,9 @@ namespace Umbraco.Core.Services.Implement public PackageDefinition GetInstalledPackageById(int id) => _installedPackages.GetById(id); - public PackageDefinition GetInstalledPackageByName(string name) + public IEnumerable GetInstalledPackageByName(string name) { - var found = _installedPackages.GetAll().FirstOrDefault(x => x.Name.InvariantEquals(name)); + var found = _installedPackages.GetAll().Where(x => x.Name.InvariantEquals(name)).OrderByDescending(x => SemVersion.Parse(x.Version)); return found; } @@ -192,7 +220,8 @@ namespace Umbraco.Core.Services.Implement if (packageName == null) throw new ArgumentNullException(nameof(packageName)); if (packageVersion == null) throw new ArgumentNullException(nameof(packageVersion)); - alreadyInstalled = GetInstalledPackageByName(packageName); + //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)) @@ -205,62 +234,6 @@ namespace Umbraco.Core.Services.Implement return PackageInstallType.Upgrade; } - public PackageDefinition MergePackageDefinition(PackageDefinition original, PackageDefinition upgrade) - { - if (!SemVersion.TryParse(original.Version, out var originalVersion)) - throw new InvalidOperationException("Could not parse the original version"); - if(!SemVersion.TryParse(upgrade.Version, out var upgradeVersion)) - throw new InvalidOperationException("Could not parse the upgrade version"); - if (originalVersion >= upgradeVersion) - throw new InvalidOperationException("The upgrade version must be higher than the original version"); - if (!original.Name.InvariantEquals(upgrade.Name)) - throw new InvalidOperationException("Cannot merge the package definitions, the package name doesn't match"); - - var result = original.Clone(); - - result.PackagePath = upgrade.PackagePath; - result.PackageId = upgrade.PackageId; - - result.UmbracoVersion = upgrade.UmbracoVersion; - result.Version = upgrade.Version; - result.Url = upgrade.Url; - result.Readme = upgrade.Readme; - result.AuthorUrl = upgrade.AuthorUrl; - result.Author = upgrade.Author; - result.LicenseUrl = upgrade.LicenseUrl; - result.PackageId = upgrade.PackageId; - result.Control = upgrade.Control; - result.IconUrl = upgrade.IconUrl; - result.License = upgrade.License; - result.ContentNodeId = upgrade.ContentNodeId; - result.ContentLoadChildNodes = upgrade.ContentLoadChildNodes; - - result.Files = original.Files.Concat(upgrade.Files).Distinct(StringComparer.InvariantCultureIgnoreCase).ToList(); - result.DataTypes = original.DataTypes.Concat(upgrade.DataTypes).Distinct(StringComparer.InvariantCultureIgnoreCase).ToList(); - result.Templates = original.Templates.Concat(upgrade.Templates).Distinct(StringComparer.InvariantCultureIgnoreCase).ToList(); - result.Languages = original.Languages.Concat(upgrade.Languages).Distinct(StringComparer.InvariantCultureIgnoreCase).ToList(); - result.Macros = original.Macros.Concat(upgrade.Macros).Distinct(StringComparer.InvariantCultureIgnoreCase).ToList(); - result.Stylesheets = original.Stylesheets.Concat(upgrade.Stylesheets).Distinct(StringComparer.InvariantCultureIgnoreCase).ToList(); - result.DocumentTypes = original.DocumentTypes.Concat(upgrade.DocumentTypes).Distinct(StringComparer.InvariantCultureIgnoreCase).ToList(); - result.DictionaryItems = original.DictionaryItems.Concat(upgrade.DictionaryItems).Distinct(StringComparer.InvariantCultureIgnoreCase).ToList(); - - var originalActions = CompiledPackageXmlParser.GetPackageActions(XElement.Parse(result.Actions), result.Name); - var upgradeActions = CompiledPackageXmlParser.GetPackageActions(XElement.Parse(upgrade.Actions), result.Name).ToList(); - var upgradeActionsLookup = upgradeActions.ToLookup(x => x.Alias, x => x.XmlData.ToString()); - - foreach (var originalAction in originalActions) - { - var upgradeActionsWithAlias = upgradeActionsLookup[originalAction.Alias]; - - //check if the original action does not exist already in our upgrade actions - if(upgradeActionsWithAlias.All(upgradeActionXml => upgradeActionXml != originalAction.XmlData.ToString())) - upgradeActions.Add(originalAction); - } - result.Actions = new XElement("actions", upgradeActions.Select(x => x.XmlData)).ToString(); - - return result; - } - public bool SaveInstalledPackage(PackageDefinition definition) => _installedPackages.SavePackage(definition); public void DeleteInstalledPackage(int packageId, int userId = 0) @@ -289,10 +262,10 @@ namespace Umbraco.Core.Services.Implement /// /// Occurs after a package is uninstalled /// - public static event TypedEventHandler> UninstalledPackage; + public static event TypedEventHandler UninstalledPackage; #endregion - + } } diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html index a66eb94746..8ed1a43e8a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html @@ -133,7 +133,7 @@ -
+
public class ContentValueSetBuilder : BaseValueSetBuilder, IContentValueSetBuilder, IPublishedContentValueSetBuilder { - private readonly IEnumerable _urlSegmentProviders; + private readonly UrlSegmentProviderCollection _urlSegmentProviders; private readonly IUserService _userService; public ContentValueSetBuilder(PropertyEditorCollection propertyEditors, - IEnumerable urlSegmentProviders, + UrlSegmentProviderCollection urlSegmentProviders, IUserService userService, bool publishedValuesOnly) : base(propertyEditors, publishedValuesOnly) diff --git a/src/Umbraco.Examine/MediaValueSetBuilder.cs b/src/Umbraco.Examine/MediaValueSetBuilder.cs index 2676093eeb..23d0414d5d 100644 --- a/src/Umbraco.Examine/MediaValueSetBuilder.cs +++ b/src/Umbraco.Examine/MediaValueSetBuilder.cs @@ -10,7 +10,7 @@ namespace Umbraco.Examine { public class MediaValueSetBuilder : BaseValueSetBuilder { - private readonly IEnumerable _urlSegmentProviders; + private readonly UrlSegmentProviderCollection _urlSegmentProviders; private readonly IUserService _userService; public MediaValueSetBuilder(PropertyEditorCollection propertyEditors, diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index 0b36398dd6..3019138809 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -31,7 +31,7 @@ namespace Umbraco.Tests.UmbracoExamine { public static ContentValueSetBuilder GetContentValueSetBuilder(PropertyEditorCollection propertyEditors, bool publishedValuesOnly) { - var contentValueSetBuilder = new ContentValueSetBuilder(propertyEditors, new[] { new DefaultUrlSegmentProvider() }, GetMockUserService(), publishedValuesOnly); + var contentValueSetBuilder = new ContentValueSetBuilder(propertyEditors, new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }), GetMockUserService(), publishedValuesOnly); return contentValueSetBuilder; } diff --git a/src/Umbraco.Web/Composing/Current.cs b/src/Umbraco.Web/Composing/Current.cs index 860d3ec88e..1e8f3d17f7 100644 --- a/src/Umbraco.Web/Composing/Current.cs +++ b/src/Umbraco.Web/Composing/Current.cs @@ -159,41 +159,6 @@ namespace Umbraco.Web.Composing #endregion - #region Web Actions - - internal static void RestartAppPool() - { - // see notes in overload - - var httpContext = HttpContext.Current; - if (httpContext != null) - { - httpContext.Application.Add("AppPoolRestarting", true); - httpContext.User = null; - } - Thread.CurrentPrincipal = null; - HttpRuntime.UnloadAppDomain(); - } - - internal static void RestartAppPool(HttpContextBase httpContext) - { - // we're going to put an application wide flag to show that the application is about to restart. - // we're doing this because if there is a script checking if the app pool is fully restarted, then - // it can check if this flag exists... if it does it means the app pool isn't restarted yet. - httpContext.Application.Add("AppPoolRestarting", true); - - // unload app domain - we must null out all identities otherwise we get serialization errors - // http://www.zpqrtbnk.net/posts/custom-iidentity-serialization-issue - httpContext.User = null; - if (HttpContext.Current != null) - HttpContext.Current.User = null; - Thread.CurrentPrincipal = null; - - HttpRuntime.UnloadAppDomain(); - } - - #endregion - #region Core Getters // proxy Core for convenience diff --git a/src/Umbraco.Web/Editors/PackageInstallController.cs b/src/Umbraco.Web/Editors/PackageInstallController.cs index dd7c4302c5..05d1e2a7a3 100644 --- a/src/Umbraco.Web/Editors/PackageInstallController.cs +++ b/src/Umbraco.Web/Editors/PackageInstallController.cs @@ -315,7 +315,7 @@ namespace Umbraco.Web.Editors var installedFiles = Services.PackagingService.InstallCompiledPackageFiles(definition, zipFile, Security.GetUserId().ResultOr(0)); //set a restarting marker and reset the app pool - Current.RestartAppPool(Request.TryGetHttpContext().Result); + UmbracoApplication.Restart(Request.TryGetHttpContext().Result); model.IsRestarting = true; diff --git a/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs b/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs index 0349bb4ec7..641146fb4a 100644 --- a/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs @@ -52,7 +52,7 @@ namespace Umbraco.Web.Install.InstallSteps var (packageFile, packageId) = await DownloadPackageFilesAsync(starterKitId.Value); - Current.RestartAppPool(); + UmbracoApplication.Restart(); return new InstallSetupResult(new Dictionary { diff --git a/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs b/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs index 805041391f..8bd699f293 100644 --- a/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs @@ -34,7 +34,7 @@ namespace Umbraco.Web.Install.InstallSteps InstallBusinessLogic(packageId); - Current.RestartAppPool(_httContext); + UmbracoApplication.Restart(_httContext); return Task.FromResult(null); } diff --git a/src/Umbraco.Web/Mvc/ContentModelBinder.cs b/src/Umbraco.Web/Mvc/ContentModelBinder.cs index b68d5a36f7..c6df52e007 100644 --- a/src/Umbraco.Web/Mvc/ContentModelBinder.cs +++ b/src/Umbraco.Web/Mvc/ContentModelBinder.cs @@ -150,7 +150,7 @@ namespace Umbraco.Web.Mvc if (context == null) AppDomain.Unload(AppDomain.CurrentDomain); else - Current.RestartAppPool(new HttpContextWrapper(context)); + UmbracoApplication.Restart(new HttpContextWrapper(context)); } throw new ModelBindingException(msg.ToString()); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index dc8fcf8c0e..36f3472c31 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -101,6 +101,9 @@ namespace Umbraco.Web.PublishedCache.NuCache _defaultCultureAccessor = defaultCultureAccessor; _globalSettings = globalSettings; _siteDomainHelper = siteDomainHelper; + + // we need an Xml serializer here so that the member cache can support XPath, + // for members this is done by navigating the serialized-to-xml member _entitySerializer = entitySerializer; // we always want to handle repository events, configured or not diff --git a/src/Umbraco.Web/Search/ExamineComposer.cs b/src/Umbraco.Web/Search/ExamineComposer.cs index 8ee2caedee..7aab3cfd8f 100644 --- a/src/Umbraco.Web/Search/ExamineComposer.cs +++ b/src/Umbraco.Web/Search/ExamineComposer.cs @@ -33,13 +33,13 @@ namespace Umbraco.Web.Search composition.RegisterUnique(factory => new ContentValueSetBuilder( factory.GetInstance(), - factory.GetInstance>(), + factory.GetInstance(), factory.GetInstance(), true)); composition.RegisterUnique(factory => new ContentValueSetBuilder( factory.GetInstance(), - factory.GetInstance>(), + factory.GetInstance(), factory.GetInstance(), false)); composition.RegisterUnique, MediaValueSetBuilder>(); diff --git a/src/Umbraco.Web/UmbracoApplication.cs b/src/Umbraco.Web/UmbracoApplication.cs index 136f09f83d..191fb9dcd6 100644 --- a/src/Umbraco.Web/UmbracoApplication.cs +++ b/src/Umbraco.Web/UmbracoApplication.cs @@ -1,4 +1,6 @@ -using Umbraco.Core; +using System.Threading; +using System.Web; +using Umbraco.Core; using Umbraco.Web.Runtime; namespace Umbraco.Web @@ -12,5 +14,46 @@ namespace Umbraco.Web { return new WebRuntime(this); } + + /// + /// Restarts the Umbraco application. + /// + public static void Restart() + { + // see notes in overload + + var httpContext = HttpContext.Current; + if (httpContext != null) + { + httpContext.Application.Add("AppPoolRestarting", true); + httpContext.User = null; + } + Thread.CurrentPrincipal = null; + HttpRuntime.UnloadAppDomain(); + } + + /// + /// Restarts the Umbraco application. + /// + public static void Restart(HttpContextBase httpContext) + { + if (httpContext != null) + { + // we're going to put an application wide flag to show that the application is about to restart. + // we're doing this because if there is a script checking if the app pool is fully restarted, then + // it can check if this flag exists... if it does it means the app pool isn't restarted yet. + httpContext.Application.Add("AppPoolRestarting", true); + + // unload app domain - we must null out all identities otherwise we get serialization errors + // http://www.zpqrtbnk.net/posts/custom-iidentity-serialization-issue + httpContext.User = null; + } + + if (HttpContext.Current != null) + HttpContext.Current.User = null; + + Thread.CurrentPrincipal = null; + HttpRuntime.UnloadAppDomain(); + } } } From a60bdf5c01f02693dbcf2a76504bf50ad8f22b6c Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 16 Jan 2019 13:26:13 +0100 Subject: [PATCH 82/93] HttpContext VariationContextAccessor fails nicely --- .../PublishedContent/HttpContextVariationContextAccessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Models/PublishedContent/HttpContextVariationContextAccessor.cs b/src/Umbraco.Web/Models/PublishedContent/HttpContextVariationContextAccessor.cs index 70672e3796..0007b346c5 100644 --- a/src/Umbraco.Web/Models/PublishedContent/HttpContextVariationContextAccessor.cs +++ b/src/Umbraco.Web/Models/PublishedContent/HttpContextVariationContextAccessor.cs @@ -21,7 +21,7 @@ namespace Umbraco.Web.Models.PublishedContent /// public VariationContext VariationContext { - get => (VariationContext) HttpContextAccessor.HttpContext.Items[ContextKey]; + get => (VariationContext) HttpContextAccessor.HttpContext?.Items[ContextKey]; set => HttpContextAccessor.HttpContext.Items[ContextKey] = value; } } From b5bda0b6375ee0134467596589f56ee4e3490b2c Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 16 Jan 2019 13:26:30 +0100 Subject: [PATCH 83/93] Some things have to be public for service events --- .../Persistence/Repositories/IDataTypeContainerRepository.cs | 2 +- .../Repositories/IDocumentTypeContainerRepository.cs | 2 +- .../Persistence/Repositories/IEntityContainerRepository.cs | 2 +- src/Umbraco.Core/Persistence/Repositories/IMacroRepository.cs | 2 +- .../Persistence/Repositories/IMediaTypeContainerRepository.cs | 2 +- src/Umbraco.Core/Services/Changes/ContentTypeChange.cs | 2 +- src/Umbraco.Core/Services/Implement/ContentTypeService.cs | 2 +- src/Umbraco.Core/Services/Implement/ContentTypeServiceBase.cs | 2 +- .../Services/Implement/ContentTypeServiceBaseOfTItemTService.cs | 2 +- .../ContentTypeServiceBaseOfTRepositoryTItemTService.cs | 2 +- src/Umbraco.Core/Services/Implement/DataTypeService.cs | 2 +- src/Umbraco.Core/Services/Implement/MacroService.cs | 2 +- src/Umbraco.Core/Services/Implement/MediaTypeService.cs | 2 +- src/Umbraco.Core/Services/Implement/MemberTypeService.cs | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/IDataTypeContainerRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IDataTypeContainerRepository.cs index 460bed71d3..57d3871e5a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IDataTypeContainerRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IDataTypeContainerRepository.cs @@ -1,5 +1,5 @@ namespace Umbraco.Core.Persistence.Repositories { - interface IDataTypeContainerRepository : IEntityContainerRepository + public interface IDataTypeContainerRepository : IEntityContainerRepository { } } diff --git a/src/Umbraco.Core/Persistence/Repositories/IDocumentTypeContainerRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IDocumentTypeContainerRepository.cs index ccf8df268d..ec8dfb9110 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IDocumentTypeContainerRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IDocumentTypeContainerRepository.cs @@ -1,5 +1,5 @@ namespace Umbraco.Core.Persistence.Repositories { - interface IDocumentTypeContainerRepository : IEntityContainerRepository + public interface IDocumentTypeContainerRepository : IEntityContainerRepository { } } diff --git a/src/Umbraco.Core/Persistence/Repositories/IEntityContainerRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IEntityContainerRepository.cs index 47f0cee52b..f1c8353a0d 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IEntityContainerRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IEntityContainerRepository.cs @@ -2,6 +2,6 @@ namespace Umbraco.Core.Persistence.Repositories { - interface IEntityContainerRepository : IReadRepository, IWriteRepository + public interface IEntityContainerRepository : IReadRepository, IWriteRepository { } } diff --git a/src/Umbraco.Core/Persistence/Repositories/IMacroRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IMacroRepository.cs index eefb48933e..1ed08352ed 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IMacroRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IMacroRepository.cs @@ -3,7 +3,7 @@ using Umbraco.Core.Models; namespace Umbraco.Core.Persistence.Repositories { - internal interface IMacroRepository : IReadWriteQueryRepository, IReadRepository + public interface IMacroRepository : IReadWriteQueryRepository, IReadRepository { //IEnumerable GetAll(params string[] aliases); diff --git a/src/Umbraco.Core/Persistence/Repositories/IMediaTypeContainerRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IMediaTypeContainerRepository.cs index a1872df0cd..6a133c053a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IMediaTypeContainerRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IMediaTypeContainerRepository.cs @@ -1,5 +1,5 @@ namespace Umbraco.Core.Persistence.Repositories { - interface IMediaTypeContainerRepository : IEntityContainerRepository + public interface IMediaTypeContainerRepository : IEntityContainerRepository { } } diff --git a/src/Umbraco.Core/Services/Changes/ContentTypeChange.cs b/src/Umbraco.Core/Services/Changes/ContentTypeChange.cs index d67f8f9200..57e1d288e4 100644 --- a/src/Umbraco.Core/Services/Changes/ContentTypeChange.cs +++ b/src/Umbraco.Core/Services/Changes/ContentTypeChange.cs @@ -4,7 +4,7 @@ using Umbraco.Core.Models; namespace Umbraco.Core.Services.Changes { - internal class ContentTypeChange + public class ContentTypeChange where TItem : class, IContentTypeComposition { public ContentTypeChange(TItem item, ContentTypeChangeTypes changeTypes) diff --git a/src/Umbraco.Core/Services/Implement/ContentTypeService.cs b/src/Umbraco.Core/Services/Implement/ContentTypeService.cs index f6498770ec..fa818496ff 100644 --- a/src/Umbraco.Core/Services/Implement/ContentTypeService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentTypeService.cs @@ -12,7 +12,7 @@ namespace Umbraco.Core.Services.Implement /// /// Represents the ContentType Service, which is an easy access to operations involving /// - internal class ContentTypeService : ContentTypeServiceBase, IContentTypeService + public class ContentTypeService : ContentTypeServiceBase, IContentTypeService { public ContentTypeService(IScopeProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory, IContentService contentService, IContentTypeRepository repository, IAuditRepository auditRepository, IDocumentTypeContainerRepository entityContainerRepository, IEntityRepository entityRepository) diff --git a/src/Umbraco.Core/Services/Implement/ContentTypeServiceBase.cs b/src/Umbraco.Core/Services/Implement/ContentTypeServiceBase.cs index d5cdd36318..26298f171c 100644 --- a/src/Umbraco.Core/Services/Implement/ContentTypeServiceBase.cs +++ b/src/Umbraco.Core/Services/Implement/ContentTypeServiceBase.cs @@ -4,7 +4,7 @@ using Umbraco.Core.Scoping; namespace Umbraco.Core.Services.Implement { - internal abstract class ContentTypeServiceBase : ScopeRepositoryService + public abstract class ContentTypeServiceBase : ScopeRepositoryService { protected ContentTypeServiceBase(IScopeProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory) : base(provider, logger, eventMessagesFactory) diff --git a/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTItemTService.cs b/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTItemTService.cs index 33fb9a0894..f4457e0991 100644 --- a/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTItemTService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTItemTService.cs @@ -6,7 +6,7 @@ using Umbraco.Core.Services.Changes; namespace Umbraco.Core.Services.Implement { - internal abstract class ContentTypeServiceBase : ContentTypeServiceBase + public abstract class ContentTypeServiceBase : ContentTypeServiceBase where TItem : class, IContentTypeComposition where TService : class, IContentTypeServiceBase { diff --git a/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs b/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs index be4f719bb1..8189c6524e 100644 --- a/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs @@ -13,7 +13,7 @@ using Umbraco.Core.Services.Changes; namespace Umbraco.Core.Services.Implement { - internal abstract class ContentTypeServiceBase : ContentTypeServiceBase, IContentTypeServiceBase + public abstract class ContentTypeServiceBase : ContentTypeServiceBase, IContentTypeServiceBase where TRepository : IContentTypeRepositoryBase where TItem : class, IContentTypeComposition where TService : class, IContentTypeServiceBase diff --git a/src/Umbraco.Core/Services/Implement/DataTypeService.cs b/src/Umbraco.Core/Services/Implement/DataTypeService.cs index 79ca851de9..84d44649da 100644 --- a/src/Umbraco.Core/Services/Implement/DataTypeService.cs +++ b/src/Umbraco.Core/Services/Implement/DataTypeService.cs @@ -16,7 +16,7 @@ namespace Umbraco.Core.Services.Implement /// /// Represents the DataType Service, which is an easy access to operations involving /// - internal class DataTypeService : ScopeRepositoryService, IDataTypeService + public class DataTypeService : ScopeRepositoryService, IDataTypeService { private readonly IDataTypeRepository _dataTypeRepository; private readonly IDataTypeContainerRepository _dataTypeContainerRepository; diff --git a/src/Umbraco.Core/Services/Implement/MacroService.cs b/src/Umbraco.Core/Services/Implement/MacroService.cs index 5176e2eb22..d4f2d95bbb 100644 --- a/src/Umbraco.Core/Services/Implement/MacroService.cs +++ b/src/Umbraco.Core/Services/Implement/MacroService.cs @@ -12,7 +12,7 @@ namespace Umbraco.Core.Services.Implement /// /// Represents the Macro Service, which is an easy access to operations involving /// - internal class MacroService : ScopeRepositoryService, IMacroService + public class MacroService : ScopeRepositoryService, IMacroService { private readonly IMacroRepository _macroRepository; private readonly IAuditRepository _auditRepository; diff --git a/src/Umbraco.Core/Services/Implement/MediaTypeService.cs b/src/Umbraco.Core/Services/Implement/MediaTypeService.cs index fe8c78e137..8cb69a655d 100644 --- a/src/Umbraco.Core/Services/Implement/MediaTypeService.cs +++ b/src/Umbraco.Core/Services/Implement/MediaTypeService.cs @@ -8,7 +8,7 @@ using Umbraco.Core.Scoping; namespace Umbraco.Core.Services.Implement { - internal class MediaTypeService : ContentTypeServiceBase, IMediaTypeService + public class MediaTypeService : ContentTypeServiceBase, IMediaTypeService { public MediaTypeService(IScopeProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory, IMediaService mediaService, IMediaTypeRepository mediaTypeRepository, IAuditRepository auditRepository, IMediaTypeContainerRepository entityContainerRepository, diff --git a/src/Umbraco.Core/Services/Implement/MemberTypeService.cs b/src/Umbraco.Core/Services/Implement/MemberTypeService.cs index 6cc7d03cfb..05f32dc99c 100644 --- a/src/Umbraco.Core/Services/Implement/MemberTypeService.cs +++ b/src/Umbraco.Core/Services/Implement/MemberTypeService.cs @@ -8,7 +8,7 @@ using Umbraco.Core.Scoping; namespace Umbraco.Core.Services.Implement { - internal class MemberTypeService : ContentTypeServiceBase, IMemberTypeService + public class MemberTypeService : ContentTypeServiceBase, IMemberTypeService { private readonly IMemberTypeRepository _memberTypeRepository; From 9f41457f45f37011d08175af7ca223ff34534150 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 16 Jan 2019 13:44:19 +0100 Subject: [PATCH 84/93] Public composition.UrlProviders --- src/Umbraco.Web/CompositionExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/CompositionExtensions.cs b/src/Umbraco.Web/CompositionExtensions.cs index f72c876a61..d33b1addf5 100644 --- a/src/Umbraco.Web/CompositionExtensions.cs +++ b/src/Umbraco.Web/CompositionExtensions.cs @@ -81,7 +81,7 @@ namespace Umbraco.Core.Components /// Gets the url providers collection builder. ///
/// The composition. - internal static UrlProviderCollectionBuilder UrlProviders(this Composition composition) + public static UrlProviderCollectionBuilder UrlProviders(this Composition composition) => composition.WithCollectionBuilder(); #endregion From 3ed1b44710d21b8a5f3b9c6321020541689dafb3 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 16 Jan 2019 15:21:10 +0100 Subject: [PATCH 85/93] Try to bugfix a failing test --- .../Packaging/PackageInstallationTest.cs | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs b/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs index d944b638ad..5e33538f17 100644 --- a/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs +++ b/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs @@ -60,7 +60,7 @@ namespace Umbraco.Tests.Packaging public void Can_Read_Compiled_Package_1() { var package = PackageInstallation.ReadPackage( - //this is where our test zip file is + //this is where our test zip file is new FileInfo(Path.Combine(IOHelper.MapPath("~/Packaging/packages"), DocumentTypePickerPackage))); Assert.IsNotNull(package); Assert.AreEqual(1, package.Files.Count); @@ -83,7 +83,7 @@ namespace Umbraco.Tests.Packaging public void Can_Read_Compiled_Package_2() { var package = PackageInstallation.ReadPackage( - //this is where our test zip file is + //this is where our test zip file is new FileInfo(Path.Combine(IOHelper.MapPath("~/Packaging/packages"), HelloPackage))); Assert.IsNotNull(package); Assert.AreEqual(0, package.Files.Count); @@ -110,18 +110,22 @@ namespace Umbraco.Tests.Packaging { //copy a file to the same path that the package will install so we can detect file conflicts var path = IOHelper.MapPath("~/" + _testBaseFolder); + Console.WriteLine(path); + var filePath = Path.Combine(path, "bin", "Auros.DocumentTypePicker.dll"); Directory.CreateDirectory(Path.GetDirectoryName(filePath)); File.WriteAllText(filePath, "test"); - var preInstallWarnings = PackageInstallation.ReadPackage( - //this is where our test zip file is - new FileInfo(Path.Combine(IOHelper.MapPath("~/Packaging/packages"), DocumentTypePickerPackage))) - .Warnings; + //this is where our test zip file is + var packageFile = Path.Combine(IOHelper.MapPath("~/Packaging/packages"), DocumentTypePickerPackage); + Console.WriteLine(packageFile); + + var package = PackageInstallation.ReadPackage(new FileInfo(packageFile)); + var preInstallWarnings = package.Warnings; Assert.IsNotNull(preInstallWarnings); - Assert.AreEqual(preInstallWarnings.FilesReplaced.Count(), 1); - Assert.AreEqual(preInstallWarnings.FilesReplaced.First(), "bin\\Auros.DocumentTypePicker.dll"); + Assert.AreEqual(1, preInstallWarnings.FilesReplaced.Count()); + Assert.AreEqual("bin\\Auros.DocumentTypePicker.dll", preInstallWarnings.FilesReplaced.First()); //TODO: More Asserts } @@ -130,7 +134,7 @@ namespace Umbraco.Tests.Packaging public void Install_Files() { var package = PackageInstallation.ReadPackage( - //this is where our test zip file is + //this is where our test zip file is new FileInfo(Path.Combine(IOHelper.MapPath("~/Packaging/packages"), DocumentTypePickerPackage))); var def = PackageDefinition.FromCompiledPackage(package); @@ -152,7 +156,7 @@ namespace Umbraco.Tests.Packaging public void Install_Data() { var package = PackageInstallation.ReadPackage( - //this is where our test zip file is + //this is where our test zip file is new FileInfo(Path.Combine(IOHelper.MapPath("~/Packaging/packages"), DocumentTypePickerPackage))); var def = PackageDefinition.FromCompiledPackage(package); def.Id = 1; @@ -161,7 +165,7 @@ namespace Umbraco.Tests.Packaging var summary = PackageInstallation.InstallPackageData(def, package, -1); Assert.AreEqual(1, summary.DataTypesInstalled.Count()); - + //make sure the def is updated too Assert.AreEqual(summary.DataTypesInstalled.Count(), def.DataTypes.Count); From 899bb812aac24d52ff1f014e2d1b2fd54a257dd9 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 17 Jan 2019 01:42:56 +1100 Subject: [PATCH 86/93] Creates RuntimeStateOptions and pre-loads the error page on the installer --- src/Umbraco.Core/RuntimeState.cs | 12 ++---- src/Umbraco.Core/RuntimeStateOptions.cs | 40 +++++++++++++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../src/installer/installer.service.js | 33 +++++++++------ 4 files changed, 64 insertions(+), 22 deletions(-) create mode 100644 src/Umbraco.Core/RuntimeStateOptions.cs diff --git a/src/Umbraco.Core/RuntimeState.cs b/src/Umbraco.Core/RuntimeState.cs index e344a67920..85e8c7370d 100644 --- a/src/Umbraco.Core/RuntimeState.cs +++ b/src/Umbraco.Core/RuntimeState.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Configuration; using System.Threading; using System.Web; using Semver; @@ -133,11 +132,6 @@ namespace Umbraco.Core /// public BootFailedException BootFailedException { get; internal set; } - // currently configured via app settings - private static bool BoolSetting(string key, bool missing) => ConfigurationManager.AppSettings[key]?.InvariantEquals("true") ?? missing; - private bool InstallMissingDatabase { get; } = BoolSetting("Umbraco.Core.RuntimeState.InstallMissingDatabase", false); - private bool InstallEmptyDatabase { get; } = BoolSetting("Umbraco.Core.RuntimeState.InstallEmptyDatabase", false); - /// /// Determines the runtime level. /// @@ -185,7 +179,7 @@ namespace Umbraco.Core // else, keep going, // anything other than install wants a database - see if we can connect // (since this is an already existing database, assume localdb is ready) - var tries = InstallMissingDatabase ? 2 : 5; + var tries = RuntimeStateOptions.InstallMissingDatabase ? 2 : 5; for (var i = 0;;) { connect = databaseFactory.CanConnect; @@ -199,7 +193,7 @@ namespace Umbraco.Core // cannot connect to configured database, this is bad, fail logger.Debug("Could not connect to database."); - if (InstallMissingDatabase) + if (RuntimeStateOptions.InstallMissingDatabase) { // ok to install on a configured but missing database Level = RuntimeLevel.Install; @@ -228,7 +222,7 @@ namespace Umbraco.Core // can connect to the database but cannot check the upgrade state... oops logger.Warn(e, "Could not check the upgrade state."); - if (InstallEmptyDatabase) + if (RuntimeStateOptions.InstallEmptyDatabase) { // ok to install on an empty database Level = RuntimeLevel.Install; diff --git a/src/Umbraco.Core/RuntimeStateOptions.cs b/src/Umbraco.Core/RuntimeStateOptions.cs new file mode 100644 index 0000000000..9262a8a990 --- /dev/null +++ b/src/Umbraco.Core/RuntimeStateOptions.cs @@ -0,0 +1,40 @@ +using System.Configuration; + +namespace Umbraco.Core +{ + /// + /// Allows configuration of the in PreApplicationStart or in appSettings + /// + public static class RuntimeStateOptions + { + // configured statically or via app settings + private static bool BoolSetting(string key, bool missing) => ConfigurationManager.AppSettings[key]?.InvariantEquals("true") ?? missing; + + /// + /// If true the RuntimeState will continue the installation sequence when a database is missing + /// + /// + /// In this case it will be up to the implementor that is setting this value to true to take over the bootup/installation sequence + /// + public static bool InstallMissingDatabase + { + get => _installEmptyDatabase ?? BoolSetting("Umbraco.Core.RuntimeState.InstallMissingDatabase", false); + set => _installEmptyDatabase = value; + } + + /// + /// If true the RuntimeState will continue the installation sequence when a database is available but is empty + /// + /// + /// In this case it will be up to the implementor that is setting this value to true to take over the bootup/installation sequence + /// + public static bool InstallEmptyDatabase + { + get => _installMissingDatabase ?? BoolSetting("Umbraco.Core.RuntimeState.InstallEmptyDatabase", false); + set => _installMissingDatabase = value; + } + + private static bool? _installMissingDatabase; + private static bool? _installEmptyDatabase; + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 50cc36d7b9..2236854351 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -512,6 +512,7 @@ + diff --git a/src/Umbraco.Web.UI.Client/src/installer/installer.service.js b/src/Umbraco.Web.UI.Client/src/installer/installer.service.js index a8e0530417..cc92935b4d 100644 --- a/src/Umbraco.Web.UI.Client/src/installer/installer.service.js +++ b/src/Umbraco.Web.UI.Client/src/installer/installer.service.js @@ -1,4 +1,4 @@ -angular.module("umbraco.install").factory('installerService', function($rootScope, $q, $timeout, $http, $location, $log){ +angular.module("umbraco.install").factory('installerService', function ($rootScope, $q, $timeout, $http, $templateRequest){ var _status = { index: 0, @@ -106,19 +106,26 @@ angular.module("umbraco.install").factory('installerService', function($rootScop //loads the needed steps and sets the intial state init : function(){ service.status.loading = true; - if(!_status.all){ - service.getSteps().then(function(response){ - service.status.steps = response.data.steps; - service.status.index = 0; - _installerModel.installId = response.data.installId; - service.findNextStep(); + if (!_status.all) { + //pre-load the error page, if an error occurs, the page might not be able to load + // so we want to make sure it's available in the templatecache first + $templateRequest("views/install/error.html").then(x => { + service.getSteps().then(response => { + service.status.steps = response.data.steps; + service.status.index = 0; + _installerModel.installId = response.data.installId; + service.findNextStep(); - $timeout(function(){ - service.status.loading = false; - service.status.configuring = true; - }, 2000); - }); - } + $timeout(function() { + service.status.loading = false; + service.status.configuring = true; + }, + 2000); + }); + }); + + + } }, //loads available packages from our.umbraco.com From 5f7bf3a211d920c8e2efca17955ecc457c1d9151 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 16 Jan 2019 17:03:36 +0100 Subject: [PATCH 87/93] Ignore failing test --- src/Umbraco.Tests/Packaging/PackageInstallationTest.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs b/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs index 5e33538f17..2b9e50e9e5 100644 --- a/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs +++ b/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs @@ -106,6 +106,7 @@ namespace Umbraco.Tests.Packaging } [Test] + [Explicit("Will fail on CI?")] public void Can_Read_Compiled_Package_Warnings() { //copy a file to the same path that the package will install so we can detect file conflicts From 67c571fb289bf7cbf2efa4f739f94a6221b8e958 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 17 Jan 2019 10:56:24 +1100 Subject: [PATCH 88/93] Fixes tests --- src/Umbraco.Core/Composing/Composers/ServicesComposer.cs | 3 +-- src/Umbraco.Core/Packaging/PackageInstallation.cs | 9 ++------- src/Umbraco.Tests/Packaging/PackageInstallationTest.cs | 4 +--- src/Umbraco.Tests/TestHelpers/TestObjects.cs | 1 - 4 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs b/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs index bd07123fb2..8c9ccd1088 100644 --- a/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs +++ b/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs @@ -67,12 +67,11 @@ namespace Umbraco.Core.Composing.Composers composition.RegisterUnique(factory => CreatePackageRepository(factory, "installedPackages.config")); composition.RegisterUnique(); composition.RegisterUnique(); - var appRoot = new DirectoryInfo(IOHelper.GetRootDirectorySafe()); composition.RegisterUnique(factory => //factory required because we need to pass in a string path new PackageInstallation( factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), - appRoot, appRoot)); + new DirectoryInfo(IOHelper.GetRootDirectorySafe()))); //TODO: These are replaced in the web project - we need to declare them so that // something is wired up, just not sure this is very nice but will work for now. diff --git a/src/Umbraco.Core/Packaging/PackageInstallation.cs b/src/Umbraco.Core/Packaging/PackageInstallation.cs index 955662f3fe..d791295b38 100644 --- a/src/Umbraco.Core/Packaging/PackageInstallation.cs +++ b/src/Umbraco.Core/Packaging/PackageInstallation.cs @@ -18,7 +18,6 @@ namespace Umbraco.Core.Packaging private readonly PackageFileInstallation _packageFileInstallation; private readonly CompiledPackageXmlParser _parser; private readonly IPackageActionRunner _packageActionRunner; - private readonly DirectoryInfo _packageExtractionFolder; private readonly DirectoryInfo _applicationRootFolder; /// @@ -31,11 +30,8 @@ namespace Umbraco.Core.Packaging /// /// The root folder of the application /// - /// - /// The destination root folder to extract the package files (generally the same as applicationRoot) but can be modified for testing - /// public PackageInstallation(PackageDataInstallation packageDataInstallation, PackageFileInstallation packageFileInstallation, CompiledPackageXmlParser parser, IPackageActionRunner packageActionRunner, - DirectoryInfo applicationRootFolder, DirectoryInfo packageExtractionFolder) + DirectoryInfo applicationRootFolder) { _packageExtraction = new PackageExtraction(); _packageFileInstallation = packageFileInstallation ?? throw new ArgumentNullException(nameof(packageFileInstallation)); @@ -43,7 +39,6 @@ namespace Umbraco.Core.Packaging _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(FileInfo packageFile) @@ -69,7 +64,7 @@ namespace Umbraco.Core.Packaging var packageZipFile = compiledPackage.PackageFile; - var files = _packageFileInstallation.InstallFiles(compiledPackage, packageZipFile, _packageExtractionFolder.FullName).ToList(); + var files = _packageFileInstallation.InstallFiles(compiledPackage, packageZipFile, _applicationRootFolder.FullName).ToList(); packageDefinition.Files = files; diff --git a/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs b/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs index 2b9e50e9e5..4256a66a2d 100644 --- a/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs +++ b/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs @@ -50,8 +50,7 @@ namespace Umbraco.Tests.Packaging PackageDataInstallation, new PackageFileInstallation(Parser, ProfilingLogger), Parser, Mock.Of(), - applicationRootFolder: new DirectoryInfo(IOHelper.GetRootDirectorySafe()), - packageExtractionFolder: new DirectoryInfo(IOHelper.MapPath("~/" + _testBaseFolder))); //we don't want to extract package files to the real root, so extract to a test folder + applicationRootFolder: new DirectoryInfo(IOHelper.MapPath("~/" + _testBaseFolder))); //we don't want to extract package files to the real root, so extract to a test folder private const string DocumentTypePickerPackage = "Document_Type_Picker_1.1.umb"; private const string HelloPackage = "Hello_1.0.0.zip"; @@ -106,7 +105,6 @@ namespace Umbraco.Tests.Packaging } [Test] - [Explicit("Will fail on CI?")] public void Can_Read_Compiled_Package_Warnings() { //copy a file to the same path that the package will install so we can detect file conflicts diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects.cs b/src/Umbraco.Tests/TestHelpers/TestObjects.cs index 684da2599e..14ffeb743f 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects.cs @@ -185,7 +185,6 @@ namespace Umbraco.Tests.TestHelpers new PackageDataInstallation(logger, fileService.Value, macroService.Value, localizationService.Value, dataTypeService.Value, entityService.Value, contentTypeService.Value, contentService.Value, propertyEditorCollection), new PackageFileInstallation(compiledPackageXmlParser, new ProfilingLogger(logger, new TestProfiler())), compiledPackageXmlParser, Mock.Of(), - new DirectoryInfo(IOHelper.GetRootDirectorySafe()), new DirectoryInfo(IOHelper.GetRootDirectorySafe()))); }); var relationService = GetLazyService(factory, c => new RelationService(scopeProvider, logger, eventMessagesFactory, entityService.Value, GetRepo(c), GetRepo(c))); From 0d0179cb476450b4c580efb95f5c6b785270e3e1 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Thu, 17 Jan 2019 12:14:17 +0100 Subject: [PATCH 89/93] Use constants in upgrade migration --- .../UpdateMemberGroupPickerData.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourteenZero/UpdateMemberGroupPickerData.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourteenZero/UpdateMemberGroupPickerData.cs index 2dbc69e58a..62cbeacc78 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourteenZero/UpdateMemberGroupPickerData.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourteenZero/UpdateMemberGroupPickerData.cs @@ -16,17 +16,17 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFourtee public override void Up() { // move the data for all member group properties from the NVarchar to the NText column and clear the NVarchar column - Execute.Sql(@"UPDATE cmsPropertyData SET dataNtext = dataNvarchar, dataNvarchar = NULL + Execute.Sql($@"UPDATE cmsPropertyData SET dataNtext = dataNvarchar, dataNvarchar = NULL WHERE dataNtext IS NULL AND id IN ( SELECT id FROM cmsPropertyData WHERE propertyTypeId in ( SELECT id from cmsPropertyType where dataTypeID IN ( - SELECT nodeId FROM cmsDataType WHERE propertyEditorAlias = 'Umbraco.MemberGroupPicker' + SELECT nodeId FROM cmsDataType WHERE propertyEditorAlias = '{Constants.PropertyEditors.MemberGroupPickerAlias}' ) ) )"); // ensure that all exising member group properties are defined as NText - Execute.Sql("UPDATE cmsDataType SET dbType = 'Ntext' WHERE propertyEditorAlias = 'Umbraco.MemberGroupPicker'"); + Execute.Sql($"UPDATE cmsDataType SET dbType = 'Ntext' WHERE propertyEditorAlias = '{Constants.PropertyEditors.MemberGroupPickerAlias}'"); } public override void Down() From 77cf071e046dc54adb92194537d9b221d28eda5f Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Thu, 17 Jan 2019 12:20:13 +0100 Subject: [PATCH 90/93] Add "Move" option for deleted items in the media tree (#3914) --- .../views/media/media.restore.controller.js | 135 +++++++++++++----- .../src/views/media/restore.html | 97 +++++++++++-- 2 files changed, 187 insertions(+), 45 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/media/media.restore.controller.js b/src/Umbraco.Web.UI.Client/src/views/media/media.restore.controller.js index 6ef9232c37..bc02217660 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/media.restore.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/media/media.restore.controller.js @@ -1,63 +1,134 @@ angular.module("umbraco").controller("Umbraco.Editors.Media.RestoreController", - function ($scope, relationResource, mediaResource, navigationService, appState, treeService, localizationService) { + function ($scope, relationResource, mediaResource, navigationService, appState, treeService, userService) { var dialogOptions = $scope.dialogOptions; - var node = dialogOptions.currentNode; + $scope.source = _.clone(dialogOptions.currentNode); - $scope.error = null; - $scope.success = false; + $scope.error = null; + $scope.loading = true; + $scope.moving = false; + $scope.success = false; - relationResource.getByChildId(node.id, "relateParentDocumentOnDelete").then(function (data) { + $scope.dialogTreeEventHandler = $({}); + $scope.searchInfo = { + showSearch: false, + results: [], + selectedSearchResults: [] + } + $scope.treeModel = { + hideHeader: false + } + userService.getCurrentUser().then(function (userData) { + $scope.treeModel.hideHeader = userData.startContentIds.length > 0 && userData.startContentIds.indexOf(-1) == -1; + }); - if (data.length == 0) { - $scope.success = false; - $scope.error = { - errorMsg: localizationService.localize('recycleBin_itemCannotBeRestored'), - data: { - Message: localizationService.localize('recycleBin_noRestoreRelation') - } - } - return; + function nodeSelectHandler(ev, args) { + + if (args && args.event) { + args.event.preventDefault(); + args.event.stopPropagation(); } + if ($scope.target) { + //un-select if there's a current one selected + $scope.target.selected = false; + } + + $scope.target = args.node; + $scope.target.selected = true; + + } + + function nodeExpandedHandler(ev, args) { + // open mini list view for list views + if (args.node.metaData.isContainer) { + openMiniListView(args.node); + } + } + + $scope.hideSearch = function () { + $scope.searchInfo.showSearch = false; + $scope.searchInfo.results = []; + } + + // method to select a search result + $scope.selectResult = function (evt, result) { + result.selected = result.selected === true ? false : true; + nodeSelectHandler(evt, { event: evt, node: result }); + }; + + //callback when there are search results + $scope.onSearchResults = function (results) { + $scope.searchInfo.results = results; + $scope.searchInfo.showSearch = true; + }; + + $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler); + $scope.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler); + + $scope.$on('$destroy', function () { + $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); + $scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler); + }); + + // Mini list view + $scope.selectListViewNode = function (node) { + node.selected = node.selected === true ? false : true; + nodeSelectHandler({}, { node: node }); + }; + + $scope.closeMiniListView = function () { + $scope.miniListView = undefined; + }; + + function openMiniListView(node) { + $scope.miniListView = node; + } + + relationResource.getByChildId($scope.source.id, "relateParentDocumentOnDelete").then(function (data) { + $scope.loading = false; + + if (!data.length) { + $scope.moving = true; + return; + } + $scope.relation = data[0]; if ($scope.relation.parentId == -1) { $scope.target = { id: -1, name: "Root" }; } else { + $scope.loading = true; + mediaResource.getById($scope.relation.parentId).then(function (data) { + $scope.loading = false; $scope.target = data; - // make sure the target item isn't in the recycle bin - if ($scope.target.path.indexOf("-20") !== -1) { - $scope.error = { - errorMsg: localizationService.localize('recycleBin_itemCannotBeRestored'), - data: { - Message: localizationService.localize('recycleBin_restoreUnderRecycled').then(function (value) { - value.replace('%0%', $scope.target.name); - }) - } - }; - $scope.success = false; + // make sure the target item isn't in the recycle bin + if ($scope.target.path.indexOf("-21") !== -1) { + $scope.moving = true; + $scope.target = null; } - }, function (err) { - $scope.success = false; + $scope.loading = false; $scope.error = err; }); } }, function (err) { - $scope.success = false; - $scope.error = err; + $scope.loading = false; + $scope.error = err; }); - $scope.restore = function () { + $scope.restore = function () { + $scope.loading = true; + // this code was copied from `content.move.controller.js` - mediaResource.move({ parentId: $scope.target.id, id: node.id }) + mediaResource.move({ parentId: $scope.target.id, id: $scope.source.id }) .then(function (path) { + $scope.loading = false; $scope.success = true; //first we need to remove the node that launched the dialog @@ -78,7 +149,7 @@ angular.module("umbraco").controller("Umbraco.Editors.Media.RestoreController", }); }, function (err) { - $scope.success = false; + $scope.loading = false; $scope.error = err; }); }; diff --git a/src/Umbraco.Web.UI.Client/src/views/media/restore.html b/src/Umbraco.Web.UI.Client/src/views/media/restore.html index 17fff154d1..a24700afe7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/restore.html +++ b/src/Umbraco.Web.UI.Client/src/views/media/restore.html @@ -1,25 +1,96 @@
-
+
+ + -

- Restore {{currentNode.name}} under {{target.name}}? -

+
+
+
{{error.errorMsg}}
+
{{error.data.Message}}
+
+
-
-
{{error.errorMsg}}
-
{{error.data.Message}}
-
+
+
+ {{source.name}} + was restored under + was moved underneath + {{target.name}} +
+ +
-
-

{{currentNode.name}} was moved underneath {{target.name}}

- -
+
+ +

+ Restore {{source.name}} under {{target.name}}? +

+ +
+ +
+
+
+
Cannot automatically restore this item
+
There is no location where this item can be automatically restored. You can move the item manually using the tree below.
+
+
+ +
+ + + +
+ + + + +
+ + +
+
+ + + + +
-