From 54d1e9d5e0521561cb2de1950720dd3a5ea733d8 Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska Date: Tue, 8 Dec 2020 15:36:37 +0100 Subject: [PATCH 01/18] Removing AllowsCultureVariation from ContentController, so we can get rid of OverrideAuthorization attribute --- .../Controllers/ContentController.cs | 16 +--------------- .../Controllers/ContentTypeController.cs | 13 +++++++++++++ .../src/common/resources/content.resource.js | 11 +---------- .../src/common/resources/contenttype.resource.js | 9 +++++++++ .../src/navigation.controller.js | 4 ++-- 5 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs index a6f6bc8fb8..b6dcdce14d 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -127,20 +127,6 @@ namespace Umbraco.Web.BackOffice.Controllers _logger = loggerFactory.CreateLogger(); _allLangs = new Lazy>(() => _localizationService.GetAllLanguages().ToDictionary(x => x.IsoCode, x => x, StringComparer.InvariantCultureIgnoreCase)); - - } - - /// - /// Returns true if any content types have culture variation enabled - /// - /// - [HttpGet] - // TODO: We need to move this since we are going to delete OverrideAuthorization - [Authorize(Policy = AuthorizationPolicies.BackOfficeAccess), OverrideAuthorization] - public bool AllowsCultureVariation() - { - var contentTypes = _contentTypeService.GetAll(); - return contentTypes.Any(contentType => contentType.VariesByCulture()); } /// diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs index f11ed373cd..9a9404f764 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs @@ -255,6 +255,19 @@ namespace Umbraco.Web.BackOffice.Controllers }); return Ok(result); } + + /// + /// Returns true if any content types have culture variation enabled + /// + /// + [HttpGet] + [Authorize(Policy = AuthorizationPolicies.BackOfficeAccess)] + public bool AllowsCultureVariation() + { + IEnumerable contentTypes = _contentTypeService.GetAll(); + return contentTypes.Any(contentType => contentType.VariesByCulture()); + } + /// /// Returns where a particular composition has been used /// This has been wrapped in a dto instead of simple parameters to support having multiple parameters in post request body diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js index 3dfbeade4f..b6f5d99d9f 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js @@ -42,16 +42,7 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { return { - allowsCultureVariation: function () { - return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "contentApiBaseUrl", - "AllowsCultureVariation")), - 'Failed to retrieve variant content types'); - }, - - savePermissions: function (saveModel) { + savePermissions: function (saveModel) { if (!saveModel) { throw "saveModel cannot be null"; } diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js index 97bebef062..4a8c65c322 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js @@ -361,6 +361,15 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter, loca "HasContentNodes", [{ id: id }])), 'Failed to retrieve indication for whether content type with id ' + id + ' has associated content nodes'); + }, + + allowsCultureVariation: function () { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "contentTypeApiBaseUrl", + "AllowsCultureVariation")), + 'Failed to retrieve variant content types'); } }; } diff --git a/src/Umbraco.Web.UI.Client/src/navigation.controller.js b/src/Umbraco.Web.UI.Client/src/navigation.controller.js index a383c2d44a..37f6ff9733 100644 --- a/src/Umbraco.Web.UI.Client/src/navigation.controller.js +++ b/src/Umbraco.Web.UI.Client/src/navigation.controller.js @@ -9,7 +9,7 @@ * * @param {navigationService} navigationService A reference to the navigationService */ -function NavigationController($scope, $rootScope, $location, $log, $q, $routeParams, $timeout, $cookies, treeService, appState, navigationService, keyboardService, historyService, eventsService, angularHelper, languageResource, contentResource, editorState) { +function NavigationController($scope, $rootScope, $location, $log, $q, $routeParams, $timeout, $cookies, treeService, appState, navigationService, keyboardService, historyService, eventsService, angularHelper, languageResource, contentTypeResource, editorState) { //this is used to trigger the tree to start loading once everything is ready var treeInitPromise = $q.defer(); @@ -380,7 +380,7 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar */ function loadLanguages() { - return contentResource.allowsCultureVariation().then(function (b) { + return contentTypeResource.allowsCultureVariation().then(function (b) { if (b === true) { return languageResource.getAll(); } else { From 940084e342de46f77b6beee32c549bed7f57405b Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska Date: Tue, 8 Dec 2020 15:37:53 +0100 Subject: [PATCH 02/18] Removing OverrideAuthorization attribute and related --- .../Extensions/UmbracoBuilderExtensions.cs | 1 - .../Filters/OverrideAuthorizationAttribute.cs | 25 ------------- .../OverrideAuthorizationFilterProvider.cs | 35 ------------------- src/Umbraco.Web/Umbraco.Web.csproj | 1 - .../EnableOverrideAuthorizationAttribute.cs | 16 --------- .../OverridableAuthorizationAttribute.cs | 4 +-- 6 files changed, 2 insertions(+), 80 deletions(-) delete mode 100644 src/Umbraco.Web.BackOffice/Filters/OverrideAuthorizationAttribute.cs delete mode 100644 src/Umbraco.Web.BackOffice/Filters/OverrideAuthorizationFilterProvider.cs delete mode 100644 src/Umbraco.Web/WebApi/Filters/EnableOverrideAuthorizationAttribute.cs diff --git a/src/Umbraco.Web.BackOffice/Extensions/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.BackOffice/Extensions/UmbracoBuilderExtensions.cs index 79c7a9d29a..336855dd1b 100644 --- a/src/Umbraco.Web.BackOffice/Extensions/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.BackOffice/Extensions/UmbracoBuilderExtensions.cs @@ -30,7 +30,6 @@ namespace Umbraco.Extensions public static IUmbracoBuilder AddBackOffice(this IUmbracoBuilder builder) { builder.Services.AddAntiforgery(); - builder.Services.AddSingleton(); builder.Services .AddAuthentication() // This just creates a builder, nothing more diff --git a/src/Umbraco.Web.BackOffice/Filters/OverrideAuthorizationAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/OverrideAuthorizationAttribute.cs deleted file mode 100644 index 3d7b68cc80..0000000000 --- a/src/Umbraco.Web.BackOffice/Filters/OverrideAuthorizationAttribute.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using Microsoft.AspNetCore.Mvc.Filters; - -namespace Umbraco.Web.BackOffice.Filters -{ - // TODO: This should probably be deleted, anything requiring this should move to a different controller - public class OverrideAuthorizationAttribute : ActionFilterAttribute - { - /// - /// Ensures a special type of authorization filter is ignored. Defaults to . - /// - /// The type of authorization filter to override. if null then is used. - /// - /// https://stackoverflow.com/questions/33558095/overrideauthorizationattribute-in-asp-net-5 - /// - public OverrideAuthorizationAttribute(Type filtersToOverride = null) - { - FiltersToOverride = filtersToOverride ?? typeof(IAuthorizationFilter); - } - - public Type FiltersToOverride { get;} - - - } -} diff --git a/src/Umbraco.Web.BackOffice/Filters/OverrideAuthorizationFilterProvider.cs b/src/Umbraco.Web.BackOffice/Filters/OverrideAuthorizationFilterProvider.cs deleted file mode 100644 index 39f691e190..0000000000 --- a/src/Umbraco.Web.BackOffice/Filters/OverrideAuthorizationFilterProvider.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Linq; -using Microsoft.AspNetCore.Mvc.Filters; -using Umbraco.Core; - -namespace Umbraco.Web.BackOffice.Filters -{ - // TODO: This should be deleted, anything requiring this should move to a different controller - public class OverrideAuthorizationFilterProvider : IFilterProvider, IFilterMetadata - { - public void OnProvidersExecuted(FilterProviderContext context) - { - - } - - public void OnProvidersExecuting(FilterProviderContext context) - { - if (context.ActionContext.ActionDescriptor.FilterDescriptors != null) - { - //Does the action have any UmbracoAuthorizeFilter? - var overrideFilters = context.Results.Where(filterItem => filterItem.Filter is OverrideAuthorizationAttribute).ToArray(); - foreach (var overrideFilter in overrideFilters) - { - context.Results.RemoveAll(filterItem => - //Remove any filter for the type indicated in the UmbracoAuthorizeFilter attribute - filterItem.Descriptor.Filter.GetType() == ((OverrideAuthorizationAttribute)overrideFilter.Filter).FiltersToOverride && - //Remove filters with lower scope (ie controller) than the override filter (ie action method) - filterItem.Descriptor.Scope < overrideFilter.Descriptor.Scope); - } - } - } - - //all framework providers have negative orders, so ours will come later - public int Order => 1; - } -} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index e59eed272c..c2b8d8c38e 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -141,7 +141,6 @@ - diff --git a/src/Umbraco.Web/WebApi/Filters/EnableOverrideAuthorizationAttribute.cs b/src/Umbraco.Web/WebApi/Filters/EnableOverrideAuthorizationAttribute.cs deleted file mode 100644 index 358c9cc3b3..0000000000 --- a/src/Umbraco.Web/WebApi/Filters/EnableOverrideAuthorizationAttribute.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; - -namespace Umbraco.Web.WebApi.Filters -{ - /// - /// This allows for Action based auth attributes to override Class based auth attributes if they are specified - /// - /// - /// This attribute can be applied at the class level and will indicate to any class level auth attribute that inherits from OverridableAuthorizationAttribute - /// - [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)] - public sealed class EnableOverrideAuthorizationAttribute : Attribute - { - // TODO: we should remove this and use the System.Web.Http.OverrideAuthorizationAttribute which uses IOverrideFilter instead - } -} diff --git a/src/Umbraco.Web/WebApi/Filters/OverridableAuthorizationAttribute.cs b/src/Umbraco.Web/WebApi/Filters/OverridableAuthorizationAttribute.cs index 070bb53fb3..243611cca0 100644 --- a/src/Umbraco.Web/WebApi/Filters/OverridableAuthorizationAttribute.cs +++ b/src/Umbraco.Web/WebApi/Filters/OverridableAuthorizationAttribute.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Web.Http; using System.Web.Http.Controllers; @@ -40,7 +40,7 @@ namespace Umbraco.Web.WebApi.Filters //if the controller is allowing authorization to be overridden at the action level and there are action level authorization attributes // then exit and let the action level auth attribute(s) execute. - if (actionContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes().Any() + if (actionContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes().Any() && actionAttributes.Any()) { return; From b6cc9f076a9b55dbe391f6faea3c851b9519adbe Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska Date: Tue, 8 Dec 2020 15:39:33 +0100 Subject: [PATCH 03/18] Removing conn string from appsettings --- src/Umbraco.Web.UI.NetCore/appsettings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.NetCore/appsettings.json b/src/Umbraco.Web.UI.NetCore/appsettings.json index 43ae07d5d6..29682186f6 100644 --- a/src/Umbraco.Web.UI.NetCore/appsettings.json +++ b/src/Umbraco.Web.UI.NetCore/appsettings.json @@ -1,6 +1,6 @@ { "ConnectionStrings": { - "umbracoDbDSN": "Server=(LocalDB)\\Umbraco;Database=NetCore;Integrated Security=true" + "umbracoDbDSN": "" }, "Serilog": { "MinimumLevel": { @@ -71,4 +71,4 @@ } } } -} \ No newline at end of file +} From c858ededf4978cd0b45aea1dbe2cb9e86872aea0 Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska Date: Tue, 8 Dec 2020 16:05:04 +0100 Subject: [PATCH 04/18] Removing OverridableAuthorizationAttribute as it is no longer needed --- src/Umbraco.Web/Umbraco.Web.csproj | 1 - .../OverridableAuthorizationAttribute.cs | 52 ------------------- 2 files changed, 53 deletions(-) delete mode 100644 src/Umbraco.Web/WebApi/Filters/OverridableAuthorizationAttribute.cs diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index c2b8d8c38e..54f02ff004 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -141,7 +141,6 @@ - diff --git a/src/Umbraco.Web/WebApi/Filters/OverridableAuthorizationAttribute.cs b/src/Umbraco.Web/WebApi/Filters/OverridableAuthorizationAttribute.cs deleted file mode 100644 index 243611cca0..0000000000 --- a/src/Umbraco.Web/WebApi/Filters/OverridableAuthorizationAttribute.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Linq; -using System.Web.Http; -using System.Web.Http.Controllers; - -namespace Umbraco.Web.WebApi.Filters -{ - // TODO: remove this since we don't need it, see notes in EnableOverrideAuthorizationAttribute - - /// - /// Abstract auth filter class that can be used to enable overriding class auth filters at the action level - /// - /// - /// To enable a class auth filter to be overridden by an action auth filter the EnableOverrideAuthorizationAttribute can be applied - /// to the class. - /// - public abstract class OverridableAuthorizationAttribute : AuthorizeAttribute - { - /// - /// If the controller has an EnabledOverrideAuthorizationAttribute attribute specified and the action has any AuthorizeAttribute - /// specified then use the action's auth attribute instead of this one - /// - /// The context. - /// The context parameter is null. - public override void OnAuthorization(HttpActionContext actionContext) - { - if (actionContext == null) throw new ArgumentNullException("actionContext"); - - var actionAttributes = actionContext.ActionDescriptor.GetCustomAttributes(); - - //if 'this' authorize attribute exists in the current collection then continue as per normal... this is because 'this' attribute instance - // is obviously assigned at an Action level and therefore it's already executing - - if (actionAttributes.Any(x => Equals(x, this))) - { - base.OnAuthorization(actionContext); - return; - } - - //if the controller is allowing authorization to be overridden at the action level and there are action level authorization attributes - // then exit and let the action level auth attribute(s) execute. - - if (actionContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes().Any() - && actionAttributes.Any()) - { - return; - } - - base.OnAuthorization(actionContext); - } - } -} From 0151c435f162b3cdbbc2a95a16f225df619a2caa Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 16 Dec 2020 22:26:47 +0100 Subject: [PATCH 05/18] Netcore: Package support for media + media types (#9547) * Add support for media when installing a package * clean up * Fix tests * Add support for media when installing a package * clean up * Fix tests * moved tests + test data * Migrated package tests + resources * Fix issue with media picker on package page, was empty after save. * Added missing files * Fix casing issue of resources * Added test for media * Fix tests for linux * Fix test * Fix issue with move media.. * Fix issue with adding files to packages * Add MediaType permissions. * Fix test * Fix test * Retry flaky tests, and added TODOs to fix those * new attempt to fix test Co-authored-by: Mole --- .../.template.config/template.json | 2 +- .../Constants-SystemDirectories.cs | 2 +- .../Models/Packaging/CompiledPackage.cs | 5 +- ...ument.cs => CompiledPackageContentBase.cs} | 16 +- .../Packaging/CompiledPackageXmlParser.cs | 5 +- .../Packaging/InstallationSummary.cs | 4 +- .../Packaging/PackageDefinition.cs | 10 +- .../Packaging/PackageDefinitionXmlParser.cs | 11 +- .../Packaging/PackagesRepository.cs | 63 ++- src/Umbraco.Core/Services/IContentService.cs | 3 +- .../Services/IContentServiceBase.cs | 11 +- .../Services/IContentTypeServiceBase.cs | 1 - src/Umbraco.Core/Services/IMediaService.cs | 3 +- .../CompositionExtensions/Services.cs | 7 +- .../Packaging/PackageDataInstallation.cs | 296 ++++++++++---- .../Runtime/CoreInitialComposer.cs | 3 + .../Services/Implement/ContentService.cs | 11 +- .../Services/Implement/EntityXmlSerializer.cs | 8 +- .../Services/Implement/MediaService.cs | 6 +- .../Packages/Document_Type_Picker_1.1.umb | Bin .../Packages/Document_Type_Picker_1.1.zip | Bin .../TestData}/Packages/Hello_1.0.0.zip | Bin .../CreatedPackagesRepositoryTests.cs | 10 +- .../Packaging/PackageDataInstallationTests.cs | 138 ++++--- .../Packaging/PackageInstallationTest.cs | 81 +--- .../Services/EntityXmlSerializerTests.cs | 2 +- .../CheckboxList-Content-Package.xml | 0 .../CompositionsTestPackage-Random.xml | 0 .../Importing/CompositionsTestPackage.xml | 0 .../Services/Importing/Fanoe-Package.xml | 0 .../Importing/ImportResources.Designer.cs | 324 ++++++++++++++- .../Services/Importing/ImportResources.resx | 90 ++-- .../Importing/InheritedDocTypes-Package.xml | 0 .../Importing/MediaTypesAndMedia-Package.xml | 158 ++++++++ .../Services/Importing/SingleDocType.xml | 0 .../Importing/StandardMvc-Package.xml | 0 .../Importing/TemplateOnly-Package.xml | 0 .../TemplateOnly-Updated-Package.xml | 0 .../Services/Importing/XsltSearch-Package.xml | 0 .../Services/Importing/uBlogsy-Package.xml | 0 .../Umbraco.Tests.Integration.csproj | 41 +- .../SnapDictionaryTests.cs | 4 + .../LegacyXmlPublishedCache/XmlStore.cs | 5 +- .../Packages/Document_Type_Picker_1.1.umb | Bin 6147 -> 0 bytes .../Importing/ImportResources.Designer.cs | 383 ------------------ .../Services/Importing/ImportResources.resx | 157 ------- src/Umbraco.Tests/Umbraco.Tests.csproj | 28 -- .../Controllers/ContentTypeController.cs | 50 +-- .../Controllers/MediaController.cs | 2 +- .../Controllers/PackageController.cs | 4 +- .../AspNetCoreUmbracoApplicationLifetime.cs | 11 - .../src/views/packages/edit.controller.js | 99 +++-- .../src/views/packages/edit.html | 27 +- .../Umbraco.Web.UI.NetCore.csproj | 1 + .../umbraco/config/lang/en_us.xml | 1 + 55 files changed, 1130 insertions(+), 953 deletions(-) rename src/Umbraco.Core/Models/Packaging/{CompiledPackageDocument.cs => CompiledPackageContentBase.cs} (52%) rename src/Umbraco.Tests.Integration/{Umbraco.Core/Packaging => TestData}/Packages/Document_Type_Picker_1.1.umb (100%) rename src/{Umbraco.Tests/Packaging => Umbraco.Tests.Integration/TestData}/Packages/Document_Type_Picker_1.1.zip (100%) rename src/{Umbraco.Tests/Packaging => Umbraco.Tests.Integration/TestData}/Packages/Hello_1.0.0.zip (100%) rename src/{Umbraco.Tests => Umbraco.Tests.Integration/Umbraco.Core}/Packaging/PackageDataInstallationTests.cs (87%) rename src/{Umbraco.Tests => Umbraco.Tests.Integration/Umbraco.Core}/Packaging/PackageInstallationTest.cs (58%) rename src/{Umbraco.Tests => Umbraco.Tests.Integration/Umbraco.Infrastructure}/Services/Importing/CheckboxList-Content-Package.xml (100%) rename src/{Umbraco.Tests => Umbraco.Tests.Integration/Umbraco.Infrastructure}/Services/Importing/CompositionsTestPackage-Random.xml (100%) rename src/{Umbraco.Tests => Umbraco.Tests.Integration/Umbraco.Infrastructure}/Services/Importing/CompositionsTestPackage.xml (100%) rename src/{Umbraco.Tests => Umbraco.Tests.Integration/Umbraco.Infrastructure}/Services/Importing/Fanoe-Package.xml (100%) rename src/{Umbraco.Tests => Umbraco.Tests.Integration/Umbraco.Infrastructure}/Services/Importing/InheritedDocTypes-Package.xml (100%) create mode 100644 src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/MediaTypesAndMedia-Package.xml rename src/{Umbraco.Tests => Umbraco.Tests.Integration/Umbraco.Infrastructure}/Services/Importing/SingleDocType.xml (100%) rename src/{Umbraco.Tests => Umbraco.Tests.Integration/Umbraco.Infrastructure}/Services/Importing/StandardMvc-Package.xml (100%) rename src/{Umbraco.Tests => Umbraco.Tests.Integration/Umbraco.Infrastructure}/Services/Importing/TemplateOnly-Package.xml (100%) rename src/{Umbraco.Tests => Umbraco.Tests.Integration/Umbraco.Infrastructure}/Services/Importing/TemplateOnly-Updated-Package.xml (100%) rename src/{Umbraco.Tests => Umbraco.Tests.Integration/Umbraco.Infrastructure}/Services/Importing/XsltSearch-Package.xml (100%) rename src/{Umbraco.Tests => Umbraco.Tests.Integration/Umbraco.Infrastructure}/Services/Importing/uBlogsy-Package.xml (100%) delete mode 100644 src/Umbraco.Tests/Packaging/Packages/Document_Type_Picker_1.1.umb delete mode 100644 src/Umbraco.Tests/Services/Importing/ImportResources.Designer.cs delete mode 100644 src/Umbraco.Tests/Services/Importing/ImportResources.resx diff --git a/build/templates/UmbracoSolution/.template.config/template.json b/build/templates/UmbracoSolution/.template.config/template.json index 04f77a556e..26d2358639 100644 --- a/build/templates/UmbracoSolution/.template.config/template.json +++ b/build/templates/UmbracoSolution/.template.config/template.json @@ -15,7 +15,7 @@ "version": { "type": "parameter", "datatype": "string", - "defaultValue": "0.5.0-alpha003", + "defaultValue": "0.5.0-alpha*", "description": "The version of Umbraco to load using NuGet", "replaces": "UMBRACO_VERSION_FROM_TEMPLATE" }, diff --git a/src/Umbraco.Core/Constants-SystemDirectories.cs b/src/Umbraco.Core/Constants-SystemDirectories.cs index 6145f4190b..464896edd9 100644 --- a/src/Umbraco.Core/Constants-SystemDirectories.cs +++ b/src/Umbraco.Core/Constants-SystemDirectories.cs @@ -18,7 +18,7 @@ namespace Umbraco.Core /// /// This is not the same as the Umbraco web folder which is configurable for serving front-end files. /// - public const string Umbraco = "~/Umbraco"; + public const string Umbraco = "~/umbraco"; /// /// The Umbraco data folder in the content root diff --git a/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs b/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs index be514eaf44..cf48d6ac53 100644 --- a/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs +++ b/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Xml.Linq; namespace Umbraco.Core.Models.Packaging @@ -40,6 +39,8 @@ namespace Umbraco.Core.Models.Packaging public IEnumerable Languages { get; set; } // TODO: make strongly typed public IEnumerable DictionaryItems { get; set; } // TODO: make strongly typed public IEnumerable DocumentTypes { get; set; } // TODO: make strongly typed - public IEnumerable Documents { get; set; } + public IEnumerable MediaTypes { get; set; } // TODO: make strongly typed + public IEnumerable Documents { get; set; } + public IEnumerable Media { get; set; } } } diff --git a/src/Umbraco.Core/Models/Packaging/CompiledPackageDocument.cs b/src/Umbraco.Core/Models/Packaging/CompiledPackageContentBase.cs similarity index 52% rename from src/Umbraco.Core/Models/Packaging/CompiledPackageDocument.cs rename to src/Umbraco.Core/Models/Packaging/CompiledPackageContentBase.cs index c41966dfe1..7b668796a4 100644 --- a/src/Umbraco.Core/Models/Packaging/CompiledPackageDocument.cs +++ b/src/Umbraco.Core/Models/Packaging/CompiledPackageContentBase.cs @@ -1,20 +1,18 @@ -using System; using System.Xml.Linq; namespace Umbraco.Core.Models.Packaging { - public class CompiledPackageDocument + /// + /// Compiled representation of a content base (Document or Media) + /// + public class CompiledPackageContentBase { - 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 + public static CompiledPackageContentBase Create(XElement xml) => + new CompiledPackageContentBase { XmlData = xml, ImportMode = xml.AttributeValue("importMode") }; - } public string ImportMode { get; set; } //this is never used @@ -23,4 +21,4 @@ namespace Umbraco.Core.Models.Packaging /// public XElement XmlData { 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 b4cd2bd03f..be3f38cb22 100644 --- a/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs +++ b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs @@ -6,7 +6,6 @@ using System.Xml.Linq; using Microsoft.Extensions.Options; using Umbraco.Core.Configuration.Models; using Umbraco.Core.Models.Packaging; -using File = System.IO.File; namespace Umbraco.Core.Packaging { @@ -64,7 +63,9 @@ 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")?.Select(CompiledPackageDocument.Create) ?? Enumerable.Empty(), + MediaTypes = xml.Root.Element("MediaTypes")?.Elements("MediaType") ?? Enumerable.Empty(), + Documents = xml.Root.Element("Documents")?.Elements("DocumentSet")?.Select(CompiledPackageContentBase.Create) ?? Enumerable.Empty(), + Media = xml.Root.Element("MediaItems")?.Elements()?.Select(CompiledPackageContentBase.Create) ?? Enumerable.Empty(), }; def.Warnings = GetPreInstallWarnings(def, applicationRootFolder); diff --git a/src/Umbraco.Core/Packaging/InstallationSummary.cs b/src/Umbraco.Core/Packaging/InstallationSummary.cs index 1cab17e220..abeaa82bc1 100644 --- a/src/Umbraco.Core/Packaging/InstallationSummary.cs +++ b/src/Umbraco.Core/Packaging/InstallationSummary.cs @@ -17,11 +17,13 @@ namespace Umbraco.Core.Models.Packaging public IEnumerable FilesInstalled { get; set; } = Enumerable.Empty(); public IEnumerable TemplatesInstalled { get; set; } = Enumerable.Empty(); public IEnumerable DocumentTypesInstalled { get; set; } = Enumerable.Empty(); + public IEnumerable MediaTypesInstalled { get; set; } = Enumerable.Empty(); public IEnumerable StylesheetsInstalled { get; set; } = Enumerable.Empty(); public IEnumerable ContentInstalled { get; set; } = Enumerable.Empty(); + public IEnumerable MediaInstalled { get; set; } = Enumerable.Empty(); public IEnumerable Actions { get; set; } = Enumerable.Empty(); public IEnumerable ActionErrors { get; set; } = Enumerable.Empty(); - + } } diff --git a/src/Umbraco.Core/Packaging/PackageDefinition.cs b/src/Umbraco.Core/Packaging/PackageDefinition.cs index 29a1919a2b..379b400e75 100644 --- a/src/Umbraco.Core/Packaging/PackageDefinition.cs +++ b/src/Umbraco.Core/Packaging/PackageDefinition.cs @@ -4,7 +4,6 @@ using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Runtime.Serialization; -using Umbraco.Core.Composing; namespace Umbraco.Core.Models.Packaging { @@ -114,6 +113,9 @@ namespace Umbraco.Core.Models.Packaging [DataMember(Name = "documentTypes")] public IList DocumentTypes { get; set; } = new List(); + [DataMember(Name = "mediaTypes")] + public IList MediaTypes { get; set; } = new List(); + [DataMember(Name = "stylesheets")] public IList Stylesheets { get; set; } = new List(); @@ -133,6 +135,12 @@ namespace Umbraco.Core.Models.Packaging [DataMember(Name = "iconUrl")] public string IconUrl { get; set; } = string.Empty; + [DataMember(Name = "mediaUdis")] + public IList MediaUdis { get; set; } = Array.Empty(); + + [DataMember(Name = "mediaLoadChildNodes")] + public bool MediaLoadChildNodes { get; set; } + } diff --git a/src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs b/src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs index a9516d7e25..d194b0ea56 100644 --- a/src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs +++ b/src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs @@ -46,10 +46,13 @@ namespace Umbraco.Core.Packaging 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, + MediaUdis = xml.Element("media")?.Elements("nodeUdi").Select(x => (GuidUdi)UdiParser.Parse(x.Value)).ToList() ?? new List(), + MediaLoadChildNodes = xml.Element("media")?.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(), + MediaTypes = xml.Element("mediaTypes")?.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(), @@ -103,11 +106,17 @@ namespace Umbraco.Core.Packaging 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("mediaTypes", string.Join(",", def.MediaTypes ?? 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("dictionaryitems", string.Join(",", def.DictionaryItems ?? Array.Empty())), + new XElement( + "media", + def.MediaUdis.Select(x=> (object)new XElement("nodeUdi", x)) + .Union(new []{new XAttribute("loadChildNodes", def.MediaLoadChildNodes) })) + ); return packageXml; } diff --git a/src/Umbraco.Core/Packaging/PackagesRepository.cs b/src/Umbraco.Core/Packaging/PackagesRepository.cs index 59741ab1ce..b89448d891 100644 --- a/src/Umbraco.Core/Packaging/PackagesRepository.cs +++ b/src/Umbraco.Core/Packaging/PackagesRepository.cs @@ -5,8 +5,8 @@ using System.IO; using System.IO.Compression; using System.Linq; using System.Xml.Linq; -using Microsoft.Extensions.Options; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.Models; using Umbraco.Core.Hosting; @@ -38,6 +38,8 @@ namespace Umbraco.Core.Packaging private readonly string _tempFolderPath; private readonly PackageDefinitionXmlParser _parser; private readonly IUmbracoVersion _umbracoVersion; + private readonly IMediaService _mediaService; + private readonly IMediaTypeService _mediaTypeService; /// /// Constructor @@ -65,6 +67,8 @@ namespace Umbraco.Core.Packaging ILoggerFactory loggerFactory, IUmbracoVersion umbracoVersion, IOptions globalSettings, + IMediaService mediaService, + IMediaTypeService mediaTypeService, string packageRepositoryFileName, string tempFolderPath = null, string packagesFolderPath = null, string mediaFolderPath = null) { @@ -87,6 +91,8 @@ namespace Umbraco.Core.Packaging _parser = new PackageDefinitionXmlParser(_loggerFactory.CreateLogger(), umbracoVersion); _umbracoVersion = umbracoVersion; + _mediaService = mediaService; + _mediaTypeService = mediaTypeService; } private string CreatedPackagesFile => _packagesFolderPath.EnsureEndsWith('/') + _packageRepositoryFileName; @@ -181,12 +187,15 @@ namespace Umbraco.Core.Packaging PackageDocumentsAndTags(definition, root); PackageDocumentTypes(definition, root); + PackageMediaTypes(definition, root); PackageTemplates(definition, root); PackageStylesheets(definition, root); PackageMacros(definition, root, filesXml, temporaryPath); PackageDictionaryItems(definition, root); PackageLanguages(definition, root); PackageDataTypes(definition, root); + PackageMedia(definition, root); + // TODO: This needs to be split into content vs web files, for now we are going to // assume all files are web (www) files. But this is a larger discussion/change since @@ -194,13 +203,13 @@ namespace Umbraco.Core.Packaging //Files foreach (var fileName in definition.Files) - AppendFileToPackage(fileName, temporaryPath, filesXml, true); + AppendFileToPackage(fileName, temporaryPath, filesXml); //Load view on install... if (!string.IsNullOrEmpty(definition.PackageView)) { var control = new XElement("view", definition.PackageView); - AppendFileToPackage(definition.PackageView, temporaryPath, filesXml, true); + AppendFileToPackage(definition.PackageView, temporaryPath, filesXml); root.Add(control); } @@ -310,7 +319,7 @@ namespace Umbraco.Core.Packaging macros.Add(macroXml); //if the macro has a file copy it to the xml if (!string.IsNullOrEmpty(macro.MacroSource)) - AppendFileToPackage(macro.MacroSource, temporaryPath, filesXml, false); + AppendFileToPackage(macro.MacroSource, temporaryPath, filesXml); } root.Add(macros); } @@ -358,6 +367,23 @@ namespace Umbraco.Core.Packaging root.Add(docTypesXml); } + private void PackageMediaTypes(PackageDefinition definition, XContainer root) + { + var mediaTypes = new HashSet(); + var mediaTypesXml = new XElement("MediaTypes"); + foreach (var mediaTypeId in definition.MediaTypes) + { + if (!int.TryParse(mediaTypeId, out var outInt)) continue; + var mediaType = _mediaTypeService.Get(outInt); + if (mediaType == null) continue; + AddMediaType(mediaType, mediaTypes); + } + foreach (var mediaType in mediaTypes) + mediaTypesXml.Add(_serializer.Serialize(mediaType)); + + root.Add(mediaTypesXml); + } + private void PackageDocumentsAndTags(PackageDefinition definition, XContainer root) { //Documents and tags @@ -442,6 +468,18 @@ namespace Umbraco.Core.Packaging } } + + private void PackageMedia(PackageDefinition definition, XElement root) + { + IEnumerable medias = _mediaService.GetByIds(definition.MediaUdis); + + root.Add( + new XElement( + "MediaItems", + medias.Select(x => new XElement("MediaSet", _serializer.Serialize(x, definition.MediaLoadChildNodes))))); + } + + /// /// Zips the package. /// @@ -461,12 +499,12 @@ namespace Umbraco.Core.Packaging /// The package directory. /// The files xml node /// true if it's a web file, false if it's a content file - private void AppendFileToPackage(string path, string packageDirectory, XContainer filesXml, bool isWebFile) + private void AppendFileToPackage(string path, string packageDirectory, XContainer filesXml) { if (!path.StartsWith("~/") && !path.StartsWith("/")) path = "~/" + path; - var serverPath = isWebFile ? _hostingEnvironment.MapPathWebRoot(path) : _hostingEnvironment.MapPathContentRoot(path); + var serverPath = _hostingEnvironment.MapPathContentRoot(path); if (File.Exists(serverPath)) AppendFileXml(new FileInfo(serverPath), path, packageDirectory, filesXml); @@ -562,6 +600,19 @@ namespace Umbraco.Core.Packaging dtl.Add(dt); } + private void AddMediaType(IMediaType mediaType, HashSet mediaTypes) + { + if (mediaType.ParentId > 0) + { + var parent = _mediaTypeService.Get(mediaType.ParentId); + if (parent != null) // could be a container + AddMediaType(parent, mediaTypes); + } + + if (!mediaTypes.Contains(mediaType)) + mediaTypes.Add(mediaType); + } + private static XElement GetPackageInfoXml(PackageDefinition definition, IUmbracoVersion umbracoVersion) { diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index 25428f420b..61504b0a98 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Core.Persistence.Querying; @@ -10,7 +9,7 @@ namespace Umbraco.Core.Services /// /// Defines the ContentService, which is an easy access to operations involving /// - public interface IContentService : IContentServiceBase + public interface IContentService : IContentServiceBase { #region Blueprints diff --git a/src/Umbraco.Core/Services/IContentServiceBase.cs b/src/Umbraco.Core/Services/IContentServiceBase.cs index c40f49347f..ca87382494 100644 --- a/src/Umbraco.Core/Services/IContentServiceBase.cs +++ b/src/Umbraco.Core/Services/IContentServiceBase.cs @@ -1,7 +1,16 @@ -using Umbraco.Core.Models; +using System; +using System.Collections.Generic; +using Umbraco.Core.Models; namespace Umbraco.Core.Services { + public interface IContentServiceBase : IContentServiceBase + where TItem: class, IContentBase + { + TItem GetById(Guid key); + Attempt Save(IEnumerable contents, int userId = Constants.Security.SuperUserId, bool raiseEvents = true); + } + /// /// Placeholder for sharing logic between the content, media (and member) services /// TODO: Start sharing the logic! diff --git a/src/Umbraco.Core/Services/IContentTypeServiceBase.cs b/src/Umbraco.Core/Services/IContentTypeServiceBase.cs index 4c818aa87c..3c91091f8c 100644 --- a/src/Umbraco.Core/Services/IContentTypeServiceBase.cs +++ b/src/Umbraco.Core/Services/IContentTypeServiceBase.cs @@ -58,7 +58,6 @@ namespace Umbraco.Core.Services void Save(TItem item, int userId = Constants.Security.SuperUserId); void Save(IEnumerable items, int userId = Constants.Security.SuperUserId); - void Delete(TItem item, int userId = Constants.Security.SuperUserId); void Delete(IEnumerable item, int userId = Constants.Security.SuperUserId); diff --git a/src/Umbraco.Core/Services/IMediaService.cs b/src/Umbraco.Core/Services/IMediaService.cs index c1537adbc8..aa6363cc36 100644 --- a/src/Umbraco.Core/Services/IMediaService.cs +++ b/src/Umbraco.Core/Services/IMediaService.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.IO; using Umbraco.Core.Models; using Umbraco.Core.Persistence.Querying; @@ -10,7 +9,7 @@ namespace Umbraco.Core.Services /// /// Defines the Media Service, which is an easy access to operations involving /// - public interface IMediaService : IContentServiceBase + public interface IMediaService : IContentServiceBase { int CountNotTrashed(string contentTypeAlias = null); int Count(string mediaTypeAlias = null); diff --git a/src/Umbraco.Infrastructure/Composing/CompositionExtensions/Services.cs b/src/Umbraco.Infrastructure/Composing/CompositionExtensions/Services.cs index 696f846d13..b027a99c67 100644 --- a/src/Umbraco.Infrastructure/Composing/CompositionExtensions/Services.cs +++ b/src/Umbraco.Infrastructure/Composing/CompositionExtensions/Services.cs @@ -2,15 +2,14 @@ using System.IO; using System.Linq; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; using Microsoft.Extensions.Logging; -using Umbraco.Core.DependencyInjection; +using Microsoft.Extensions.Options; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.Models; +using Umbraco.Core.DependencyInjection; using Umbraco.Core.Events; using Umbraco.Core.Hosting; -using Umbraco.Core.IO; using Umbraco.Core.Packaging; using Umbraco.Core.Routing; using Umbraco.Core.Services; @@ -99,6 +98,8 @@ namespace Umbraco.Core.Composing.CompositionExtensions factory.GetRequiredService(), factory.GetRequiredService(), factory.GetRequiredService>(), + factory.GetRequiredService(), + factory.GetRequiredService(), packageRepoFileName); private static LocalizedTextServiceFileSources SourcesFactory(IServiceProvider container) diff --git a/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs b/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs index 0b9572cb20..f4c3326769 100644 --- a/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs +++ b/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs @@ -34,15 +34,32 @@ namespace Umbraco.Core.Packaging private readonly GlobalSettings _globalSettings; private readonly ILocalizedTextService _localizedTextService; private readonly IConfigurationEditorJsonSerializer _serializer; + private readonly IMediaService _mediaService; + private readonly IMediaTypeService _mediaTypeService; private readonly IJsonSerializer _jsonSerializer; private readonly IEntityService _entityService; private readonly IContentTypeService _contentTypeService; private readonly IContentService _contentService; - public PackageDataInstallation(ILogger logger, ILoggerFactory loggerFactory, IFileService fileService, IMacroService macroService, ILocalizationService localizationService, - IDataTypeService dataTypeService, IEntityService entityService, IContentTypeService contentTypeService, - IContentService contentService, PropertyEditorCollection propertyEditors, IScopeProvider scopeProvider, IShortStringHelper shortStringHelper, IOptions globalSettings, - ILocalizedTextService localizedTextService, IConfigurationEditorJsonSerializer serializer, IJsonSerializer jsonSerializer) + public PackageDataInstallation( + ILogger logger, + ILoggerFactory loggerFactory, + IFileService fileService, + IMacroService macroService, + ILocalizationService localizationService, + IDataTypeService dataTypeService, + IEntityService entityService, + IContentTypeService contentTypeService, + IContentService contentService, + PropertyEditorCollection propertyEditors, + IScopeProvider scopeProvider, + IShortStringHelper shortStringHelper, + IOptions globalSettings, + ILocalizedTextService localizedTextService, + IConfigurationEditorJsonSerializer serializer, + IMediaService mediaService, + IMediaTypeService mediaTypeService, + IJsonSerializer jsonSerializer) { _logger = logger; _loggerFactory = loggerFactory; @@ -57,6 +74,8 @@ namespace Umbraco.Core.Packaging _localizedTextService = localizedTextService; _serializer = serializer; _jsonSerializer = jsonSerializer; + _mediaService = mediaService; + _mediaTypeService = mediaTypeService; _entityService = entityService; _contentTypeService = contentTypeService; _contentService = contentService; @@ -197,29 +216,58 @@ namespace Umbraco.Core.Packaging DictionaryItemsInstalled = ImportDictionaryItems(compiledPackage.DictionaryItems, userId), MacrosInstalled = ImportMacros(compiledPackage.Macros, userId), TemplatesInstalled = ImportTemplates(compiledPackage.Templates.ToList(), userId), - DocumentTypesInstalled = ImportDocumentTypes(compiledPackage.DocumentTypes, userId) + DocumentTypesInstalled = ImportDocumentTypes(compiledPackage.DocumentTypes, userId), + MediaTypesInstalled = ImportMediaTypes(compiledPackage.MediaTypes, userId), }; //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); installationSummary.StylesheetsInstalled = ImportStylesheets(compiledPackage.Stylesheets, userId); - installationSummary.ContentInstalled = ImportContent(compiledPackage.Documents, importedDocTypes, userId); + installationSummary.ContentInstalled = ImportContentBase(compiledPackage.Documents, importedDocTypes, userId, _contentTypeService, _contentService); + installationSummary.MediaInstalled = ImportContentBase(compiledPackage.Media, importedMediaTypes, userId, _mediaTypeService, _mediaService); scope.Complete(); return installationSummary; } } + /// + /// Imports and saves package xml as + /// + /// Xml to import + /// 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) + { + return ImportDocumentTypes(docTypeElements.ToList(), true, userId, _mediaTypeService); + } + + #endregion + #region Content - public IReadOnlyList ImportContent(IEnumerable docs, IDictionary importedDocumentTypes, int userId) + public IReadOnlyList ImportContentBase( + IEnumerable docs, + IDictionary importedDocumentTypes, + int userId, + IContentTypeBaseService typeService, + IContentServiceBase service) + where T: class, IContentBase + where S: IContentTypeComposition { - return docs.SelectMany(x => ImportContent(x, -1, importedDocumentTypes, userId)).ToList(); + return docs.SelectMany(x => ImportContentBase( + x.XmlData.Elements().Where(doc => (string)doc.Attribute("isDoc") == string.Empty), + -1, + importedDocumentTypes, + userId, + typeService, + service)).ToList(); } /// @@ -230,17 +278,20 @@ namespace Umbraco.Core.Packaging /// A dictionary of already imported document types (basically used as a cache) /// Optional Id of the user performing the import /// An enumerable list of generated content - public IEnumerable ImportContent(CompiledPackageDocument packageDocument, int parentId, IDictionary importedDocumentTypes, int userId) + public IEnumerable ImportContentBase( + IEnumerable roots, + int parentId, + IDictionary importedDocumentTypes, + int userId, + IContentTypeBaseService typeService, + IContentServiceBase service) + where T: class, IContentBase + where S: IContentTypeComposition { - var element = packageDocument.XmlData; - var roots = from doc in element.Elements() - where (string)doc.Attribute("isDoc") == "" - select doc; - - var contents = ParseDocumentRootXml(roots, parentId, importedDocumentTypes).ToList(); + var contents = ParseContentBaseRootXml(roots, parentId, importedDocumentTypes, typeService, service).ToList(); if (contents.Any()) - _contentService.Save(contents, userId); + service.Save(contents, userId); return contents; @@ -249,7 +300,7 @@ namespace Umbraco.Core.Packaging //{ // //This is a single doc import // var elements = new List { element }; - // var contents = ParseDocumentRootXml(elements, parentId, importedDocumentTypes).ToList(); + // var contents = ParseContentBaseRootXml(elements, parentId, importedDocumentTypes).ToList(); // if (contents.Any()) // _contentService.Save(contents, userId); @@ -261,50 +312,64 @@ namespace Umbraco.Core.Packaging // "'DocumentSet' (for structured imports) nor is the first element a Document (for single document import)."); } - private IEnumerable ParseDocumentRootXml(IEnumerable roots, int parentId, IDictionary importedContentTypes) + private IEnumerable ParseContentBaseRootXml( + IEnumerable roots, + int parentId, + IDictionary importedContentTypes, + IContentTypeBaseService typeService, + IContentServiceBase service) + where T: class, IContentBase + where S: IContentTypeComposition { - var contents = new List(); + var contents = new List(); foreach (var root in roots) { var contentTypeAlias = root.Name.LocalName; if (!importedContentTypes.ContainsKey(contentTypeAlias)) { - var contentType = FindContentTypeByAlias(contentTypeAlias); + var contentType = FindContentTypeByAlias(contentTypeAlias, typeService); importedContentTypes.Add(contentTypeAlias, contentType); } - var content = CreateContentFromXml(root, importedContentTypes[contentTypeAlias], null, parentId); + var content = CreateContentFromXml(root, importedContentTypes[contentTypeAlias], default(T), parentId, service); if (content == null) continue; contents.Add(content); - var children = (from child in root.Elements() - where (string)child.Attribute("isDoc") == "" - select child) + var children = root.Elements().Where(doc => (string)doc.Attribute("isDoc") == string.Empty) .ToList(); if (children.Count > 0) - contents.AddRange(CreateContentFromXml(children, content, importedContentTypes).WhereNotNull()); + { + contents.AddRange(CreateContentFromXml(children, content, importedContentTypes, typeService, service).WhereNotNull()); + } } return contents; } - private IEnumerable CreateContentFromXml(IEnumerable children, IContent parent, IDictionary importedContentTypes) + private IEnumerable CreateContentFromXml( + IEnumerable children, + T parent, + IDictionary importedContentTypes, + IContentTypeBaseService typeService, + IContentServiceBase service) + where T: class, IContentBase + where S: IContentTypeComposition { - var list = new List(); + var list = new List(); foreach (var child in children) { string contentTypeAlias = child.Name.LocalName; if (importedContentTypes.ContainsKey(contentTypeAlias) == false) { - var contentType = FindContentTypeByAlias(contentTypeAlias); + var contentType = FindContentTypeByAlias(contentTypeAlias, typeService); importedContentTypes.Add(contentTypeAlias, contentType); } //Create and add the child to the list - var content = CreateContentFromXml(child, importedContentTypes[contentTypeAlias], parent, default); + var content = CreateContentFromXml(child, importedContentTypes[contentTypeAlias], parent, default, service); list.Add(content); //Recursive call @@ -314,19 +379,26 @@ namespace Umbraco.Core.Packaging select grand).ToList(); if (grandChildren.Any()) - list.AddRange(CreateContentFromXml(grandChildren, content, importedContentTypes)); + list.AddRange(CreateContentFromXml(grandChildren, content,importedContentTypes, typeService, service)); } return list; } - private IContent CreateContentFromXml(XElement element, IContentType contentType, IContent parent, int parentId) + private T CreateContentFromXml( + XElement element, + S contentType, + T parent, + int parentId, + IContentServiceBase service) + where T: class, IContentBase + where S: IContentTypeComposition { 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) + if (service.GetById(key) != null) return null; } @@ -346,9 +418,9 @@ namespace Umbraco.Core.Packaging var template = templateId.HasValue ? _fileService.GetTemplate(templateId.Value) : null; //now double check this is correct since its an INT it could very well be pointing to an invalid template :/ - if (template != null) + if (template != null && contentType is IContentType contentTypex) { - if (!contentType.IsAllowedTemplate(template.Alias)) + if (!contentTypex.IsAllowedTemplate(template.Alias)) { //well this is awkward, we'll set the template to null and it will be wired up to the default template // when it's persisted in the document repository @@ -356,21 +428,15 @@ namespace Umbraco.Core.Packaging } } - IContent content = parent == null - ? new Content(nodeName, parentId, contentType) - { - Level = int.Parse(level), - SortOrder = int.Parse(sortOrder), - TemplateId = template?.Id, - Key = key - } - : new Content(nodeName, parent, contentType) - { - Level = int.Parse(level), - SortOrder = int.Parse(sortOrder), - TemplateId = template?.Id, - Key = key - }; + T content = CreateContent( + nodeName, + parent, + parentId, + contentType, + key, + int.Parse(level), + int.Parse(sortOrder), + template?.Id); //Here we make sure that we take composition properties in account as well //otherwise we would skip them and end up losing content @@ -396,6 +462,37 @@ namespace Umbraco.Core.Packaging return content; } + private T CreateContent(string name, T parent, int parentId, S contentType, Guid key, int level, int sortOrder, int? templateId) + where T : class, IContentBase + where S : IContentTypeComposition + { + switch (contentType) + { + case IContentType c: + if (parent is null) + { + return new Content(name, parentId, c) { Key = key, Level = level, SortOrder = sortOrder, TemplateId = templateId, } as T; + } + else + { + return new Content(name, (IContent)parent, c) { Key = key, Level = level, SortOrder = sortOrder, TemplateId = templateId, } as T; + } + + case IMediaType m: + if (parent is null) + { + return new Umbraco.Core.Models.Media(name, parentId, m) { Key = key, Level = level, SortOrder = sortOrder, } as T; + } + else + { + return new Umbraco.Core.Models.Media(name, (IMedia)parent, m) { Key = key, Level = level, SortOrder = sortOrder, } as T; + } + + default: + throw new NotSupportedException($"Type {typeof(S)} is not supported"); + } + } + #endregion #region DocumentTypes @@ -413,7 +510,7 @@ namespace Umbraco.Core.Packaging /// An enumerable list of generated ContentTypes public IReadOnlyList ImportDocumentTypes(IEnumerable docTypeElements, int userId) { - return ImportDocumentTypes(docTypeElements.ToList(), true, userId); + return ImportDocumentTypes(docTypeElements.ToList(), true, userId, _contentTypeService); } /// @@ -423,9 +520,10 @@ namespace Umbraco.Core.Packaging /// Boolean indicating whether or not to import the /// Optional id of the User performing the operation. Default is zero (admin). /// An enumerable list of generated ContentTypes - public IReadOnlyList ImportDocumentTypes(IReadOnlyCollection unsortedDocumentTypes, bool importStructure, int userId) + public IReadOnlyList ImportDocumentTypes(IReadOnlyCollection unsortedDocumentTypes, bool importStructure, int userId, IContentTypeBaseService service) + where T: class, IContentTypeComposition { - var importedContentTypes = new Dictionary(); + var importedContentTypes = new Dictionary(); //When you are importing a single doc type we have to assume that the dependencies are already there. //Otherwise something like uSync won't work. @@ -479,10 +577,10 @@ namespace Umbraco.Core.Packaging var alias = documentType.Element("Info").Element("Alias").Value; if (importedContentTypes.ContainsKey(alias) == false) { - var contentType = _contentTypeService.Get(alias); + var contentType = service.Get(alias); importedContentTypes.Add(alias, contentType == null - ? CreateContentTypeFromXml(documentType, importedContentTypes) - : UpdateContentTypeFromXml(documentType, contentType, importedContentTypes)); + ? CreateContentTypeFromXml(documentType, importedContentTypes, service) + : UpdateContentTypeFromXml(documentType, contentType, importedContentTypes, service)); } } @@ -497,27 +595,27 @@ namespace Umbraco.Core.Packaging //Save the newly created/updated IContentType objects var list = importedContentTypes.Select(x => x.Value).ToList(); - _contentTypeService.Save(list, userId); + service.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(); + var updatedContentTypes = new List(); //Update the structure here - we can't do it until 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; + if (structureElement == null || structureElement.Elements().Any() == false) continue; - var updated = UpdateContentTypesStructure(importedContentTypes[alias], structureElement, importedContentTypes); + var updated = UpdateContentTypesStructure(importedContentTypes[alias], structureElement, importedContentTypes, service); updatedContentTypes.Add(updated); } //Update ContentTypes with a newly added structure/list of allowed children if (updatedContentTypes.Any()) - _contentTypeService.Save(updatedContentTypes, userId); + service.Save(updatedContentTypes, userId); } return list; @@ -586,33 +684,65 @@ namespace Umbraco.Core.Packaging return _contentTypeService.GetContainer(tryCreateFolder.Result.Entity.Id); } - private IContentType CreateContentTypeFromXml(XElement documentType, IReadOnlyDictionary importedContentTypes) + private T CreateContentTypeFromXml(XElement documentType, IReadOnlyDictionary importedContentTypes, IContentTypeBaseService service) + where T : class, IContentTypeComposition { var infoElement = documentType.Element("Info"); //Name of the master corresponds to the parent var masterElement = infoElement.Element("Master"); - IContentType parent = null; + T parent = default; if (masterElement != null) { var masterAlias = masterElement.Value; parent = importedContentTypes.ContainsKey(masterAlias) ? importedContentTypes[masterAlias] - : _contentTypeService.Get(masterAlias); + : service.Get(masterAlias); } var alias = infoElement.Element("Alias").Value; - var contentType = parent == null - ? new ContentType(_shortStringHelper, -1) { Alias = alias } - : new ContentType(_shortStringHelper, parent, alias); + T contentType = CreateContentType(parent, -1, alias); if (parent != null) contentType.AddContentType(parent); - return UpdateContentTypeFromXml(documentType, contentType, importedContentTypes); + return UpdateContentTypeFromXml(documentType, contentType, importedContentTypes, service); } - private IContentType UpdateContentTypeFromXml(XElement documentType, IContentType contentType, IReadOnlyDictionary importedContentTypes) + private T CreateContentType(T parent, int parentId, string alias) + where T : class, IContentTypeComposition + { + if (typeof(T) == typeof(IContentType)) + { + if (parent is null) + { + return new ContentType(_shortStringHelper, parentId) { Alias = alias } as T; + } + else + { + return new ContentType(_shortStringHelper, (IContentType) parent, alias) as T; + } + + } + + if (typeof(T) == typeof(IMediaType)) + { + if (parent is null) + { + return new MediaType(_shortStringHelper, parentId) { Alias = alias } as T; + } + else + { + return new MediaType(_shortStringHelper, (IMediaType)parent, alias) as T; + } + + } + + throw new NotSupportedException($"Type {typeof(T)} is not supported"); + } + + private T UpdateContentTypeFromXml(XElement documentType, T contentType, IReadOnlyDictionary importedContentTypes, IContentTypeBaseService service) + where T : IContentTypeComposition { var infoElement = documentType.Element("Info"); var defaultTemplateElement = infoElement.Element("DefaultTemplate"); @@ -646,9 +776,9 @@ namespace Umbraco.Core.Packaging if (masterElement != null) { var masterAlias = masterElement.Value; - IContentType parent = importedContentTypes.ContainsKey(masterAlias) + T parent = importedContentTypes.ContainsKey(masterAlias) ? importedContentTypes[masterAlias] - : _contentTypeService.Get(masterAlias); + : service.Get(masterAlias); contentType.SetParent(parent); } @@ -665,13 +795,17 @@ namespace Umbraco.Core.Packaging var compositionAlias = composition.Value; var compositionContentType = importedContentTypes.ContainsKey(compositionAlias) ? importedContentTypes[compositionAlias] - : _contentTypeService.Get(compositionAlias); + : service.Get(compositionAlias); contentType.AddContentType(compositionContentType); } } } - UpdateContentTypesAllowedTemplates(contentType, infoElement.Element("AllowedTemplates"), defaultTemplateElement); + if (contentType is IContentType contentTypex) + { + UpdateContentTypesAllowedTemplates(contentTypex, infoElement.Element("AllowedTemplates"), defaultTemplateElement); + } + UpdateContentTypesTabs(contentType, documentType.Element("Tabs")); UpdateContentTypesProperties(contentType, documentType.Element("GenericProperties")); @@ -716,7 +850,8 @@ namespace Umbraco.Core.Packaging } } - private void UpdateContentTypesTabs(IContentType contentType, XElement tabElement) + private void UpdateContentTypesTabs(T contentType, XElement tabElement) + where T: IContentTypeComposition { if (tabElement == null) return; @@ -742,7 +877,8 @@ namespace Umbraco.Core.Packaging } } - private void UpdateContentTypesProperties(IContentType contentType, XElement genericPropertiesElement) + private void UpdateContentTypesProperties(T contentType, XElement genericPropertiesElement) + where T: IContentTypeComposition { var properties = genericPropertiesElement.Elements("GenericProperty"); foreach (var property in properties) @@ -834,15 +970,16 @@ namespace Umbraco.Core.Packaging } } - private IContentType UpdateContentTypesStructure(IContentType contentType, XElement structureElement, IReadOnlyDictionary importedContentTypes) + private T UpdateContentTypesStructure(T contentType, XElement structureElement, IReadOnlyDictionary importedContentTypes, IContentTypeBaseService service) + where T: IContentTypeComposition { var allowedChildren = contentType.AllowedContentTypes.ToList(); int sortOrder = allowedChildren.Any() ? allowedChildren.Last().SortOrder : 0; - foreach (var element in structureElement.Elements("DocumentType")) + foreach (var element in structureElement.Elements()) { var alias = element.Value; - var allowedChild = importedContentTypes.ContainsKey(alias) ? importedContentTypes[alias] : _contentTypeService.Get(alias); + var allowedChild = importedContentTypes.ContainsKey(alias) ? importedContentTypes[alias] : service.Get(alias); if (allowedChild == null) { _logger.LogWarning( @@ -866,9 +1003,10 @@ namespace Umbraco.Core.Packaging /// /// /// - private IContentType FindContentTypeByAlias(string contentTypeAlias) + private S FindContentTypeByAlias(string contentTypeAlias, IContentTypeBaseService typeService) + where S: IContentTypeComposition { - var contentType = _contentTypeService.Get(contentTypeAlias); + var contentType = typeService.Get(contentTypeAlias); if (contentType == null) throw new Exception($"ContentType matching the passed in Alias: '{contentTypeAlias}' was null"); diff --git a/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs b/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs index c9a67a8543..126d235ae0 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs @@ -22,6 +22,7 @@ using Umbraco.Core.Migrations; using Umbraco.Core.Migrations.Install; using Umbraco.Core.Migrations.PostMigrations; using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Packaging; using Umbraco.Core.Persistence; using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.Validators; @@ -380,6 +381,8 @@ namespace Umbraco.Core.Runtime builder.Services.AddUnique(); builder.Services.AddUnique(); + + builder.Services.AddUnique(); } } } diff --git a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs index 120c0500e2..69dadb2b21 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Globalization; using System.Linq; using Microsoft.Extensions.Logging; @@ -387,6 +386,16 @@ namespace Umbraco.Core.Services.Implement } } + /// + /// + /// + /// + /// + /// + /// + Attempt IContentServiceBase.Save(IEnumerable contents, int userId, + bool raiseEvents) => Attempt.Succeed(Save(contents, userId, raiseEvents)); + /// /// Gets objects by Ids /// diff --git a/src/Umbraco.Infrastructure/Services/Implement/EntityXmlSerializer.cs b/src/Umbraco.Infrastructure/Services/Implement/EntityXmlSerializer.cs index 479e5cc4d8..c33265c987 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/EntityXmlSerializer.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/EntityXmlSerializer.cs @@ -4,8 +4,6 @@ using System.Globalization; using System.Linq; using System.Net; using System.Xml.Linq; -using Newtonsoft.Json; -using Umbraco.Core.Composing; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Serialization; @@ -120,6 +118,7 @@ namespace Umbraco.Core.Services.Implement //xml.Add(new XAttribute("creatorID", media.CreatorId)); xml.Add(new XAttribute("writerName", media.GetWriterProfile(_userService)?.Name ?? string.Empty)); xml.Add(new XAttribute("writerID", media.WriterId)); + xml.Add(new XAttribute("udi", media.GetUdi())); //xml.Add(new XAttribute("template", 0)); // no template for media @@ -335,6 +334,7 @@ namespace Umbraco.Core.Services.Implement return xml; } + public XElement Serialize(IMediaType mediaType) { var info = new XElement("Info", @@ -375,7 +375,7 @@ namespace Umbraco.Core.Services.Implement new XElement("Validation", propertyType.ValidationRegExp), new XElement("ValidationRegExpMessage", propertyType.ValidationRegExpMessage), new XElement("LabelOnTop", propertyType.LabelOnTop), - new XElement("Description", new XCData(propertyType.Description))); + new XElement("Description", new XCData(propertyType.Description ?? string.Empty))); genericProperties.Add(genericProperty); } @@ -498,7 +498,7 @@ namespace Umbraco.Core.Services.Implement new XElement("Alias", propertyType.Alias), new XElement("Key", propertyType.Key), new XElement("Type", propertyType.PropertyEditorAlias), - new XElement("Definition", definition.Key), + new XElement("Definition", definition.Key), new XElement("Tab", propertyGroup == null ? "" : propertyGroup.Name), new XElement("SortOrder", propertyType.SortOrder), new XElement("Mandatory", propertyType.Mandatory.ToString()), diff --git a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs index 0672c3218f..5d5d4b7bd1 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Globalization; using System.IO; using System.Linq; @@ -8,7 +7,6 @@ using Microsoft.Extensions.Logging; using Umbraco.Core.Events; using Umbraco.Core.IO; using Umbraco.Core.Models; -using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Scoping; @@ -360,6 +358,8 @@ namespace Umbraco.Core.Services.Implement } } + + /// /// Gets an object by Id /// @@ -643,6 +643,8 @@ namespace Umbraco.Core.Services.Implement #region Save + + /// /// Saves a single object /// diff --git a/src/Umbraco.Tests.Integration/Umbraco.Core/Packaging/Packages/Document_Type_Picker_1.1.umb b/src/Umbraco.Tests.Integration/TestData/Packages/Document_Type_Picker_1.1.umb similarity index 100% rename from src/Umbraco.Tests.Integration/Umbraco.Core/Packaging/Packages/Document_Type_Picker_1.1.umb rename to src/Umbraco.Tests.Integration/TestData/Packages/Document_Type_Picker_1.1.umb diff --git a/src/Umbraco.Tests/Packaging/Packages/Document_Type_Picker_1.1.zip b/src/Umbraco.Tests.Integration/TestData/Packages/Document_Type_Picker_1.1.zip similarity index 100% rename from src/Umbraco.Tests/Packaging/Packages/Document_Type_Picker_1.1.zip rename to src/Umbraco.Tests.Integration/TestData/Packages/Document_Type_Picker_1.1.zip diff --git a/src/Umbraco.Tests/Packaging/Packages/Hello_1.0.0.zip b/src/Umbraco.Tests.Integration/TestData/Packages/Hello_1.0.0.zip similarity index 100% rename from src/Umbraco.Tests/Packaging/Packages/Hello_1.0.0.zip rename to src/Umbraco.Tests.Integration/TestData/Packages/Hello_1.0.0.zip diff --git a/src/Umbraco.Tests.Integration/Umbraco.Core/Packaging/CreatedPackagesRepositoryTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Core/Packaging/CreatedPackagesRepositoryTests.cs index 6f3702765e..e3ec2d872b 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Core/Packaging/CreatedPackagesRepositoryTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Core/Packaging/CreatedPackagesRepositoryTests.cs @@ -45,6 +45,8 @@ namespace Umbraco.Tests.Integration.Umbraco.Core.Packaging private IEntityXmlSerializer EntityXmlSerializer => GetRequiredService(); private IHostingEnvironment HostingEnvironment => GetRequiredService(); private IUmbracoVersion UmbracoVersion => GetRequiredService(); + private IMediaService MediaService => GetRequiredService(); + private IMediaTypeService MediaTypeService => GetRequiredService(); public ICreatedPackagesRepository PackageBuilder => new PackagesRepository( ContentService, ContentTypeService, DataTypeService, @@ -53,6 +55,8 @@ namespace Umbraco.Tests.Integration.Umbraco.Core.Packaging EntityXmlSerializer, LoggerFactory, UmbracoVersion, Microsoft.Extensions.Options.Options.Create(new GlobalSettings()), + MediaService, + MediaTypeService, "createdPackages.config", //temp paths tempFolderPath: "~/" + _testBaseFolder + "/temp", @@ -158,8 +162,8 @@ namespace Umbraco.Tests.Integration.Umbraco.Core.Packaging { 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); + var mappedFile1 = HostingEnvironment.MapPathContentRoot(file1); + var mappedFile2 = HostingEnvironment.MapPathContentRoot(file2); Directory.CreateDirectory(Path.GetDirectoryName(mappedFile1)); Directory.CreateDirectory(Path.GetDirectoryName(mappedFile2)); File.WriteAllText(mappedFile1, "hello world"); @@ -183,7 +187,7 @@ namespace Umbraco.Tests.Integration.Umbraco.Core.Packaging def = PackageBuilder.GetById(def.Id); //re-get Assert.IsNotNull(def.PackagePath); - using (var archive = ZipFile.OpenRead(IOHelper.MapPath(zip))) + using (var archive = ZipFile.OpenRead(HostingEnvironment.MapPathWebRoot(zip))) { Assert.AreEqual(3, archive.Entries.Count); diff --git a/src/Umbraco.Tests/Packaging/PackageDataInstallationTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Core/Packaging/PackageDataInstallationTests.cs similarity index 87% rename from src/Umbraco.Tests/Packaging/PackageDataInstallationTests.cs rename to src/Umbraco.Tests.Integration/Umbraco.Core/Packaging/PackageDataInstallationTests.cs index 0383eb6ef8..f250a43b7f 100644 --- a/src/Umbraco.Tests/Packaging/PackageDataInstallationTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Core/Packaging/PackageDataInstallationTests.cs @@ -2,13 +2,11 @@ using System.Linq; using System.Threading; using System.Xml.Linq; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Composing; -using Umbraco.Core.Composing.CompositionExtensions; using Umbraco.Core.Configuration.Models; using Umbraco.Core.Models; using Umbraco.Core.Models.Packaging; @@ -18,7 +16,7 @@ using Umbraco.Core.PropertyEditors; using Umbraco.Core.Serialization; using Umbraco.Core.Services; using Umbraco.Core.Strings; -using Umbraco.Tests.Services; +using Umbraco.Tests.Integration.Testing; using Umbraco.Tests.Services.Importing; using Umbraco.Tests.Testing; @@ -27,9 +25,12 @@ namespace Umbraco.Tests.Packaging [TestFixture] [Category("Slow")] [Apartment(ApartmentState.STA)] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] - public class PackageDataInstallationTests : TestWithSomeContentBase + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, WithApplication = true)] + public class PackageDataInstallationTests : UmbracoIntegrationTestWithContent { + private ILocalizationService LocalizationService => GetRequiredService(); + private IMacroService MacroService => GetRequiredService(); + [HideFromTypeFinder] [DataEditor("7e062c13-7c41-4ad9-b389-41d88aeef87c", "Editor1", "editor1")] public class Editor1 : DataEditor @@ -50,31 +51,36 @@ namespace Umbraco.Tests.Packaging } } - protected override void Compose() - { - base.Compose(); + // protected override void Compose() + // { + // base.Compose(); + // + // // the packages that are used by these tests reference totally bogus property + // // editors that must exist - so they are defined here - and in order not to + // // pollute everything, they are ignored by the type finder and explicitely + // // added to the editors collection + // + // Builder.WithCollectionBuilder() + // .Add() + // .Add(); + // } + // + // protected override void ComposeApplication(bool withApplication) + // { + // base.ComposeApplication(withApplication); + // + // if (!withApplication) return; + // + // // re-register with actual media fs + // Builder.ComposeFileSystems(); + // } - // the packages that are used by these tests reference totally bogus property - // editors that must exist - so they are defined here - and in order not to - // pollute everything, they are ignored by the type finder and explicitely - // added to the editors collection + private PackageDataInstallation PackageDataInstallation => GetRequiredService(); + private IContentService ContentService => GetRequiredService(); + private IContentTypeService ContentTypeService => GetRequiredService(); - Builder.WithCollectionBuilder() - .Add() - .Add(); - } - - protected override void ComposeApplication(bool withApplication) - { - base.ComposeApplication(withApplication); - - if (!withApplication) return; - - // re-register with actual media fs - Builder.ComposeFileSystems(); - } - - private PackageDataInstallation PackageDataInstallation => Factory.GetRequiredService(); + private IMediaService MediaService => GetRequiredService(); + private IMediaTypeService MediaTypeService => GetRequiredService(); [Test] public void Can_Import_uBlogsy_ContentTypes_And_Verify_Structure() @@ -195,12 +201,12 @@ namespace Umbraco.Tests.Packaging var xml = XElement.Parse(strXml); var element = xml.Descendants("Templates").First(); - var init = ServiceContext.FileService.GetTemplates().Count(); + var init = FileService.GetTemplates().Count(); // Act 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(); + var allTemplates = FileService.GetTemplates(); // Assert Assert.That(templates, Is.Not.Null); @@ -322,16 +328,16 @@ namespace Umbraco.Tests.Packaging var dataTypeElement = xml.Descendants("DataTypes").First(); var docTypesElement = xml.Descendants("DocumentTypes").First(); var element = xml.Descendants("DocumentSet").First(); - var packageDocument = CompiledPackageDocument.Create(element); + var packageDocument = CompiledPackageContentBase.Create(element); // Act 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 = PackageDataInstallation.ImportContent(packageDocument, -1, importedContentTypes, 0); + var contents = PackageDataInstallation.ImportContentBase(packageDocument.Yield(), importedContentTypes, 0, ContentTypeService, ContentService); var numberOfDocs = (from doc in element.Descendants() - where (string) doc.Attribute("isDoc") == "" - select doc).Count(); + where (string) doc.Attribute("isDoc") == "" + select doc).Count(); // Assert Assert.That(contents, Is.Not.Null); @@ -341,6 +347,32 @@ namespace Umbraco.Tests.Packaging Assert.That(contents.Count(), Is.EqualTo(numberOfDocs)); } + [Test] + public void Can_Import_Media_Package_Xml() + { + // Arrange + Core.Services.Implement.MediaTypeService.ClearScopeEvents(); + string strXml = ImportResources.MediaTypesAndMedia_Package_xml; + var xml = XElement.Parse(strXml); + var mediaTypesElement = xml.Descendants("MediaTypes").First(); + var element = xml.Descendants("MediaSet").First(); + var packageMedia = CompiledPackageContentBase.Create(element); + + // Act + var mediaTypes = PackageDataInstallation.ImportMediaTypes(mediaTypesElement.Elements("MediaType"), 0); + var importedMediaTypes = mediaTypes.ToDictionary(x => x.Alias, x => x); + var medias = PackageDataInstallation.ImportContentBase(packageMedia.Yield(), importedMediaTypes, 0, MediaTypeService, MediaService); + var numberOfDocs = (from doc in element.Descendants() + where (string) doc.Attribute("isDoc") == "" + select doc).Count(); + + // Assert + Assert.That(medias, Is.Not.Null); + Assert.That(mediaTypes.Any(), Is.True); + Assert.That(medias.Any(), Is.True); + Assert.That(medias.Count(), Is.EqualTo(numberOfDocs)); + } + [Test] public void Can_Import_CheckboxList_Content_Package_Xml_With_Property_Editor_Aliases() { @@ -356,13 +388,13 @@ namespace Umbraco.Tests.Packaging var dataTypeElement = xml.Descendants("DataTypes").First(); var docTypesElement = xml.Descendants("DocumentTypes").First(); var element = xml.Descendants("DocumentSet").First(); - var packageDocument = CompiledPackageDocument.Create(element); + var packageDocument = CompiledPackageContentBase.Create(element); // Act 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 = PackageDataInstallation.ImportContent(packageDocument, -1, importedContentTypes, 0); + var contents = PackageDataInstallation.ImportContentBase(packageDocument.Yield(), importedContentTypes, 0, ContentTypeService, ContentService); var numberOfDocs = (from doc in element.Descendants() where (string)doc.Attribute("isDoc") == "" select doc).Count(); @@ -427,7 +459,7 @@ namespace Umbraco.Tests.Packaging string strXml = ImportResources.SingleDocType; var docTypeElement = XElement.Parse(strXml); - var serializer = Factory.GetRequiredService(); + var serializer = GetRequiredService(); // Act var contentTypes = PackageDataInstallation.ImportDocumentType(docTypeElement, 0); @@ -476,7 +508,7 @@ namespace Umbraco.Tests.Packaging var templateElement = newPackageXml.Descendants("Templates").First(); var templateElementUpdated = updatedPackageXml.Descendants("Templates").First(); - var fileService = ServiceContext.FileService; + var fileService = FileService; // kill default test data fileService.DeleteTemplate("Textpage"); @@ -535,11 +567,11 @@ namespace Umbraco.Tests.Packaging var dictionaryItems = PackageDataInstallation.ImportDictionaryItems(dictionaryItemsElement.Elements("DictionaryItem"), 0); // Assert - Assert.That(ServiceContext.LocalizationService.DictionaryItemExists(parentKey), "DictionaryItem parentKey does not exist"); - Assert.That(ServiceContext.LocalizationService.DictionaryItemExists(childKey), "DictionaryItem childKey does not exist"); + Assert.That(LocalizationService.DictionaryItemExists(parentKey), "DictionaryItem parentKey does not exist"); + Assert.That(LocalizationService.DictionaryItemExists(childKey), "DictionaryItem childKey does not exist"); - var parentDictionaryItem = ServiceContext.LocalizationService.GetDictionaryItemByKey(parentKey); - var childDictionaryItem = ServiceContext.LocalizationService.GetDictionaryItemByKey(childKey); + var parentDictionaryItem = LocalizationService.GetDictionaryItemByKey(parentKey); + var childDictionaryItem = LocalizationService.GetDictionaryItemByKey(childKey); Assert.That(parentDictionaryItem.ParentId, Is.Not.EqualTo(childDictionaryItem.ParentId)); Assert.That(childDictionaryItem.ParentId, Is.EqualTo(parentDictionaryItem.Key)); @@ -604,7 +636,7 @@ namespace Umbraco.Tests.Packaging // Act var languages = PackageDataInstallation.ImportLanguages(LanguageItemsElement.Elements("Language"), 0); - var allLanguages = ServiceContext.LocalizationService.GetAllLanguages(); + var allLanguages = LocalizationService.GetAllLanguages(); // Assert Assert.That(languages.Any(x => x.HasIdentity == false), Is.False); @@ -629,7 +661,7 @@ namespace Umbraco.Tests.Packaging // Assert Assert.That(macros.Any(), Is.True); - var allMacros = ServiceContext.MacroService.GetAll().ToList(); + var allMacros = MacroService.GetAll().ToList(); foreach (var macro in macros) { Assert.That(allMacros.Any(x => x.Alias == macro.Alias), Is.True); @@ -652,7 +684,7 @@ namespace Umbraco.Tests.Packaging Assert.That(macros.Any(), Is.True); Assert.That(macros.First().Properties.Values.Any(), Is.True); - var allMacros = ServiceContext.MacroService.GetAll().ToList(); + var allMacros = MacroService.GetAll().ToList(); foreach (var macro in macros) { Assert.That(allMacros.Any(x => x.Alias == macro.Alias), Is.True); @@ -718,14 +750,14 @@ namespace Umbraco.Tests.Packaging var globalSettings = new GlobalSettings(); var norwegian = new Core.Models.Language(globalSettings, "nb-NO"); var english = new Core.Models.Language(globalSettings, "en-GB"); - ServiceContext.LocalizationService.Save(norwegian, 0); - ServiceContext.LocalizationService.Save(english, 0); + LocalizationService.Save(norwegian, 0); + LocalizationService.Save(english, 0); } private void AssertDictionaryItem(string key, string expectedValue, string cultureCode) { - Assert.That(ServiceContext.LocalizationService.DictionaryItemExists(key), "DictionaryItem key does not exist"); - var dictionaryItem = ServiceContext.LocalizationService.GetDictionaryItemByKey(key); + Assert.That(LocalizationService.DictionaryItemExists(key), "DictionaryItem key does not exist"); + var dictionaryItem = LocalizationService.GetDictionaryItemByKey(key); var translation = dictionaryItem.Translations.SingleOrDefault(i => i.Language.IsoCode == cultureCode); Assert.IsNotNull(translation, "Translation to {0} was not added", cultureCode); var value = translation.Value; @@ -734,9 +766,9 @@ namespace Umbraco.Tests.Packaging private void AddExistingEnglishParentDictionaryItem(string expectedEnglishParentValue) { - var languages = ServiceContext.LocalizationService.GetAllLanguages().ToList(); + var languages = LocalizationService.GetAllLanguages().ToList(); var englishLanguage = languages.Single(l => l.IsoCode == "en-GB"); - ServiceContext.LocalizationService.Save( + LocalizationService.Save( new DictionaryItem("Parent") { Translations = new List @@ -749,10 +781,10 @@ namespace Umbraco.Tests.Packaging private void AddExistingEnglishAndNorwegianParentDictionaryItem(string expectedEnglishParentValue, string expectedNorwegianParentValue) { - var languages = ServiceContext.LocalizationService.GetAllLanguages().ToList(); + var languages = LocalizationService.GetAllLanguages().ToList(); var englishLanguage = languages.Single(l => l.IsoCode == "en-GB"); var norwegianLanguage = languages.Single(l => l.IsoCode == "nb-NO"); - ServiceContext.LocalizationService.Save( + LocalizationService.Save( new DictionaryItem("Parent") { Translations = new List diff --git a/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs b/src/Umbraco.Tests.Integration/Umbraco.Core/Packaging/PackageInstallationTest.cs similarity index 58% rename from src/Umbraco.Tests/Packaging/PackageInstallationTest.cs rename to src/Umbraco.Tests.Integration/Umbraco.Core/Packaging/PackageInstallationTest.cs index 2d238c9057..93a6ef010b 100644 --- a/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Core/Packaging/PackageInstallationTest.cs @@ -2,74 +2,21 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Moq; using NUnit.Framework; -using Umbraco.Core.Configuration.Models; using Umbraco.Core.Hosting; using Umbraco.Core.Models.Packaging; using Umbraco.Core.Packaging; -using Umbraco.Core.PropertyEditors; -using Umbraco.Core.Scoping; -using Umbraco.Core.Serialization; -using Umbraco.Core.Services; -using Umbraco.Core.Strings; -using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.Integration.Testing; using Umbraco.Tests.Testing; namespace Umbraco.Tests.Packaging { [TestFixture] [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] - public class PackageInstallationTest : TestWithDatabaseBase + public class PackageInstallationTest : UmbracoIntegrationTest { - private DirectoryInfo _testBaseFolder; - - public override void SetUp() - { - base.SetUp(); - var path = Path.Combine(TestHelper.WorkingDirectory, Guid.NewGuid().ToString()); - if (!Directory.Exists(path)) - Directory.CreateDirectory(path); - _testBaseFolder = new DirectoryInfo(path); - } - - public override void TearDown() - { - base.TearDown(); - - //clear out files/folders - if (_testBaseFolder.Exists) - _testBaseFolder.Delete(true); - } - - private CompiledPackageXmlParser Parser => new CompiledPackageXmlParser( - new ConflictingPackageData( - ServiceContext.MacroService, - ServiceContext.FileService), - Microsoft.Extensions.Options.Options.Create(new GlobalSettings())); - - private PackageDataInstallation PackageDataInstallation => new PackageDataInstallation( - NullLoggerFactory.Instance.CreateLogger(), NullLoggerFactory.Instance, ServiceContext.FileService, ServiceContext.MacroService, ServiceContext.LocalizationService, - ServiceContext.DataTypeService, ServiceContext.EntityService, - ServiceContext.ContentTypeService, ServiceContext.ContentService, - Factory.GetRequiredService(), - Factory.GetRequiredService(), - Factory.GetRequiredService(), - Microsoft.Extensions.Options.Options.Create(new GlobalSettings()), - Factory.GetRequiredService(), - Factory.GetRequiredService(), - Factory.GetRequiredService() - ); - - private IPackageInstallation PackageInstallation => new PackageInstallation( - PackageDataInstallation, - new PackageFileInstallation(Parser, IOHelper, HostingEnvironment, ProfilingLogger), - Parser, Mock.Of(), - //we don't want to extract package files to the real root, so extract to a test folder - Mock.Of(x => x.ApplicationPhysicalPath == _testBaseFolder.FullName)); + private IHostingEnvironment HostingEnvironment => GetRequiredService(); + private IPackageInstallation PackageInstallation => GetRequiredService(); private const string DocumentTypePickerPackage = "Document_Type_Picker_1.1.umb"; private const string HelloPackage = "Hello_1.0.0.zip"; @@ -79,7 +26,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"), DocumentTypePickerPackage))); + new FileInfo(Path.Combine(HostingEnvironment.MapPathContentRoot("~/TestData/Packages"), DocumentTypePickerPackage))); Assert.IsNotNull(package); Assert.AreEqual(1, package.Files.Count); Assert.AreEqual("095e064b-ba4d-442d-9006-3050983c13d8.dll", package.Files[0].UniqueFileName); @@ -102,7 +49,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"), HelloPackage))); + new FileInfo(Path.Combine(HostingEnvironment.MapPathContentRoot("~/TestData/Packages"), HelloPackage))); Assert.IsNotNull(package); Assert.AreEqual(0, package.Files.Count); Assert.AreEqual("Hello", package.Name); @@ -128,12 +75,12 @@ namespace Umbraco.Tests.Packaging { //copy a file to the same path that the package will install so we can detect file conflicts - var filePath = Path.Combine(_testBaseFolder.FullName, "bin", "Auros.DocumentTypePicker.dll"); + var filePath = Path.Combine(HostingEnvironment.MapPathContentRoot("~/"), "bin", "Auros.DocumentTypePicker.dll"); Directory.CreateDirectory(Path.GetDirectoryName(filePath)); File.WriteAllText(filePath, "test"); //this is where our test zip file is - var packageFile = Path.Combine(IOHelper.MapPath("~/Packaging/packages"), DocumentTypePickerPackage); + var packageFile = Path.Combine(HostingEnvironment.MapPathContentRoot("~/TestData/Packages"), DocumentTypePickerPackage); Console.WriteLine(packageFile); var package = PackageInstallation.ReadPackage(new FileInfo(packageFile)); @@ -141,7 +88,7 @@ namespace Umbraco.Tests.Packaging Assert.IsNotNull(preInstallWarnings); Assert.AreEqual(1, preInstallWarnings.FilesReplaced.Count()); - Assert.AreEqual("bin\\Auros.DocumentTypePicker.dll", preInstallWarnings.FilesReplaced.First()); + Assert.AreEqual(Path.Combine("bin", "Auros.DocumentTypePicker.dll"), preInstallWarnings.FilesReplaced.First()); // TODO: More Asserts } @@ -151,7 +98,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"), DocumentTypePickerPackage))); + new FileInfo(Path.Combine(HostingEnvironment.MapPathContentRoot("~/TestData/Packages"), DocumentTypePickerPackage))); var def = PackageDefinition.FromCompiledPackage(package); def.Id = 1; @@ -161,8 +108,8 @@ namespace Umbraco.Tests.Packaging 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(_testBaseFolder.FullName, result[0]))); + Assert.AreEqual(Path.Combine("bin", "Auros.DocumentTypePicker.dll"), result[0]); + Assert.IsTrue(File.Exists(Path.Combine(HostingEnvironment.MapPathContentRoot("~/"), result[0]))); //make sure the def is updated too Assert.AreEqual(result.Count, def.Files.Count); @@ -173,7 +120,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"), DocumentTypePickerPackage))); + new FileInfo(Path.Combine(HostingEnvironment.MapPathContentRoot("~/TestData/Packages"), DocumentTypePickerPackage))); var def = PackageDefinition.FromCompiledPackage(package); def.Id = 1; def.PackageId = Guid.NewGuid(); @@ -186,7 +133,5 @@ namespace Umbraco.Tests.Packaging //make sure the def is updated too Assert.AreEqual(summary.DataTypesInstalled.Count(), def.DataTypes.Count); } - - } } diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/EntityXmlSerializerTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/EntityXmlSerializerTests.cs index a0068f8c3d..8a04caeac7 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/EntityXmlSerializerTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/EntityXmlSerializerTests.cs @@ -8,7 +8,7 @@ using Umbraco.Core.Services; using Umbraco.Tests.Common.Builders; using Umbraco.Tests.Common.Builders.Extensions; using Umbraco.Tests.Integration.Testing; -using Umbraco.Tests.Integration.Umbraco.Infrastructure.Services.Importing; +using Umbraco.Tests.Services.Importing; using Umbraco.Tests.Testing; namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services diff --git a/src/Umbraco.Tests/Services/Importing/CheckboxList-Content-Package.xml b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/CheckboxList-Content-Package.xml similarity index 100% rename from src/Umbraco.Tests/Services/Importing/CheckboxList-Content-Package.xml rename to src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/CheckboxList-Content-Package.xml diff --git a/src/Umbraco.Tests/Services/Importing/CompositionsTestPackage-Random.xml b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/CompositionsTestPackage-Random.xml similarity index 100% rename from src/Umbraco.Tests/Services/Importing/CompositionsTestPackage-Random.xml rename to src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/CompositionsTestPackage-Random.xml diff --git a/src/Umbraco.Tests/Services/Importing/CompositionsTestPackage.xml b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/CompositionsTestPackage.xml similarity index 100% rename from src/Umbraco.Tests/Services/Importing/CompositionsTestPackage.xml rename to src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/CompositionsTestPackage.xml diff --git a/src/Umbraco.Tests/Services/Importing/Fanoe-Package.xml b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/Fanoe-Package.xml similarity index 100% rename from src/Umbraco.Tests/Services/Importing/Fanoe-Package.xml rename to src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/Fanoe-Package.xml diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/ImportResources.Designer.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/ImportResources.Designer.cs index 488108ebda..f7e0541d95 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/ImportResources.Designer.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/ImportResources.Designer.cs @@ -8,7 +8,7 @@ // //------------------------------------------------------------------------------ -namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services.Importing { +namespace Umbraco.Tests.Services.Importing { using System; @@ -19,7 +19,7 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services.Importing { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class ImportResources { @@ -61,6 +61,88 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services.Importing { } } + /// + /// Looks up a localized string similar to <?xml version="1.0" encoding="UTF-8" standalone="no"?> + ///<umbPackage> + /// <files /> + /// <info> + /// <package> + /// <name>CheckboxListTest</name> + /// <version>1</version> + /// <license url="http://www.opensource.org/licenses/mit-license.php">MIT license</license> + /// <url>1</url> + /// <requirements> + /// <major>3</major> + /// <minor>0</minor> + /// <patch>0</patch> + /// </requirements> + /// </package> + /// <author> + /// <name>1</name> + /// <website>1</website> + /// </author> + /// <r [rest of string was truncated]";. + /// + internal static string CheckboxList_Content_Package { + get { + return ResourceManager.GetString("CheckboxList_Content_Package", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to <?xml version="1.0" encoding="UTF-8" standalone="no"?> + ///<umbPackage> + /// <files /> + /// <info> + /// <package> + /// <name>Compositions Packaged</name> + /// <version>1.0</version> + /// <license url="http://opensource.org/licenses/MIT">MIT License</license> + /// <url>http://blog.sitereactor.dk</url> + /// <requirements> + /// <major>3</major> + /// <minor>0</minor> + /// <patch>0</patch> + /// </requirements> + /// </package> + /// <author> + /// <name>Morten Christensen</name> + /// <website>h [rest of string was truncated]";. + /// + internal static string CompositionsTestPackage { + get { + return ResourceManager.GetString("CompositionsTestPackage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to <?xml version="1.0" encoding="UTF-8" standalone="no"?> + ///<umbPackage> + /// <files /> + /// <info> + /// <package> + /// <name>Composite Test</name> + /// <version>dfsfd</version> + /// <license url="http://opensource.org/licenses/MIT">MIT License</license> + /// <url>ddsff</url> + /// <requirements> + /// <major>3</major> + /// <minor>0</minor> + /// <patch>0</patch> + /// </requirements> + /// </package> + /// <author> + /// <name>fsdfds</name> + /// <website>sfdf</website> + /// </author> + /// <rea [rest of string was truncated]";. + /// + internal static string CompositionsTestPackage_Random { + get { + return ResourceManager.GetString("CompositionsTestPackage_Random", resourceCulture); + } + } + /// /// Looks up a localized string similar to <?xml version="1.0" encoding="utf-8" ?> ///<umbPackage> @@ -86,5 +168,243 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services.Importing { return ResourceManager.GetString("Dictionary_Package", resourceCulture); } } + + /// + /// Looks up a localized string similar to <?xml version="1.0" encoding="UTF-8" standalone="no"?> + ///<umbPackage> + /// <files> + /// <file> + /// <guid>bootstrap.min.js</guid> + /// <orgPath>/js</orgPath> + /// <orgName>bootstrap.min.js</orgName> + /// </file> + /// <file> + /// <guid>jquery.min.js</guid> + /// <orgPath>/js</orgPath> + /// <orgName>jquery.min.js</orgName> + /// </file> + /// <file> + /// <guid>top-image.jpg</guid> + /// <orgPath>/Media/1001</orgPath> + /// <orgName>top-image.jpg</orgName> + /// </file> + /// <file> + /// <guid>top-im [rest of string was truncated]";. + /// + internal static string Fanoe_Package { + get { + return ResourceManager.GetString("Fanoe_Package", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to <?xml version="1.0" encoding="UTF-8" standalone="no"?> + ///<umbPackage> + /// <files /> + /// <info> + /// <package> + /// <name>DocTypeError</name> + /// <version>1</version> + /// <license url="http://www.opensource.org/licenses/mit-license.php">Personal license</license> + /// <url>http://www.iseli-webconsulting.de</url> + /// <requirements> + /// <major>3</major> + /// <minor>0</minor> + /// <patch>0</patch> + /// </requirements> + /// </package> + /// <author> + /// <name>Iseli Webconsulting</name> [rest of string was truncated]";. + /// + internal static string InheritedDocTypes_Package { + get { + return ResourceManager.GetString("InheritedDocTypes_Package", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to <?xml version="1.0" encoding="utf-8"?> + ///<umbPackage> + /// <files /> + /// <info> + /// <package> + /// <name>Package With MediaTypes And Media + Folder</name> + /// <version>1.0.0</version> + /// <iconUrl></iconUrl> + /// <license url="http://opensource.org/licenses/MIT">MIT License</license> + /// <url>http://www.umbraco.com</url> + /// <requirements type="Strict"> + /// <major>0</major> + /// <minor>5</minor> + /// <patch>0</patch> + /// </requirements> + /// </package> + /// <author> + /// <name [rest of string was truncated]";. + /// + internal static string MediaTypesAndMedia_Package_xml { + get { + return ResourceManager.GetString("MediaTypesAndMedia_Package.xml", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to <?xml version="1.0" encoding="utf-8" ?> + ///<DocumentType> + /// <Info> + /// <Name>test</Name> + /// <Alias>test</Alias> + /// <Icon>folder.gif</Icon> + /// <Thumbnail>folder.png</Thumbnail> + /// <Description> + /// </Description> + /// <AllowAtRoot>False</AllowAtRoot> + /// <AllowedTemplates> + /// <Template>test</Template> + /// </AllowedTemplates> + /// <DefaultTemplate>test</DefaultTemplate> + /// </Info> + /// <Structure> + /// <DocumentType>test</DocumentType> + /// </Structure> + /// <GenericProperties> + /// <GenericProperty> [rest of string was truncated]";. + /// + internal static string SingleDocType { + get { + return ResourceManager.GetString("SingleDocType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to <?xml version="1.0" encoding="UTF-8" standalone="no"?> + ///<umbPackage> + /// <files> + /// <file> + /// <guid>Map.cshtml</guid> + /// <orgPath>/macroScripts</orgPath> + /// <orgName>Map.cshtml</orgName> + /// </file> + /// <file> + /// <guid>AccountController.cs</guid> + /// <orgPath>/App_Code</orgPath> + /// <orgName>AccountController.cs</orgName> + /// </file> + /// <file> + /// <guid>ContactController.cs</guid> + /// <orgPath>/App_Code</orgPath> + /// <orgName>ContactController.cs</orgName> + /// </file> + /// [rest of string was truncated]";. + /// + internal static string StandardMvc_Package { + get { + return ResourceManager.GetString("StandardMvc_Package", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to <?xml version="1.0" encoding="UTF-8" standalone="no"?> + ///<umbPackage> + /// <files /> + /// <info> + /// <package> + /// <name>Template-Update</name> + /// <version>0.1</version> + /// <license url="http://www.opensource.org/licenses/mit-license.php">MIT license</license> + /// <url>https://our.umbraco.com/projects</url> + /// <requirements> + /// <major>3</major> + /// <minor>0</minor> + /// <patch>0</patch> + /// </requirements> + /// </package> + /// <author> + /// <name>Morten Christensen</name> + /// [rest of string was truncated]";. + /// + internal static string TemplateOnly_Package { + get { + return ResourceManager.GetString("TemplateOnly_Package", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to <?xml version="1.0" encoding="UTF-8" standalone="no"?> + ///<umbPackage> + /// <files /> + /// <info> + /// <package> + /// <name>Template-Update</name> + /// <version>0.1</version> + /// <license url="http://www.opensource.org/licenses/mit-license.php">MIT license</license> + /// <url>https://our.umbraco.com/projects</url> + /// <requirements> + /// <major>3</major> + /// <minor>0</minor> + /// <patch>0</patch> + /// </requirements> + /// </package> + /// <author> + /// <name>Morten Christensen</name> + /// [rest of string was truncated]";. + /// + internal static string TemplateOnly_Updated_Package { + get { + return ResourceManager.GetString("TemplateOnly_Updated_Package", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to <?xml version="1.0" encoding="UTF-8" standalone="no"?> + ///<umbPackage> + /// <files> + /// <file> + /// <guid>uBlogsy.BusinessLogic.dll</guid> + /// <orgPath>/bin</orgPath> + /// <orgName>uBlogsy.BusinessLogic.dll</orgName> + /// </file> + /// <file> + /// <guid>uBlogsy.BusinessLogic.pdb</guid> + /// <orgPath>/bin</orgPath> + /// <orgName>uBlogsy.BusinessLogic.pdb</orgName> + /// </file> + /// <file> + /// <guid>uBlogsy.Common.dll</guid> + /// <orgPath>/bin</orgPath> + /// <orgName>uBlogsy.Common.dll</orgNam [rest of string was truncated]";. + /// + internal static string uBlogsy_Package { + get { + return ResourceManager.GetString("uBlogsy_Package", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to <?xml version="1.0" encoding="UTF-8" standalone="no"?> + ///<umbPackage> + /// <files> + /// <file> + /// <guid>XSLTsearch.xslt</guid> + /// <orgPath>/xslt</orgPath> + /// <orgName>XSLTsearch.xslt</orgName> + /// </file> + /// <file> + /// <guid>XSLTsearch.cs</guid> + /// <orgPath>/App_Code</orgPath> + /// <orgName>XSLTsearch.cs</orgName> + /// </file> + /// </files> + /// <info> + /// <package> + /// <name>XSLTsearch</name> + /// <version>3.0.4</version> + /// <license url="http://www.opensource.org/licenses/mit-li [rest of string was truncated]";. + /// + internal static string XsltSearch_Package { + get { + return ResourceManager.GetString("XsltSearch_Package", resourceCulture); + } + } } } diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/ImportResources.resx b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/ImportResources.resx index fdf7880297..e8029b6520 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/ImportResources.resx +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/ImportResources.resx @@ -1,17 +1,17 @@  - @@ -118,7 +118,43 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + InheritedDocTypes-Package.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + + StandardMvc-Package.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + + uBlogsy-Package.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + + XsltSearch-Package.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + + SingleDocType.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + + TemplateOnly-Package.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + + TemplateOnly-Updated-Package.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + + CheckboxList-Content-Package.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + Dictionary-Package.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + CompositionsTestPackage.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + + CompositionsTestPackage-Random.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + + Fanoe-Package.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + + MediaTypesAndMedia-Package.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + diff --git a/src/Umbraco.Tests/Services/Importing/InheritedDocTypes-Package.xml b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/InheritedDocTypes-Package.xml similarity index 100% rename from src/Umbraco.Tests/Services/Importing/InheritedDocTypes-Package.xml rename to src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/InheritedDocTypes-Package.xml diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/MediaTypesAndMedia-Package.xml b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/MediaTypesAndMedia-Package.xml new file mode 100644 index 0000000000..e51fee48f8 --- /dev/null +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/MediaTypesAndMedia-Package.xml @@ -0,0 +1,158 @@ + + + + + + Package With MediaTypes And Media + Folder + 1.0.0 + + MIT License + http://www.umbraco.com + + 0 + 5 + 0 + + + + dteam + http://www.umbraco.com + + + + + + + + + Folder + Folder + icon-folder + icon-folder + + True + + + Folder + Image + File + + + + + + + Image + Image + icon-picture + icon-picture + + True + + + + + Upload image + umbracoFile + Umbraco.ImageCropper + 1df9f033-e6d4-451f-b8d2-e0cbc50a836f + Image + True + + + + false + + + + Width + umbracoWidth + Umbraco.Label + 8e7f995c-bd81-4627-9932-c40e568ec788 + Image + False + + + + false + + + + Height + umbracoHeight + Umbraco.Label + 8e7f995c-bd81-4627-9932-c40e568ec788 + Image + False + + + + false + + + + Size + umbracoBytes + Umbraco.Label + 930861bf-e262-4ead-a704-f99453565708 + Image + False + + + + false + + + + Type + umbracoExtension + Umbraco.Label + f0bc4bfb-b499-40d6-ba86-058885a5178c + Image + False + + + + false + + + + + + 3 + Image + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Umbraco.Tests/Services/Importing/SingleDocType.xml b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/SingleDocType.xml similarity index 100% rename from src/Umbraco.Tests/Services/Importing/SingleDocType.xml rename to src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/SingleDocType.xml diff --git a/src/Umbraco.Tests/Services/Importing/StandardMvc-Package.xml b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/StandardMvc-Package.xml similarity index 100% rename from src/Umbraco.Tests/Services/Importing/StandardMvc-Package.xml rename to src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/StandardMvc-Package.xml diff --git a/src/Umbraco.Tests/Services/Importing/TemplateOnly-Package.xml b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/TemplateOnly-Package.xml similarity index 100% rename from src/Umbraco.Tests/Services/Importing/TemplateOnly-Package.xml rename to src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/TemplateOnly-Package.xml diff --git a/src/Umbraco.Tests/Services/Importing/TemplateOnly-Updated-Package.xml b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/TemplateOnly-Updated-Package.xml similarity index 100% rename from src/Umbraco.Tests/Services/Importing/TemplateOnly-Updated-Package.xml rename to src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/TemplateOnly-Updated-Package.xml diff --git a/src/Umbraco.Tests/Services/Importing/XsltSearch-Package.xml b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/XsltSearch-Package.xml similarity index 100% rename from src/Umbraco.Tests/Services/Importing/XsltSearch-Package.xml rename to src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/XsltSearch-Package.xml diff --git a/src/Umbraco.Tests/Services/Importing/uBlogsy-Package.xml b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/uBlogsy-Package.xml similarity index 100% rename from src/Umbraco.Tests/Services/Importing/uBlogsy-Package.xml rename to src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/Importing/uBlogsy-Package.xml diff --git a/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj b/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj index b996205712..0614405e41 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj +++ b/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj @@ -19,6 +19,16 @@ + + ResXFileCodeGenerator + ImportResources.Designer.cs + Designer + + + True + True + ImportResources.resx + @@ -26,7 +36,22 @@ + + Designer + + + + + + + + Designer + + + + + @@ -58,20 +83,4 @@ - - - ImportResources.resx - True - True - - - - - - Designer - ImportResources.Designer.cs - ResXFileCodeGenerator - - - diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.PublishedCache.NuCache/SnapDictionaryTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.PublishedCache.NuCache/SnapDictionaryTests.cs index 91317848c6..05157d961a 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.PublishedCache.NuCache/SnapDictionaryTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.PublishedCache.NuCache/SnapDictionaryTests.cs @@ -92,6 +92,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Umbraco.PublishedCache Assert.AreEqual("one", s1.Get(1)); } + [Retry(5)] // TODO make this test non-flaky. [Test] public async Task CollectValues() { @@ -218,6 +219,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Umbraco.PublishedCache Assert.AreEqual(0, d.Count); } + [Retry(5)] // TODO make this test non-flaky. [Test] public async Task CollectNulls() { @@ -527,6 +529,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Umbraco.PublishedCache Assert.AreEqual(1, d.Test.GetValues(1).Length); } + [Retry(5)] // TODO make this test non-flaky. [Test] public async Task RandomTest1() { @@ -573,6 +576,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Umbraco.PublishedCache Assert.AreEqual(0, d.SnapCount); } + [Retry(5)] // TODO make this test non-flaky. [Test] public async Task RandomTest2() { diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs index 41705c7774..420ddbe952 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs @@ -4,11 +4,11 @@ using System.Globalization; using System.IO; using System.Linq; using System.Text; +using System.Threading.Tasks; using System.Xml; using Microsoft.Extensions.Logging; using NPoco; using Umbraco.Core; -using Umbraco.Core.Configuration; using Umbraco.Core.Hosting; using Umbraco.Core.IO; using Umbraco.Core.Models; @@ -28,7 +28,6 @@ using Umbraco.Web.Composing; using Umbraco.Web.PublishedCache; using Umbraco.Web.Scheduling; using File = System.IO.File; -using Task = System.Threading.Tasks.Task; namespace Umbraco.Tests.LegacyXmlPublishedCache { @@ -761,7 +760,7 @@ AND (umbracoNode.id=@id)"; // The indenting resumes once the mixed content element is closed." - says MSDN // about XmlWriterSettings.Indent - // so ImportContent must also make sure of ignoring whitespaces! + // so ImportContentBase must also make sure of ignoring whitespaces! var sb = new StringBuilder(); using (var xmlWriter = XmlWriter.Create(sb, new XmlWriterSettings diff --git a/src/Umbraco.Tests/Packaging/Packages/Document_Type_Picker_1.1.umb b/src/Umbraco.Tests/Packaging/Packages/Document_Type_Picker_1.1.umb deleted file mode 100644 index 18449bd3735870c98e711525a7ea9883941e321f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6147 zcma)=RZJWVl!bAp6qn)@DHL~ich`a9GDv|U9o*dqXP~$j8>HwIcc+6CcZxg3_Ww7V z%_f^o_TGmt_vYr@m-BSARFP4L5D*YB5dOwu%SOrHjmG{vav>nF{0m`zTP^`UD|RbO zJ{xvEJ{}u(VJ6 z>RqIdc64-an*}K5AZ-UAemx4uIqk2~WIP~j*f$8$!If+UInR2e$q(g!=FuH-F4L-4 z9dYM91Y;{#CsPV><>WPb+O>B}bWg&$d6f8SWagJ$DRHjNnOwXeRSe4r{pSursKP2+ z5*!Csj==m?xdmqNt(gZb?I}|u@T-17dXyRCfw)Zm+V*Ay#4LOW7g^5bn$2t^_RxbI zb~nFqnEJIPe82VAW1vgLGN5?NciA2C@u8*RO#64`l1El`KGb^eq{hBl4d;)_RN9|h z;2vSvnrG6N7_BRX3JZ;r19(S{2)`5E;kr~#Mr_~Kl~nJtaO8z|tn0FriIPLEk-0X6 zvkrodN)M#{Ib62%^qF1q_<~==kbKIOK6YgSp`>(Lk|N2qo8u_hSPg*eGz2WtsELJg z&i!U|fb7dz**KzB*2#Yx-xvl~W{KI5olssKlTbSoK4HwQbkz%LRT0Zs&^mDvdZM^f zaJ6-Hs!Z$PJ3v>ei2UT60fWv}du)Y_hI){-2L5Ct%oxT=k6UZvoJj>&I^!)jbKH&Z zn`AY%?Dat5!Xs-{1DBUi;|eN+y0ooBU9<+W zXtE^aR5vrFAIN4;N9Ijz8F;tVH*fW17b6FKG25!*j&7OkS`7Kf{_6PgnqvHT(@?hk zLv`$3uPtrWhuOD|xBV3K-Z=x^L*J84#xI&3h8UoEF81qz>!y#mJLY5D1@>wIkH3r( zKD4hMlFtyiV)%T#VfSo7!H3Sox<$um(_bE!wiwmw%hWWmIl0OA#aZY==|=ILqn824 zdZOgHV!5W3XWn)GHS;s9Br+0=)89}`$X;b0zt|G{kaO!YVoO;^U|9U?4*?C z!%|*r>zZj^}+;2PT53CIGVe$h&mj**Cis2A- z`8%{XPB83WbizI%%r;n-d1*hl?rK}yc6^H~msEv1`WhYfE;4vHO~yK60KQbPy+H~& z`rp(kJ4~kAZ*nz~nKy2r#MMU^21S{qW`tix;>?ZXUT`dLZ6bRY-q$g#o^Br|Ldz)_ zDg*O{d2CY7!@cjB=Op+FYx~N>fq<(Ap`nroe+W26+&nUTGK}0iDtO^=(%MYj)iRO# z?oT?0ZWTHZ_P``(^9-`JtTQyLdIA#NI|)c0!3Dc?);nWKZ{Cszq-_5F9y*io>5Hs_ zAnWq_~2s-hqXHNdHM>xV6h`*qRK!=$MrB=hXz+vHdBI$$LU=ggLL z^jO=a$aVtKjpt#M5lUhlQ9Qx@QXkS}vB$}6U~xu4-=rlJC}joEaE7=&(6 z-FR1gL)Z7leecjH^`_wsAd@Ib5u$uuuP82a*C4daRl_d$88oY z?qOz0+t3y^;Qp(6uXXmoj0;jop*I%skt<_q-YP(blUACHztsDilz|0Jd@ULU$sc+RLK@h*@r?;B}v z5IR{Rml=D#;)V+nRmdHtzuv~~FmDRVv)(rn+fk1Yzhcp14yw3Di#tW^85Hq1ZQVh1EGVb^Pax`C0-tS*wo!YH)*C?b4l>Vd)h$bQvM zNSz2P#E`kRXV;MRxn)F7ARMWiXcSQ(iu@Eq6X`bGc#i~zADH>Am4e1_OzOkKBI!m< z5joA{8()ep_|OZrSOWD$LqRZUQF*|}qFR(&V0a?+IQvVvdeiP8IhwTVglv`-h2`=uZ zwI0SrzXM<)cRy#(T3;a9U#glApBlWuG3yBb(Dk~5p$5_3H;wnI@2XOw=YrfS{!*7u z!a)pbk#MBP%1@F8K3KPxe-~fzp(YrDTIB`9&np ztMlJ}{Q*|$yDUgbRY&CGkDxUh_5?3`NWEGwS7;n+Y6(spup!c*h{@AP*SUfef@0ej zlCCAABKL2mkX~}vCOp`U((16TP~2 zxKU*Dd2$2VNmPQMwt~1_3Aj|k8m7@uWe7mq1%nXTy!oGrE^CkEty^Pbnro5D>Aspl ziUt=k7gWZqNu}1Qa&_*oo?Z2$e^eRLTjSg3$omYe6;Z8!i9pU+Of8oM{Iqvl_}wkRr_ix0&v0i6~@8!2pJC z0?yO1Lt}PzprseZS^620EvXAmX#;6|$gCV5aw66pHHh;7pohN53HJUgym11h0jGT- znJ#Tb$S;1B9Xh0Q7{kcJ_icsw*?<*Go{dAqDRdLD{m}#B1gZm$H28~O^a20@7UFd; z^36^G%h6?Y1+qrj_`+%8r5B+Oov?kBJsBZ})OR8HZny>6jN6V{zL7h7RpcaO z5gO`zz>0is7z+KjO(6iDFK#}CfU5I7l!)6=*Ub>G`aIq+tQvmPydGtaCD@oHL7j90pUrRf%j^&T{Jpd~)}G*FsWLG6xG|$2-QLDh zs_|#`jtveNrTh3?w*S(#Uy?=#qbF-XKV_X`86NIM@j3^XQuIuxS==-_^47lWv$vJq zAS-P47_7Qm>CT+iJm*k8G{$l8J-3y)##sctTD3kLG_*=!qr&g1_QQB(#59Eb%crLV zyB0Zy2en_nt@k~(T!~f6Jdu38;wfg|vSD{Jz{iU_vPH+URQobavCM`S`ojBP9;v#WL= z>Z>yLbwtLvJL$L%%hHAtZ)_WxxdjDH?IaWh#|7-DgT4f|^*0Mk?5N0h@9(^Ql#GIQ zIPZo_?1=li8lhhfl>D5VUi_o>5u6|BkcdlML!dv$Kug0CZ>N#VXU~Vj14NKiemoKL z8^EUPT9E^p7+)OUXJn4d@ETz@+s$N&yHVJsIlvm?kZ-d?&o2WH?C?`O%u(#MA9=o$ zFRG_EyMLUP8e&~#83rVlo?X{NHswSt)xi)PQDuT0WK_FkPNI*INOd%XY!yZvU(ZCP zliy@uGXh7|XauM@$j8aYgvj>o&U@JHj=HIZDP;bHTs~l z#J%r#h;A3H8V{i4hs=?{$rrsAJ!&sZf`nK1fw!_E1@2Ahg8SUhmgqI(azDLQ4M@iN zC^^OE24Bns*a5 zYX13M#}>VKWJ{)NbfNLRWwol7POHxS-zz(`+@#DH?~BEaN{`5>vKM7uzs4yEHtZ6? z%b<$VnhJ`OpC?)Tqq&=9t&ST?%NVFxmMrV7#gut+RWr>z947bo(U#Wsuepn;P z2CFR7mLNf@aY{4MQuz%Ey_;>92&Gazd0)1pkuV>KwP$FYH0)fzzAyzRb18KS!#fP| zDm)@sn%t?!zS76PA{H;7zsbm`LvQ< zujr5-h3qLt)ms{ZJ(|eq>yHUna6T$~q`<+!`i{I(0_}hdrjfw{)sLA>FU8Y#MTJzp z*e50C?c_m#FH~OBZ=h(72(>P=r?NoiPd(G^7sqAn!C@OdA#19pGdl0kztLc)mT@T{ z7Be~GoNdd0Ql|LXVMXGm$s{cIE-K^)y>^vSo|GH9rXpxxY-9~l>K#GNBhG=cQF4E7 zPQ)#W|4u#$G>eS?!x}d`c5DPSs!rJR`EPK;qm=L;;3B%{6}`SczsYyVAJc^r&E~K3qG|;RjqSU&+G3p*#4?VcqDZL6ezyGN!?Y&PAwMCGXttI%{xhL8yTaq?Eq< z!Dq0=I+ttmY;wh-`*-ieub$qXlxxTIW~q$@is3z}pp}|?M^7ZTK{O0Ozv+k!XItJ+ ztD2PAu@d&U|2j?_Wf89v($Jg`iub|tXMYh$t*YDIY6l0NCw1QrW4HSAJsemBZ5|$j zc=CN$8#*Yuse0CY|4tT1nYIGD4da+(W(J81-!t|qml8NsXvJnZY`DH*u52LwX6eOO zS=lmpgRjK@9j`nq4@p?zC}%LCs(}HYF7g11dgq<8GP&Ae+?Lx6RC8wWoWnR|#or^n z#*ni+O-L?dB$%ySp}o{={FHmXPRfKTAdMzNu`Kd!M#z({447Y!hR=?#UPT;3TsRPy zp4Ci{VaAufHd_@(ddu4eCH3r1WMq6r})JmSFPFdJrM`@a{?6axWC`G}A^DqGoIlg>9x%ssmFHAc zA;yh4FL&ObY{WN%1-N@ZkQEL$$G;3WwzQ5C?2izkG6-CG9J)TO|IP@-u!NrV(IF!O z^B$y&*w-qd>0l#76lq7<_+~i>wK9}j-kQ0>a__4_Y*flT4t|0TMxo|Gdl@&YMrW|_ zyf>Rk#+sy%!PnQtW14igo!1F%z6^)&_4m-!DYYxrb*s~~n$M{#zt~jJ>Kv8V(9+i4 z*g{(07N zU#$Pmp^gUD6>_j~06U-(Tzh@KF1lDf`}$<|G`?Nw)~I&+x44+O+%wT@SuA!g(T~rN zmHKA#w_@Giy|&*|g5qw%ZWaBZm22Khof#gU!Ab#UAiM>3)4w63*ZCrZ{O}|}InXr) zDEm-=^Z;@c|HA%q!+sU`IP!MD@9;zfov$>&X%zS7{fYQ{R}k>5k%iHQbAPk6 zv>`9*-r5WeOnzihj!z!MY(Xwx=F_No(zlR6)h*&N*Y?j*K;h+iW6?!6m(t{sdS4>4 zTY#&rxiRB(jqCp|IZW2TpJOt1#LY=mIgpIsnu_FWt6e;Mw;{xXkgAk8nI`qZsD(Bv zyvkm`XZB0;m2<6&qi_R=jZU%pn;+RV=FBo@`K9B9KhnO?%gL_4Nh)<;z>P1{#oja~ zP5Tq~;tO(M&{(VFdC$4z)(}~%)j} zHaDuLH34?Tfy}eJl|Mlb3)|1$JHVBocd@TYa85+6|IRZ*9m?fCGU0)xkq{6-Cf*ZaN#F@~PP8t@EI#8X^FN|lf;z-*2tigPkH zbrg##Zrs60*X2O={)MXU%7Jr7nG#W4#aq<9*%R6EOj#p%)rC#(AeEk5y&=)qjcZUYRKJY3dvzFX0C%hZGvfmP~T#f zQE#0|`ZS;WNqe8?#T>YB${6@t4y*v{!=MN#$xTUjBy>z@K=aez!CKP*$1RsptqQGc zk|dGUV%496zS~E$+R}9%)*?sIBLm?z(d9%m`4Vn|Q4@2fa3w3t2p9ictT=2AdNUk? zLMN+KIpxkKthpLGVjEiap8)2v_dljd3GU)?Wp^bc@nX?%6L_7;3XYi#EDg^QzNTP` zBCVgG%9kK`2UACUoY-(lhqhT1bsm4*L4MLduukZszN-JxqW4DwVYV}pKLAJ%KHlO7 zpyewOysjBD1=o3I%T2PT-pko?rE%RcxC@rsV8_gkw$Oz4V4M&15?no@=0_tjhZ+fC z8$IL`v60`W-u5o)k=v(nZlh|cA|eqX{NLI6KX&^c_5V%M|5cUzFV+9;;P?NfNc#_` z|9SmC{EUV0?-Bt45rGV0H$DE%j^h-f<+aM~_CI_MRr*hAsiLC&dxP{(H~m8q(toG_ E0K6^9ApigX diff --git a/src/Umbraco.Tests/Services/Importing/ImportResources.Designer.cs b/src/Umbraco.Tests/Services/Importing/ImportResources.Designer.cs deleted file mode 100644 index c0f070d1e9..0000000000 --- a/src/Umbraco.Tests/Services/Importing/ImportResources.Designer.cs +++ /dev/null @@ -1,383 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Umbraco.Tests.Services.Importing { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class ImportResources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal ImportResources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Umbraco.Tests.Services.Importing.ImportResources", typeof(ImportResources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to <?xml version="1.0" encoding="UTF-8" standalone="no"?> - ///<umbPackage> - /// <files /> - /// <info> - /// <package> - /// <name>CheckboxListTest</name> - /// <version>1</version> - /// <license url="http://www.opensource.org/licenses/mit-license.php">MIT license</license> - /// <url>1</url> - /// <requirements> - /// <major>3</major> - /// <minor>0</minor> - /// <patch>0</patch> - /// </requirements> - /// </package> - /// <author> - /// <name>1</name> - /// <website>1</website> - /// </author> - /// <r [rest of string was truncated]";. - /// - internal static string CheckboxList_Content_Package { - get { - return ResourceManager.GetString("CheckboxList_Content_Package", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to <?xml version="1.0" encoding="UTF-8" standalone="no"?> - ///<umbPackage> - /// <files /> - /// <info> - /// <package> - /// <name>Compositions Packaged</name> - /// <version>1.0</version> - /// <license url="http://opensource.org/licenses/MIT">MIT License</license> - /// <url>http://blog.sitereactor.dk</url> - /// <requirements> - /// <major>3</major> - /// <minor>0</minor> - /// <patch>0</patch> - /// </requirements> - /// </package> - /// <author> - /// <name>Morten Christensen</name> - /// <website>h [rest of string was truncated]";. - /// - internal static string CompositionsTestPackage { - get { - return ResourceManager.GetString("CompositionsTestPackage", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to <?xml version="1.0" encoding="UTF-8" standalone="no"?> - ///<umbPackage> - /// <files /> - /// <info> - /// <package> - /// <name>Composite Test</name> - /// <version>dfsfd</version> - /// <license url="http://opensource.org/licenses/MIT">MIT License</license> - /// <url>ddsff</url> - /// <requirements> - /// <major>3</major> - /// <minor>0</minor> - /// <patch>0</patch> - /// </requirements> - /// </package> - /// <author> - /// <name>fsdfds</name> - /// <website>sfdf</website> - /// </author> - /// <rea [rest of string was truncated]";. - /// - internal static string CompositionsTestPackage_Random { - get { - return ResourceManager.GetString("CompositionsTestPackage_Random", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to <?xml version="1.0" encoding="utf-8" ?> - ///<umbPackage> - /// <files /> - /// <info> - /// <package> - /// <name>Dictionary-Package</name> - /// <version>1.0</version> - /// <license url="http://www.opensource.org/licenses/mit-license.php">MIT license</license> - /// <url>http://not.available</url> - /// <requirements> - /// <major>3</major> - /// <minor>0</minor> - /// <patch>0</patch> - /// </requirements> - /// </package> - /// <author> - /// <name>Test</name> - /// <website>http://not.available</w [rest of string was truncated]";. - /// - internal static string Dictionary_Package { - get { - return ResourceManager.GetString("Dictionary_Package", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to <?xml version="1.0" encoding="UTF-8" standalone="no"?> - ///<umbPackage> - /// <files> - /// <file> - /// <guid>bootstrap.min.js</guid> - /// <orgPath>/js</orgPath> - /// <orgName>bootstrap.min.js</orgName> - /// </file> - /// <file> - /// <guid>jquery.min.js</guid> - /// <orgPath>/js</orgPath> - /// <orgName>jquery.min.js</orgName> - /// </file> - /// <file> - /// <guid>top-image.jpg</guid> - /// <orgPath>/Media/1001</orgPath> - /// <orgName>top-image.jpg</orgName> - /// </file> - /// <file> - /// <guid>top-im [rest of string was truncated]";. - /// - internal static string Fanoe_Package { - get { - return ResourceManager.GetString("Fanoe_Package", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to <?xml version="1.0" encoding="UTF-8" standalone="no"?> - ///<umbPackage> - /// <files /> - /// <info> - /// <package> - /// <name>DocTypeError</name> - /// <version>1</version> - /// <license url="http://www.opensource.org/licenses/mit-license.php">Personal license</license> - /// <url>http://www.iseli-webconsulting.de</url> - /// <requirements> - /// <major>3</major> - /// <minor>0</minor> - /// <patch>0</patch> - /// </requirements> - /// </package> - /// <author> - /// <name>Iseli Webconsulting</name> [rest of string was truncated]";. - /// - internal static string InheritedDocTypes_Package { - get { - return ResourceManager.GetString("InheritedDocTypes_Package", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to <?xml version="1.0" encoding="utf-8" ?> - ///<DocumentType> - /// <Info> - /// <Name>test</Name> - /// <Alias>test</Alias> - /// <Icon>folder.gif</Icon> - /// <Thumbnail>folder.png</Thumbnail> - /// <Description> - /// </Description> - /// <AllowAtRoot>False</AllowAtRoot> - /// <AllowedTemplates> - /// <Template>test</Template> - /// </AllowedTemplates> - /// <DefaultTemplate>test</DefaultTemplate> - /// </Info> - /// <Structure> - /// <DocumentType>test</DocumentType> - /// </Structure> - /// <GenericProperties> - /// <GenericProperty> [rest of string was truncated]";. - /// - internal static string SingleDocType { - get { - return ResourceManager.GetString("SingleDocType", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to <?xml version="1.0" encoding="UTF-8" standalone="no"?> - ///<umbPackage> - /// <files> - /// <file> - /// <guid>Map.cshtml</guid> - /// <orgPath>/macroScripts</orgPath> - /// <orgName>Map.cshtml</orgName> - /// </file> - /// <file> - /// <guid>AccountController.cs</guid> - /// <orgPath>/App_Code</orgPath> - /// <orgName>AccountController.cs</orgName> - /// </file> - /// <file> - /// <guid>ContactController.cs</guid> - /// <orgPath>/App_Code</orgPath> - /// <orgName>ContactController.cs</orgName> - /// </file> - /// [rest of string was truncated]";. - /// - internal static string StandardMvc_Package { - get { - return ResourceManager.GetString("StandardMvc_Package", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to <?xml version="1.0" encoding="UTF-8" standalone="no"?> - ///<umbPackage> - /// <files /> - /// <info> - /// <package> - /// <name>Template-Update</name> - /// <version>0.1</version> - /// <license url="http://www.opensource.org/licenses/mit-license.php">MIT license</license> - /// <url>https://our.umbraco.com/projects</url> - /// <requirements> - /// <major>3</major> - /// <minor>0</minor> - /// <patch>0</patch> - /// </requirements> - /// </package> - /// <author> - /// <name>Morten Christensen</name> - /// [rest of string was truncated]";. - /// - internal static string TemplateOnly_Package { - get { - return ResourceManager.GetString("TemplateOnly_Package", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to <?xml version="1.0" encoding="UTF-8" standalone="no"?> - ///<umbPackage> - /// <files /> - /// <info> - /// <package> - /// <name>Template-Update</name> - /// <version>0.1</version> - /// <license url="http://www.opensource.org/licenses/mit-license.php">MIT license</license> - /// <url>https://our.umbraco.com/projects</url> - /// <requirements> - /// <major>3</major> - /// <minor>0</minor> - /// <patch>0</patch> - /// </requirements> - /// </package> - /// <author> - /// <name>Morten Christensen</name> - /// [rest of string was truncated]";. - /// - internal static string TemplateOnly_Updated_Package { - get { - return ResourceManager.GetString("TemplateOnly_Updated_Package", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to <?xml version="1.0" encoding="UTF-8" standalone="no"?> - ///<umbPackage> - /// <files> - /// <file> - /// <guid>uBlogsy.BusinessLogic.dll</guid> - /// <orgPath>/bin</orgPath> - /// <orgName>uBlogsy.BusinessLogic.dll</orgName> - /// </file> - /// <file> - /// <guid>uBlogsy.BusinessLogic.pdb</guid> - /// <orgPath>/bin</orgPath> - /// <orgName>uBlogsy.BusinessLogic.pdb</orgName> - /// </file> - /// <file> - /// <guid>uBlogsy.Common.dll</guid> - /// <orgPath>/bin</orgPath> - /// <orgName>uBlogsy.Common.dll</orgNam [rest of string was truncated]";. - /// - internal static string uBlogsy_Package { - get { - return ResourceManager.GetString("uBlogsy_Package", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to <?xml version="1.0" encoding="UTF-8" standalone="no"?> - ///<umbPackage> - /// <files> - /// <file> - /// <guid>XSLTsearch.xslt</guid> - /// <orgPath>/xslt</orgPath> - /// <orgName>XSLTsearch.xslt</orgName> - /// </file> - /// <file> - /// <guid>XSLTsearch.cs</guid> - /// <orgPath>/App_Code</orgPath> - /// <orgName>XSLTsearch.cs</orgName> - /// </file> - /// </files> - /// <info> - /// <package> - /// <name>XSLTsearch</name> - /// <version>3.0.4</version> - /// <license url="http://www.opensource.org/licenses/mit-li [rest of string was truncated]";. - /// - internal static string XsltSearch_Package { - get { - return ResourceManager.GetString("XsltSearch_Package", resourceCulture); - } - } - } -} diff --git a/src/Umbraco.Tests/Services/Importing/ImportResources.resx b/src/Umbraco.Tests/Services/Importing/ImportResources.resx deleted file mode 100644 index 97fb2469be..0000000000 --- a/src/Umbraco.Tests/Services/Importing/ImportResources.resx +++ /dev/null @@ -1,157 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - inheriteddoctypes-package.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - - - standardmvc-package.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - - - ublogsy-package.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - - - xsltsearch-package.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - - - singledoctype.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - - - templateonly-package.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - - - templateonly-updated-package.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - - - checkboxlist-content-package.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - - - dictionary-package.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - - - compositionstestpackage.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - - - compositionstestpackage-random.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - - - fanoe-package.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - - \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 807c64f315..861a16a2f3 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -200,7 +200,6 @@ - @@ -217,12 +216,6 @@ - - - True - True - ImportResources.resx - @@ -271,7 +264,6 @@ Designer - Designer @@ -311,11 +303,6 @@ - - ResXFileCodeGenerator - ImportResources.Designer.cs - Designer - ResXFileCodeGenerator TestFiles.Designer.cs @@ -323,23 +310,8 @@ - - Designer - - - - - - - - Designer - - - - - diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs index cc87797b85..05e4db5daa 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs @@ -11,18 +11,14 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; using Umbraco.Core; -using Umbraco.Core.Configuration.Models; using Umbraco.Core.Dictionary; using Umbraco.Core.Hosting; using Umbraco.Core.Mapping; using Umbraco.Core.Models; using Umbraco.Core.Packaging; using Umbraco.Core.PropertyEditors; -using Umbraco.Core.Scoping; using Umbraco.Core.Security; -using Umbraco.Core.Serialization; using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Web.Common.Attributes; @@ -45,9 +41,7 @@ namespace Umbraco.Web.BackOffice.Controllers // It would be possible to have something like a ContentTypeInfoController for the GetAllPropertyTypeAliases/GetCount/GetAllowedChildren/etc... actions private readonly IEntityXmlSerializer _serializer; - private readonly GlobalSettings _globalSettings; private readonly PropertyEditorCollection _propertyEditors; - private readonly IScopeProvider _scopeProvider; private readonly IContentTypeService _contentTypeService; private readonly UmbracoMapper _umbracoMapper; private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor; @@ -56,15 +50,10 @@ namespace Umbraco.Web.BackOffice.Controllers private readonly ILocalizedTextService _localizedTextService; private readonly IFileService _fileService; private readonly ILogger _logger; - private readonly ILoggerFactory _loggerFactory; private readonly IContentService _contentService; private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; - private readonly ILocalizationService _LocalizationService; - private readonly IMacroService _macroService; - private readonly IEntityService _entityService; private readonly IHostingEnvironment _hostingEnvironment; - private readonly IConfigurationEditorJsonSerializer _configurationEditorJsonSerializer; - private readonly IJsonSerializer _jsonSerializer; + private readonly PackageDataInstallation _packageDataInstallation; public ContentTypeController( ICultureDictionary cultureDictionary, @@ -74,24 +63,17 @@ namespace Umbraco.Web.BackOffice.Controllers UmbracoMapper umbracoMapper, ILocalizedTextService localizedTextService, IEntityXmlSerializer serializer, - IOptions globalSettings, PropertyEditorCollection propertyEditors, - IScopeProvider scopeProvider, IBackOfficeSecurityAccessor backofficeSecurityAccessor, IDataTypeService dataTypeService, IShortStringHelper shortStringHelper, IFileService fileService, ILogger logger, - ILoggerFactory loggerFactory, IContentService contentService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, - ILocalizationService localizationService, - IMacroService macroService, - IEntityService entityService, IHostingEnvironment hostingEnvironment, EditorValidatorCollection editorValidatorCollection, - IConfigurationEditorJsonSerializer configurationEditorJsonSerializer, - IJsonSerializer jsonSerializer) + PackageDataInstallation packageDataInstallation) : base(cultureDictionary, editorValidatorCollection, contentTypeService, @@ -101,9 +83,7 @@ namespace Umbraco.Web.BackOffice.Controllers localizedTextService) { _serializer = serializer; - _globalSettings = globalSettings.Value; _propertyEditors = propertyEditors; - _scopeProvider = scopeProvider; _contentTypeService = contentTypeService; _umbracoMapper = umbracoMapper; _backofficeSecurityAccessor = backofficeSecurityAccessor; @@ -112,15 +92,10 @@ namespace Umbraco.Web.BackOffice.Controllers _localizedTextService = localizedTextService; _fileService = fileService; _logger = logger; - _loggerFactory = loggerFactory; _contentService = contentService; _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider; - _LocalizationService = localizationService; - _macroService = macroService; - _entityService = entityService; _hostingEnvironment = hostingEnvironment; - _configurationEditorJsonSerializer = configurationEditorJsonSerializer; - _jsonSerializer = jsonSerializer; + _packageDataInstallation = packageDataInstallation; } [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] @@ -640,30 +615,13 @@ namespace Umbraco.Web.BackOffice.Controllers return NotFound(); } - var dataInstaller = new PackageDataInstallation( - _loggerFactory.CreateLogger(), - _loggerFactory, - _fileService, - _macroService, - _LocalizationService, - _dataTypeService, - _entityService, - _contentTypeService, - _contentService, - _propertyEditors, - _scopeProvider, - _shortStringHelper, - Options.Create(_globalSettings), - _localizedTextService, - _configurationEditorJsonSerializer, - _jsonSerializer); var xd = new XmlDocument {XmlResolver = null}; xd.Load(filePath); var userId = _backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().ResultOr(0); var element = XElement.Parse(xd.InnerXml); - dataInstaller.ImportDocumentType(element, userId); + _packageDataInstallation.ImportDocumentType(element, userId); // Try to clean up the temporary file. try diff --git a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs index f03130b537..67cd41ff29 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs @@ -479,7 +479,7 @@ namespace Umbraco.Web.BackOffice.Controllers { // Authorize... var requirement = new MediaPermissionsResourceRequirement(); - var authorizationResult = await _authorizationService.AuthorizeAsync(User, _mediaService.GetById(move.Id), requirement); + var authorizationResult = await _authorizationService.AuthorizeAsync(User, new MediaPermissionsResource(_mediaService.GetById(move.Id)), requirement); if (!authorizationResult.Succeeded) { return Forbid(); diff --git a/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs b/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs index 36bf4d2fca..7f6bfe781f 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs @@ -13,11 +13,9 @@ using Umbraco.Core.Hosting; using Umbraco.Core.Models.Packaging; using Umbraco.Core.Security; using Umbraco.Core.Services; -using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.Common.Attributes; using Umbraco.Web.Common.Authorization; using Umbraco.Web.Common.Exceptions; -using Umbraco.Web.Security; namespace Umbraco.Web.BackOffice.Controllers { @@ -105,7 +103,7 @@ namespace Umbraco.Web.BackOffice.Controllers if (package == null) return NotFound(); - var fullPath = _hostingEnvironment.MapPathContentRoot(package.PackagePath); + var fullPath = _hostingEnvironment.MapPathWebRoot(package.PackagePath); if (!System.IO.File.Exists(fullPath)) throw HttpResponseException.CreateNotificationValidationErrorResponse("No file found for path " + package.PackagePath); diff --git a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs index 38d78860b0..f34197d23e 100644 --- a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs +++ b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreUmbracoApplicationLifetime.cs @@ -1,5 +1,4 @@ using System; -using System.Threading; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Hosting; using Umbraco.Net; @@ -21,16 +20,6 @@ namespace Umbraco.Web.Common.AspNetCore public void Restart() { IsRestarting = true; - - var httpContext = _httpContextAccessor.HttpContext; - if (httpContext != null) - { - // 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; - } - - Thread.CurrentPrincipal = null; _hostApplicationLifetime.StopApplication(); } 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 de8ad6d1c4..cb9c42e44d 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 @@ -16,6 +16,7 @@ vm.filesOpen = true; vm.actionsOpen = true; vm.loading = true; + vm.mediaNodeDisplayModels = []; vm.back = back; vm.createOrUpdatePackage = createOrUpdatePackage; vm.removeContentItem = removeContentItem; @@ -28,6 +29,7 @@ vm.contributorsEditor = null; vm.selectDocumentType = selectDocumentType; + vm.selectMediaType = selectMediaType; vm.selectTemplate = selectTemplate; vm.selectStyleSheet = selectStyleSheet; vm.selectMacro = selectMacro; @@ -35,6 +37,15 @@ vm.selectDictionaryItem = selectDictionaryItem; vm.selectDataType = selectDataType; + vm.mediaPickerModel = { + hideLabel: true, + view: "mediapicker", + value: "", + config: { + multiPicker: true, + allowEdit:false + } + } vm.labels = {}; vm.versionRegex = /^(\d+\.)(\d+\.)(\*|\d+)$/; @@ -76,6 +87,7 @@ }); } + vm.mediaPickerModel.value = vm.package.mediaUdis.join(','); }); @@ -88,20 +100,31 @@ function loadResources() { - // Get all document types - 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 => { - documentType.id = documentType.id.toString(); - documentType.selected = vm.package.documentTypes.indexOf(documentType.id) !== -1; - }); - vm.documentTypes = documentTypes; + // Get all document types + 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 => { + documentType.id = documentType.id.toString(); + documentType.selected = vm.package.documentTypes.indexOf(documentType.id) !== -1; }); + vm.documentTypes = documentTypes; + }); + + // Get all media types + entityResource.getAll("MediaType").then(mediaTypes => { + // a package stores the id as a string so we + // need to convert all ids to string for comparison + mediaTypes.forEach(mediaType => { + mediaType.id = mediaType.id.toString(); + mediaType.selected = vm.package.mediaTypes.indexOf(mediaType.id) !== -1; + }); + vm.mediaTypes = mediaTypes; + }); // Get all templates entityResource.getAll("Template").then(templates => { - // a package stores the id as a string so we + // 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(); @@ -120,7 +143,7 @@ // Get all macros entityResource.getAll("Macro").then(macros => { - // a package stores the id as a string so we + // 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(); @@ -131,7 +154,7 @@ // Get all languages entityResource.getAll("Language").then(languages => { - // a package stores the id as a string so we + // a package stores the id as a string so we // need to convert all ids to string for comparison languages.forEach(language => { language.id = language.id.toString(); @@ -142,7 +165,7 @@ // Get all dictionary items entityResource.getAll("DictionaryItem").then(dictionaryItems => { - // a package stores the id as a string so we + // a package stores the id as a string so we // need to convert all ids to string for comparison dictionaryItems.forEach(dictionaryItem => { dictionaryItem.id = dictionaryItem.id.toString(); @@ -153,7 +176,7 @@ // Get all data types entityResource.getAll("DataType").then(dataTypes => { - // a package stores the id as a string so we + // a package stores the id as a string so we // need to convert all ids to string for comparison dataTypes.forEach(dataType => { dataType.id = dataType.id.toString(); @@ -181,10 +204,12 @@ function createOrUpdatePackage(editPackageForm) { - let contributors = vm.contributorsEditor.value.map(o => o.value); + let contributors = vm.contributorsEditor.value.map(o => o.value) vm.package.contributors = contributors; + // Split by comma and remove empty entries + vm.package.mediaUdis = vm.mediaPickerModel.value.split(",").filter(i => i); if (formHelper.submitForm({ formCtrl: editPackageForm, scope: $scope })) { vm.buttonState = "busy"; @@ -215,23 +240,23 @@ vm.package.contentNodeId = null; } - function openContentPicker() { - const contentPicker = { - submit: function (model) { - if (model.selection && model.selection.length > 0) { - vm.package.contentNodeId = model.selection[0].id.toString(); - vm.contentNodeDisplayModel = model.selection[0]; - } - editorService.close(); - }, - close: function () { - editorService.close(); - } - }; - editorService.contentPicker(contentPicker); - } + function openContentPicker() { + const contentPicker = { + submit: function (model) { + if (model.selection && model.selection.length > 0) { + vm.package.contentNodeId = model.selection[0].id.toString(); + vm.contentNodeDisplayModel = model.selection[0]; + } + editorService.close(); + }, + close: function () { + editorService.close(); + } + }; + editorService.contentPicker(contentPicker); + } - function openFilePicker() { + function openFilePicker() { let selection = Utilities.copy(vm.package.files); @@ -313,6 +338,18 @@ } } + function selectMediaType(mediatype) { + + // Check if the document type is already selected. + var index = vm.package.mediaTypes.indexOf(mediatype.id); + + if (index === -1) { + vm.package.mediaTypes.push(mediatype.id); + } else { + vm.package.mediaTypes.splice(index, 1); + } + } + function selectTemplate(template) { // Check if the template is already selected. 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 ce65d1cff6..f4011a9d78 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html @@ -137,6 +137,22 @@ + + + + + + + + + + + + + +
+ +
+ + +
+
+
+ text="{{template.name}}">umb-expansion-panel__content
diff --git a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj index 211238d951..8d9ca58ea9 100644 --- a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj +++ b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj @@ -22,6 +22,7 @@ + diff --git a/src/Umbraco.Web.UI.NetCore/umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI.NetCore/umbraco/config/lang/en_us.xml index 0bbba93c89..b48fad5edd 100644 --- a/src/Umbraco.Web.UI.NetCore/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI.NetCore/umbraco/config/lang/en_us.xml @@ -730,6 +730,7 @@ Logout Macro Mandatory + Media Message Move Name From a69739a7a261ad1c959f833e703d2aeb54567edc Mon Sep 17 00:00:00 2001 From: Paul Johnson Date: Tue, 15 Dec 2020 15:20:36 +0000 Subject: [PATCH 06/18] Remove CoreInitialComponent --- .../Events/UmbracoApplicationStarting.cs | 16 +++++ .../Events/UmbracoApplicationStopping.cs | 4 ++ src/Umbraco.Core/Services/IRuntime.cs | 11 +--- src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../Runtime/CoreInitialComposer.cs | 10 +-- .../Runtime/CoreRuntime.cs | 64 ++++++++++++++----- ...ponent.cs => EssentialDirectoryCreator.cs} | 20 +++--- src/Umbraco.Tests.Integration/RuntimeTests.cs | 48 -------------- .../UmbracoTestServerTestBase.cs | 13 +--- .../Testing/UmbracoIntegrationTest.cs | 39 +++++------ .../Builder/UmbracoBuilderExtensions.cs | 4 +- .../ApplicationBuilderExtensions.cs | 48 +------------- src/Umbraco.Web/UmbracoApplicationBase.cs | 6 -- 13 files changed, 111 insertions(+), 173 deletions(-) create mode 100644 src/Umbraco.Core/Events/UmbracoApplicationStarting.cs create mode 100644 src/Umbraco.Core/Events/UmbracoApplicationStopping.cs rename src/Umbraco.Infrastructure/Runtime/{CoreInitialComponent.cs => EssentialDirectoryCreator.cs} (67%) diff --git a/src/Umbraco.Core/Events/UmbracoApplicationStarting.cs b/src/Umbraco.Core/Events/UmbracoApplicationStarting.cs new file mode 100644 index 0000000000..422673a823 --- /dev/null +++ b/src/Umbraco.Core/Events/UmbracoApplicationStarting.cs @@ -0,0 +1,16 @@ +namespace Umbraco.Core.Events +{ + public class UmbracoApplicationStarting : INotification + { + /// + /// Initializes a new instance of the class. + /// + /// The runtime level + public UmbracoApplicationStarting(RuntimeLevel runtimeLevel) => RuntimeLevel = runtimeLevel; + + /// + /// Gets the runtime level of execution. + /// + public RuntimeLevel RuntimeLevel { get; } + } +} diff --git a/src/Umbraco.Core/Events/UmbracoApplicationStopping.cs b/src/Umbraco.Core/Events/UmbracoApplicationStopping.cs new file mode 100644 index 0000000000..bef6f0de19 --- /dev/null +++ b/src/Umbraco.Core/Events/UmbracoApplicationStopping.cs @@ -0,0 +1,4 @@ +namespace Umbraco.Core.Events +{ + public class UmbracoApplicationStopping : INotification { } +} diff --git a/src/Umbraco.Core/Services/IRuntime.cs b/src/Umbraco.Core/Services/IRuntime.cs index 8a1be721d0..d1254c219f 100644 --- a/src/Umbraco.Core/Services/IRuntime.cs +++ b/src/Umbraco.Core/Services/IRuntime.cs @@ -1,22 +1,15 @@ -using System; +using Microsoft.Extensions.Hosting; namespace Umbraco.Core { /// /// Defines the Umbraco runtime. /// - public interface IRuntime + public interface IRuntime : IHostedService { /// /// Gets the runtime state. /// IRuntimeState State { get; } - - void Start(); - - /// - /// Terminates the runtime. - /// - void Terminate(); } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 300dedc1c6..24b2fae683 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -16,6 +16,7 @@ + diff --git a/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs b/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs index 126d235ae0..e658405400 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs @@ -1,4 +1,4 @@ -using System; +using System; using Examine; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -38,6 +38,7 @@ using Umbraco.Core.Templates; using Umbraco.Examine; using Umbraco.Infrastructure.Examine; using Umbraco.Infrastructure.Media; +using Umbraco.Infrastructure.Runtime; using Umbraco.Web; using Umbraco.Web.Actions; using Umbraco.Web.Cache; @@ -66,11 +67,12 @@ namespace Umbraco.Core.Runtime { // core's initial composer composes before all core composers [ComposeBefore(typeof(ICoreComposer))] - public class CoreInitialComposer : ComponentComposer + public class CoreInitialComposer : IComposer { - public override void Compose(IUmbracoBuilder builder) + /// + public void Compose(IUmbracoBuilder builder) { - base.Compose(builder); + builder.AddNotificationHandler(); // composers builder diff --git a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs index cb02a90ebe..55fa3457ed 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs @@ -1,7 +1,10 @@ -using System; +using System; +using System.Threading; +using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Umbraco.Core; using Umbraco.Core.Composing; +using Umbraco.Core.Events; using Umbraco.Core.Hosting; using Umbraco.Core.Logging; using Umbraco.Core.Persistence; @@ -10,71 +13,102 @@ namespace Umbraco.Infrastructure.Runtime { public class CoreRuntime : IRuntime { - public IRuntimeState State { get; } - private readonly ILogger _logger; + private readonly ILoggerFactory _loggerFactory; private readonly ComponentCollection _components; private readonly IApplicationShutdownRegistry _applicationShutdownRegistry; private readonly IProfilingLogger _profilingLogger; private readonly IMainDom _mainDom; private readonly IUmbracoDatabaseFactory _databaseFactory; + private readonly IEventAggregator _eventAggregator; + private readonly IHostingEnvironment _hostingEnvironment; public CoreRuntime( - ILogger logger, + ILoggerFactory loggerFactory, IRuntimeState state, ComponentCollection components, IApplicationShutdownRegistry applicationShutdownRegistry, IProfilingLogger profilingLogger, IMainDom mainDom, - IUmbracoDatabaseFactory databaseFactory) + IUmbracoDatabaseFactory databaseFactory, + IEventAggregator eventAggregator, + IHostingEnvironment hostingEnvironment) { State = state; - _logger = logger; + _loggerFactory = loggerFactory; _components = components; _applicationShutdownRegistry = applicationShutdownRegistry; _profilingLogger = profilingLogger; _mainDom = mainDom; _databaseFactory = databaseFactory; - } - + _eventAggregator = eventAggregator; + _hostingEnvironment = hostingEnvironment; - public void Start() + + _logger = _loggerFactory.CreateLogger(); + } + + /// + /// Gets the state of the Umbraco runtime. + /// + public IRuntimeState State { get; } + + /// + public async Task StartAsync(CancellationToken cancellationToken) { + StaticApplicationLogging.Initialize(_loggerFactory); + AppDomain.CurrentDomain.UnhandledException += (_, args) => { var exception = (Exception)args.ExceptionObject; var isTerminating = args.IsTerminating; // always true? var msg = "Unhandled exception in AppDomain"; - if (isTerminating) msg += " (terminating)"; + + if (isTerminating) + { + msg += " (terminating)"; + } + msg += "."; + _logger.LogError(exception, msg); }; + AppDomain.CurrentDomain.SetData("DataDirectory", _hostingEnvironment?.MapPathContentRoot(Core.Constants.SystemDirectories.Data)); + DetermineRuntimeLevel(); if (State.Level <= RuntimeLevel.BootFailed) + { throw new InvalidOperationException($"Cannot start the runtime if the runtime level is less than or equal to {RuntimeLevel.BootFailed}"); + } - var hostingEnvironmentLifetime = _applicationShutdownRegistry; + IApplicationShutdownRegistry hostingEnvironmentLifetime = _applicationShutdownRegistry; if (hostingEnvironmentLifetime == null) - throw new InvalidOperationException($"An instance of {typeof(IApplicationShutdownRegistry)} could not be resolved from the container, ensure that one if registered in your runtime before calling {nameof(IRuntime)}.{nameof(Start)}"); + { + throw new InvalidOperationException($"An instance of {typeof(IApplicationShutdownRegistry)} could not be resolved from the container, ensure that one if registered in your runtime before calling {nameof(IRuntime)}.{nameof(StartAsync)}"); + } // acquire the main domain - if this fails then anything that should be registered with MainDom will not operate AcquireMainDom(); + await _eventAggregator.PublishAsync(new UmbracoApplicationStarting(State.Level), cancellationToken); + // create & initialize the components _components.Initialize(); } - public void Terminate() + public async Task StopAsync(CancellationToken cancellationToken) { _components.Terminate(); + await _eventAggregator.PublishAsync(new UmbracoApplicationStopping(), cancellationToken); + StaticApplicationLogging.Initialize(null); } private void AcquireMainDom() { - using (var timer = _profilingLogger.DebugDuration("Acquiring MainDom.", "Acquired.")) + using (DisposableTimer timer = _profilingLogger.DebugDuration("Acquiring MainDom.", "Acquired.")) { try { @@ -90,7 +124,7 @@ namespace Umbraco.Infrastructure.Runtime private void DetermineRuntimeLevel() { - using var timer = _profilingLogger.DebugDuration("Determining runtime level.", "Determined."); + using DisposableTimer timer = _profilingLogger.DebugDuration("Determining runtime level.", "Determined."); try { diff --git a/src/Umbraco.Infrastructure/Runtime/CoreInitialComponent.cs b/src/Umbraco.Infrastructure/Runtime/EssentialDirectoryCreator.cs similarity index 67% rename from src/Umbraco.Infrastructure/Runtime/CoreInitialComponent.cs rename to src/Umbraco.Infrastructure/Runtime/EssentialDirectoryCreator.cs index 73318208b6..5543662464 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreInitialComponent.cs +++ b/src/Umbraco.Infrastructure/Runtime/EssentialDirectoryCreator.cs @@ -1,25 +1,28 @@ -using Microsoft.Extensions.Options; -using Umbraco.Core.Composing; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Options; +using Umbraco.Core; using Umbraco.Core.Configuration.Models; +using Umbraco.Core.Events; using Umbraco.Core.Hosting; using Umbraco.Core.IO; -namespace Umbraco.Core.Runtime +namespace Umbraco.Infrastructure.Runtime { - public class CoreInitialComponent : IComponent + public class EssentialDirectoryCreator : INotificationHandler { private readonly IIOHelper _ioHelper; private readonly IHostingEnvironment _hostingEnvironment; private readonly GlobalSettings _globalSettings; - public CoreInitialComponent(IIOHelper ioHelper, IHostingEnvironment hostingEnvironment, IOptions globalSettings) + public EssentialDirectoryCreator(IIOHelper ioHelper, IHostingEnvironment hostingEnvironment, IOptions globalSettings) { _ioHelper = ioHelper; _hostingEnvironment = hostingEnvironment; _globalSettings = globalSettings.Value; } - public void Initialize() + public Task HandleAsync(UmbracoApplicationStarting notification, CancellationToken cancellationToken) { // ensure we have some essential directories // every other component can then initialize safely @@ -28,9 +31,8 @@ namespace Umbraco.Core.Runtime _ioHelper.EnsurePathExists(_hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.MvcViews)); _ioHelper.EnsurePathExists(_hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.PartialViews)); _ioHelper.EnsurePathExists(_hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.MacroPartials)); - } - public void Terminate() - { } + return Task.CompletedTask; + } } } diff --git a/src/Umbraco.Tests.Integration/RuntimeTests.cs b/src/Umbraco.Tests.Integration/RuntimeTests.cs index 77ee27b8c4..1e3317262a 100644 --- a/src/Umbraco.Tests.Integration/RuntimeTests.cs +++ b/src/Umbraco.Tests.Integration/RuntimeTests.cs @@ -33,54 +33,6 @@ namespace Umbraco.Tests.Integration MyComposer.Reset(); } - /// - /// Calling AddUmbracoCore to configure the container - /// - [Test] - public async Task AddUmbracoCore() - { - var testHelper = new TestHelper(); - - var hostBuilder = new HostBuilder() - .UseUmbraco() - .ConfigureServices((hostContext, services) => - { - var webHostEnvironment = testHelper.GetWebHostEnvironment(); - services.AddSingleton(testHelper.DbProviderFactoryCreator); - services.AddRequiredNetCoreServices(testHelper, webHostEnvironment); - - // Add it! - var typeLoader = services.AddTypeLoader( - GetType().Assembly, - webHostEnvironment, - testHelper.GetHostingEnvironment(), - testHelper.ConsoleLoggerFactory, - AppCaches.NoCache, - hostContext.Configuration, - testHelper.Profiler); - - var builder = new UmbracoBuilder(services, hostContext.Configuration, typeLoader, testHelper.ConsoleLoggerFactory); - builder.Services.AddUnique(AppCaches.NoCache); - builder.AddConfiguration(); - builder.AddUmbracoCore(); - }); - - var host = await hostBuilder.StartAsync(); - var app = new ApplicationBuilder(host.Services); - - // assert results - var runtimeState = app.ApplicationServices.GetRequiredService(); - var mainDom = app.ApplicationServices.GetRequiredService(); - - Assert.IsFalse(mainDom.IsMainDom); // We haven't "Started" the runtime yet - Assert.IsNull(runtimeState.BootFailedException); - Assert.IsFalse(MyComponent.IsInit); // We haven't "Started" the runtime yet - - await host.StopAsync(); - - Assert.IsFalse(MyComponent.IsTerminated); // we didn't "Start" the runtime so nothing was registered for shutdown - } - /// /// Calling AddUmbracoCore to configure the container and UseUmbracoCore to start the runtime /// diff --git a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs index 93769eaaed..c3c01ed977 100644 --- a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs +++ b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs @@ -1,4 +1,4 @@ - + using System; using System.Linq.Expressions; using System.Net.Http; @@ -119,15 +119,6 @@ namespace Umbraco.Tests.Integration.TestServerTest protected LinkGenerator LinkGenerator { get; private set; } protected WebApplicationFactory Factory { get; private set; } - [TearDown] - public override void TearDown() - { - base.TearDown(); - base.TerminateCoreRuntime(); - - Factory.Dispose(); - } - #region IStartup public override void ConfigureServices(IServiceCollection services) @@ -162,7 +153,5 @@ namespace Umbraco.Tests.Integration.TestServerTest } #endregion - - } } diff --git a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs index 43b2d236c7..f72139576e 100644 --- a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs +++ b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs @@ -71,9 +71,14 @@ namespace Umbraco.Tests.Integration.Testing foreach (var a in _testTeardown) a(); } + _testTeardown = null; FirstTestInFixture = false; FirstTestInSession = false; + + // Ensure CoreRuntime stopped (now it's a HostedService) + IHost host = Services.GetRequiredService(); + host.StopAsync().GetAwaiter().GetResult(); } [TearDown] @@ -100,8 +105,6 @@ namespace Umbraco.Tests.Integration.Testing var app = new ApplicationBuilder(host.Services); Configure(app); - - OnFixtureTearDown(() => host.Dispose()); } #region Generic Host Builder and Runtime @@ -135,6 +138,8 @@ namespace Umbraco.Tests.Integration.Testing /// public virtual IHostBuilder CreateHostBuilder() { + + var testOptions = TestOptionAttributeBase.GetTestOptions(); var hostBuilder = Host.CreateDefaultBuilder() // IMPORTANT: We Cannot use UseStartup, there's all sorts of threads about this with testing. Although this can work // if you want to setup your tests this way, it is a bit annoying to do that as the WebApplicationFactory will @@ -154,6 +159,12 @@ namespace Umbraco.Tests.Integration.Testing { services.AddTransient(_ => CreateLoggerFactory()); ConfigureServices(services); + + if (!testOptions.Boot) + { + // If boot is false, we don't want the CoreRuntime hosted service to start + services.AddUnique(Mock.Of()); + } }); return hostBuilder; } @@ -215,27 +226,9 @@ namespace Umbraco.Tests.Integration.Testing { UseTestLocalDb(app.ApplicationServices); - //get the currently set options - var testOptions = TestOptionAttributeBase.GetTestOptions(); - if (testOptions.Boot) - { - Services.GetRequiredService().EnsureBackOfficeSecurity(); - Services.GetRequiredService().EnsureUmbracoContext(); - app.UseUmbracoCore(); // Takes 200 ms - - OnTestTearDown(TerminateCoreRuntime); - } - } - - /// - /// Some IComponents hook onto static events (e.g. Published in ContentService) - /// If these fire after the components host has been shutdown, errors can occur. - /// If CoreRuntime.Start() is called We also need to de-register the events. - /// - protected void TerminateCoreRuntime() - { - Services.GetRequiredService().Terminate(); - StaticApplicationLogging.Initialize(null); + Services.GetRequiredService().EnsureBackOfficeSecurity(); + Services.GetRequiredService().EnsureUmbracoContext(); + app.UseUmbracoCore(); } #endregion diff --git a/src/Umbraco.Web.Common/Builder/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Common/Builder/UmbracoBuilderExtensions.cs index 17c91d433a..4e528bafda 100644 --- a/src/Umbraco.Web.Common/Builder/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/Builder/UmbracoBuilderExtensions.cs @@ -143,11 +143,13 @@ namespace Umbraco.Core.DependencyInjection builder.Services.AddUnique(factory => factory.GetRequiredService().CreateDatabase()); builder.Services.AddUnique(factory => factory.GetRequiredService().SqlContext); builder.Services.AddUnique(); - builder.Services.AddUnique(); builder.Services.AddUnique(); builder.Services.AddUnique(); builder.Services.AddUnique(); + builder.Services.AddUnique(); + builder.Services.AddHostedService(factory => factory.GetRequiredService()); + builder.AddComposers(); return builder; diff --git a/src/Umbraco.Web.Common/Extensions/ApplicationBuilderExtensions.cs b/src/Umbraco.Web.Common/Extensions/ApplicationBuilderExtensions.cs index caf4132664..a245b8121a 100644 --- a/src/Umbraco.Web.Common/Extensions/ApplicationBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/ApplicationBuilderExtensions.cs @@ -1,13 +1,11 @@ using System; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using Serilog.Context; using Smidge; using Smidge.Nuglify; using StackExchange.Profiling; using Umbraco.Core; -using Umbraco.Core.Hosting; using Umbraco.Infrastructure.Logging.Serilog.Enrichers; using Umbraco.Web.Common.Middleware; @@ -23,9 +21,9 @@ namespace Umbraco.Extensions /// public static bool UmbracoCanBoot(this IApplicationBuilder app) { - var runtime = app.ApplicationServices.GetRequiredService(); + var state = app.ApplicationServices.GetRequiredService(); // can't continue if boot failed - return runtime.State.Level > RuntimeLevel.BootFailed; + return state.Level > RuntimeLevel.BootFailed; } /// @@ -39,25 +37,10 @@ namespace Umbraco.Extensions if (!app.UmbracoCanBoot()) return app; - var hostingEnvironment = app.ApplicationServices.GetRequiredService(); - AppDomain.CurrentDomain.SetData("DataDirectory", hostingEnvironment?.MapPathContentRoot(Core.Constants.SystemDirectories.Data)); - - var runtime = app.ApplicationServices.GetRequiredService(); - - // Register a listener for application shutdown in order to terminate the runtime - var hostLifetime = app.ApplicationServices.GetRequiredService(); - var runtimeShutdown = new CoreRuntimeShutdown(runtime, hostLifetime); - hostLifetime.RegisterObject(runtimeShutdown); - // Register our global threadabort enricher for logging var threadAbortEnricher = app.ApplicationServices.GetRequiredService(); LogContext.Push(threadAbortEnricher); // NOTE: We are not in a using clause because we are not removing it, it is on the global context - StaticApplicationLogging.Initialize(app.ApplicationServices.GetRequiredService()); - - // Start the runtime! - runtime.Start(); - return app; } @@ -121,33 +104,6 @@ namespace Umbraco.Extensions return app; } - - /// - /// Ensures the runtime is shutdown when the application is shutting down - /// - private class CoreRuntimeShutdown : IRegisteredObject - { - public CoreRuntimeShutdown(IRuntime runtime, IApplicationShutdownRegistry hostLifetime) - { - _runtime = runtime; - _hostLifetime = hostLifetime; - } - - private bool _completed = false; - private readonly IRuntime _runtime; - private readonly IApplicationShutdownRegistry _hostLifetime; - - public void Stop(bool immediate) - { - if (!_completed) - { - _completed = true; - _runtime.Terminate(); - _hostLifetime.UnregisterObject(this); - } - - } - } } } diff --git a/src/Umbraco.Web/UmbracoApplicationBase.cs b/src/Umbraco.Web/UmbracoApplicationBase.cs index f10f4491d9..bdf5b40a02 100644 --- a/src/Umbraco.Web/UmbracoApplicationBase.cs +++ b/src/Umbraco.Web/UmbracoApplicationBase.cs @@ -46,7 +46,6 @@ namespace Umbraco.Web protected UmbracoApplicationBase() { - HostingSettings hostingSettings = null; GlobalSettings globalSettings = null; SecuritySettings securitySettings = null; @@ -60,8 +59,6 @@ namespace Umbraco.Web var backOfficeInfo = new AspNetBackOfficeInfo(globalSettings, ioHelper, _loggerFactory.CreateLogger(), Options.Create(webRoutingSettings)); var profiler = GetWebProfiler(hostingEnvironment); - StaticApplicationLogging.Initialize(_loggerFactory); - Logger = NullLogger.Instance; } private IProfiler GetWebProfiler(IHostingEnvironment hostingEnvironment) @@ -87,7 +84,6 @@ namespace Umbraco.Web _loggerFactory = loggerFactory; Logger = logger; - StaticApplicationLogging.Initialize(_loggerFactory); } protected ILogger Logger { get; } @@ -189,7 +185,6 @@ namespace Umbraco.Web LogContext.Push(new HttpRequestIdEnricher(_factory.GetRequiredService())); _runtime = _factory.GetRequiredService(); - _runtime.Start(); } // called by ASP.NET (auto event wireup) once per app domain @@ -237,7 +232,6 @@ namespace Umbraco.Web { if (_runtime != null) { - _runtime.Terminate(); _runtime.DisposeIfDisposable(); _runtime = null; From ef310920cbee4459aac0cb59319e26c448b22024 Mon Sep 17 00:00:00 2001 From: Paul Johnson Date: Tue, 15 Dec 2020 21:31:01 +0000 Subject: [PATCH 07/18] Remove ManifestWatcherComponent --- .../UmbracoBuilder.Events.cs | 18 +++++ .../Compose/ManifestWatcher.cs | 65 +++++++++++++++++++ .../Compose/ManifestWatcherComponent.cs | 51 --------------- .../Compose/ManifestWatcherComposer.cs | 16 ++++- 4 files changed, 96 insertions(+), 54 deletions(-) create mode 100644 src/Umbraco.Infrastructure/Compose/ManifestWatcher.cs delete mode 100644 src/Umbraco.Infrastructure/Compose/ManifestWatcherComponent.cs diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Events.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Events.cs index a21ae74976..a4ae2874e3 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Events.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Events.cs @@ -1,6 +1,7 @@ // Copyright (c) Umbraco. // See LICENSE for more details. +using System; using Microsoft.Extensions.DependencyInjection; using Umbraco.Core.Events; @@ -26,5 +27,22 @@ namespace Umbraco.Core.DependencyInjection builder.Services.AddTransient(typeof(INotificationHandler), typeof(TNotificationHandler)); return builder; } + + /// + /// Registers a notification handler against the Umbraco service collection. + /// + /// The type of notification. + /// The type of notificiation handler. + /// The Umbraco builder. + /// Factory method + /// The . + public static IUmbracoBuilder AddNotificationHandler(this IUmbracoBuilder builder, Func factory) + where TNotificationHandler : class, INotificationHandler + where TNotification : INotification + { + // Register the handler as transient. This ensures that anything can be injected into it. + builder.Services.AddTransient(typeof(INotificationHandler), factory); + return builder; + } } } diff --git a/src/Umbraco.Infrastructure/Compose/ManifestWatcher.cs b/src/Umbraco.Infrastructure/Compose/ManifestWatcher.cs new file mode 100644 index 0000000000..fc18033a28 --- /dev/null +++ b/src/Umbraco.Infrastructure/Compose/ManifestWatcher.cs @@ -0,0 +1,65 @@ +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Umbraco.Core; +using Umbraco.Core.Events; +using Umbraco.Core.Hosting; +using Umbraco.Net; + +namespace Umbraco.Core.Compose +{ + public sealed class ManifestWatcher : + INotificationHandler, + INotificationHandler + { + private readonly IHostingEnvironment _hosting; + private readonly ILoggerFactory _loggerFactory; + private readonly IHostingEnvironment _hostingEnvironment; + private readonly IUmbracoApplicationLifetime _umbracoApplicationLifetime; + + // if configured and in debug mode, a ManifestWatcher watches App_Plugins folders for + // package.manifest chances and restarts the application on any change + private Core.Manifest.ManifestWatcher _mw; + + public ManifestWatcher(IHostingEnvironment hosting, ILoggerFactory loggerFactory, IHostingEnvironment hostingEnvironment, IUmbracoApplicationLifetime umbracoApplicationLifetime) + { + _hosting = hosting; + _loggerFactory = loggerFactory; + _hostingEnvironment = hostingEnvironment; + _umbracoApplicationLifetime = umbracoApplicationLifetime; + } + + public Task HandleAsync(UmbracoApplicationStarting notification, CancellationToken cancellationToken) + { + if (_hosting.IsDebugMode == false) + { + return Task.CompletedTask; + } + + var appPlugins = _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.AppPlugins); + if (Directory.Exists(appPlugins) == false) + { + return Task.CompletedTask; + } + + _mw = new Core.Manifest.ManifestWatcher(_loggerFactory.CreateLogger(), _umbracoApplicationLifetime); + _mw.Start(Directory.GetDirectories(appPlugins)); + + return Task.CompletedTask; + } + + public Task HandleAsync(UmbracoApplicationStopping notification, CancellationToken cancellationToken) + { + if (_mw == null) + { + return Task.CompletedTask; + } + + _mw.Dispose(); + _mw = null; + + return Task.CompletedTask; + } + } +} diff --git a/src/Umbraco.Infrastructure/Compose/ManifestWatcherComponent.cs b/src/Umbraco.Infrastructure/Compose/ManifestWatcherComponent.cs deleted file mode 100644 index dd3a800821..0000000000 --- a/src/Umbraco.Infrastructure/Compose/ManifestWatcherComponent.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.IO; -using Microsoft.Extensions.Logging; -using Umbraco.Core.Composing; -using Umbraco.Core.Hosting; -using Umbraco.Core.Manifest; -using Umbraco.Net; - -namespace Umbraco.Core.Compose -{ - public sealed class ManifestWatcherComponent : IComponent - { - private readonly IHostingEnvironment _hosting; - private readonly ILoggerFactory _loggerFactory; - private readonly IHostingEnvironment _hostingEnvironment; - private readonly IUmbracoApplicationLifetime _umbracoApplicationLifetime; - - // if configured and in debug mode, a ManifestWatcher watches App_Plugins folders for - // package.manifest chances and restarts the application on any change - private ManifestWatcher _mw; - - public ManifestWatcherComponent(IHostingEnvironment hosting, ILoggerFactory loggerFactory, IHostingEnvironment hostingEnvironment, IUmbracoApplicationLifetime umbracoApplicationLifetime) - { - _hosting = hosting; - _loggerFactory = loggerFactory; - _hostingEnvironment = hostingEnvironment; - _umbracoApplicationLifetime = umbracoApplicationLifetime; - } - - public void Initialize() - { - if (_hosting.IsDebugMode == false) return; - - //if (ApplicationContext.Current.IsConfigured == false || GlobalSettings.DebugMode == false) - // return; - - var appPlugins = _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.AppPlugins); - if (Directory.Exists(appPlugins) == false) return; - - _mw = new ManifestWatcher(_loggerFactory.CreateLogger(), _umbracoApplicationLifetime); - _mw.Start(Directory.GetDirectories(appPlugins)); - } - - public void Terminate() - { - if (_mw == null) return; - - _mw.Dispose(); - _mw = null; - } - } -} diff --git a/src/Umbraco.Infrastructure/Compose/ManifestWatcherComposer.cs b/src/Umbraco.Infrastructure/Compose/ManifestWatcherComposer.cs index 786a3ed0ce..a94106f6dc 100644 --- a/src/Umbraco.Infrastructure/Compose/ManifestWatcherComposer.cs +++ b/src/Umbraco.Infrastructure/Compose/ManifestWatcherComposer.cs @@ -1,7 +1,17 @@ -using Umbraco.Core.Composing; +using Microsoft.Extensions.DependencyInjection; +using Umbraco.Core.Composing; +using Umbraco.Core.DependencyInjection; +using Umbraco.Core.Events; namespace Umbraco.Core.Compose { - public class ManifestWatcherComposer : ComponentComposer, ICoreComposer - { } + public class ManifestWatcherComposer : ICoreComposer + { + public void Compose(IUmbracoBuilder builder) + { + builder.Services.AddSingleton(); + builder.AddNotificationHandler(factory => factory.GetRequiredService()); + builder.AddNotificationHandler(factory => factory.GetRequiredService()); + } + } } From 91e2f58822b5fac2653c8c748849807a37a0e761 Mon Sep 17 00:00:00 2001 From: Paul Johnson Date: Tue, 15 Dec 2020 21:47:15 +0000 Subject: [PATCH 08/18] Drop ManifestWatcherComposer, Convert CoreInitialComposer to extension on UmbracoBuilder --- .../Compose/ManifestWatcherComposer.cs | 17 --------------- ...tialComposer.cs => CoreInitialServices.cs} | 21 +++++++++++-------- .../{Compose => Runtime}/ManifestWatcher.cs | 2 +- .../Builder/UmbracoBuilderExtensions.cs | 1 + .../Runtime/AspNetCoreComposer.cs | 6 ------ src/Umbraco.Web/Runtime/WebInitialComposer.cs | 7 ------- 6 files changed, 14 insertions(+), 40 deletions(-) delete mode 100644 src/Umbraco.Infrastructure/Compose/ManifestWatcherComposer.cs rename src/Umbraco.Infrastructure/Runtime/{CoreInitialComposer.cs => CoreInitialServices.cs} (96%) rename src/Umbraco.Infrastructure/{Compose => Runtime}/ManifestWatcher.cs (98%) diff --git a/src/Umbraco.Infrastructure/Compose/ManifestWatcherComposer.cs b/src/Umbraco.Infrastructure/Compose/ManifestWatcherComposer.cs deleted file mode 100644 index a94106f6dc..0000000000 --- a/src/Umbraco.Infrastructure/Compose/ManifestWatcherComposer.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Umbraco.Core.Composing; -using Umbraco.Core.DependencyInjection; -using Umbraco.Core.Events; - -namespace Umbraco.Core.Compose -{ - public class ManifestWatcherComposer : ICoreComposer - { - public void Compose(IUmbracoBuilder builder) - { - builder.Services.AddSingleton(); - builder.AddNotificationHandler(factory => factory.GetRequiredService()); - builder.AddNotificationHandler(factory => factory.GetRequiredService()); - } - } -} diff --git a/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs b/src/Umbraco.Infrastructure/Runtime/CoreInitialServices.cs similarity index 96% rename from src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs rename to src/Umbraco.Infrastructure/Runtime/CoreInitialServices.cs index e658405400..0fa3250522 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreInitialServices.cs @@ -3,8 +3,8 @@ using Examine; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; using Umbraco.Core.Composing.CompositionExtensions; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.Grid; @@ -38,7 +38,6 @@ using Umbraco.Core.Templates; using Umbraco.Examine; using Umbraco.Infrastructure.Examine; using Umbraco.Infrastructure.Media; -using Umbraco.Infrastructure.Runtime; using Umbraco.Web; using Umbraco.Web.Actions; using Umbraco.Web.Cache; @@ -63,17 +62,18 @@ using Umbraco.Web.Templates; using Umbraco.Web.Trees; using TextStringValueConverter = Umbraco.Core.PropertyEditors.ValueConverters.TextStringValueConverter; -namespace Umbraco.Core.Runtime +namespace Umbraco.Infrastructure.Runtime { - // core's initial composer composes before all core composers - [ComposeBefore(typeof(ICoreComposer))] - public class CoreInitialComposer : IComposer + public static class CoreInitialServices { - /// - public void Compose(IUmbracoBuilder builder) + public static IUmbracoBuilder AddCoreInitialServices(this IUmbracoBuilder builder) { builder.AddNotificationHandler(); + builder.Services.AddSingleton(); + builder.AddNotificationHandler(factory => factory.GetRequiredService()); + builder.AddNotificationHandler(factory => factory.GetRequiredService()); + // composers builder .ComposeRepositories() @@ -304,7 +304,7 @@ namespace Umbraco.Core.Runtime // register *all* checks, except those marked [HideFromTypeFinder] of course builder.Services.AddUnique(); builder.HealthChecks() - .Add(() => builder.TypeLoader.GetTypes()); + .Add(() => builder.TypeLoader.GetTypes()); builder.WithCollectionBuilder() .Add(() => builder.TypeLoader.GetTypes()); @@ -384,7 +384,10 @@ namespace Umbraco.Core.Runtime builder.Services.AddUnique(); builder.Services.AddUnique(); + builder.Services.AddUnique(); + + return builder; } } } diff --git a/src/Umbraco.Infrastructure/Compose/ManifestWatcher.cs b/src/Umbraco.Infrastructure/Runtime/ManifestWatcher.cs similarity index 98% rename from src/Umbraco.Infrastructure/Compose/ManifestWatcher.cs rename to src/Umbraco.Infrastructure/Runtime/ManifestWatcher.cs index fc18033a28..358dfb2ed5 100644 --- a/src/Umbraco.Infrastructure/Compose/ManifestWatcher.cs +++ b/src/Umbraco.Infrastructure/Runtime/ManifestWatcher.cs @@ -7,7 +7,7 @@ using Umbraco.Core.Events; using Umbraco.Core.Hosting; using Umbraco.Net; -namespace Umbraco.Core.Compose +namespace Umbraco.Infrastructure.Runtime { public sealed class ManifestWatcher : INotificationHandler, diff --git a/src/Umbraco.Web.Common/Builder/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Common/Builder/UmbracoBuilderExtensions.cs index 4e528bafda..9ecda715ee 100644 --- a/src/Umbraco.Web.Common/Builder/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/Builder/UmbracoBuilderExtensions.cs @@ -150,6 +150,7 @@ namespace Umbraco.Core.DependencyInjection builder.Services.AddUnique(); builder.Services.AddHostedService(factory => factory.GetRequiredService()); + builder.AddCoreInitialServices(); builder.AddComposers(); return builder; diff --git a/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs b/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs index f6849215df..508f4e987f 100644 --- a/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs +++ b/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs @@ -1,22 +1,17 @@ using System.Linq; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; using Umbraco.Core; using Umbraco.Core.DependencyInjection; using Umbraco.Core.Composing; -using Umbraco.Core.Configuration.Models; using Umbraco.Core.Diagnostics; using Umbraco.Core.Hosting; using Umbraco.Core.Logging; -using Umbraco.Core.Runtime; using Umbraco.Core.Security; -using Umbraco.Core.Services; using Umbraco.Extensions; using Umbraco.Net; using Umbraco.Web.Common.AspNetCore; using Umbraco.Web.Common.Controllers; -using Umbraco.Web.Common.Formatters; using Umbraco.Web.Common.Install; using Umbraco.Web.Common.Lifetime; using Umbraco.Web.Common.Macros; @@ -38,7 +33,6 @@ namespace Umbraco.Web.Common.Runtime /// Adds/replaces AspNetCore specific services /// [ComposeBefore(typeof(ICoreComposer))] - [ComposeAfter(typeof(CoreInitialComposer))] public class AspNetCoreComposer : ComponentComposer, IComposer { public override void Compose(IUmbracoBuilder builder) diff --git a/src/Umbraco.Web/Runtime/WebInitialComposer.cs b/src/Umbraco.Web/Runtime/WebInitialComposer.cs index 49cff7196e..7528755865 100644 --- a/src/Umbraco.Web/Runtime/WebInitialComposer.cs +++ b/src/Umbraco.Web/Runtime/WebInitialComposer.cs @@ -1,26 +1,19 @@ -using System.Web.Mvc; using System.Web.Security; -using Microsoft.AspNet.SignalR; using Microsoft.Extensions.DependencyInjection; using Umbraco.Core; using Umbraco.Core.DependencyInjection; using Umbraco.Core.Composing; using Umbraco.Core.Dictionary; using Umbraco.Core.Templates; -using Umbraco.Core.Runtime; using Umbraco.Core.Security; using Umbraco.Core.Services; -using Umbraco.Web.Composing.CompositionExtensions; using Umbraco.Web.Macros; -using Umbraco.Web.Mvc; using Umbraco.Web.PublishedCache; using Umbraco.Web.Security; using Umbraco.Web.Security.Providers; namespace Umbraco.Web.Runtime { - // web's initial composer composes after core's, and before all core composers - [ComposeAfter(typeof(CoreInitialComposer))] [ComposeBefore(typeof(ICoreComposer))] public sealed class WebInitialComposer : ComponentComposer { From 431403e372d34aab4c12293ebe8125be22ff3a67 Mon Sep 17 00:00:00 2001 From: Paul Johnson Date: Wed, 16 Dec 2020 01:54:49 +0000 Subject: [PATCH 09/18] Fixup CoreRuntime so it starts after Startup.Configure Makes integration tests play nice with Components as RuntimeLevel will be correct --- .gitignore | 1 + .../Composing/HostBuilderExtensions.cs | 20 ++++-- .../PublishedSnapshotService.cs | 10 +-- src/Umbraco.Tests.Integration/RuntimeTests.cs | 11 ++-- .../UmbracoTestServerTestBase.cs | 13 ++-- .../Testing/TestDatabaseFactory.cs | 25 +++++--- .../Testing/UmbracoIntegrationTest.cs | 64 +++++++++---------- .../Builder/UmbracoBuilderExtensions.cs | 2 - src/Umbraco.Web.UI.NetCore/Program.cs | 4 +- 9 files changed, 82 insertions(+), 68 deletions(-) diff --git a/.gitignore b/.gitignore index 022acb5db7..ea2ddfbb68 100644 --- a/.gitignore +++ b/.gitignore @@ -191,6 +191,7 @@ src/Umbraco.Web.UI/Umbraco/telemetrics-id.umb /src/Umbraco.Web.UI.NetCore/App_Data/Smidge/Cache/* /src/Umbraco.Web.UI.NetCore/umbraco/logs +src/Umbraco.Tests.Integration/umbraco/Data/ src/Umbraco.Tests.Integration/umbraco/logs/ src/Umbraco.Tests.Integration/Views/ diff --git a/src/Umbraco.Infrastructure/Composing/HostBuilderExtensions.cs b/src/Umbraco.Infrastructure/Composing/HostBuilderExtensions.cs index 7c17a8a338..e2be3569d6 100644 --- a/src/Umbraco.Infrastructure/Composing/HostBuilderExtensions.cs +++ b/src/Umbraco.Infrastructure/Composing/HostBuilderExtensions.cs @@ -1,19 +1,29 @@ -using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; namespace Umbraco.Core.Composing { /// - /// Extends the to enable Umbraco to be used as the service container. + /// Extends the to add CoreRuntime as a HostedService /// public static class HostBuilderExtensions { /// - /// Assigns a custom service provider factory to use Umbraco's container + /// Adds CoreRuntime as HostedService /// - /// - /// + /// + /// When running the site should be called before ConfigureWebDefaults. + /// + /// + /// When testing should be called after ConfigureWebDefaults to ensure UseTestDatabase is called before CoreRuntime + /// starts or we initialize components with incorrect run level. + /// + /// public static IHostBuilder UseUmbraco(this IHostBuilder builder) { + _ = builder.ConfigureServices((context, services) => + services.AddSingleton(factory => factory.GetRequiredService())); + return builder; } } diff --git a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs index 97e3df16a6..82c2fa8dd1 100644 --- a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs @@ -135,16 +135,16 @@ namespace Umbraco.Web.PublishedCache.NuCache _entitySerializer = entitySerializer; _publishedModelFactory = publishedModelFactory; - // we always want to handle repository events, configured or not - // assuming no repository event will trigger before the whole db is ready - // (ideally we'd have Upgrading.App vs Upgrading.Data application states...) - InitializeRepositoryEvents(); - _lifeTime.ApplicationInit += OnApplicationInit; } internal void OnApplicationInit(object sender, EventArgs e) { + // we always want to handle repository events, configured or not + // assuming no repository event will trigger before the whole db is ready + // (ideally we'd have Upgrading.App vs Upgrading.Data application states...) + InitializeRepositoryEvents(); + // however, the cache is NOT available until we are configured, because loading // content (and content types) from database cannot be consistent (see notes in "Handle // Notifications" region), so diff --git a/src/Umbraco.Tests.Integration/RuntimeTests.cs b/src/Umbraco.Tests.Integration/RuntimeTests.cs index 1e3317262a..5d721ad176 100644 --- a/src/Umbraco.Tests.Integration/RuntimeTests.cs +++ b/src/Umbraco.Tests.Integration/RuntimeTests.cs @@ -43,7 +43,6 @@ namespace Umbraco.Tests.Integration var testHelper = new TestHelper(); var hostBuilder = new HostBuilder() - .UseUmbraco() .ConfigureServices((hostContext, services) => { var webHostEnvironment = testHelper.GetWebHostEnvironment(); @@ -61,14 +60,16 @@ namespace Umbraco.Tests.Integration hostContext.Configuration, testHelper.Profiler); - var builder = new UmbracoBuilder(services, hostContext.Configuration, typeLoader, testHelper.ConsoleLoggerFactory); + var builder = new UmbracoBuilder(services, hostContext.Configuration, typeLoader, + testHelper.ConsoleLoggerFactory); builder.Services.AddUnique(AppCaches.NoCache); builder.AddConfiguration() - .AddUmbracoCore() - .Build(); + .AddUmbracoCore() + .Build(); services.AddRouting(); // LinkGenerator - }); + }) + .UseUmbraco(); var host = await hostBuilder.StartAsync(); var app = new ApplicationBuilder(host.Services); diff --git a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs index c3c01ed977..87ac4505e5 100644 --- a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs +++ b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs @@ -1,8 +1,6 @@ - using System; using System.Linq.Expressions; using System.Net.Http; -using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; @@ -11,7 +9,6 @@ using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using NUnit.Framework; using Umbraco.Core; using Umbraco.Extensions; @@ -22,8 +19,7 @@ using Umbraco.Core.DependencyInjection; using Umbraco.Web.Common.Controllers; using Microsoft.Extensions.Hosting; using Umbraco.Core.Cache; -using Umbraco.Core.Persistence; -using Umbraco.Core.Runtime; +using Umbraco.Core.Composing; using Umbraco.Web.BackOffice.Controllers; namespace Umbraco.Tests.Integration.TestServerTest @@ -75,11 +71,14 @@ namespace Umbraco.Tests.Integration.TestServerTest // call startup builder.Configure(app => { - UseTestLocalDb(app.ApplicationServices); + UseTestDatabase(app.ApplicationServices); Services = app.ApplicationServices; Configure(app); }); - }).UseEnvironment(Environments.Development); + + }).UseEnvironment(Environments.Development); + + builder.UseUmbraco(); // Ensures CoreRuntime.StartAsync is called, must be after ConfigureWebHost return builder; } diff --git a/src/Umbraco.Tests.Integration/Testing/TestDatabaseFactory.cs b/src/Umbraco.Tests.Integration/Testing/TestDatabaseFactory.cs index 9bcbfa4d3a..60c767e17e 100644 --- a/src/Umbraco.Tests.Integration/Testing/TestDatabaseFactory.cs +++ b/src/Umbraco.Tests.Integration/Testing/TestDatabaseFactory.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using Microsoft.Extensions.Logging; using Umbraco.Core.Persistence; @@ -8,13 +9,20 @@ namespace Umbraco.Tests.Integration.Testing { public static ITestDatabase Create(string filesPath, ILoggerFactory loggerFactory, TestUmbracoDatabaseFactoryProvider dbFactory) { - return string.IsNullOrEmpty(Environment.GetEnvironmentVariable("UmbracoIntegrationTestConnectionString")) - ? CreateLocalDb(filesPath, loggerFactory, dbFactory.Create()) - : CreateSqlDeveloper(loggerFactory, dbFactory.Create()); + var connectionString = Environment.GetEnvironmentVariable("UmbracoIntegrationTestConnectionString"); + + return string.IsNullOrEmpty(connectionString) + ? CreateLocalDb(filesPath, loggerFactory, dbFactory) + : CreateSqlDeveloper(loggerFactory, dbFactory, connectionString); } - private static ITestDatabase CreateLocalDb(string filesPath, ILoggerFactory loggerFactory, IUmbracoDatabaseFactory dbFactory) + private static ITestDatabase CreateLocalDb(string filesPath, ILoggerFactory loggerFactory, TestUmbracoDatabaseFactoryProvider dbFactory) { + if (!Directory.Exists(filesPath)) + { + Directory.CreateDirectory(filesPath); + } + var localDb = new LocalDb(); if (!localDb.IsAvailable) @@ -22,22 +30,21 @@ namespace Umbraco.Tests.Integration.Testing throw new InvalidOperationException("LocalDB is not available."); } - return new LocalDbTestDatabase(loggerFactory, localDb, filesPath, dbFactory); + return new LocalDbTestDatabase(loggerFactory, localDb, filesPath, dbFactory.Create()); } - private static ITestDatabase CreateSqlDeveloper(ILoggerFactory loggerFactory, IUmbracoDatabaseFactory dbFactory) + private static ITestDatabase CreateSqlDeveloper(ILoggerFactory loggerFactory, TestUmbracoDatabaseFactoryProvider dbFactory, string connectionString) { + // NOTE: Example setup for Linux box. // $ export SA_PASSWORD=Foobar123! // $ export UmbracoIntegrationTestConnectionString="Server=localhost,1433;User Id=sa;Password=$SA_PASSWORD;" // $ docker run -e 'ACCEPT_EULA=Y' -e "SA_PASSWORD=$SA_PASSWORD" -e 'MSSQL_PID=Developer' -p 1433:1433 -d mcr.microsoft.com/mssql/server:2017-latest-ubuntu - var connectionString = Environment.GetEnvironmentVariable("UmbracoIntegrationTestConnectionString"); - if (string.IsNullOrEmpty(connectionString)) { throw new InvalidOperationException("ENV: UmbracoIntegrationTestConnectionString is not set"); } - return new SqlDeveloperTestDatabase(loggerFactory, dbFactory, connectionString); + return new SqlDeveloperTestDatabase(loggerFactory, dbFactory.Create(), connectionString); } } } diff --git a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs index f72139576e..5fb3c50cc8 100644 --- a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs +++ b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs @@ -97,10 +97,10 @@ namespace Umbraco.Tests.Integration.Testing public virtual void Setup() { InMemoryConfiguration[Constants.Configuration.ConfigGlobal + ":" + nameof(GlobalSettings.InstallEmptyDatabase)] = "true"; - var hostBuilder = CreateHostBuilder(); + var hostBuilder = CreateHostBuilder() + .UseUmbraco(); // This ensures CoreRuntime.StartAsync will be called (however it's a mock if boot = false) var host = hostBuilder.Start(); - Services = host.Services; var app = new ApplicationBuilder(host.Services); @@ -113,8 +113,7 @@ namespace Umbraco.Tests.Integration.Testing { try { - var testOptions = TestOptionAttributeBase.GetTestOptions(); - switch (testOptions.Logger) + switch (TestOptions.Logger) { case UmbracoTestOptions.Logger.Mock: return NullLoggerFactory.Instance; @@ -138,15 +137,13 @@ namespace Umbraco.Tests.Integration.Testing /// public virtual IHostBuilder CreateHostBuilder() { - - var testOptions = TestOptionAttributeBase.GetTestOptions(); var hostBuilder = Host.CreateDefaultBuilder() // IMPORTANT: We Cannot use UseStartup, there's all sorts of threads about this with testing. Although this can work // if you want to setup your tests this way, it is a bit annoying to do that as the WebApplicationFactory will // create separate Host instances. So instead of UseStartup, we just call ConfigureServices/Configure ourselves, // and in the case of the UmbracoTestServerTestBase it will use the ConfigureWebHost to Configure the IApplicationBuilder directly. //.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(GetType()); }) - .UseUmbraco() + .ConfigureAppConfiguration((context, configBuilder) => { context.HostingEnvironment = TestHelper.GetWebHostEnvironment(); @@ -157,12 +154,13 @@ namespace Umbraco.Tests.Integration.Testing }) .ConfigureServices((hostContext, services) => { - services.AddTransient(_ => CreateLoggerFactory()); ConfigureServices(services); + services.AddUnique(CreateLoggerFactory()); - if (!testOptions.Boot) + if (!TestOptions.Boot) { // If boot is false, we don't want the CoreRuntime hosted service to start + // So we replace it with a Mock services.AddUnique(Mock.Of()); } }); @@ -224,11 +222,15 @@ namespace Umbraco.Tests.Integration.Testing public virtual void Configure(IApplicationBuilder app) { - UseTestLocalDb(app.ApplicationServices); + UseTestDatabase(app.ApplicationServices); - Services.GetRequiredService().EnsureBackOfficeSecurity(); - Services.GetRequiredService().EnsureUmbracoContext(); - app.UseUmbracoCore(); + if (TestOptions.Boot) + { + Services.GetRequiredService().EnsureBackOfficeSecurity(); + Services.GetRequiredService().EnsureUmbracoContext(); + } + + app.UseUmbracoCore(); // This no longer starts CoreRuntime, it's very fast } #endregion @@ -239,25 +241,20 @@ namespace Umbraco.Tests.Integration.Testing private static ITestDatabase _dbInstance; private static TestDbMeta _fixtureDbMeta; - protected void UseTestLocalDb(IServiceProvider serviceProvider) + protected void UseTestDatabase(IServiceProvider serviceProvider) { var state = serviceProvider.GetRequiredService(); var testDatabaseFactoryProvider = serviceProvider.GetRequiredService(); var databaseFactory = serviceProvider.GetRequiredService(); + var loggerFactory = serviceProvider.GetRequiredService(); // This will create a db, install the schema and ensure the app is configured to run - InstallTestLocalDb(testDatabaseFactoryProvider, databaseFactory, serviceProvider.GetRequiredService(), state, TestHelper.WorkingDirectory); + SetupTestDatabase(testDatabaseFactoryProvider, databaseFactory, loggerFactory, state, TestHelper.WorkingDirectory); } /// - /// Get or create an instance of + /// Get or create an instance of /// - /// - /// - /// - /// - /// - /// /// /// There must only be ONE instance shared between all tests in a session /// @@ -266,9 +263,12 @@ namespace Umbraco.Tests.Integration.Testing lock (_dbLocker) { if (_dbInstance != null) + { return _dbInstance; + } _dbInstance = TestDatabaseFactory.Create(filesPath, loggerFactory, dbFactory); + return _dbInstance; } } @@ -276,30 +276,26 @@ namespace Umbraco.Tests.Integration.Testing /// /// Creates a LocalDb instance to use for the test /// - private void InstallTestLocalDb( + private void SetupTestDatabase( TestUmbracoDatabaseFactoryProvider testUmbracoDatabaseFactoryProvider, IUmbracoDatabaseFactory databaseFactory, ILoggerFactory loggerFactory, IRuntimeState runtimeState, string workingDirectory) { - var dbFilePath = Path.Combine(workingDirectory, "LocalDb"); - - // get the currently set db options - var testOptions = TestOptionAttributeBase.GetTestOptions(); - - if (testOptions.Database == UmbracoTestOptions.Database.None) + if (TestOptions.Database == UmbracoTestOptions.Database.None) + { return; + } // need to manually register this factory DbProviderFactories.RegisterFactory(Constants.DbProviderNames.SqlServer, SqlClientFactory.Instance); - if (!Directory.Exists(dbFilePath)) - Directory.CreateDirectory(dbFilePath); + var dbFilePath = Path.Combine(workingDirectory, "LocalDb"); var db = GetOrCreateDatabase(dbFilePath, loggerFactory, testUmbracoDatabaseFactoryProvider); - switch (testOptions.Database) + switch (TestOptions.Database) { case UmbracoTestOptions.Database.NewSchemaPerTest: @@ -385,7 +381,7 @@ namespace Umbraco.Tests.Integration.Testing break; default: - throw new ArgumentOutOfRangeException(nameof(testOptions), testOptions, null); + throw new ArgumentOutOfRangeException(nameof(TestOptions), TestOptions, null); } } @@ -393,6 +389,8 @@ namespace Umbraco.Tests.Integration.Testing #region Common services + protected UmbracoTestAttribute TestOptions => TestOptionAttributeBase.GetTestOptions(); + protected virtual T GetRequiredService() => Services.GetRequiredService(); public Dictionary InMemoryConfiguration { get; } = new Dictionary(); diff --git a/src/Umbraco.Web.Common/Builder/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Common/Builder/UmbracoBuilderExtensions.cs index 9ecda715ee..b8dccfbe33 100644 --- a/src/Umbraco.Web.Common/Builder/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/Builder/UmbracoBuilderExtensions.cs @@ -146,9 +146,7 @@ namespace Umbraco.Core.DependencyInjection builder.Services.AddUnique(); builder.Services.AddUnique(); builder.Services.AddUnique(); - builder.Services.AddUnique(); - builder.Services.AddHostedService(factory => factory.GetRequiredService()); builder.AddCoreInitialServices(); builder.AddComposers(); diff --git a/src/Umbraco.Web.UI.NetCore/Program.cs b/src/Umbraco.Web.UI.NetCore/Program.cs index 4a7722597d..95322cb1b0 100644 --- a/src/Umbraco.Web.UI.NetCore/Program.cs +++ b/src/Umbraco.Web.UI.NetCore/Program.cs @@ -20,7 +20,7 @@ namespace Umbraco.Web.UI.NetCore { x.ClearProviders(); }) - .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }) - .UseUmbraco(); + .UseUmbraco() + .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); } } From 6dd5e04a2be26e82ed8df8227e86a819fdd556f4 Mon Sep 17 00:00:00 2001 From: Paul Johnson Date: Thu, 17 Dec 2020 11:12:13 +0000 Subject: [PATCH 10/18] Fix broken integration test, must set ServiceProvider before using --- .../TestServerTest/UmbracoTestServerTestBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs index 87ac4505e5..8e6c02ca15 100644 --- a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs +++ b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs @@ -71,8 +71,8 @@ namespace Umbraco.Tests.Integration.TestServerTest // call startup builder.Configure(app => { - UseTestDatabase(app.ApplicationServices); Services = app.ApplicationServices; + UseTestDatabase(app.ApplicationServices); Configure(app); }); From 79aa2e1cc24dad6d72548811a8d8317a55c1a0dc Mon Sep 17 00:00:00 2001 From: Paul Johnson Date: Thu, 17 Dec 2020 11:15:58 +0000 Subject: [PATCH 11/18] LocalDb TestDatabase cleanup LocalDbTestDatabase now renames the final database like it used to (guid vs sensible name) Made serilog log to integration tests TEMP as far as I can tell it wasn't writing at all before --- .../Testing/BaseTestDatabase.cs | 4 +- .../Testing/LocalDbTestDatabase.cs | 14 +++- .../Testing/UmbracoIntegrationTest.cs | 70 +++++++++---------- 3 files changed, 48 insertions(+), 40 deletions(-) diff --git a/src/Umbraco.Tests.Integration/Testing/BaseTestDatabase.cs b/src/Umbraco.Tests.Integration/Testing/BaseTestDatabase.cs index 02a3da676a..2a9733fd9d 100644 --- a/src/Umbraco.Tests.Integration/Testing/BaseTestDatabase.cs +++ b/src/Umbraco.Tests.Integration/Testing/BaseTestDatabase.cs @@ -17,8 +17,8 @@ namespace Umbraco.Tests.Integration.Testing { protected ILoggerFactory _loggerFactory; protected IUmbracoDatabaseFactory _databaseFactory; - protected IEnumerable _testDatabases; - + protected IList _testDatabases; + protected UmbracoDatabase.CommandInfo[] _cachedDatabaseInitCommands; protected BlockingCollection _prepareQueue; diff --git a/src/Umbraco.Tests.Integration/Testing/LocalDbTestDatabase.cs b/src/Umbraco.Tests.Integration/Testing/LocalDbTestDatabase.cs index a9a842cdcd..add3df9fe8 100644 --- a/src/Umbraco.Tests.Integration/Testing/LocalDbTestDatabase.cs +++ b/src/Umbraco.Tests.Integration/Testing/LocalDbTestDatabase.cs @@ -64,13 +64,17 @@ namespace Umbraco.Tests.Integration.Testing var tempName = Guid.NewGuid().ToString("N"); _localDbInstance.CreateDatabase(tempName, _filesPath); _localDbInstance.DetachDatabase(tempName); + _prepareQueue = new BlockingCollection(); _readySchemaQueue = new BlockingCollection(); _readyEmptyQueue = new BlockingCollection(); - foreach (var meta in _testDatabases) + for (var i = 0; i < _testDatabases.Count; i++) { - _localDb.CopyDatabaseFiles(tempName, _filesPath, targetDatabaseName: meta.Name, overwrite: true, delete: false); + var meta = _testDatabases[i]; + var isLast = i == _testDatabases.Count - 1; + + _localDb.CopyDatabaseFiles(tempName, _filesPath, targetDatabaseName: meta.Name, overwrite: true, delete: isLast); meta.ConnectionString = _localDbInstance.GetAttachedConnectionString(meta.Name, _filesPath); _prepareQueue.Add(meta); } @@ -85,7 +89,9 @@ namespace Umbraco.Tests.Integration.Testing public void Finish() { if (_prepareQueue == null) + { return; + } _prepareQueue.CompleteAdding(); while (_prepareQueue.TryTake(out _)) @@ -100,14 +106,18 @@ namespace Umbraco.Tests.Integration.Testing { } if (_filesPath == null) + { return; + } var filename = Path.Combine(_filesPath, DatabaseName).ToUpper(); foreach (var database in _localDbInstance.GetDatabases()) { if (database.StartsWith(filename)) + { _localDbInstance.DropDatabase(database); + } } foreach (var file in Directory.EnumerateFiles(_filesPath)) diff --git a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs index 5fb3c50cc8..52b74f6bbb 100644 --- a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs +++ b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs @@ -100,8 +100,10 @@ namespace Umbraco.Tests.Integration.Testing var hostBuilder = CreateHostBuilder() .UseUmbraco(); // This ensures CoreRuntime.StartAsync will be called (however it's a mock if boot = false) - var host = hostBuilder.Start(); + IHost host = hostBuilder.Build(); Services = host.Services; + host.Start(); + var app = new ApplicationBuilder(host.Services); Configure(app); @@ -118,7 +120,16 @@ namespace Umbraco.Tests.Integration.Testing case UmbracoTestOptions.Logger.Mock: return NullLoggerFactory.Instance; case UmbracoTestOptions.Logger.Serilog: - return Microsoft.Extensions.Logging.LoggerFactory.Create(builder => { builder.AddSerilog(); }); + return Microsoft.Extensions.Logging.LoggerFactory.Create(builder => + { + var path = Path.Combine(TestHelper.WorkingDirectory, "logs", "umbraco_integration_tests_.txt"); + + Log.Logger = new LoggerConfiguration() + .WriteTo.File(path, rollingInterval: RollingInterval.Day) + .CreateLogger(); + + builder.AddSerilog(Log.Logger); + }); case UmbracoTestOptions.Logger.Console: return Microsoft.Extensions.Logging.LoggerFactory.Create(builder => { builder.AddConsole(); }); } @@ -300,37 +311,23 @@ namespace Umbraco.Tests.Integration.Testing case UmbracoTestOptions.Database.NewSchemaPerTest: // New DB + Schema - var newSchemaDbMeta = db.AttachSchema(); + TestDbMeta newSchemaDbMeta = db.AttachSchema(); // Add teardown callback OnTestTearDown(() => db.Detach(newSchemaDbMeta)); - // We must re-configure our current factory since attaching a new LocalDb from the pool changes connection strings - if (!databaseFactory.Configured) - { - databaseFactory.Configure(newSchemaDbMeta.ConnectionString, Constants.DatabaseProviders.SqlServer); - } - - // re-run the runtime level check - runtimeState.DetermineRuntimeLevel(); + ConfigureTestDatabaseFactory(newSchemaDbMeta, databaseFactory, runtimeState); Assert.AreEqual(RuntimeLevel.Run, runtimeState.Level); break; case UmbracoTestOptions.Database.NewEmptyPerTest: - var newEmptyDbMeta = db.AttachEmpty(); + TestDbMeta newEmptyDbMeta = db.AttachEmpty(); // Add teardown callback OnTestTearDown(() => db.Detach(newEmptyDbMeta)); - // We must re-configure our current factory since attaching a new LocalDb from the pool changes connection strings - if (!databaseFactory.Configured) - { - databaseFactory.Configure(newEmptyDbMeta.ConnectionString, Constants.DatabaseProviders.SqlServer); - } - - // re-run the runtime level check - runtimeState.DetermineRuntimeLevel(); + ConfigureTestDatabaseFactory(newEmptyDbMeta, databaseFactory, runtimeState); Assert.AreEqual(RuntimeLevel.Install, runtimeState.Level); @@ -342,21 +339,14 @@ namespace Umbraco.Tests.Integration.Testing if (FirstTestInFixture) { // New DB + Schema - var newSchemaFixtureDbMeta = db.AttachSchema(); + TestDbMeta newSchemaFixtureDbMeta = db.AttachSchema(); _fixtureDbMeta = newSchemaFixtureDbMeta; // Add teardown callback OnFixtureTearDown(() => db.Detach(newSchemaFixtureDbMeta)); } - // We must re-configure our current factory since attaching a new LocalDb from the pool changes connection strings - if (!databaseFactory.Configured) - { - databaseFactory.Configure(_fixtureDbMeta.ConnectionString, Constants.DatabaseProviders.SqlServer); - } - - // re-run the runtime level check - runtimeState.DetermineRuntimeLevel(); + ConfigureTestDatabaseFactory(_fixtureDbMeta, databaseFactory, runtimeState); break; case UmbracoTestOptions.Database.NewEmptyPerFixture: @@ -366,18 +356,14 @@ namespace Umbraco.Tests.Integration.Testing if (FirstTestInFixture) { // New DB + Schema - var newEmptyFixtureDbMeta = db.AttachEmpty(); + TestDbMeta newEmptyFixtureDbMeta = db.AttachEmpty(); _fixtureDbMeta = newEmptyFixtureDbMeta; // Add teardown callback OnFixtureTearDown(() => db.Detach(newEmptyFixtureDbMeta)); } - // We must re-configure our current factory since attaching a new LocalDb from the pool changes connection strings - if (!databaseFactory.Configured) - { - databaseFactory.Configure(_fixtureDbMeta.ConnectionString, Constants.DatabaseProviders.SqlServer); - } + ConfigureTestDatabaseFactory(_fixtureDbMeta, databaseFactory, runtimeState); break; default: @@ -385,6 +371,19 @@ namespace Umbraco.Tests.Integration.Testing } } + private void ConfigureTestDatabaseFactory(TestDbMeta meta, IUmbracoDatabaseFactory factory, IRuntimeState state) + { + ILogger log = Services.GetRequiredService>(); + log.LogInformation($"ConfigureTestDatabaseFactory - Using test database: [{meta.Name}] - IsEmpty: [{meta.IsEmpty}]"); + + // It's just been pulled from container and wasn't used to create test database + Assert.IsFalse(factory.Configured); + + factory.Configure(meta.ConnectionString, Constants.DatabaseProviders.SqlServer); + state.DetermineRuntimeLevel(); + log.LogInformation($"ConfigureTestDatabaseFactory - Determined RuntimeLevel: [{state.Level}]"); + } + #endregion #region Common services @@ -420,7 +419,6 @@ namespace Umbraco.Tests.Integration.Testing /// Returns the ///
protected ILoggerFactory LoggerFactory => Services.GetRequiredService(); - protected AppCaches AppCaches => Services.GetRequiredService(); protected IIOHelper IOHelper => Services.GetRequiredService(); protected IShortStringHelper ShortStringHelper => Services.GetRequiredService(); From e4857abc57e05ccce71c9d35d3cda77ce24f5c48 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Thu, 17 Dec 2020 12:58:15 +0100 Subject: [PATCH 12/18] Added missing package to nuspec --- build/NuSpecs/UmbracoCms.Core.nuspec | 1 + 1 file changed, 1 insertion(+) diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index a88f260431..bf2ca3c5a2 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -62,6 +62,7 @@ + From a883df9d882ec064cf65c15453499cb102036cbf Mon Sep 17 00:00:00 2001 From: Paul Johnson Date: Thu, 17 Dec 2020 12:18:28 +0000 Subject: [PATCH 13/18] Use HostBuilder.UseUmbraco in same order for tests and running web app --- .../Composing/HostBuilderExtensions.cs | 7 +------ src/Umbraco.Tests.Integration/RuntimeTests.cs | 4 ++-- .../UmbracoTestServerTestBase.cs | 7 +------ .../UmbracoWebApplicationFactory.cs | 18 ++++++++++++++++-- .../Testing/UmbracoIntegrationTest.cs | 16 +++++++++------- 5 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/Umbraco.Infrastructure/Composing/HostBuilderExtensions.cs b/src/Umbraco.Infrastructure/Composing/HostBuilderExtensions.cs index e2be3569d6..8e1176747c 100644 --- a/src/Umbraco.Infrastructure/Composing/HostBuilderExtensions.cs +++ b/src/Umbraco.Infrastructure/Composing/HostBuilderExtensions.cs @@ -12,12 +12,7 @@ namespace Umbraco.Core.Composing /// Adds CoreRuntime as HostedService ///
/// - /// When running the site should be called before ConfigureWebDefaults. - /// - /// - /// When testing should be called after ConfigureWebDefaults to ensure UseTestDatabase is called before CoreRuntime - /// starts or we initialize components with incorrect run level. - /// + /// Should be called before ConfigureWebDefaults. /// public static IHostBuilder UseUmbraco(this IHostBuilder builder) { diff --git a/src/Umbraco.Tests.Integration/RuntimeTests.cs b/src/Umbraco.Tests.Integration/RuntimeTests.cs index 5d721ad176..cd6909a8c5 100644 --- a/src/Umbraco.Tests.Integration/RuntimeTests.cs +++ b/src/Umbraco.Tests.Integration/RuntimeTests.cs @@ -43,6 +43,7 @@ namespace Umbraco.Tests.Integration var testHelper = new TestHelper(); var hostBuilder = new HostBuilder() + .UseUmbraco() .ConfigureServices((hostContext, services) => { var webHostEnvironment = testHelper.GetWebHostEnvironment(); @@ -68,8 +69,7 @@ namespace Umbraco.Tests.Integration .Build(); services.AddRouting(); // LinkGenerator - }) - .UseUmbraco(); + }); var host = await hostBuilder.StartAsync(); var app = new ApplicationBuilder(host.Services); diff --git a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs index 8e6c02ca15..2b7aea8189 100644 --- a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs +++ b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs @@ -19,7 +19,6 @@ using Umbraco.Core.DependencyInjection; using Umbraco.Web.Common.Controllers; using Microsoft.Extensions.Hosting; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; using Umbraco.Web.BackOffice.Controllers; namespace Umbraco.Tests.Integration.TestServerTest @@ -35,7 +34,7 @@ namespace Umbraco.Tests.Integration.TestServerTest InMemoryConfiguration["Umbraco:CMS:Hosting:Debug"] = "true"; // create new WebApplicationFactory specifying 'this' as the IStartup instance - var factory = new UmbracoWebApplicationFactory(CreateHostBuilder); + var factory = new UmbracoWebApplicationFactory(CreateHostBuilder, BeforeHostStart); // additional host configuration for web server integration tests Factory = factory.WithWebHostBuilder(builder => @@ -71,15 +70,11 @@ namespace Umbraco.Tests.Integration.TestServerTest // call startup builder.Configure(app => { - Services = app.ApplicationServices; - UseTestDatabase(app.ApplicationServices); Configure(app); }); }).UseEnvironment(Environments.Development); - builder.UseUmbraco(); // Ensures CoreRuntime.StartAsync is called, must be after ConfigureWebHost - return builder; } diff --git a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoWebApplicationFactory.cs b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoWebApplicationFactory.cs index 251f155d2c..256c5c59a9 100644 --- a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoWebApplicationFactory.cs +++ b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoWebApplicationFactory.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Extensions.Hosting; @@ -8,16 +8,30 @@ namespace Umbraco.Tests.Integration.TestServerTest public class UmbracoWebApplicationFactory : WebApplicationFactory where TStartup : class { private readonly Func _createHostBuilder; + private readonly Action _beforeStart; /// /// Constructor to create a new WebApplicationFactory /// /// Method to create the IHostBuilder - public UmbracoWebApplicationFactory(Func createHostBuilder) + /// Method to perform an action before IHost starts + public UmbracoWebApplicationFactory(Func createHostBuilder, Action beforeStart = null) { _createHostBuilder = createHostBuilder; + _beforeStart = beforeStart; } protected override IHostBuilder CreateHostBuilder() => _createHostBuilder(); + + protected override IHost CreateHost(IHostBuilder builder) + { + IHost host = builder.Build(); + + _beforeStart?.Invoke(host); + + host.Start(); + + return host; + } } } diff --git a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs index 52b74f6bbb..e62b3f5c7f 100644 --- a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs +++ b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs @@ -97,18 +97,22 @@ namespace Umbraco.Tests.Integration.Testing public virtual void Setup() { InMemoryConfiguration[Constants.Configuration.ConfigGlobal + ":" + nameof(GlobalSettings.InstallEmptyDatabase)] = "true"; - var hostBuilder = CreateHostBuilder() - .UseUmbraco(); // This ensures CoreRuntime.StartAsync will be called (however it's a mock if boot = false) + var hostBuilder = CreateHostBuilder(); IHost host = hostBuilder.Build(); - Services = host.Services; + BeforeHostStart(host); host.Start(); var app = new ApplicationBuilder(host.Services); - Configure(app); } + protected void BeforeHostStart(IHost host) + { + Services = host.Services; + UseTestDatabase(Services); + } + #region Generic Host Builder and Runtime private ILoggerFactory CreateLoggerFactory() @@ -149,12 +153,12 @@ namespace Umbraco.Tests.Integration.Testing public virtual IHostBuilder CreateHostBuilder() { var hostBuilder = Host.CreateDefaultBuilder() + .UseUmbraco() // IMPORTANT: We Cannot use UseStartup, there's all sorts of threads about this with testing. Although this can work // if you want to setup your tests this way, it is a bit annoying to do that as the WebApplicationFactory will // create separate Host instances. So instead of UseStartup, we just call ConfigureServices/Configure ourselves, // and in the case of the UmbracoTestServerTestBase it will use the ConfigureWebHost to Configure the IApplicationBuilder directly. //.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(GetType()); }) - .ConfigureAppConfiguration((context, configBuilder) => { context.HostingEnvironment = TestHelper.GetWebHostEnvironment(); @@ -233,8 +237,6 @@ namespace Umbraco.Tests.Integration.Testing public virtual void Configure(IApplicationBuilder app) { - UseTestDatabase(app.ApplicationServices); - if (TestOptions.Boot) { Services.GetRequiredService().EnsureBackOfficeSecurity(); From cd169fbcce5a28673bce41df5dddbff3bd6ec61a Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Thu, 17 Dec 2020 14:26:17 +0100 Subject: [PATCH 14/18] Update version number in umbraco template from azure pipeline in continuous builds --- build/azure-pipelines.yml | 9 ++++++++- .../UmbracoSolution/.template.config/template.json | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/build/azure-pipelines.yml b/build/azure-pipelines.yml index c545d6884e..92c2cb13ed 100644 --- a/build/azure-pipelines.yml +++ b/build/azure-pipelines.yml @@ -18,7 +18,7 @@ resources: ACCEPT_EULA: Y SA_PASSWORD: $(SA_PASSWORD) MSSQL_PID: Developer - ports: + ports: - 1433:1433 options: --name mssql @@ -170,6 +170,13 @@ stages: $continuous = "$($version.Release)-alpha.$(Build.BuildNumber)" } $ubuild.SetUmbracoVersion($continuous) + + #Update the version in template also + $templatePath = 'build/templates/UmbracoSolution/.template.config/template.json' + $a = Get-Content $templatePath -raw | ConvertFrom-Json + $a.symbols.version.defaultValue = $continuous + $a | ConvertTo-Json -depth 32| set-content $templatePath + Write-Host "Building: $continuous" - task: PowerShell@1 diff --git a/build/templates/UmbracoSolution/.template.config/template.json b/build/templates/UmbracoSolution/.template.config/template.json index 26d2358639..6dc582ca26 100644 --- a/build/templates/UmbracoSolution/.template.config/template.json +++ b/build/templates/UmbracoSolution/.template.config/template.json @@ -9,13 +9,13 @@ "language": "C#", "type": "project" }, - "sourceName": "UmbracoSolution", // Will be replaced with the value provided via -n + "sourceName": "UmbracoSolution", "preferNameDirectory": true, "symbols": { "version": { "type": "parameter", "datatype": "string", - "defaultValue": "0.5.0-alpha*", + "defaultValue": "0.5.0-alpha003", "description": "The version of Umbraco to load using NuGet", "replaces": "UMBRACO_VERSION_FROM_TEMPLATE" }, From 3395f4cc35e05fc098f9424d8125fd22f4a76877 Mon Sep 17 00:00:00 2001 From: Paul Johnson Date: Thu, 17 Dec 2020 12:55:59 +0000 Subject: [PATCH 15/18] Fixes for ContentTypeServiceVariantsTests PublishedSnapshotService hack These pass if running the full suite, but fail when ran alone otherwise --- .../ContentTypeServiceVariantsTests.cs | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceVariantsTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceVariantsTests.cs index d11210c093..e9ec0cbae0 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceVariantsTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceVariantsTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -10,10 +10,12 @@ using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Services; using Umbraco.Core.Sync; +using Umbraco.Net; using Umbraco.Tests.Common.Builders; using Umbraco.Tests.Integration.Testing; using Umbraco.Tests.Testing; using Umbraco.Web.PublishedCache; +using Umbraco.Web.PublishedCache.NuCache; namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services { @@ -510,7 +512,9 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services { // one simple content type, variant, with both variant and invariant properties // can change it to invariant and back - GetRequiredService(); //hack to ensure events are initialized + + //hack to ensure events are initialized + (GetRequiredService() as PublishedSnapshotService)?.OnApplicationInit(null, null); CreateFrenchAndEnglishLangs(); var contentType = CreateContentType(ContentVariation.Culture); @@ -599,7 +603,9 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services // one simple content type, invariant // can change it to variant and back // can then switch one property to variant - GetRequiredService(); //hack to ensure events are initialized + + //hack to ensure events are initialized + (GetRequiredService() as PublishedSnapshotService)?.OnApplicationInit(null, null); var globalSettings = new GlobalSettings(); @@ -690,7 +696,9 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services { // one simple content type, variant, with both variant and invariant properties // can change an invariant property to variant and back - GetRequiredService(); //hack to ensure events are initialized + + //hack to ensure events are initialized + (GetRequiredService() as PublishedSnapshotService)?.OnApplicationInit(null, null); CreateFrenchAndEnglishLangs(); var contentType = CreateContentType(ContentVariation.Culture); @@ -967,8 +975,8 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services // can change the composing content type to invariant and back // can change the composed content type to invariant and back - - GetRequiredService(); //hack to ensure events are initialized + //hack to ensure events are initialized + (GetRequiredService() as PublishedSnapshotService)?.OnApplicationInit(null, null); CreateFrenchAndEnglishLangs(); var composing = CreateContentType(ContentVariation.Culture, "composing"); @@ -1064,7 +1072,8 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services // can change the composing content type to invariant and back // can change the variant composed content type to invariant and back - GetRequiredService(); //hack to ensure events are initialized + //hack to ensure events are initialized + (GetRequiredService() as PublishedSnapshotService)?.OnApplicationInit(null, null); CreateFrenchAndEnglishLangs(); var composing = CreateContentType(ContentVariation.Culture, "composing"); From fa061d36ba4464d0b7d3a8fec916ee6102dc0f8a Mon Sep 17 00:00:00 2001 From: Paul Johnson Date: Thu, 17 Dec 2020 17:06:51 +0000 Subject: [PATCH 16/18] What's up with ThreadSafetyServiceTest? Really interesting that it has this at top if (Environment.GetEnvironmentVariable("UMBRACO_TMP") != null) Assert.Ignore("Do not run on VSTS."); But this test passes on other peoples builds :( --- .../Services/ThreadSafetyServiceTest.cs | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ThreadSafetyServiceTest.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ThreadSafetyServiceTest.cs index 1d3b799679..9600d62051 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ThreadSafetyServiceTest.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ThreadSafetyServiceTest.cs @@ -1,8 +1,9 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; +using Microsoft.Extensions.Logging; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Models; @@ -31,7 +32,7 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services [TestFixture] [Apartment(ApartmentState.STA)] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, Logger = UmbracoTestOptions.Logger.Console)] public class ThreadSafetyServiceTest : UmbracoIntegrationTest { private IContentService ContentService => GetRequiredService(); @@ -98,13 +99,15 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services if (Environment.GetEnvironmentVariable("UMBRACO_TMP") != null) Assert.Ignore("Do not run on VSTS."); + var log = GetRequiredService>(); + // the ServiceContext in that each repository in a service (i.e. ContentService) is a singleton var contentService = (ContentService)ContentService; var threads = new List(); var exceptions = new List(); - Debug.WriteLine("Starting..."); + log.LogInformation("Starting..."); var done = TraceLocks(); @@ -114,12 +117,12 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services { try { - Debug.WriteLine("[{0}] Running...", Thread.CurrentThread.ManagedThreadId); + log.LogInformation("[{0}] Running...", Thread.CurrentThread.ManagedThreadId); var name1 = "test-" + Guid.NewGuid(); var content1 = contentService.Create(name1, -1, "umbTextpage"); - Debug.WriteLine("[{0}] Saving content #1.", Thread.CurrentThread.ManagedThreadId); + log.LogInformation("[{0}] Saving content #1.", Thread.CurrentThread.ManagedThreadId); Save(contentService, content1); Thread.Sleep(100); //quick pause for maximum overlap! @@ -127,7 +130,7 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services var name2 = "test-" + Guid.NewGuid(); var content2 = contentService.Create(name2, -1, "umbTextpage"); - Debug.WriteLine("[{0}] Saving content #2.", Thread.CurrentThread.ManagedThreadId); + log.LogInformation("[{0}] Saving content #2.", Thread.CurrentThread.ManagedThreadId); Save(contentService, content2); } catch (Exception e) @@ -139,16 +142,16 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services } // start all threads - Debug.WriteLine("Starting threads"); + log.LogInformation("Starting threads"); threads.ForEach(x => x.Start()); // wait for all to complete - Debug.WriteLine("Joining threads"); + log.LogInformation("Joining threads"); threads.ForEach(x => x.Join()); done.Set(); - Debug.WriteLine("Checking exceptions"); + log.LogInformation("Checking exceptions"); if (exceptions.Count == 0) { //now look up all items, there should be 40! @@ -166,13 +169,16 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services { if (Environment.GetEnvironmentVariable("UMBRACO_TMP") != null) Assert.Ignore("Do not run on VSTS."); + + var log = GetRequiredService>(); + // mimick the ServiceContext in that each repository in a service (i.e. ContentService) is a singleton var mediaService = (MediaService)MediaService; var threads = new List(); var exceptions = new List(); - Debug.WriteLine("Starting..."); + log.LogInformation("Starting..."); var done = TraceLocks(); @@ -182,18 +188,18 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services { try { - Debug.WriteLine("[{0}] Running...", Thread.CurrentThread.ManagedThreadId); + log.LogInformation("[{0}] Running...", Thread.CurrentThread.ManagedThreadId); var name1 = "test-" + Guid.NewGuid(); var media1 = mediaService.CreateMedia(name1, -1, Constants.Conventions.MediaTypes.Folder); - Debug.WriteLine("[{0}] Saving media #1.", Thread.CurrentThread.ManagedThreadId); + log.LogInformation("[{0}] Saving media #1.", Thread.CurrentThread.ManagedThreadId); Save(mediaService, media1); Thread.Sleep(100); //quick pause for maximum overlap! var name2 = "test-" + Guid.NewGuid(); var media2 = mediaService.CreateMedia(name2, -1, Constants.Conventions.MediaTypes.Folder); - Debug.WriteLine("[{0}] Saving media #2.", Thread.CurrentThread.ManagedThreadId); + log.LogInformation("[{0}] Saving media #2.", Thread.CurrentThread.ManagedThreadId); Save(mediaService, media2); } catch (Exception e) From 6114fffb4a97d6a71bc5b57cfb877d18458ac679 Mon Sep 17 00:00:00 2001 From: Paul Johnson Date: Fri, 18 Dec 2020 09:53:01 +0000 Subject: [PATCH 17/18] Remove HostBuilder UseUmbraco extension, it's not required. --- .../Composing/HostBuilderExtensions.cs | 25 ------------------- src/Umbraco.Tests.Integration/RuntimeTests.cs | 1 - .../Testing/UmbracoIntegrationTest.cs | 1 - .../Builder/UmbracoBuilderExtensions.cs | 2 ++ src/Umbraco.Web.UI.NetCore/Program.cs | 1 - 5 files changed, 2 insertions(+), 28 deletions(-) delete mode 100644 src/Umbraco.Infrastructure/Composing/HostBuilderExtensions.cs diff --git a/src/Umbraco.Infrastructure/Composing/HostBuilderExtensions.cs b/src/Umbraco.Infrastructure/Composing/HostBuilderExtensions.cs deleted file mode 100644 index 8e1176747c..0000000000 --- a/src/Umbraco.Infrastructure/Composing/HostBuilderExtensions.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace Umbraco.Core.Composing -{ - /// - /// Extends the to add CoreRuntime as a HostedService - /// - public static class HostBuilderExtensions - { - /// - /// Adds CoreRuntime as HostedService - /// - /// - /// Should be called before ConfigureWebDefaults. - /// - public static IHostBuilder UseUmbraco(this IHostBuilder builder) - { - _ = builder.ConfigureServices((context, services) => - services.AddSingleton(factory => factory.GetRequiredService())); - - return builder; - } - } -} diff --git a/src/Umbraco.Tests.Integration/RuntimeTests.cs b/src/Umbraco.Tests.Integration/RuntimeTests.cs index cd6909a8c5..f74a95af4c 100644 --- a/src/Umbraco.Tests.Integration/RuntimeTests.cs +++ b/src/Umbraco.Tests.Integration/RuntimeTests.cs @@ -43,7 +43,6 @@ namespace Umbraco.Tests.Integration var testHelper = new TestHelper(); var hostBuilder = new HostBuilder() - .UseUmbraco() .ConfigureServices((hostContext, services) => { var webHostEnvironment = testHelper.GetWebHostEnvironment(); diff --git a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs index e62b3f5c7f..d3abd76d7b 100644 --- a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs +++ b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs @@ -153,7 +153,6 @@ namespace Umbraco.Tests.Integration.Testing public virtual IHostBuilder CreateHostBuilder() { var hostBuilder = Host.CreateDefaultBuilder() - .UseUmbraco() // IMPORTANT: We Cannot use UseStartup, there's all sorts of threads about this with testing. Although this can work // if you want to setup your tests this way, it is a bit annoying to do that as the WebApplicationFactory will // create separate Host instances. So instead of UseStartup, we just call ConfigureServices/Configure ourselves, diff --git a/src/Umbraco.Web.Common/Builder/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Common/Builder/UmbracoBuilderExtensions.cs index b8dccfbe33..445025a80c 100644 --- a/src/Umbraco.Web.Common/Builder/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/Builder/UmbracoBuilderExtensions.cs @@ -146,7 +146,9 @@ namespace Umbraco.Core.DependencyInjection builder.Services.AddUnique(); builder.Services.AddUnique(); builder.Services.AddUnique(); + builder.Services.AddUnique(); + builder.Services.AddHostedService(factory => factory.GetRequiredService()); builder.AddCoreInitialServices(); builder.AddComposers(); diff --git a/src/Umbraco.Web.UI.NetCore/Program.cs b/src/Umbraco.Web.UI.NetCore/Program.cs index 95322cb1b0..4849ee226a 100644 --- a/src/Umbraco.Web.UI.NetCore/Program.cs +++ b/src/Umbraco.Web.UI.NetCore/Program.cs @@ -20,7 +20,6 @@ namespace Umbraco.Web.UI.NetCore { x.ClearProviders(); }) - .UseUmbraco() .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); } } From e4e26159aea883d4e2b0012965ee2178ff1b1082 Mon Sep 17 00:00:00 2001 From: Paul Johnson Date: Fri, 18 Dec 2020 14:10:11 +0000 Subject: [PATCH 18/18] Prevent multiple first time schema create --- .../Testing/BaseTestDatabase.cs | 70 +++++++++++-------- .../Testing/LocalDbTestDatabase.cs | 2 - .../Testing/SqlDeveloperTestDatabase.cs | 2 - 3 files changed, 39 insertions(+), 35 deletions(-) diff --git a/src/Umbraco.Tests.Integration/Testing/BaseTestDatabase.cs b/src/Umbraco.Tests.Integration/Testing/BaseTestDatabase.cs index 2a9733fd9d..52a0778a59 100644 --- a/src/Umbraco.Tests.Integration/Testing/BaseTestDatabase.cs +++ b/src/Umbraco.Tests.Integration/Testing/BaseTestDatabase.cs @@ -19,7 +19,9 @@ namespace Umbraco.Tests.Integration.Testing protected IUmbracoDatabaseFactory _databaseFactory; protected IList _testDatabases; - protected UmbracoDatabase.CommandInfo[] _cachedDatabaseInitCommands; + protected const int _threadCount = 2; + + protected UmbracoDatabase.CommandInfo[] _cachedDatabaseInitCommands = new UmbracoDatabase.CommandInfo[0]; protected BlockingCollection _prepareQueue; protected BlockingCollection _readySchemaQueue; @@ -92,46 +94,52 @@ namespace Umbraco.Tests.Integration.Testing }); } - protected void RebuildSchema(IDbCommand command, TestDbMeta meta) + private void RebuildSchema(IDbCommand command, TestDbMeta meta) { - if (_cachedDatabaseInitCommands != null) + lock (_cachedDatabaseInitCommands) { - foreach (var dbCommand in _cachedDatabaseInitCommands) + if (!_cachedDatabaseInitCommands.Any()) { - - if (dbCommand.Text.StartsWith("SELECT ")) - { - continue; - } - - command.CommandText = dbCommand.Text; - command.Parameters.Clear(); - - foreach (var parameterInfo in dbCommand.Parameters) - { - AddParameter(command, parameterInfo); - } - - command.ExecuteNonQuery(); + RebuildSchemaFirstTime(command, meta); + return; } } - else + + foreach (var dbCommand in _cachedDatabaseInitCommands) { - _databaseFactory.Configure(meta.ConnectionString, Core.Constants.DatabaseProviders.SqlServer); - - using (var database = (UmbracoDatabase)_databaseFactory.CreateDatabase()) + if (dbCommand.Text.StartsWith("SELECT ")) { - database.LogCommands = true; + continue; + } - using (var transaction = database.GetTransaction()) - { - var schemaCreator = new DatabaseSchemaCreator(database, _loggerFactory.CreateLogger(), _loggerFactory, new UmbracoVersion()); - schemaCreator.InitializeDatabaseSchema(); + command.CommandText = dbCommand.Text; + command.Parameters.Clear(); - transaction.Complete(); + foreach (var parameterInfo in dbCommand.Parameters) + { + AddParameter(command, parameterInfo); + } - _cachedDatabaseInitCommands = database.Commands.ToArray(); - } + command.ExecuteNonQuery(); + } + } + + private void RebuildSchemaFirstTime(IDbCommand command, TestDbMeta meta) + { + _databaseFactory.Configure(meta.ConnectionString, Core.Constants.DatabaseProviders.SqlServer); + + using (var database = (UmbracoDatabase)_databaseFactory.CreateDatabase()) + { + database.LogCommands = true; + + using (var transaction = database.GetTransaction()) + { + var schemaCreator = new DatabaseSchemaCreator(database, _loggerFactory.CreateLogger(), _loggerFactory, new UmbracoVersion()); + schemaCreator.InitializeDatabaseSchema(); + + transaction.Complete(); + + _cachedDatabaseInitCommands = database.Commands.ToArray(); } } } diff --git a/src/Umbraco.Tests.Integration/Testing/LocalDbTestDatabase.cs b/src/Umbraco.Tests.Integration/Testing/LocalDbTestDatabase.cs index add3df9fe8..59a9b00215 100644 --- a/src/Umbraco.Tests.Integration/Testing/LocalDbTestDatabase.cs +++ b/src/Umbraco.Tests.Integration/Testing/LocalDbTestDatabase.cs @@ -19,8 +19,6 @@ namespace Umbraco.Tests.Integration.Testing private static LocalDb.Instance _localDbInstance; private static string _filesPath; - private const int _threadCount = 2; - public static LocalDbTestDatabase Instance { get; private set; } //It's internal because `Umbraco.Core.Persistence.LocalDb` is internal diff --git a/src/Umbraco.Tests.Integration/Testing/SqlDeveloperTestDatabase.cs b/src/Umbraco.Tests.Integration/Testing/SqlDeveloperTestDatabase.cs index 4a7f602ac6..2e1f75cd45 100644 --- a/src/Umbraco.Tests.Integration/Testing/SqlDeveloperTestDatabase.cs +++ b/src/Umbraco.Tests.Integration/Testing/SqlDeveloperTestDatabase.cs @@ -17,8 +17,6 @@ namespace Umbraco.Tests.Integration.Testing private readonly string _masterConnectionString; public const string DatabaseName = "UmbracoTests"; - private const int _threadCount = 2; - public static SqlDeveloperTestDatabase Instance { get; private set; } public SqlDeveloperTestDatabase(ILoggerFactory loggerFactory, IUmbracoDatabaseFactory databaseFactory, string masterConnectionString)