From 5a3efa72f95975833e281edd24092658080082aa Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Mon, 4 Oct 2021 09:45:33 +0200 Subject: [PATCH 01/18] Remove inherited property group id/key when local properties are added (#11231) * Remove inherited property group id/key when local properties are added * Rebind saved content type values * Remove inherited from save group (cherry picked from commit 20b9db87d091e7d53f9f25d4d11744afcffa2584) --- .../services/contenttypehelper.service.js | 39 +++++++++++++++++ .../services/umbdataformatter.service.js | 40 +++++++++-------- .../views/documentTypes/edit.controller.js | 32 ++------------ .../src/views/mediaTypes/edit.controller.js | 35 ++------------- .../src/views/memberTypes/edit.controller.js | 43 +++---------------- 5 files changed, 73 insertions(+), 116 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/contenttypehelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/contenttypehelper.service.js index 5198f5d9bd..c638841066 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/contenttypehelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/contenttypehelper.service.js @@ -439,6 +439,45 @@ function contentTypeHelper(contentTypeResource, dataTypeResource, $filter, $inje array.push(placeholder); + }, + + rebindSavedContentType: function (contentType, savedContentType) { + // The saved content type might have updated values (eg. new IDs/keys), so make sure the view model is updated + contentType.ModelState = savedContentType.ModelState; + contentType.id = savedContentType.id; + contentType.groups.forEach(function (group) { + if (!group.alias) return; + + var k = 0; + while (k < savedContentType.groups.length && savedContentType.groups[k].alias != group.alias) + k++; + + if (k == savedContentType.groups.length) { + group.id = 0; + return; + } + + var savedGroup = savedContentType.groups[k]; + group.id = savedGroup.id; + group.key = savedGroup.key; + group.contentTypeId = savedGroup.contentTypeId; + + group.properties.forEach(function (property) { + if (property.id || !property.alias) return; + + k = 0; + while (k < savedGroup.properties.length && savedGroup.properties[k].alias != property.alias) + k++; + + if (k == savedGroup.properties.length) { + property.id = 0; + return; + } + + var savedProperty = savedGroup.properties[k]; + property.id = savedProperty.id; + }); + }); } }; diff --git a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js index f6b24dd12b..40266f7ac5 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js @@ -37,7 +37,7 @@ return { - formatChangePasswordModel: function(model) { + formatChangePasswordModel: function (model) { if (!model) { return null; } @@ -59,26 +59,23 @@ }, formatContentTypePostData: function (displayModel, action) { - - //create the save model from the display model + // Create the save model from the display model var saveModel = _.pick(displayModel, 'compositeContentTypes', 'isContainer', 'allowAsRoot', 'allowedTemplates', 'allowedContentTypes', 'alias', 'description', 'thumbnail', 'name', 'id', 'icon', 'trashed', 'key', 'parentId', 'alias', 'path', 'allowCultureVariant', 'allowSegmentVariant', 'isElement'); - // TODO: Map these saveModel.allowedTemplates = _.map(displayModel.allowedTemplates, function (t) { return t.alias; }); saveModel.defaultTemplate = displayModel.defaultTemplate ? displayModel.defaultTemplate.alias : null; var realGroups = _.reject(displayModel.groups, function (g) { - //do not include these tabs + // Do not include groups with init state return g.tabState === "init"; }); saveModel.groups = _.map(realGroups, function (g) { - - var saveGroup = _.pick(g, 'inherited', 'id', 'sortOrder', 'name', 'key', 'alias', 'type'); + var saveGroup = _.pick(g, 'id', 'sortOrder', 'name', 'key', 'alias', 'type'); var realProperties = _.reject(g.properties, function (p) { - //do not include these properties + // Do not include properties with init state or inherited from a composition return p.propertyState === "init" || p.inherited === true; }); @@ -89,16 +86,21 @@ saveGroup.properties = saveProperties; - //if this is an inherited group and there are not non-inherited properties on it, then don't send up the data - if (saveGroup.inherited === true && saveProperties.length === 0) { - return null; + if (g.inherited === true) { + if (saveProperties.length === 0) { + // All properties are inherited from the compositions, no need to save this group + return null; + } else if (g.contentTypeId != saveModel.id) { + // We have local properties, but the group id is not local, ensure a new id/key is generated on save + saveGroup = _.omit(saveGroup, 'id', 'key'); + } } return saveGroup; }); - //we don't want any null groups saveModel.groups = _.reject(saveModel.groups, function (g) { + // Do not include empty/null groups return !g; }); @@ -127,17 +129,17 @@ }, /** formats the display model used to display the dictionary to the model used to save the dictionary */ - formatDictionaryPostData : function(dictionary, nameIsDirty) { + formatDictionaryPostData: function (dictionary, nameIsDirty) { var saveModel = { parentId: dictionary.parentId, id: dictionary.id, name: dictionary.name, nameIsDirty: nameIsDirty, translations: [], - key : dictionary.key + key: dictionary.key }; - for(var i = 0; i < dictionary.translations.length; i++) { + for (var i = 0; i < dictionary.translations.length; i++) { saveModel.translations.push({ isoCode: dictionary.translations[i].isoCode, languageId: dictionary.translations[i].languageId, @@ -335,7 +337,7 @@ parentId: displayModel.parentId, //set the action on the save model action: action, - variants: _.map(displayModel.variants, function(v) { + variants: _.map(displayModel.variants, function (v) { return { name: v.name || "", //if its null/empty,we must pass up an empty string else we get json converter errors properties: getContentProperties(v.tabs), @@ -365,7 +367,7 @@ * @param {} displayModel * @returns {} */ - formatContentGetData: function(displayModel) { + formatContentGetData: function (displayModel) { // We need to check for invariant properties among the variant variants, // as the value of an invariant property is shared between different variants. @@ -431,12 +433,12 @@ * Formats the display model used to display the relation type to a model used to save the relation type. * @param {Object} relationType */ - formatRelationTypePostData : function(relationType) { + formatRelationTypePostData: function (relationType) { var saveModel = { id: relationType.id, name: relationType.name, alias: relationType.alias, - key : relationType.key, + key: relationType.key, isBidirectional: relationType.isBidirectional, parentObjectType: relationType.parentObjectType, childObjectType: relationType.childObjectType diff --git a/src/Umbraco.Web.UI.Client/src/views/documentTypes/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/documentTypes/edit.controller.js index b8fdf7b89d..0e13657e68 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documentTypes/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/documentTypes/edit.controller.js @@ -324,35 +324,9 @@ scope: $scope, content: vm.contentType, infiniteMode: infiniteMode, - // we need to rebind... the IDs that have been created! - rebindCallback: function (origContentType, savedContentType) { - vm.contentType.ModelState = savedContentType.ModelState; - vm.contentType.id = savedContentType.id; - vm.contentType.groups.forEach(function (group) { - if (!group.name) return; - var k = 0; - while (k < savedContentType.groups.length && savedContentType.groups[k].name != group.name) - k++; - if (k == savedContentType.groups.length) { - group.id = 0; - return; - } - var savedGroup = savedContentType.groups[k]; - if (!group.id) group.id = savedGroup.id; - - group.properties.forEach(function (property) { - if (property.id || !property.alias) return; - k = 0; - while (k < savedGroup.properties.length && savedGroup.properties[k].alias != property.alias) - k++; - if (k == savedGroup.properties.length) { - property.id = 0; - return; - } - var savedProperty = savedGroup.properties[k]; - property.id = savedProperty.id; - }); - }); + rebindCallback: function (_, savedContentType) { + // we need to rebind... the IDs that have been created! + contentTypeHelper.rebindSavedContentType(vm.contentType, savedContentType); } }).then(function (data) { // allow UI to access server validation state diff --git a/src/Umbraco.Web.UI.Client/src/views/mediaTypes/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/mediaTypes/edit.controller.js index 6d23a525a5..8c0ce561d9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/mediaTypes/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/mediaTypes/edit.controller.js @@ -295,38 +295,9 @@ saveMethod: mediaTypeResource.save, scope: $scope, content: vm.contentType, - // we need to rebind... the IDs that have been created! - rebindCallback: function (origContentType, savedContentType) { - vm.contentType.id = savedContentType.id; - vm.contentType.groups.forEach(function (group) { - if (!group.name) return; - - var k = 0; - while (k < savedContentType.groups.length && savedContentType.groups[k].name != group.name) - k++; - if (k == savedContentType.groups.length) { - group.id = 0; - return; - } - - var savedGroup = savedContentType.groups[k]; - if (!group.id) group.id = savedGroup.id; - - group.properties.forEach(function (property) { - if (property.id || !property.alias) return; - - k = 0; - while (k < savedGroup.properties.length && savedGroup.properties[k].alias != property.alias) - k++; - if (k == savedGroup.properties.length) { - property.id = 0; - return; - } - - var savedProperty = savedGroup.properties[k]; - property.id = savedProperty.id; - }); - }); + rebindCallback: function (_, savedContentType) { + // we need to rebind... the IDs that have been created! + contentTypeHelper.rebindSavedContentType(vm.contentType, savedContentType); } }).then(function (data) { //success diff --git a/src/Umbraco.Web.UI.Client/src/views/memberTypes/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/memberTypes/edit.controller.js index 8854b960e7..f5658ad1df 100644 --- a/src/Umbraco.Web.UI.Client/src/views/memberTypes/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/memberTypes/edit.controller.js @@ -175,11 +175,11 @@ //we are creating so get an empty data type item memberTypeResource.getScaffold(memberTypeId) - .then(function (dt) { - init(dt); + .then(function (dt) { + init(dt); - vm.page.loading = false; - }); + vm.page.loading = false; + }); } else { loadMemberType(); @@ -215,38 +215,9 @@ saveMethod: memberTypeResource.save, scope: $scope, content: vm.contentType, - // we need to rebind... the IDs that have been created! - rebindCallback: function (origContentType, savedContentType) { - vm.contentType.id = savedContentType.id; - vm.contentType.groups.forEach(function (group) { - if (!group.name) return; - - var k = 0; - while (k < savedContentType.groups.length && savedContentType.groups[k].name != group.name) - k++; - if (k == savedContentType.groups.length) { - group.id = 0; - return; - } - - var savedGroup = savedContentType.groups[k]; - if (!group.id) group.id = savedGroup.id; - - group.properties.forEach(function (property) { - if (property.id || !property.alias) return; - - k = 0; - while (k < savedGroup.properties.length && savedGroup.properties[k].alias != property.alias) - k++; - if (k == savedGroup.properties.length) { - property.id = 0; - return; - } - - var savedProperty = savedGroup.properties[k]; - property.id = savedProperty.id; - }); - }); + rebindCallback: function (_, savedContentType) { + // we need to rebind... the IDs that have been created! + contentTypeHelper.rebindSavedContentType(vm.contentType, savedContentType); } }).then(function (data) { //success From 5300d74cdae3ecab9ef140ad71e464664030b820 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 4 Oct 2021 11:15:28 +0200 Subject: [PATCH 02/18] Bump version to 9.0.1 --- .../UmbracoPackage/.template.config/template.json | 2 +- .../UmbracoProject/.template.config/template.json | 2 +- src/Directory.Build.props | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build/templates/UmbracoPackage/.template.config/template.json b/build/templates/UmbracoPackage/.template.config/template.json index ea2f65a37c..9daa959f7c 100644 --- a/build/templates/UmbracoPackage/.template.config/template.json +++ b/build/templates/UmbracoPackage/.template.config/template.json @@ -24,7 +24,7 @@ "version": { "type": "parameter", "datatype": "string", - "defaultValue": "9.0.0", + "defaultValue": "9.0.1", "description": "The version of Umbraco to load using NuGet", "replaces": "UMBRACO_VERSION_FROM_TEMPLATE" }, diff --git a/build/templates/UmbracoProject/.template.config/template.json b/build/templates/UmbracoProject/.template.config/template.json index 587b8eb292..ca72873db0 100644 --- a/build/templates/UmbracoProject/.template.config/template.json +++ b/build/templates/UmbracoProject/.template.config/template.json @@ -57,7 +57,7 @@ "version": { "type": "parameter", "datatype": "string", - "defaultValue": "9.0.0", + "defaultValue": "9.0.1", "description": "The version of Umbraco to load using NuGet", "replaces": "UMBRACO_VERSION_FROM_TEMPLATE" }, diff --git a/src/Directory.Build.props b/src/Directory.Build.props index d51557b8ee..0461176c23 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,9 +1,9 @@  - 9.0.0 - 9.0.0 - 9.0.0 - 9.0.0 + 9.0.1 + 9.0.1 + 9.0.1 + 9.0.1 9.0 en-US Umbraco CMS From 2922871a730b62fa20b53dd252076ddf0e2a0165 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 4 Oct 2021 13:42:06 +0200 Subject: [PATCH 03/18] https://github.com/umbraco/Umbraco-CMS/issues/11227 Set culture from domain when internal redirecting due to public access --- src/Umbraco.Core/Routing/PublishedRouter.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Routing/PublishedRouter.cs b/src/Umbraco.Core/Routing/PublishedRouter.cs index 548387a675..590262946d 100644 --- a/src/Umbraco.Core/Routing/PublishedRouter.cs +++ b/src/Umbraco.Core/Routing/PublishedRouter.cs @@ -237,7 +237,7 @@ namespace Umbraco.Cms.Core.Routing // re-route await RouteRequestInternalAsync(builder); - + // return if we are redirect if (builder.IsRedirect()) { @@ -252,6 +252,11 @@ namespace Umbraco.Cms.Core.Routing builder.SetPublishedContent(content); } + if (!builder.HasDomain()) + { + FindDomain(builder); + } + return BuildRequest(builder); } From f3f674437d101ebdd647dd21aa1bbba4b51b6229 Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Mon, 4 Oct 2021 18:44:14 +0200 Subject: [PATCH 04/18] Ensured order of Deploy artifact dependencies irrespective of ICU or NLS globalization settings. (#11265) --- src/Umbraco.Core/Udi.cs | 4 +-- .../Umbraco.Core/Deploy/ArtifactBaseTests.cs | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Udi.cs b/src/Umbraco.Core/Udi.cs index b861bcc68b..208ac536c1 100644 --- a/src/Umbraco.Core/Udi.cs +++ b/src/Umbraco.Core/Udi.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel; using System.Linq; @@ -44,7 +44,7 @@ namespace Umbraco.Cms.Core public int CompareTo(Udi other) { - return string.Compare(UriValue.ToString(), other.UriValue.ToString(), StringComparison.InvariantCultureIgnoreCase); + return string.Compare(UriValue.ToString(), other.UriValue.ToString(), StringComparison.OrdinalIgnoreCase); } public override string ToString() diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Deploy/ArtifactBaseTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Deploy/ArtifactBaseTests.cs index 1a2ffa8011..c4cd4f0c02 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Deploy/ArtifactBaseTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Deploy/ArtifactBaseTests.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using Newtonsoft.Json; using NUnit.Framework; using Umbraco.Cms.Core; @@ -29,6 +30,33 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Deploy Assert.AreEqual(expected, serialized); } + [Test] + public void Dependencies_Are_Correctly_Ordered() + { + // This test was introduced following: https://github.com/umbraco/Umbraco.Deploy.Issues/issues/72 to verify + // that consistent ordering rules are used across platforms. + var udi = new GuidUdi("test", Guid.Parse("3382d5433b5749d08919bc9961422a1f")); + var artifact = new TestArtifact(udi, new List()) + { + Name = "Test Name", + Alias = "testAlias", + }; + + var dependencies = new ArtifactDependencyCollection(); + + var dependencyUdi1 = new GuidUdi("template", Guid.Parse("d4651496fad24c1290a53ea4d55d945b")); + dependencies.Add(new ArtifactDependency(dependencyUdi1, true, ArtifactDependencyMode.Exist)); + + var dependencyUdi2 = new StringUdi(Constants.UdiEntityType.TemplateFile, "TestPage.cshtml"); + dependencies.Add(new ArtifactDependency(dependencyUdi2, true, ArtifactDependencyMode.Exist)); + + artifact.Dependencies = dependencies; + + Assert.AreEqual( + "umb://template-file/TestPage.cshtml,umb://template/d4651496fad24c1290a53ea4d55d945b", + string.Join(",", artifact.Dependencies.Select(x => x.Udi.ToString()))); + } + private class TestArtifact : ArtifactBase { public TestArtifact(GuidUdi udi, IEnumerable dependencies = null) : base(udi, dependencies) From 02d89ce613a6153f721c75e97b4ef03251e01dc5 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 4 Oct 2021 19:40:32 +0200 Subject: [PATCH 05/18] https://github.com/umbraco/Umbraco-CMS/issues/11263 Fixes issue where we used the current users email when sending invite emails. Now we use the one from SMTP settings like in v8 --- src/Umbraco.Web.BackOffice/Controllers/UsersController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs index 7e62e514f7..672495c7eb 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs @@ -557,7 +557,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers // i.e. "Some Person" var toMailBoxAddress = new MailboxAddress(to.Name, to.Email); - var mailMessage = new EmailMessage(fromEmail, toMailBoxAddress.ToString(), emailSubject, emailBody, true); + var mailMessage = new EmailMessage(null /*use info from smtp settings*/, toMailBoxAddress.ToString(), emailSubject, emailBody, true); await _emailSender.SendAsync(mailMessage, Constants.Web.EmailTypes.UserInvite, true); } From c5ddc3d872d467b7ea234602d1915f3aca3bb97d Mon Sep 17 00:00:00 2001 From: Louis JR <17555062+louisjrdev@users.noreply.github.com> Date: Mon, 4 Oct 2021 18:48:52 +0100 Subject: [PATCH 06/18] Change template helper to use async partials (#11243) --- .../src/common/services/templatehelper.service.js | 2 +- .../test/unit/common/services/template-helper.spec.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/templatehelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/templatehelper.service.js index 1a2f0735ce..aa10d5bf2f 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/templatehelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/templatehelper.service.js @@ -16,7 +16,7 @@ partialViewName = parentId + "/" + partialViewName; } - return "@Html.Partial(\"" + partialViewName + "\")"; + return "@await Html.PartialAsync(\"" + partialViewName + "\")"; } function getQuerySnippet(queryExpression) { diff --git a/src/Umbraco.Web.UI.Client/test/unit/common/services/template-helper.spec.js b/src/Umbraco.Web.UI.Client/test/unit/common/services/template-helper.spec.js index 316cfa7c59..69da0ce786 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/common/services/template-helper.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/common/services/template-helper.spec.js @@ -26,28 +26,28 @@ describe('service: templateHelper', function () { it('should return the snippet for inserting a partial from the root', function () { var parentId = ""; var nodeName = "Footer.cshtml"; - var snippet = '@Html.Partial("Footer")'; + var snippet = '@await Html.PartialAsync("Footer")'; expect(templateHelper.getInsertPartialSnippet(parentId, nodeName)).toBe(snippet); }); it('should return the snippet for inserting a partial from a folder', function () { var parentId = "Folder"; var nodeName = "Footer.cshtml"; - var snippet = '@Html.Partial("Folder/Footer")'; + var snippet = '@await Html.PartialAsync("Folder/Footer")'; expect(templateHelper.getInsertPartialSnippet(parentId, nodeName)).toBe(snippet); }); it('should return the snippet for inserting a partial from a nested folder', function () { var parentId = "Folder/NestedFolder"; var nodeName = "Footer.cshtml"; - var snippet = '@Html.Partial("Folder/NestedFolder/Footer")'; + var snippet = '@await Html.PartialAsync("Folder/NestedFolder/Footer")'; expect(templateHelper.getInsertPartialSnippet(parentId, nodeName)).toBe(snippet); }); it('should return the snippet for inserting a partial from a folder with spaces in its name', function () { var parentId = "Folder with spaces"; var nodeName = "Footer.cshtml"; - var snippet = '@Html.Partial("Folder with spaces/Footer")'; + var snippet = '@await Html.PartialAsync("Folder with spaces/Footer")'; expect(templateHelper.getInsertPartialSnippet(parentId, nodeName)).toBe(snippet); }); From 4d4fa4d0412b5c6f5172aaebedef6a135f9abf9d Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 4 Oct 2021 20:01:23 +0200 Subject: [PATCH 07/18] https://github.com/umbraco/Umbraco-CMS/issues/11188 Fixes Missing JSON Schema Property for RuntimeMinification:Version --- .../Configuration/Models/RuntimeMinificationSettings.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Umbraco.Core/Configuration/Models/RuntimeMinificationSettings.cs b/src/Umbraco.Core/Configuration/Models/RuntimeMinificationSettings.cs index b03528fd0a..fe999f7bc0 100644 --- a/src/Umbraco.Core/Configuration/Models/RuntimeMinificationSettings.cs +++ b/src/Umbraco.Core/Configuration/Models/RuntimeMinificationSettings.cs @@ -7,6 +7,7 @@ namespace Umbraco.Cms.Core.Configuration.Models { internal const bool StaticUseInMemoryCache = false; internal const string StaticCacheBuster = "Version"; + internal const string StaticVersion = null; /// /// Use in memory cache @@ -19,5 +20,11 @@ namespace Umbraco.Cms.Core.Configuration.Models /// [DefaultValue(StaticCacheBuster)] public RuntimeMinificationCacheBuster CacheBuster { get; set; } = Enum.Parse(StaticCacheBuster); + + /// + /// The unique version string used if CacheBuster is 'Version'. + /// + [DefaultValue(StaticVersion)] + public string Version { get; set; } = StaticVersion; } } From 931e91760d9e4406d64c9b9b1acdfbebd02c0dbe Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 5 Oct 2021 12:34:39 +0200 Subject: [PATCH 08/18] Force ICU usage on windows. --- build/templates/UmbracoProject/UmbracoProject.csproj | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/build/templates/UmbracoProject/UmbracoProject.csproj b/build/templates/UmbracoProject/UmbracoProject.csproj index 6b0f92858d..efc5425728 100644 --- a/build/templates/UmbracoProject/UmbracoProject.csproj +++ b/build/templates/UmbracoProject/UmbracoProject.csproj @@ -8,6 +8,12 @@ $(DefaultItemExcludes);wwwroot/media/**; + + + + + + From 62f2bdbafaebfdf74b0eaf77c6c7c58bdba296d7 Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle Date: Tue, 5 Oct 2021 13:41:58 +0200 Subject: [PATCH 09/18] Removed obsolete method Signed-off-by: Nikolaj Geisle --- .../Persistence/UmbracoPocoDataBuilder.cs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/Umbraco.Infrastructure/Persistence/UmbracoPocoDataBuilder.cs b/src/Umbraco.Infrastructure/Persistence/UmbracoPocoDataBuilder.cs index 2e9fb6cebc..7bfb7c82ea 100644 --- a/src/Umbraco.Infrastructure/Persistence/UmbracoPocoDataBuilder.cs +++ b/src/Umbraco.Infrastructure/Persistence/UmbracoPocoDataBuilder.cs @@ -18,6 +18,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence /// So far, this is very manual. We don't try to be clever and figure out whether the /// columns exist already. We just ignore it. /// Beware, the application MUST restart when this class behavior changes. + /// You can override the GetColmunnInfo method to control which columns this includes /// internal class UmbracoPocoDataBuilder : PocoDataBuilder { @@ -28,19 +29,5 @@ namespace Umbraco.Cms.Infrastructure.Persistence { _upgrading = upgrading; } - - protected override ColumnInfo GetColumnInfo(MemberInfo mi, Type type) - { - var columnInfo = base.GetColumnInfo(mi, type); - - // TODO: Is this upgrade flag still relevant? It's a lot of hacking to just set this value - // including the interface method ConfigureForUpgrade for this one circumstance. - if (_upgrading) - { - if (type == typeof(UserDto) && mi.Name == "TourData") columnInfo.IgnoreColumn = true; - } - - return columnInfo; - } } } From 80c74968ef666b0e10d3b858701fe484e5af3b0d Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle Date: Tue, 5 Oct 2021 13:48:42 +0200 Subject: [PATCH 10/18] Updated UmbracoPocoDataBuilder.cs Signed-off-by: Nikolaj Geisle --- .../Persistence/UmbracoPocoDataBuilder.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Umbraco.Infrastructure/Persistence/UmbracoPocoDataBuilder.cs b/src/Umbraco.Infrastructure/Persistence/UmbracoPocoDataBuilder.cs index 7bfb7c82ea..0b874a80c2 100644 --- a/src/Umbraco.Infrastructure/Persistence/UmbracoPocoDataBuilder.cs +++ b/src/Umbraco.Infrastructure/Persistence/UmbracoPocoDataBuilder.cs @@ -1,7 +1,5 @@ using System; -using System.Reflection; using NPoco; -using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Persistence { From 223e8661dcfc9a7136d6d33f28c272661e62e099 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 5 Oct 2021 21:25:15 +0200 Subject: [PATCH 11/18] Fixed bug where the wrong schema was signed in. --- src/Umbraco.Web.Common/Security/UmbracoSignInManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.Common/Security/UmbracoSignInManager.cs b/src/Umbraco.Web.Common/Security/UmbracoSignInManager.cs index 4817956ef8..4f03255fae 100644 --- a/src/Umbraco.Web.Common/Security/UmbracoSignInManager.cs +++ b/src/Umbraco.Web.Common/Security/UmbracoSignInManager.cs @@ -362,7 +362,7 @@ namespace Umbraco.Cms.Web.Common.Security { // Store the userId for use after two factor check var userId = await UserManager.GetUserIdAsync(user); - await Context.SignInAsync(IdentityConstants.TwoFactorUserIdScheme, StoreTwoFactorInfo(userId, loginProvider)); + await Context.SignInAsync(TwoFactorRememberMeAuthenticationType, StoreTwoFactorInfo(userId, loginProvider)); return SignInResult.TwoFactorRequired; } } @@ -372,7 +372,7 @@ namespace Umbraco.Cms.Web.Common.Security await Context.SignOutAsync(ExternalAuthenticationType); } if (loginProvider == null) - { + { await SignInWithClaimsAsync(user, isPersistent, new Claim[] { new Claim("amr", "pwd") }); } else From 9b6421947af7a123a49754c0a1db49b8bd55cd64 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 5 Oct 2021 21:57:02 +0200 Subject: [PATCH 12/18] Fixed to not use remember me type --- src/Umbraco.Web.Common/Security/UmbracoSignInManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.Common/Security/UmbracoSignInManager.cs b/src/Umbraco.Web.Common/Security/UmbracoSignInManager.cs index 4f03255fae..cc07b6bd28 100644 --- a/src/Umbraco.Web.Common/Security/UmbracoSignInManager.cs +++ b/src/Umbraco.Web.Common/Security/UmbracoSignInManager.cs @@ -362,7 +362,7 @@ namespace Umbraco.Cms.Web.Common.Security { // Store the userId for use after two factor check var userId = await UserManager.GetUserIdAsync(user); - await Context.SignInAsync(TwoFactorRememberMeAuthenticationType, StoreTwoFactorInfo(userId, loginProvider)); + await Context.SignInAsync(TwoFactorAuthenticationType, StoreTwoFactorInfo(userId, loginProvider)); return SignInResult.TwoFactorRequired; } } From a6492871e8084d9565e70a4ef947379b90d428a9 Mon Sep 17 00:00:00 2001 From: Jeavon Leopold Date: Fri, 1 Oct 2021 16:15:49 +0100 Subject: [PATCH 13/18] Remove all ImageSharp.Web Processors and the re-add in the correct order --- .../DependencyInjection/UmbracoBuilder.ImageSharp.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs index 62573cfc7b..6755159fc1 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs @@ -54,8 +54,14 @@ namespace Umbraco.Extensions .Configure(options => options.CacheFolder = builder.BuilderHostingEnvironment.MapPathContentRoot(imagingSettings.Cache.CacheFolder)) // We need to add CropWebProcessor before ResizeWebProcessor (until https://github.com/SixLabors/ImageSharp.Web/issues/182 is fixed) .RemoveProcessor() + .RemoveProcessor() + .RemoveProcessor() + .RemoveProcessor() .AddProcessor() - .AddProcessor(); + .AddProcessor() + .AddProcessor() + .AddProcessor() + .AddProcessor(); builder.Services.AddTransient, ImageSharpConfigurationOptions>(); From 114ab93a6a5ea3244c2afc3bd3ac7572f92b115f Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 6 Oct 2021 10:20:50 +0200 Subject: [PATCH 14/18] Moved endpoint to new controller to avoid issue with too hard access requirements (#11264) * Fixed https://github.com/umbraco/Umbraco-CMS/issues/11258 Moved endpoint and obsoleted the old one to avoid breaking changes.. The issue is the auth policies cannot be overridden.. You need all of them, and the controller requires you to have access to member types * Update src/Umbraco.Web.BackOffice/Controllers/MemberTypeQueryController.cs Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> --- .../Controllers/BackOfficeServerVariables.cs | 4 ++ .../Controllers/MemberTypeController.cs | 1 + .../Controllers/MemberTypeQueryController.cs | 42 +++++++++++++++++++ .../common/resources/membertype.resource.js | 4 +- 4 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 src/Umbraco.Web.BackOffice/Controllers/MemberTypeQueryController.cs diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs index a982ed1744..58f3622e67 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs @@ -279,6 +279,10 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers "memberTypeApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( controller => controller.GetAllTypes()) }, + { + "memberTypeQueryApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( + controller => controller.GetAllTypes()) + }, { "memberGroupApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( controller => controller.GetAllGroups()) diff --git a/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs index 4af907bdfc..7c1f6f4187 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs @@ -182,6 +182,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// Returns all member types /// + [Obsolete("Use MemberTypeQueryController.GetAllTypes instead as it only requires AuthorizationPolicies.TreeAccessMembersOrMemberTypes and not both this and AuthorizationPolicies.TreeAccessMemberTypes")] [Authorize(Policy = AuthorizationPolicies.TreeAccessMembersOrMemberTypes)] public IEnumerable GetAllTypes() { diff --git a/src/Umbraco.Web.BackOffice/Controllers/MemberTypeQueryController.cs b/src/Umbraco.Web.BackOffice/Controllers/MemberTypeQueryController.cs new file mode 100644 index 0000000000..1d15a6448a --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Controllers/MemberTypeQueryController.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Authorization; +using Umbraco.Cms.Core.Mapping; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.ContentEditing; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Web.Common.Attributes; +using Umbraco.Cms.Web.Common.Authorization; +using Constants = Umbraco.Cms.Core.Constants; + +namespace Umbraco.Cms.Web.BackOffice.Controllers +{ + /// + /// An API controller used for dealing with member types + /// + [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] + [Authorize(Policy = AuthorizationPolicies.TreeAccessMembersOrMemberTypes)] + public class MemberTypeQueryController : BackOfficeNotificationsController + { + private readonly IMemberTypeService _memberTypeService; + private readonly IUmbracoMapper _umbracoMapper; + + + public MemberTypeQueryController( + IMemberTypeService memberTypeService, + IUmbracoMapper umbracoMapper) + { + _memberTypeService = memberTypeService ?? throw new ArgumentNullException(nameof(memberTypeService)); + _umbracoMapper = umbracoMapper ?? throw new ArgumentNullException(nameof(umbracoMapper)); + } + + /// + /// Returns all member types + /// + public IEnumerable GetAllTypes() => + _memberTypeService.GetAll() + .Select(_umbracoMapper.Map); + + } +} diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/membertype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/membertype.resource.js index bf02d9618e..e1d0fbe8ac 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/membertype.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/membertype.resource.js @@ -46,10 +46,10 @@ function memberTypeResource($q, $http, umbRequestHelper, umbDataFormatter, local return umbRequestHelper.resourcePromise( $http.get( umbRequestHelper.getApiUrl( - "memberTypeApiBaseUrl", + "memberTypeQueryApiBaseUrl", "GetAllTypes")), 'Failed to retrieve data for member types id'); - }, + }, getById: function (id) { From 1bd827cbba63f7c39b645ec48752fb9477654938 Mon Sep 17 00:00:00 2001 From: Blake Irwin Date: Wed, 29 Sep 2021 23:44:40 +1300 Subject: [PATCH 15/18] Execute CopyUmbracoAssets @ BeforeBuild to prevent Rebuild failures --- build/NuSpecs/build/Umbraco.Cms.StaticAssets.targets | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build/NuSpecs/build/Umbraco.Cms.StaticAssets.targets b/build/NuSpecs/build/Umbraco.Cms.StaticAssets.targets index 0d712e083b..d271653e44 100644 --- a/build/NuSpecs/build/Umbraco.Cms.StaticAssets.targets +++ b/build/NuSpecs/build/Umbraco.Cms.StaticAssets.targets @@ -6,9 +6,8 @@ umbraco - + - From 2a29cdc6c6598acb31fd5394e4157d75f124a82a Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 6 Oct 2021 11:13:59 +0200 Subject: [PATCH 16/18] Allow opt out of import embedded schema file (#11296) * Introduce an opt-out options from the import of embedded schema files. * Moved the initialization of the static service provider into CoreRuntime as this runs before the IStartupFilters, and otherwise the static service provider is not available in hosted services. E.g. for migrations * fix build * Minor code tidy and naming alignment. * Update src/Umbraco.Web.UI/Umbraco.Web.UI.csproj * Removed default installation of starter kit. Co-authored-by: Andy Butland --- src/JsonSchema/AppSettings.cs | 1 + .../Models/PackageMigrationSettings.cs | 40 +++++++++++++++++++ .../Models/UnattendedSettings.cs | 10 +++-- src/Umbraco.Core/Constants-Configuration.cs | 1 + .../StaticServiceProvider.cs | 8 ++-- .../UmbracoBuilder.Configuration.cs | 3 +- .../DependencyInjection/UmbracoBuilder.cs | 1 + .../Packaging/ImportPackageBuilder.cs | 9 +++-- .../ImportPackageBuilderExpression.cs | 17 +++++++- .../Packaging/PackageMigrationBase.cs | 37 +++++++++++++++-- .../Runtime/CoreRuntime.cs | 40 ++++++++++++++++++- .../UmbracoApplicationServicesCapture.cs | 20 ---------- .../UmbracoBuilderExtensions.cs | 4 -- .../Umbraco.Web.Common.csproj | 1 - 14 files changed, 148 insertions(+), 44 deletions(-) create mode 100644 src/Umbraco.Core/Configuration/Models/PackageMigrationSettings.cs rename src/{Umbraco.Web.Common => Umbraco.Core}/DependencyInjection/StaticServiceProvider.cs (64%) delete mode 100644 src/Umbraco.Web.Common/DependencyInjection/UmbracoApplicationServicesCapture.cs diff --git a/src/JsonSchema/AppSettings.cs b/src/JsonSchema/AppSettings.cs index 4045421eb1..1b7c6d46fc 100644 --- a/src/JsonSchema/AppSettings.cs +++ b/src/JsonSchema/AppSettings.cs @@ -47,6 +47,7 @@ namespace JsonSchema public RichTextEditorSettings RichTextEditor { get; set; } public RuntimeMinificationSettings RuntimeMinification { get; set; } public BasicAuthSettings BasicAuth { get; set; } + public PackageMigrationSettings PackageMigration { get; set; } } /// diff --git a/src/Umbraco.Core/Configuration/Models/PackageMigrationSettings.cs b/src/Umbraco.Core/Configuration/Models/PackageMigrationSettings.cs new file mode 100644 index 0000000000..27968fdcd2 --- /dev/null +++ b/src/Umbraco.Core/Configuration/Models/PackageMigrationSettings.cs @@ -0,0 +1,40 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.ComponentModel; + +namespace Umbraco.Cms.Core.Configuration.Models +{ + /// + /// Typed configuration options for package migration settings. + /// + [UmbracoOptions(Constants.Configuration.ConfigPackageMigration)] + public class PackageMigrationSettings + { + private const bool StaticRunSchemaAndContentMigrations = true; + private const bool StaticAllowComponentOverrideOfRunSchemaAndContentMigrations = true; + + /// + /// Gets or sets a value indicating whether package migration steps that install schema and content should run. + /// + /// + /// By default this is true and schema and content defined in a package migration are installed. + /// Using configuration, administrators can optionally switch this off in certain environments. + /// Deployment tools such as Umbraco Deploy can also configure this option to run or not run these migration + /// steps as is appropriate for normal use of the tool. + /// + [DefaultValue(StaticRunSchemaAndContentMigrations)] + public bool RunSchemaAndContentMigrations { get; set; } = StaticRunSchemaAndContentMigrations; + + /// + /// Gets or sets a value indicating whether components can override the configured value for . + /// + /// + /// By default this is true and components can override the configured setting for . + /// If an administrator wants explicit control over which environments migration steps installing schema and content can run, + /// they can set this to false. Components should respect this and not override the configuration. + /// + [DefaultValue(StaticAllowComponentOverrideOfRunSchemaAndContentMigrations)] + public bool AllowComponentOverrideOfRunSchemaAndContentMigrations { get; set; } = StaticAllowComponentOverrideOfRunSchemaAndContentMigrations; + } +} diff --git a/src/Umbraco.Core/Configuration/Models/UnattendedSettings.cs b/src/Umbraco.Core/Configuration/Models/UnattendedSettings.cs index 08020f6e89..7103a9534e 100644 --- a/src/Umbraco.Core/Configuration/Models/UnattendedSettings.cs +++ b/src/Umbraco.Core/Configuration/Models/UnattendedSettings.cs @@ -1,17 +1,19 @@ -using System.ComponentModel; +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.ComponentModel; using System.ComponentModel.DataAnnotations; namespace Umbraco.Cms.Core.Configuration.Models { - /// /// Typed configuration options for unattended settings. /// [UmbracoOptions(Constants.Configuration.ConfigUnattended)] public class UnattendedSettings { - internal const bool StaticInstallUnattended = false; - internal const bool StaticUpgradeUnattended = false; + private const bool StaticInstallUnattended = false; + private const bool StaticUpgradeUnattended = false; /// /// Gets or sets a value indicating whether unattended installs are enabled. diff --git a/src/Umbraco.Core/Constants-Configuration.cs b/src/Umbraco.Core/Constants-Configuration.cs index 0c7657d07e..c36f5813ab 100644 --- a/src/Umbraco.Core/Constants-Configuration.cs +++ b/src/Umbraco.Core/Constants-Configuration.cs @@ -52,6 +52,7 @@ public const string ConfigWebRouting = ConfigPrefix + "WebRouting"; public const string ConfigUserPassword = ConfigPrefix + "Security:UserPassword"; public const string ConfigRichTextEditor = ConfigPrefix + "RichTextEditor"; + public const string ConfigPackageMigration = ConfigPrefix + "PackageMigration"; } } } diff --git a/src/Umbraco.Web.Common/DependencyInjection/StaticServiceProvider.cs b/src/Umbraco.Core/DependencyInjection/StaticServiceProvider.cs similarity index 64% rename from src/Umbraco.Web.Common/DependencyInjection/StaticServiceProvider.cs rename to src/Umbraco.Core/DependencyInjection/StaticServiceProvider.cs index c73685b41d..8d195c56f4 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/StaticServiceProvider.cs +++ b/src/Umbraco.Core/DependencyInjection/StaticServiceProvider.cs @@ -4,22 +4,22 @@ using System.ComponentModel; namespace Umbraco.Cms.Web.Common.DependencyInjection { /// - /// INTERNAL Service locator. Should only be used if no other ways exist. + /// Service locator for internal (umbraco cms) only purposes. Should only be used if no other ways exist. /// /// /// It is created with only two goals in mind /// 1) Continue to have the same extension methods on IPublishedContent and IPublishedElement as in V8. To make migration easier. - /// 2) To have a tool to avoid breaking changes in minor versions. All methods using this should in theory be obsolete. + /// 2) To have a tool to avoid breaking changes in minor and patch versions. All methods using this should in theory be obsolete. /// /// Keep in mind, every time this is used, the code becomes basically untestable. /// [EditorBrowsable(EditorBrowsableState.Never)] - internal static class StaticServiceProvider + public static class StaticServiceProvider { /// /// The service locator. /// [EditorBrowsable(EditorBrowsableState.Never)] - internal static IServiceProvider Instance { get; set; } + public static IServiceProvider Instance { get; set; } } } diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs index 77902cc5c1..9b31ed7056 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs @@ -72,7 +72,8 @@ namespace Umbraco.Cms.Core.DependencyInjection .AddUmbracoOptions() .AddUmbracoOptions() .AddUmbracoOptions() - .AddUmbracoOptions(); + .AddUmbracoOptions() + .AddUmbracoOptions(); return builder; } diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs index 6f6a53df66..bf3b3edaf9 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs @@ -38,6 +38,7 @@ using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Sync; using Umbraco.Cms.Core.Templates; using Umbraco.Cms.Core.Web; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; namespace Umbraco.Cms.Core.DependencyInjection diff --git a/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilder.cs b/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilder.cs index c14d3e5119..fef61a54c3 100644 --- a/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilder.cs +++ b/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilder.cs @@ -1,8 +1,9 @@ using System; using System.Xml.Linq; +using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.PropertyEditors; -using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Migrations; @@ -20,7 +21,8 @@ namespace Umbraco.Cms.Infrastructure.Packaging MediaUrlGeneratorCollection mediaUrlGenerators, IShortStringHelper shortStringHelper, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, - IMigrationContext context) + IMigrationContext context, + IOptions options) : base(new ImportPackageBuilderExpression( packagingService, mediaService, @@ -28,7 +30,8 @@ namespace Umbraco.Cms.Infrastructure.Packaging mediaUrlGenerators, shortStringHelper, contentTypeBaseServiceProvider, - context)) + context, + options)) { } diff --git a/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilderExpression.cs b/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilderExpression.cs index 8eda0f0b45..838d59e14e 100644 --- a/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilderExpression.cs +++ b/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilderExpression.cs @@ -5,7 +5,9 @@ using System.Linq; using System.Xml.Linq; using System.Xml.XPath; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Packaging; @@ -25,7 +27,9 @@ namespace Umbraco.Cms.Infrastructure.Packaging private readonly MediaUrlGeneratorCollection _mediaUrlGenerators; private readonly IPackagingService _packagingService; private readonly IShortStringHelper _shortStringHelper; - private bool _executed; + private readonly PackageMigrationSettings _packageMigrationSettings; + + private bool _executed; public ImportPackageBuilderExpression( IPackagingService packagingService, @@ -34,7 +38,8 @@ namespace Umbraco.Cms.Infrastructure.Packaging MediaUrlGeneratorCollection mediaUrlGenerators, IShortStringHelper shortStringHelper, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, - IMigrationContext context) : base(context) + IMigrationContext context, + IOptions packageMigrationSettings) : base(context) { _packagingService = packagingService; _mediaService = mediaService; @@ -42,6 +47,7 @@ namespace Umbraco.Cms.Infrastructure.Packaging _mediaUrlGenerators = mediaUrlGenerators; _shortStringHelper = shortStringHelper; _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider; + _packageMigrationSettings = packageMigrationSettings.Value; } /// @@ -59,6 +65,7 @@ namespace Umbraco.Cms.Infrastructure.Packaging } _executed = true; + Context.BuildingExpression = false; if (EmbeddedResourceMigrationType == null && PackageDataManifest == null) @@ -67,6 +74,12 @@ namespace Umbraco.Cms.Infrastructure.Packaging $"Nothing to execute, neither {nameof(EmbeddedResourceMigrationType)} or {nameof(PackageDataManifest)} has been set."); } + if (!_packageMigrationSettings.RunSchemaAndContentMigrations) + { + Logger.LogInformation("Skipping import of embedded schema file, due to configuration"); + return; + } + InstallationSummary installationSummary; if (EmbeddedResourceMigrationType != null) { diff --git a/src/Umbraco.Infrastructure/Packaging/PackageMigrationBase.cs b/src/Umbraco.Infrastructure/Packaging/PackageMigrationBase.cs index 3166cdbd4f..54b96955d4 100644 --- a/src/Umbraco.Infrastructure/Packaging/PackageMigrationBase.cs +++ b/src/Umbraco.Infrastructure/Packaging/PackageMigrationBase.cs @@ -1,12 +1,17 @@ +using System; +using System.ComponentModel; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Migrations; +using Umbraco.Cms.Web.Common.DependencyInjection; namespace Umbraco.Cms.Infrastructure.Packaging { - public abstract class PackageMigrationBase : MigrationBase { private readonly IPackagingService _packagingService; @@ -15,6 +20,7 @@ namespace Umbraco.Cms.Infrastructure.Packaging private readonly MediaUrlGeneratorCollection _mediaUrlGenerators; private readonly IShortStringHelper _shortStringHelper; private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; + private readonly IOptions _packageMigrationsSettings; public PackageMigrationBase( IPackagingService packagingService, @@ -23,7 +29,8 @@ namespace Umbraco.Cms.Infrastructure.Packaging MediaUrlGeneratorCollection mediaUrlGenerators, IShortStringHelper shortStringHelper, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, - IMigrationContext context) + IMigrationContext context, + IOptions packageMigrationsSettings) : base(context) { _packagingService = packagingService; @@ -32,6 +39,29 @@ namespace Umbraco.Cms.Infrastructure.Packaging _mediaUrlGenerators = mediaUrlGenerators; _shortStringHelper = shortStringHelper; _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider; + _packageMigrationsSettings = packageMigrationsSettings; + } + + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("Use ctor with all params")] + public PackageMigrationBase( + IPackagingService packagingService, + IMediaService mediaService, + MediaFileManager mediaFileManager, + MediaUrlGeneratorCollection mediaUrlGenerators, + IShortStringHelper shortStringHelper, + IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, + IMigrationContext context) + : this( + packagingService, + mediaService, + mediaFileManager, + mediaUrlGenerators, + shortStringHelper, + contentTypeBaseServiceProvider, + context, + StaticServiceProvider.Instance.GetRequiredService>()) + { } public IImportPackageBuilder ImportPackage => BeginBuild( @@ -42,7 +72,8 @@ namespace Umbraco.Cms.Infrastructure.Packaging _mediaUrlGenerators, _shortStringHelper, _contentTypeBaseServiceProvider, - Context)); + Context, + _packageMigrationsSettings)); } } diff --git a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs index 86f4e070c2..4ec87dfde7 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs @@ -1,9 +1,9 @@ using System; +using System.ComponentModel; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Configuration; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Exceptions; @@ -13,7 +13,9 @@ using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Runtime; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.Persistence; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; +using ComponentCollection = Umbraco.Cms.Core.Composing.ComponentCollection; namespace Umbraco.Cms.Infrastructure.Runtime { @@ -29,6 +31,7 @@ namespace Umbraco.Cms.Infrastructure.Runtime private readonly IEventAggregator _eventAggregator; private readonly IHostingEnvironment _hostingEnvironment; private readonly IUmbracoVersion _umbracoVersion; + private readonly IServiceProvider _serviceProvider; private CancellationToken _cancellationToken; /// @@ -44,7 +47,8 @@ namespace Umbraco.Cms.Infrastructure.Runtime IUmbracoDatabaseFactory databaseFactory, IEventAggregator eventAggregator, IHostingEnvironment hostingEnvironment, - IUmbracoVersion umbracoVersion) + IUmbracoVersion umbracoVersion, + IServiceProvider serviceProvider) { State = state; _loggerFactory = loggerFactory; @@ -56,9 +60,40 @@ namespace Umbraco.Cms.Infrastructure.Runtime _eventAggregator = eventAggregator; _hostingEnvironment = hostingEnvironment; _umbracoVersion = umbracoVersion; + _serviceProvider = serviceProvider; _logger = _loggerFactory.CreateLogger(); } + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete] + public CoreRuntime( + ILoggerFactory loggerFactory, + IRuntimeState state, + ComponentCollection components, + IApplicationShutdownRegistry applicationShutdownRegistry, + IProfilingLogger profilingLogger, + IMainDom mainDom, + IUmbracoDatabaseFactory databaseFactory, + IEventAggregator eventAggregator, + IHostingEnvironment hostingEnvironment, + IUmbracoVersion umbracoVersion + ):this( + loggerFactory, + state, + components, + applicationShutdownRegistry, + profilingLogger, + mainDom, + databaseFactory, + eventAggregator, + hostingEnvironment, + umbracoVersion, + null + ) + { + + } + /// /// Gets the state of the Umbraco runtime. /// @@ -76,6 +111,7 @@ namespace Umbraco.Cms.Infrastructure.Runtime { _cancellationToken = cancellationToken; StaticApplicationLogging.Initialize(_loggerFactory); + StaticServiceProvider.Instance = _serviceProvider; AppDomain.CurrentDomain.UnhandledException += (_, args) => { diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoApplicationServicesCapture.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoApplicationServicesCapture.cs deleted file mode 100644 index fa5adf7aeb..0000000000 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoApplicationServicesCapture.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; - -namespace Umbraco.Cms.Web.Common.DependencyInjection -{ - /// - /// A registered to automatically capture application services - /// - internal class UmbracoApplicationServicesCapture : IStartupFilter - { - /// - public Action Configure(Action next) => - app => - { - StaticServiceProvider.Instance = app.ApplicationServices; - next(app); - }; - } -} diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs index ef98553ba2..2d584f198e 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs @@ -123,10 +123,6 @@ namespace Umbraco.Extensions config, profiler); - // adds the umbraco startup filter which will call UseUmbraco early on before - // other start filters are applied (depending on the ordering of IStartupFilters in DI). - services.AddTransient(); - return new UmbracoBuilder(services, config, typeLoader, loggerFactory, profiler, appCaches, tempHostingEnvironment); } diff --git a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj index fcd62febf4..537df5aab4 100644 --- a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj +++ b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj @@ -48,5 +48,4 @@ <_Parameter1>Umbraco.Tests.UnitTests - From e786491a0c46047f4c602d44a2af918a2d027b38 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 6 Oct 2021 14:38:50 +0200 Subject: [PATCH 17/18] Added CopyRazorGenerateFilesToPublishDirectory to the template (#11301) --- build/templates/UmbracoProject/UmbracoProject.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build/templates/UmbracoProject/UmbracoProject.csproj b/build/templates/UmbracoProject/UmbracoProject.csproj index efc5425728..99e72bae0f 100644 --- a/build/templates/UmbracoProject/UmbracoProject.csproj +++ b/build/templates/UmbracoProject/UmbracoProject.csproj @@ -36,6 +36,10 @@ + + true + + false From bef1ccedca45b16a1a51178c45c2bec3302caf53 Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Wed, 6 Oct 2021 20:11:06 +0200 Subject: [PATCH 18/18] Exposes the entity containers (folders) created during a package installation in the summary available from the ImportedPackageNotification. (#11303) * Exposes the entity containers (folders) created during a package installation in the summary available from the ImportedPackageNotification. * Restored original constructors. * Refactored to use out parameters for tracking installed entity containers. * Removed unnecessary variable initialization. --- .../Packaging/InstallationSummary.cs | 2 + .../Packaging/PackageDataInstallation.cs | 80 ++++++++++++++++--- 2 files changed, 71 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Core/Packaging/InstallationSummary.cs b/src/Umbraco.Core/Packaging/InstallationSummary.cs index 2aa74474d1..42ac9f7ef0 100644 --- a/src/Umbraco.Core/Packaging/InstallationSummary.cs +++ b/src/Umbraco.Core/Packaging/InstallationSummary.cs @@ -32,6 +32,7 @@ namespace Umbraco.Cms.Core.Packaging public IEnumerable PartialViewsInstalled { get; set; } = Enumerable.Empty(); public IEnumerable ContentInstalled { get; set; } = Enumerable.Empty(); public IEnumerable MediaInstalled { get; set; } = Enumerable.Empty(); + public IEnumerable EntityContainersInstalled { get; set; } = Enumerable.Empty(); public override string ToString() { @@ -77,6 +78,7 @@ namespace Umbraco.Cms.Core.Packaging WriteCount("Stylesheets installed: ", StylesheetsInstalled); WriteCount("Scripts installed: ", ScriptsInstalled); WriteCount("Partial views installed: ", PartialViewsInstalled); + WriteCount("Entity containers installed: ", EntityContainersInstalled); WriteCount("Content items installed: ", ContentInstalled); WriteCount("Media items installed: ", MediaInstalled, false); diff --git a/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs b/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs index c691b74a0c..5c9942f945 100644 --- a/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs +++ b/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs @@ -91,19 +91,25 @@ namespace Umbraco.Cms.Infrastructure.Packaging var installationSummary = new InstallationSummary(compiledPackage.Name) { Warnings = compiledPackage.Warnings, - DataTypesInstalled = ImportDataTypes(compiledPackage.DataTypes.ToList(), userId), + DataTypesInstalled = ImportDataTypes(compiledPackage.DataTypes.ToList(), userId, out IEnumerable dataTypeEntityContainersInstalled), LanguagesInstalled = ImportLanguages(compiledPackage.Languages, userId), DictionaryItemsInstalled = ImportDictionaryItems(compiledPackage.DictionaryItems, userId), MacrosInstalled = ImportMacros(compiledPackage.Macros, userId), MacroPartialViewsInstalled = ImportMacroPartialViews(compiledPackage.MacroPartialViews, userId), TemplatesInstalled = ImportTemplates(compiledPackage.Templates.ToList(), userId), - DocumentTypesInstalled = ImportDocumentTypes(compiledPackage.DocumentTypes, userId), - MediaTypesInstalled = ImportMediaTypes(compiledPackage.MediaTypes, userId), + DocumentTypesInstalled = ImportDocumentTypes(compiledPackage.DocumentTypes, userId, out IEnumerable documentTypeEntityContainersInstalled), + MediaTypesInstalled = ImportMediaTypes(compiledPackage.MediaTypes, userId, out IEnumerable mediaTypeEntityContainersInstalled), StylesheetsInstalled = ImportStylesheets(compiledPackage.Stylesheets, userId), ScriptsInstalled = ImportScripts(compiledPackage.Scripts, userId), PartialViewsInstalled = ImportPartialViews(compiledPackage.PartialViews, userId) }; + var entityContainersInstalled = new List(); + entityContainersInstalled.AddRange(dataTypeEntityContainersInstalled); + entityContainersInstalled.AddRange(documentTypeEntityContainersInstalled); + entityContainersInstalled.AddRange(mediaTypeEntityContainersInstalled); + installationSummary.EntityContainersInstalled = entityContainersInstalled; + // We need a reference to the imported doc types to continue var importedDocTypes = installationSummary.DocumentTypesInstalled.ToDictionary(x => x.Alias, x => x); var importedMediaTypes = installationSummary.MediaTypesInstalled.ToDictionary(x => x.Alias, x => x); @@ -116,6 +122,7 @@ namespace Umbraco.Cms.Infrastructure.Packaging return installationSummary; } } + /// /// Imports and saves package xml as /// @@ -123,7 +130,17 @@ namespace Umbraco.Cms.Infrastructure.Packaging /// Optional id of the User performing the operation. Default is zero (admin). /// An enumerable list of generated ContentTypes public IReadOnlyList ImportMediaTypes(IEnumerable docTypeElements, int userId) - => ImportDocumentTypes(docTypeElements.ToList(), true, userId, _mediaTypeService); + => ImportMediaTypes(docTypeElements, userId, out _); + + /// + /// Imports and saves package xml as + /// + /// Xml to import + /// Optional id of the User performing the operation. Default is zero (admin). + /// Collection of entity containers installed by the package to be populated with those created in installing data types. + /// An enumerable list of generated ContentTypes + public IReadOnlyList ImportMediaTypes(IEnumerable docTypeElements, int userId, out IEnumerable entityContainersInstalled) + => ImportDocumentTypes(docTypeElements.ToList(), true, userId, _mediaTypeService, out entityContainersInstalled); #endregion @@ -408,7 +425,7 @@ namespace Umbraco.Cms.Infrastructure.Packaging #region DocumentTypes public IReadOnlyList ImportDocumentType(XElement docTypeElement, int userId) - => ImportDocumentTypes(new[] { docTypeElement }, userId); + => ImportDocumentTypes(new[] { docTypeElement }, userId, out _); /// /// Imports and saves package xml as @@ -417,7 +434,17 @@ namespace Umbraco.Cms.Infrastructure.Packaging /// Optional id of the User performing the operation. Default is zero (admin). /// An enumerable list of generated ContentTypes public IReadOnlyList ImportDocumentTypes(IEnumerable docTypeElements, int userId) - => ImportDocumentTypes(docTypeElements.ToList(), true, userId, _contentTypeService); + => ImportDocumentTypes(docTypeElements.ToList(), true, userId, _contentTypeService, out _); + + /// + /// Imports and saves package xml as + /// + /// Xml to import + /// Optional id of the User performing the operation. Default is zero (admin). + /// Collection of entity containers installed by the package to be populated with those created in installing data types. + /// An enumerable list of generated ContentTypes + public IReadOnlyList ImportDocumentTypes(IEnumerable docTypeElements, int userId, out IEnumerable entityContainersInstalled) + => ImportDocumentTypes(docTypeElements.ToList(), true, userId, _contentTypeService, out entityContainersInstalled); /// /// Imports and saves package xml as @@ -428,6 +455,18 @@ namespace Umbraco.Cms.Infrastructure.Packaging /// An enumerable list of generated ContentTypes public IReadOnlyList ImportDocumentTypes(IReadOnlyCollection unsortedDocumentTypes, bool importStructure, int userId, IContentTypeBaseService service) where T : class, IContentTypeComposition + => ImportDocumentTypes(unsortedDocumentTypes, importStructure, userId, service); + + /// + /// 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). + /// Collection of entity containers installed by the package to be populated with those created in installing data types. + /// An enumerable list of generated ContentTypes + public IReadOnlyList ImportDocumentTypes(IReadOnlyCollection unsortedDocumentTypes, bool importStructure, int userId, IContentTypeBaseService service, out IEnumerable entityContainersInstalled) + where T : class, IContentTypeComposition { var importedContentTypes = new Dictionary(); @@ -436,7 +475,7 @@ namespace Umbraco.Cms.Infrastructure.Packaging var graph = new TopoGraph>(x => x.Key, x => x.Dependencies); var isSingleDocTypeImport = unsortedDocumentTypes.Count == 1; - var importedFolders = CreateContentTypeFolderStructure(unsortedDocumentTypes); + var importedFolders = CreateContentTypeFolderStructure(unsortedDocumentTypes, out entityContainersInstalled); if (isSingleDocTypeImport == false) { @@ -532,9 +571,10 @@ namespace Umbraco.Cms.Infrastructure.Packaging return list; } - private Dictionary CreateContentTypeFolderStructure(IEnumerable unsortedDocumentTypes) + private Dictionary CreateContentTypeFolderStructure(IEnumerable unsortedDocumentTypes, out IEnumerable entityContainersInstalled) { var importedFolders = new Dictionary(); + var trackEntityContainersInstalled = new List(); foreach (var documentType in unsortedDocumentTypes) { var foldersAttribute = documentType.Attribute("Folders"); @@ -578,8 +618,10 @@ namespace Umbraco.Cms.Infrastructure.Packaging _logger.LogError(tryCreateFolder.Exception, "Could not create folder: {FolderName}", rootFolder); throw tryCreateFolder.Exception; } + var rootFolderId = tryCreateFolder.Result.Entity.Id; current = _contentTypeService.GetContainer(rootFolderId); + trackEntityContainersInstalled.Add(current); } importedFolders.Add(alias, current.Id); @@ -589,11 +631,13 @@ namespace Umbraco.Cms.Infrastructure.Packaging var folderName = WebUtility.UrlDecode(folders[i]); Guid? folderKey = (folderKeys.Length == folders.Length) ? folderKeys[i] : null; current = CreateContentTypeChildFolder(folderName, folderKey ?? Guid.NewGuid(), current); + trackEntityContainersInstalled.Add(current); importedFolders[alias] = current.Id; } } } + entityContainersInstalled = trackEntityContainersInstalled; return importedFolders; } @@ -1012,10 +1056,20 @@ namespace Umbraco.Cms.Infrastructure.Packaging /// Optional id of the user /// An enumerable list of generated DataTypeDefinitions public IReadOnlyList ImportDataTypes(IReadOnlyCollection dataTypeElements, int userId) + => ImportDataTypes(dataTypeElements, userId, out _); + + /// + /// Imports and saves package xml as + /// + /// Xml to import + /// Optional id of the user + /// Collection of entity containers installed by the package to be populated with those created in installing data types. + /// An enumerable list of generated DataTypeDefinitions + public IReadOnlyList ImportDataTypes(IReadOnlyCollection dataTypeElements, int userId, out IEnumerable entityContainersInstalled) { var dataTypes = new List(); - var importedFolders = CreateDataTypeFolderStructure(dataTypeElements); + var importedFolders = CreateDataTypeFolderStructure(dataTypeElements, out entityContainersInstalled); foreach (var dataTypeElement in dataTypeElements) { @@ -1072,9 +1126,10 @@ namespace Umbraco.Cms.Infrastructure.Packaging return dataTypes; } - private Dictionary CreateDataTypeFolderStructure(IEnumerable datatypeElements) + private Dictionary CreateDataTypeFolderStructure(IEnumerable datatypeElements, out IEnumerable entityContainersInstalled) { var importedFolders = new Dictionary(); + var trackEntityContainersInstalled = new List(); foreach (var datatypeElement in datatypeElements) { var foldersAttribute = datatypeElement.Attribute("Folders"); @@ -1103,7 +1158,9 @@ namespace Umbraco.Cms.Infrastructure.Packaging _logger.LogError(tryCreateFolder.Exception, "Could not create folder: {FolderName}", rootFolder); throw tryCreateFolder.Exception; } + current = _dataTypeService.GetContainer(tryCreateFolder.Result.Entity.Id); + trackEntityContainersInstalled.Add(current); } importedFolders.Add(name, current.Id); @@ -1113,11 +1170,12 @@ namespace Umbraco.Cms.Infrastructure.Packaging var folderName = WebUtility.UrlDecode(folders[i]); Guid? folderKey = (folderKeys.Length == folders.Length) ? folderKeys[i] : null; current = CreateDataTypeChildFolder(folderName, folderKey ?? Guid.NewGuid(), current); + trackEntityContainersInstalled.Add(current); importedFolders[name] = current.Id; } } } - + entityContainersInstalled = trackEntityContainersInstalled; return importedFolders; }