From 97d20baecb07dbac6436b915333540dbc44d3995 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 20 Jan 2021 15:30:41 +0100 Subject: [PATCH 001/182] skipValidation for content save --- .../components/content/edit.controller.js | 22 ++++++++++++------- .../services/contenteditinghelper.service.js | 19 ++++++++++------ 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js index f2dc0622c7..ac470642e7 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js @@ -271,7 +271,7 @@ * @param {any} app the active content app */ function createButtons(content) { - + var isBlueprint = content.isBlueprint; if ($scope.page.isNew && $location.path().search(/contentBlueprints/i) !== -1) { @@ -456,7 +456,8 @@ create: $scope.page.isNew, action: args.action, showNotifications: args.showNotifications, - softRedirect: true + softRedirect: true, + skipValidation: args.skipValidation }).then(function (data) { //success init(); @@ -468,7 +469,9 @@ eventsService.emit("content.saved", { content: $scope.content, action: args.action }); - resetNestedFieldValiation(fieldsToRollback); + if($scope.contentForm.$invalid !== true) { + resetNestedFieldValiation(fieldsToRollback); + } ensureDirtyIsSetIfAnyVariantIsDirty(); return $q.when(data); @@ -476,7 +479,9 @@ function (err) { syncTreeNode($scope.content, $scope.content.path); - resetNestedFieldValiation(fieldsToRollback); + if($scope.contentForm.$invalid !== true) { + resetNestedFieldValiation(fieldsToRollback); + } return $q.reject(err); }); @@ -729,9 +734,9 @@ clearNotifications($scope.content); // TODO: Add "..." to save button label if there are more than one variant to publish - currently it just adds the elipses if there's more than 1 variant if (hasVariants($scope.content)) { - //before we launch the dialog we want to execute all client side validations first - if (formHelper.submitForm({ scope: $scope, action: "openSaveDialog" })) { + //before we launch the dialog we want to execute all client side validations first + if (formHelper.submitForm({ scope: $scope, action: "openSaveDialog", skipValidation:true, keepServerValidation:true })) { var dialog = { parentScope: $scope, view: "views/content/overlays/save.html", @@ -778,7 +783,8 @@ $scope.page.saveButtonState = "busy"; return performSave({ saveMethod: $scope.saveMethod(), - action: "save" + action: "save", + skipValidation: true }).then(function () { $scope.page.saveButtonState = "success"; }, function (err) { @@ -981,7 +987,7 @@ $scope.appChanged = function (activeApp) { $scope.activeApp = activeApp; - + _.forEach($scope.content.apps, function (app) { app.active = false; if (app.alias === $scope.activeApp.alias) { diff --git a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js index 6d41ea087d..fd0bd3efd9 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js @@ -84,7 +84,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt //when true, the url will change but it won't actually re-route //this is merely here for compatibility, if only the content/media/members used this service we'd prob be ok but tons of editors //use this service unfortunately and probably packages too. - args.softRedirect = false; + args.softRedirect = false; } @@ -93,7 +93,12 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt //we will use the default one for content if not specified var rebindCallback = args.rebindCallback === undefined ? self.reBindChangedProperties : args.rebindCallback; - if (formHelper.submitForm({ scope: args.scope, action: args.action })) { + var formSubmitOptions = { scope: args.scope, action: args.action }; + if(args.skipValidation === true) { + formSubmitOptions.skipValidation = true; + formSubmitOptions.keepServerValidation = true; + } + if (formHelper.submitForm(formSubmitOptions)) { return args.saveMethod(args.content, args.create, fileManager.getFiles(), args.showNotifications) .then(function (data) { @@ -298,7 +303,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt } // if publishing is allowed also allow schedule publish - // we add this manually becuase it doesn't have a permission so it wont + // we add this manually becuase it doesn't have a permission so it wont // get picked up by the loop through permissions if (_.contains(args.content.allowedActions, "U")) { buttons.subButtons.push(createButtonDefinition("SCHEDULE")); @@ -622,7 +627,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt if (!args.err) { throw "args.err cannot be null"; } - + //When the status is a 400 status with a custom header: X-Status-Reason: Validation failed, we have validation errors. //Otherwise the error is probably due to invalid data (i.e. someone mucking around with the ids or something). //Or, some strange server error @@ -640,7 +645,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt if (!this.redirectToCreatedContent(args.err.data.id, args.softRedirect) || args.softRedirect) { // If we are not redirecting it's because this is not newly created content, else in some cases we are - // soft-redirecting which means the URL will change but the route wont (i.e. creating content). + // soft-redirecting which means the URL will change but the route wont (i.e. creating content). // In this case we need to detect what properties have changed and re-bind them with the server data. if (args.rebindCallback && angular.isFunction(args.rebindCallback)) { @@ -687,7 +692,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt if (!this.redirectToCreatedContent(args.redirectId ? args.redirectId : args.savedContent.id, args.softRedirect) || args.softRedirect) { // If we are not redirecting it's because this is not newly created content, else in some cases we are - // soft-redirecting which means the URL will change but the route wont (i.e. creating content). + // soft-redirecting which means the URL will change but the route wont (i.e. creating content). // In this case we need to detect what properties have changed and re-bind them with the server data. if (args.rebindCallback && angular.isFunction(args.rebindCallback)) { @@ -723,7 +728,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt navigationService.setSoftRedirect(); } //change to new path - $location.path("/" + $routeParams.section + "/" + $routeParams.tree + "/" + $routeParams.method + "/" + id); + $location.path("/" + $routeParams.section + "/" + $routeParams.tree + "/" + $routeParams.method + "/" + id); //don't add a browser history for this $location.replace(); return true; From 3185a285d93a2835044b0cdc7b95301dd2929fa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 1 Feb 2021 11:56:51 +0100 Subject: [PATCH 002/182] Correcting merge --- .../common/directives/components/content/edit.controller.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js index c3e23e4cfd..fb70143fa5 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js @@ -481,13 +481,14 @@ syncTreeNode($scope.content, $scope.content.path); + if($scope.contentForm.$invalid !== true) { + resetNestedFieldValiation(fieldsToRollback); + } if (err.status === 400 && err.data) { // content was saved but is invalid. eventsService.emit("content.saved", { content: $scope.content, action: args.action, valid: false }); } - resetNestedFieldValiation(fieldsToRollback); - return $q.reject(err); }); } From 7d135899be68a4b79bfada012c2b438461772042 Mon Sep 17 00:00:00 2001 From: nzdev <834725+nzdev@users.noreply.github.com> Date: Sun, 21 Feb 2021 20:45:03 +1300 Subject: [PATCH 003/182] load only once --- .../Sync/DatabaseServerMessenger.cs | 90 ++++++++++++------- .../Sync/ISyncBootStateAccessor.cs | 35 ++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../PublishedContent/NuCacheChildrenTests.cs | 4 +- .../PublishedContent/NuCacheTests.cs | 4 +- .../Scoping/ScopedNuCacheTests.cs | 3 +- .../ContentTypeServiceVariantsTests.cs | 4 +- .../TestHelpers/TestSyncBootStateAccessor.cs | 23 +++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + ...aseServerRegistrarAndMessengerComponent.cs | 1 + .../NuCache/PublishedSnapshotService.cs | 17 +++- 11 files changed, 146 insertions(+), 37 deletions(-) create mode 100644 src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs create mode 100644 src/Umbraco.Tests/TestHelpers/TestSyncBootStateAccessor.cs mode change 100755 => 100644 src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs diff --git a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs index 7442169b44..8264ddd79c 100644 --- a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs +++ b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs @@ -28,7 +28,7 @@ namespace Umbraco.Core.Sync // but only processes instructions coming from remote servers, // thus ensuring that instructions run only once // - public class DatabaseServerMessenger : ServerMessengerBase + public class DatabaseServerMessenger : ServerMessengerBase, ISyncBootStateAccessor { private readonly IRuntimeState _runtime; private readonly ManualResetEvent _syncIdle; @@ -172,35 +172,7 @@ namespace Umbraco.Core.Sync lock (_locko) { if (_released) return; - - var coldboot = false; - if (_lastId < 0) // never synced before - { - // we haven't synced - in this case we aren't going to sync the whole thing, we will assume this is a new - // server and it will need to rebuild it's own caches, eg Lucene or the xml cache file. - Logger.Warn("No last synced Id found, this generally means this is a new server/install." - + " The server will build its caches and indexes, and then adjust its last synced Id to the latest found in" - + " the database and maintain cache updates based on that Id."); - - coldboot = true; - } - else - { - //check for how many instructions there are to process, each row contains a count of the number of instructions contained in each - //row so we will sum these numbers to get the actual count. - var count = database.ExecuteScalar("SELECT SUM(instructionCount) FROM umbracoCacheInstruction WHERE id > @lastId", new {lastId = _lastId}); - if (count > Options.MaxProcessingInstructionCount) - { - //too many instructions, proceed to cold boot - Logger.Warn( - "The instruction count ({InstructionCount}) exceeds the specified MaxProcessingInstructionCount ({MaxProcessingInstructionCount})." - + " The server will skip existing instructions, rebuild its caches and indexes entirely, adjust its last synced Id" - + " to the latest found in the database and maintain cache updates based on that Id.", - count, Options.MaxProcessingInstructionCount); - - coldboot = true; - } - } + var coldboot = IsColdBoot(database); if (coldboot) { @@ -223,6 +195,40 @@ namespace Umbraco.Core.Sync } } + private bool IsColdBoot(IUmbracoDatabase database) + { + var coldboot = false; + if (_lastId < 0) // never synced before + { + // we haven't synced - in this case we aren't going to sync the whole thing, we will assume this is a new + // server and it will need to rebuild it's own caches, eg Lucene or the xml cache file. + Logger.Warn("No last synced Id found, this generally means this is a new server/install." + + " The server will build its caches and indexes, and then adjust its last synced Id to the latest found in" + + " the database and maintain cache updates based on that Id."); + + coldboot = true; + } + else + { + //check for how many instructions there are to process, each row contains a count of the number of instructions contained in each + //row so we will sum these numbers to get the actual count. + var count = database.ExecuteScalar("SELECT SUM(instructionCount) FROM umbracoCacheInstruction WHERE id > @lastId", new {lastId = _lastId}); + if (count > Options.MaxProcessingInstructionCount) + { + //too many instructions, proceed to cold boot + Logger.Warn( + "The instruction count ({InstructionCount}) exceeds the specified MaxProcessingInstructionCount ({MaxProcessingInstructionCount})." + + " The server will skip existing instructions, rebuild its caches and indexes entirely, adjust its last synced Id" + + " to the latest found in the database and maintain cache updates based on that Id.", + count, Options.MaxProcessingInstructionCount); + + coldboot = true; + } + } + + return coldboot; + } + /// /// Synchronize the server (throttled). /// @@ -548,6 +554,30 @@ namespace Umbraco.Core.Sync #endregion + public SyncBootState GetSyncBootState() + { + try + { + ReadLastSynced(); // get _lastId + using (var scope = ScopeProvider.CreateScope()) + { + EnsureInstructions(scope.Database); + bool isColdBoot = IsColdBoot(scope.Database); + + if (isColdBoot) + { + return SyncBootState.ColdBoot; + } + return SyncBootState.HasSyncState; + } + } + catch(Exception ex) + { + Logger.Warn("Error determining Sync Boot State", ex); + return SyncBootState.Unknown; + } + } + #region Notify refreshers private static ICacheRefresher GetRefresher(Guid id) diff --git a/src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs b/src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs new file mode 100644 index 0000000000..a7b7c58235 --- /dev/null +++ b/src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Umbraco.Core.Sync +{ + /// + /// Retrieve the state of the sync service + /// + public interface ISyncBootStateAccessor + { + /// + /// Get the boot state + /// + /// + SyncBootState GetSyncBootState(); + } + public enum SyncBootState + { + /// + /// Unknown state. Treat as HasSyncState + /// + Unknown = 0, + /// + /// Cold boot. No Sync state + /// + ColdBoot = 1, + /// + /// Warm boot. Sync state present + /// + HasSyncState = 2 + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 465ddee6ee..5c3dd074dd 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -173,6 +173,7 @@ + diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs index 834d211994..068a161268 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs @@ -17,6 +17,7 @@ using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Services.Changes; using Umbraco.Core.Strings; +using Umbraco.Core.Sync; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing.Objects; using Umbraco.Tests.Testing.Objects.Accessors; @@ -155,7 +156,8 @@ namespace Umbraco.Tests.PublishedContent globalSettings, Mock.Of(), Mock.Of(), - new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() })); + new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }), + new TestSyncBootStateAccessor(SyncBootState.HasSyncState)); // invariant is the current default _variationAccesor.VariationContext = new VariationContext(); diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs index 0e05e6baad..652891c476 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs @@ -17,6 +17,7 @@ using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Services.Changes; using Umbraco.Core.Strings; +using Umbraco.Core.Sync; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing.Objects; using Umbraco.Tests.Testing.Objects.Accessors; @@ -201,7 +202,8 @@ namespace Umbraco.Tests.PublishedContent globalSettings, Mock.Of(), Mock.Of(), - new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() })); + new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }), + new TestSyncBootStateAccessor(SyncBootState.HasSyncState)); // invariant is the current default _variationAccesor.VariationContext = new VariationContext(); diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index c7c403b260..e204bd7b5f 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -99,7 +99,8 @@ namespace Umbraco.Tests.Scoping Factory.GetInstance(), Factory.GetInstance(), Mock.Of(), - new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() })); + new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }), + new TestSyncBootStateAccessor(SyncBootState.HasSyncState)); } protected UmbracoContext GetUmbracoContextNu(string url, int templateId = 1234, RouteData routeData = null, bool setSingleton = false, IUmbracoSettingsSection umbracoSettings = null, IEnumerable urlProviders = null) diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs index 9391b7442f..9cd4bb63e8 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs @@ -17,6 +17,7 @@ using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Core.Sync; +using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; using Umbraco.Tests.Testing; using Umbraco.Web.PublishedCache; @@ -70,7 +71,8 @@ namespace Umbraco.Tests.Services Factory.GetInstance(), Factory.GetInstance(), Mock.Of(), - new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() })); + new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }), + new TestSyncBootStateAccessor(SyncBootState.HasSyncState)); } public class LocalServerMessenger : ServerMessengerBase diff --git a/src/Umbraco.Tests/TestHelpers/TestSyncBootStateAccessor.cs b/src/Umbraco.Tests/TestHelpers/TestSyncBootStateAccessor.cs new file mode 100644 index 0000000000..e5f6989381 --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/TestSyncBootStateAccessor.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Umbraco.Core.Sync; + +namespace Umbraco.Tests.TestHelpers +{ + class TestSyncBootStateAccessor : ISyncBootStateAccessor + { + private readonly SyncBootState _syncBootState; + + public TestSyncBootStateAccessor(SyncBootState syncBootState) + { + _syncBootState = syncBootState; + } + public SyncBootState GetSyncBootState() + { + return _syncBootState; + } + } +} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 2ac28aa7d7..6c4f7415ea 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -175,6 +175,7 @@ + diff --git a/src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs b/src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs index 2fa9d80779..688fc268b0 100644 --- a/src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs +++ b/src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs @@ -72,6 +72,7 @@ namespace Umbraco.Web.Compose composition.SetDatabaseServerMessengerOptions(GetDefaultOptions); composition.SetServerMessenger(); + composition.Register(Lifetime.Singleton); } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs old mode 100755 new mode 100644 index a39e26e2b1..a592fed4be --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -24,6 +24,7 @@ using Umbraco.Core.Services; using Umbraco.Core.Services.Changes; using Umbraco.Core.Services.Implement; using Umbraco.Core.Strings; +using Umbraco.Core.Sync; using Umbraco.Web.Cache; using Umbraco.Web.Install; using Umbraco.Web.PublishedCache.NuCache.DataSource; @@ -63,6 +64,8 @@ namespace Umbraco.Web.PublishedCache.NuCache private bool _localContentDbExists; private bool _localMediaDbExists; + private readonly ISyncBootStateAccessor _syncBootStateAccessor; + // define constant - determines whether to use cache when previewing // to store eg routes, property converted values, anything - caching // means faster execution, but uses memory - not sure if we want it @@ -81,7 +84,8 @@ namespace Umbraco.Web.PublishedCache.NuCache IDataSource dataSource, IGlobalSettings globalSettings, IEntityXmlSerializer entitySerializer, IPublishedModelFactory publishedModelFactory, - UrlSegmentProviderCollection urlSegmentProviders) + UrlSegmentProviderCollection urlSegmentProviders, + ISyncBootStateAccessor syncBootStateAccessor) : base(publishedSnapshotAccessor, variationContextAccessor) { //if (Interlocked.Increment(ref _singletonCheck) > 1) @@ -99,6 +103,8 @@ namespace Umbraco.Web.PublishedCache.NuCache _globalSettings = globalSettings; _urlSegmentProviders = urlSegmentProviders; + _syncBootStateAccessor = syncBootStateAccessor; + // we need an Xml serializer here so that the member cache can support XPath, // for members this is done by navigating the serialized-to-xml member _entitySerializer = entitySerializer; @@ -217,7 +223,12 @@ namespace Umbraco.Web.PublishedCache.NuCache { var okContent = false; var okMedia = false; - + if (_syncBootStateAccessor.GetSyncBootState() == SyncBootState.ColdBoot) + { + _logger.Warn("Sync Service is in a Cold Boot state. Skip LoadCachesOnStartup as the Sync Service will trigger a full reload"); + _isReady = true; + return; + } try { if (_localContentDbExists) @@ -233,7 +244,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (!okMedia) _logger.Warn("Loading media from local db raised warnings, will reload from database."); } - + if (!okContent) LockAndLoadContent(scope => LoadContentFromDatabaseLocked(scope, true)); From 5f9d126ab7d087a204e32aed78148b07bc12ea2b Mon Sep 17 00:00:00 2001 From: nzdev <834725+nzdev@users.noreply.github.com> Date: Fri, 26 Mar 2021 16:13:47 +1300 Subject: [PATCH 004/182] fix support for non run states --- .../Sync/ISyncBootStateAccessor.cs | 15 ------------ .../Sync/NonRuntimeLevelBootStateAccessor.cs | 19 +++++++++++++++ src/Umbraco.Core/Sync/SyncBootState.cs | 24 +++++++++++++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 2 ++ ...aseServerRegistrarAndMessengerComponent.cs | 2 +- .../PublishedCache/NuCache/NuCacheComposer.cs | 4 ++++ 6 files changed, 50 insertions(+), 16 deletions(-) create mode 100644 src/Umbraco.Core/Sync/NonRuntimeLevelBootStateAccessor.cs create mode 100644 src/Umbraco.Core/Sync/SyncBootState.cs diff --git a/src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs b/src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs index a7b7c58235..4b8500f2d9 100644 --- a/src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs +++ b/src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs @@ -17,19 +17,4 @@ namespace Umbraco.Core.Sync /// SyncBootState GetSyncBootState(); } - public enum SyncBootState - { - /// - /// Unknown state. Treat as HasSyncState - /// - Unknown = 0, - /// - /// Cold boot. No Sync state - /// - ColdBoot = 1, - /// - /// Warm boot. Sync state present - /// - HasSyncState = 2 - } } diff --git a/src/Umbraco.Core/Sync/NonRuntimeLevelBootStateAccessor.cs b/src/Umbraco.Core/Sync/NonRuntimeLevelBootStateAccessor.cs new file mode 100644 index 0000000000..70cec6cc96 --- /dev/null +++ b/src/Umbraco.Core/Sync/NonRuntimeLevelBootStateAccessor.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Umbraco.Core.Sync +{ + /// + /// Boot state implementation for when umbraco is not in the run state + /// + public class NonRuntimeLevelBootStateAccessor : ISyncBootStateAccessor + { + public SyncBootState GetSyncBootState() + { + return SyncBootState.Unknown; + } + } +} diff --git a/src/Umbraco.Core/Sync/SyncBootState.cs b/src/Umbraco.Core/Sync/SyncBootState.cs new file mode 100644 index 0000000000..4abc53abba --- /dev/null +++ b/src/Umbraco.Core/Sync/SyncBootState.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Umbraco.Core.Sync +{ + public enum SyncBootState + { + /// + /// Unknown state. Treat as HasSyncState + /// + Unknown = 0, + /// + /// Cold boot. No Sync state + /// + ColdBoot = 1, + /// + /// Warm boot. Sync state present + /// + HasSyncState = 2 + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 5c3dd074dd..e0c0d78112 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -174,6 +174,8 @@ + + diff --git a/src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs b/src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs index 688fc268b0..26ba0db324 100644 --- a/src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs +++ b/src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs @@ -72,7 +72,7 @@ namespace Umbraco.Web.Compose composition.SetDatabaseServerMessengerOptions(GetDefaultOptions); composition.SetServerMessenger(); - composition.Register(Lifetime.Singleton); + composition.Register(factory=> factory.GetInstance() as BatchedDatabaseServerMessenger, Lifetime.Singleton); } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComposer.cs b/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComposer.cs index f748fd555c..99f2786d49 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComposer.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComposer.cs @@ -1,5 +1,6 @@ using Umbraco.Core; using Umbraco.Core.Composing; +using Umbraco.Core.Sync; using Umbraco.Web.PublishedCache.NuCache.DataSource; namespace Umbraco.Web.PublishedCache.NuCache @@ -10,6 +11,9 @@ namespace Umbraco.Web.PublishedCache.NuCache { base.Compose(composition); + //Overriden on Run state in DatabaseServerRegistrarAndMessengerComposer + composition.Register(Lifetime.Singleton); + // register the NuCache database data source composition.Register(); From 26d11a83141abc5e4d94c24e733cdca768d1a6f1 Mon Sep 17 00:00:00 2001 From: ric <60885685+ricbrady@users.noreply.github.com> Date: Tue, 6 Apr 2021 14:44:49 +0200 Subject: [PATCH 005/182] Fixed copy preserving sort order (#10091) --- .../Services/Implement/ContentService.cs | 10 +++---- .../Services/ContentServiceTests.cs | 26 +++++++++++++++++++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index e5363d0e2b..d8e99663ea 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -1415,7 +1415,7 @@ namespace Umbraco.Core.Services.Implement var result = CommitDocumentChangesInternal(scope, d, saveEventArgs, allLangs.Value, d.WriterId); if (result.Success == false) - Logger.Error(null, "Failed to publish document id={DocumentId}, reason={Reason}.", d.Id, result.Result); + Logger.Error(null, "Failed to publish document id={DocumentId}, reason={Reason}.", d.Id, result.Result); results.Add(result); } @@ -2201,7 +2201,7 @@ namespace Umbraco.Core.Services.Implement while (page * pageSize < total) { var descendants = GetPagedDescendants(content.Id, page++, pageSize, out total); - foreach (var descendant in descendants) + foreach (var descendant in descendants.OrderBy(x => x.Level).ThenBy(y => y.SortOrder)) { // if parent has not been copied, skip, else gets its copy id if (idmap.TryGetValue(descendant.ParentId, out parentId) == false) continue; @@ -2420,7 +2420,7 @@ namespace Umbraco.Core.Services.Implement if (report.FixedIssues.Count > 0) { //The event args needs a content item so we'll make a fake one with enough properties to not cause a null ref - var root = new Content("root", -1, new ContentType(-1)) {Id = -1, Key = Guid.Empty}; + var root = new Content("root", -1, new ContentType(-1)) { Id = -1, Key = Guid.Empty }; scope.Events.Dispatch(TreeChanged, this, new TreeChange.EventArgs(new TreeChange(root, TreeChangeTypes.RefreshAll))); } @@ -3169,7 +3169,7 @@ namespace Umbraco.Core.Services.Implement if (rollbackSaveResult.Success == false) { //Log the error/warning - Logger.Error("User '{UserId}' was unable to rollback content '{ContentId}' to version '{VersionId}'", userId, id, versionId); + Logger.Error("User '{UserId}' was unable to rollback content '{ContentId}' to version '{VersionId}'", userId, id, versionId); } else { @@ -3178,7 +3178,7 @@ namespace Umbraco.Core.Services.Implement scope.Events.Dispatch(RolledBack, this, rollbackEventArgs); //Logging & Audit message - Logger.Info("User '{UserId}' rolled back content '{ContentId}' to version '{VersionId}'", userId, id, versionId); + Logger.Info("User '{UserId}' rolled back content '{ContentId}' to version '{VersionId}'", userId, id, versionId); Audit(AuditType.RollBack, userId, id, $"Content '{content.Name}' was rolled back to version '{versionId}'"); } diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 008c24fcbf..0faa4af316 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -2082,6 +2082,32 @@ namespace Umbraco.Tests.Services Assert.AreEqual("world", copiedTags[1].Text); } + [Test] + public void Copy_Recursive_Preserves_Sort_Order() + { + // Arrange + var contentService = ServiceContext.ContentService; + var temp = contentService.GetById(NodeDto.NodeIdSeed + 2); + Assert.AreEqual("Home", temp.Name); + Assert.AreEqual(3, contentService.CountChildren(temp.Id)); + var reversedChildren = contentService.GetPagedChildren(temp.Id, 0, 10, out var total1).Reverse().ToArray(); + contentService.Sort(reversedChildren); + + // Act + var copy = contentService.Copy(temp, temp.ParentId, false, true, Constants.Security.SuperUserId); + var content = contentService.GetById(NodeDto.NodeIdSeed + 2); + + // Assert + Assert.That(copy, Is.Not.Null); + Assert.That(copy.Id, Is.Not.EqualTo(content.Id)); + Assert.AreNotSame(content, copy); + Assert.AreEqual(3, contentService.CountChildren(copy.Id)); + + var copiedChildren = contentService.GetPagedChildren(copy.Id, 0, 10, out var total2).OrderBy(c => c.SortOrder).ToArray(); + Assert.AreEqual(reversedChildren.First().Name, copiedChildren.First().Name); + Assert.AreEqual(reversedChildren.Last().Name, copiedChildren.Last().Name); + } + [Test] public void Can_Rollback_Version_On_Content() { From e21cc6f97eb1d49e2f0060186b57680e4ef44a59 Mon Sep 17 00:00:00 2001 From: Rachel Breeze Date: Sat, 10 Apr 2021 01:34:18 +0100 Subject: [PATCH 006/182] Accessibility: Adding label fors and control ids for the macro picker (#10101) * Added support for screeen reader alerts on the embed so that assitive technology knows when a url retrieve has been succesfull. Added labels for the controls Preview reload only triggered if the values for height and width change * Added support for label fors for the macro picker and also gave the ,acro search box a title * Now displays a count of the matching macros returned. Please note the language file amends shared with #10100 * Removed src-only class for the display of the count of messages --- .../macropicker/macropicker.controller.js | 31 +++++++++++++++++-- .../macropicker/macropicker.html | 24 +++++++------- 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.controller.js index 40338f2dca..1701553efc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.controller.js @@ -1,12 +1,13 @@ function MacroPickerController($scope, entityResource, macroResource, umbPropEditorHelper, macroService, formHelper, localizationService) { $scope.macros = []; + $scope.a11yInfo = ""; $scope.model.selectedMacro = null; $scope.model.macroParams = []; - + $scope.displayA11YMessageForFilter = displayA11YMessageForFilter; $scope.wizardStep = "macroSelect"; $scope.noMacroParams = false; - + $scope.model.searchTerm = ""; function onInit() { if (!$scope.model.title) { localizationService.localize("defaultdialogs_selectMacro").then(function (value) { @@ -49,6 +50,7 @@ function MacroPickerController($scope, entityResource, macroResource, umbPropEdi $scope.model.submit($scope.model); } else { $scope.wizardStep = 'macroSelect'; + displayA11yMessages($scope.macros); } } else { @@ -95,6 +97,28 @@ function MacroPickerController($scope, entityResource, macroResource, umbPropEdi }); } + function displayA11yMessages(macros) { + if ($scope.noMacroParams || !macros || macros.length === 0) + localizationService.localize("general_searchNoResult").then(function (value) { + $scope.a11yInfo = value; + }); + else if (macros) { + if (macros.length === 1) { + localizationService.localize("treeSearch_searchResult").then(function(value) { + $scope.a11yInfo = "1 " + value; + }); + } else { + localizationService.localize("treeSearch_searchResults").then(function (value) { + $scope.a11yInfo = macros.length + " " + value; + }); + } + } + } + + function displayA11YMessageForFilter() { + var macros = _.filter($scope.macros, v => v.name.toLowerCase().includes($scope.model.searchTerm.toLowerCase())); + displayA11yMessages(macros); + } //here we check to see if we've been passed a selected macro and if so we'll set the //editor to start with parameter editing if ($scope.model.dialogData && $scope.model.dialogData.macroData) { @@ -141,10 +165,11 @@ function MacroPickerController($scope, entityResource, macroResource, umbPropEdi //we don't have a pre-selected macro so ensure the correct step is set $scope.wizardStep = 'macroSelect'; } + displayA11yMessages($scope.macros); }); onInit(); - + } angular.module("umbraco").controller("Umbraco.Overlays.MacroPickerController", MacroPickerController); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.html index 33d7a471a5..8bda49b328 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.html @@ -16,18 +16,18 @@
- - + + - +

    -
  • +
+ position="center"> There are no macros available to insert @@ -53,7 +53,7 @@
  • - + From 3b6de9cb9ec3b2b510642379da00a6cea8cb4f3a Mon Sep 17 00:00:00 2001 From: Carole Rennie Logan Date: Fri, 9 Apr 2021 18:43:55 +0100 Subject: [PATCH 007/182] Updating typo --- src/Umbraco.Core/Constants-Security.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Constants-Security.cs b/src/Umbraco.Core/Constants-Security.cs index f900288ef5..2b6244debb 100644 --- a/src/Umbraco.Core/Constants-Security.cs +++ b/src/Umbraco.Core/Constants-Security.cs @@ -25,7 +25,7 @@ namespace Umbraco.Core /// /// The name of the 'unknown' user. /// - public const string UnknownUserName = "SYTEM"; + public const string UnknownUserName = "SYSTEM"; public const string AdminGroupAlias = "admin"; public const string EditorGroupAlias = "editor"; From 4f49e57573ba0739fdbade18fee59465fa1aa5ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20=C3=96hman?= Date: Sun, 11 Apr 2021 16:40:13 +0200 Subject: [PATCH 008/182] Removed top-margin from switcher icon --- .../src/less/components/editor/umb-variant-switcher.less | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/editor/umb-variant-switcher.less b/src/Umbraco.Web.UI.Client/src/less/components/editor/umb-variant-switcher.less index 9d2782f184..ce9286e5f5 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/editor/umb-variant-switcher.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/editor/umb-variant-switcher.less @@ -69,7 +69,6 @@ button.umb-variant-switcher__toggle { .umb-variant-switcher__expand { color: @ui-action-discreet-type; - margin-top: 3px; margin-left: 5px; margin-right: -5px; transition: color 0.2s ease-in-out; From c8b6841ef16e40670a89ba8a84aa21b2d066e9a3 Mon Sep 17 00:00:00 2001 From: Jeavon Date: Tue, 13 Apr 2021 02:10:44 +0100 Subject: [PATCH 009/182] Allow KeepAlive controller Ping method to be requested by non local requests (#10126) * Allow KeepAlive controller Ping method to be requested by non local requests and accept head requests * removed unused references --- src/Umbraco.Web/Editors/KeepAliveController.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Umbraco.Web/Editors/KeepAliveController.cs b/src/Umbraco.Web/Editors/KeepAliveController.cs index 23815e1bbe..f29ee6c60a 100644 --- a/src/Umbraco.Web/Editors/KeepAliveController.cs +++ b/src/Umbraco.Web/Editors/KeepAliveController.cs @@ -1,14 +1,12 @@ using System.Runtime.Serialization; using System.Web.Http; -using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; -using Umbraco.Web.WebApi.Filters; namespace Umbraco.Web.Editors { public class KeepAliveController : UmbracoApiController { - [OnlyLocalRequests] + [HttpHead] [HttpGet] public KeepAlivePingResult Ping() { From 57ed4698f01be40328698c991c88de4e8a495064 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Mon, 19 Apr 2021 17:31:09 +0200 Subject: [PATCH 010/182] Revert "Revert "The Value() method for IPublishedContent was not working with the defaultValue parameter" (#9989)" This reverts commit 156c1c94161171f9592923f20de4e142c845469a. --- src/Umbraco.Web/PublishedContentExtensions.cs | 6 ++-- src/Umbraco.Web/PublishedPropertyExtension.cs | 33 ++++++------------- 2 files changed, 13 insertions(+), 26 deletions(-) diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index c851894149..6384b0f8d4 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Data; using System.Linq; @@ -173,8 +173,8 @@ namespace Umbraco.Web return value; // else... if we have a property, at least let the converter return its own - // vision of 'no value' (could be an empty enumerable) - otherwise, default - return property == null ? default : property.Value(culture, segment, fallback, defaultValue); + // vision of 'no value' (could be an empty enumerable) - otherwise, defaultValue + return property == null ? defaultValue : property.Value(culture, segment, defaultValue: defaultValue); } #endregion diff --git a/src/Umbraco.Web/PublishedPropertyExtension.cs b/src/Umbraco.Web/PublishedPropertyExtension.cs index 6e8647db47..0c3aa57cc2 100644 --- a/src/Umbraco.Web/PublishedPropertyExtension.cs +++ b/src/Umbraco.Web/PublishedPropertyExtension.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Models.PublishedContent; @@ -36,20 +37,13 @@ namespace Umbraco.Web // we have a value // try to cast or convert it var value = property.GetValue(culture, segment); - if (value is T valueAsT) - { - return valueAsT; - } - + if (value is T valueAsT) return valueAsT; var valueConverted = value.TryConvertTo(); - if (valueConverted) - { - return valueConverted.Result; - } + if (valueConverted) return valueConverted.Result; - // cannot cast nor convert the value, nothing we can return but 'default' + // cannot cast nor convert the value, nothing we can return but 'defaultValue' // note: we don't want to fallback in that case - would make little sense - return default; + return defaultValue; } // we don't have a value, try fallback @@ -63,22 +57,15 @@ namespace Umbraco.Web var noValue = property.GetValue(culture, segment); if (noValue == null) { - return default; - } - - if (noValue is T noValueAsT) - { - return noValueAsT; + return defaultValue; } + if (noValue is T noValueAsT) return noValueAsT; var noValueConverted = noValue.TryConvertTo(); - if (noValueConverted) - { - return noValueConverted.Result; - } + if (noValueConverted) return noValueConverted.Result; - // cannot cast noValue nor convert it, nothing we can return but 'default' - return default; + // cannot cast noValue nor convert it, nothing we can return but 'defaultValue' + return defaultValue; } #endregion From f1e4fec4c4ad0299a787d350ea32e6ff2d26cf4e Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Mon, 19 Apr 2021 17:31:53 +0200 Subject: [PATCH 011/182] Revert "The Value() method for IPublishedContent was not working with the defaultValue parameter (#9888)" This reverts commit 52b670973d562b86a89665dd4b1b23fba8ec3900. --- src/Umbraco.Web/PublishedContentExtensions.cs | 4 ++-- src/Umbraco.Web/PublishedPropertyExtension.cs | 15 ++++----------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 6384b0f8d4..b43717d418 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -173,8 +173,8 @@ namespace Umbraco.Web return value; // else... if we have a property, at least let the converter return its own - // vision of 'no value' (could be an empty enumerable) - otherwise, defaultValue - return property == null ? defaultValue : property.Value(culture, segment, defaultValue: defaultValue); + // vision of 'no value' (could be an empty enumerable) - otherwise, default + return property == null ? default : property.Value(culture, segment); } #endregion diff --git a/src/Umbraco.Web/PublishedPropertyExtension.cs b/src/Umbraco.Web/PublishedPropertyExtension.cs index 0c3aa57cc2..b431f24828 100644 --- a/src/Umbraco.Web/PublishedPropertyExtension.cs +++ b/src/Umbraco.Web/PublishedPropertyExtension.cs @@ -41,31 +41,24 @@ namespace Umbraco.Web var valueConverted = value.TryConvertTo(); if (valueConverted) return valueConverted.Result; - // cannot cast nor convert the value, nothing we can return but 'defaultValue' + // cannot cast nor convert the value, nothing we can return but 'default' // note: we don't want to fallback in that case - would make little sense - return defaultValue; + return default; } // we don't have a value, try fallback if (PublishedValueFallback.TryGetValue(property, culture, segment, fallback, defaultValue, out var fallbackValue)) - { return fallbackValue; - } // we don't have a value - neither direct nor fallback // give a chance to the converter to return something (eg empty enumerable) var noValue = property.GetValue(culture, segment); - if (noValue == null) - { - return defaultValue; - } if (noValue is T noValueAsT) return noValueAsT; - var noValueConverted = noValue.TryConvertTo(); if (noValueConverted) return noValueConverted.Result; - // cannot cast noValue nor convert it, nothing we can return but 'defaultValue' - return defaultValue; + // cannot cast noValue nor convert it, nothing we can return but 'default' + return default; } #endregion From a3e3e83a3df2f450106c41909cf92eb1300c5104 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Mon, 19 Apr 2021 17:34:01 +0200 Subject: [PATCH 012/182] Bump version to 8.13.0 --- src/SolutionInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index 2a7386cb45..1009ec125b 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -19,4 +19,4 @@ using System.Resources; // these are FYI and changed automatically [assembly: AssemblyFileVersion("8.13.0")] -[assembly: AssemblyInformationalVersion("8.13.0-rc")] +[assembly: AssemblyInformationalVersion("8.13.0")] From 12cde0c571318847e5a23ec58093c09b22ad7af5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 20 Apr 2021 11:10:07 +0200 Subject: [PATCH 013/182] Use warning style when saving --- .../components/content/edit.controller.js | 7 ++- .../validation/valformmanager.directive.js | 57 ++++++++++++++++++- .../services/contenteditinghelper.service.js | 18 ++++-- .../src/less/alerts.less | 16 ++++++ .../umb-editor-navigation-item.less | 23 +++++--- .../less/components/umb-nested-content.less | 6 +- .../src/less/components/umb-tabs.less | 7 +++ src/Umbraco.Web.UI.Client/src/less/forms.less | 10 ++++ .../src/less/variables.less | 4 +- .../labelblock/labelblock.editor.less | 6 ++ 10 files changed, 137 insertions(+), 17 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js index da93450522..c7f56eeda6 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js @@ -788,7 +788,12 @@ }).then(function () { $scope.page.saveButtonState = "success"; }, function (err) { - $scope.page.saveButtonState = "error"; + // Because this is the "save"-action, then we actually save though there was a validation error, therefor we will show success and display the validation errors politely. + if(err && err.data && err.data.ModelState && Object.keys(err.data.ModelState).length > 0) { + $scope.page.saveButtonState = "success"; + } else { + $scope.page.saveButtonState = "error"; + } handleHttpException(err); }); } diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js index 55878db2e9..be84d45075 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valformmanager.directive.js @@ -15,6 +15,7 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location, overlayService, eventsService, $routeParams, navigationService, editorService, localizationService, angularHelper) { var SHOW_VALIDATION_CLASS_NAME = "show-validation"; + var SHOW_VALIDATION_Type_CLASS_NAME = "show-validation-type-"; var SAVING_EVENT_NAME = "formSubmitting"; var SAVED_EVENT_NAME = "formSubmitted"; @@ -25,7 +26,7 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location function ValFormManagerController($scope) { //This exposes an API for direct use with this directive - // We need this as a way to reference this directive in the scope chain. Since this directive isn't a component and + // We need this as a way to reference this directive in the scope chain. Since this directive isn't a component and // because it's an attribute instead of an element, we can't use controllerAs or anything like that. Plus since this is // an attribute an isolated scope doesn't work so it's a bit weird. By doing this we are able to lookup the parent valFormManager // in the scope hierarchy even if the DOM hierarchy doesn't match (i.e. in infinite editing) @@ -44,6 +45,8 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location this.isShowingValidation = () => $scope.showValidation === true; + this.getValidationMessageType = () => $scope.valMsgType; + this.notify = notify; this.isValid = function () { @@ -94,6 +97,7 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location var parentFormMgr = scope.parentFormMgr = getAncestorValFormManager(scope, ctrls, 1); var subView = ctrls.length > 1 ? ctrls[2] : null; var labels = {}; + var valMsgType = 2;// error var labelKeys = [ "prompt_unsavedChanges", @@ -109,6 +113,46 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location labels.stayButton = values[3]; }); + var lastValidationMessageType = null; + function setValidationMessageType(type) { + + removeValidationMessageType(); + scope.valMsgType = type; + + // overall a copy of message types from notifications.service: + var postfix = ""; + switch(type) { + case 0: + //save + break; + case 1: + //info + postfix = "info"; + break; + case 2: + //error + postfix = "error"; + break; + case 3: + //success + postfix = "success"; + break; + case 4: + //warning + postfix = "warning"; + break; + } + var cssClass = SHOW_VALIDATION_Type_CLASS_NAME+postfix; + element.addClass(cssClass); + lastValidationMessageType = cssClass; + } + function removeValidationMessageType() { + if(lastValidationMessageType) { + element.removeClass(lastValidationMessageType); + lastValidationMessageType = null; + } + } + //watch the list of validation errors to notify the application of any validation changes // TODO: Wouldn't it be easier/faster to watch formCtrl.$invalid ? scope.$watch(() => angularHelper.countAllFormErrors(formCtrl), @@ -138,6 +182,8 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location if (serverValidationManager.items.length > 0 || (parentFormMgr && parentFormMgr.isShowingValidation())) { element.addClass(SHOW_VALIDATION_CLASS_NAME); scope.showValidation = true; + var parentValMsgType = parentFormMgr ? parentFormMgr.getValidationMessageType() : 2; + setValidationMessageType(parentValMsgType || 2); notifySubView(); } @@ -145,8 +191,16 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location //listen for the forms saving event unsubscribe.push(scope.$on(SAVING_EVENT_NAME, function (ev, args) { + + var messageType = 2;//error + switch (args.action) { + case "save": + messageType = 4;//warning + break; + } element.addClass(SHOW_VALIDATION_CLASS_NAME); scope.showValidation = true; + setValidationMessageType(messageType); notifySubView(); //set the flag so we can check to see if we should display the error. isSavingNewItem = $routeParams.create; @@ -156,6 +210,7 @@ function valFormManager(serverValidationManager, $rootScope, $timeout, $location unsubscribe.push(scope.$on(SAVED_EVENT_NAME, function (ev, args) { //remove validation class element.removeClass(SHOW_VALIDATION_CLASS_NAME); + removeValidationMessageType(); scope.showValidation = false; notifySubView(); })); diff --git a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js index 8524b960c6..b7f765d8e0 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js @@ -32,7 +32,8 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt return true; } - function showNotificationsForModelsState(ms) { + function showNotificationsForModelsState(ms, messageType) { + messageType = messageType || 2; for (const [key, value] of Object.entries(ms)) { var errorMsg = value[0]; @@ -42,12 +43,14 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt var idsToErrors = serverValidationManager.parseComplexEditorError(errorMsg, ""); idsToErrors.forEach(x => { if (x.modelState) { - showNotificationsForModelsState(x.modelState); + showNotificationsForModelsState(x.modelState, messageType); } }); } else if (value[0]) { - notificationsService.error("Validation", value[0]); + //notificationsService.error("Validation", value[0]); + console.log({type:messageType, header:"Validation", message:value[0]}) + notificationsService.showNotification({type:messageType, header:"Validation", message:value[0]}) } } } @@ -124,6 +127,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt showNotifications: args.showNotifications, softRedirect: args.softRedirect, err: err, + action: args.action, rebindCallback: function () { // if the error contains data, we want to map that back as we want to continue editing this save. Especially important when the content is new as the returned data will contain ID etc. if(err.data) { @@ -639,9 +643,15 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt //wire up the server validation errs formHelper.handleServerValidation(args.err.data.ModelState); + var messageType = 2;//error + console.log(args) + if (args.action === "save") { + messageType = 4;//warning + } + //add model state errors to notifications if (args.showNotifications) { - showNotificationsForModelsState(args.err.data.ModelState); + showNotificationsForModelsState(args.err.data.ModelState, messageType); } if (!this.redirectToCreatedContent(args.err.data.id, args.softRedirect) || args.softRedirect) { diff --git a/src/Umbraco.Web.UI.Client/src/less/alerts.less b/src/Umbraco.Web.UI.Client/src/less/alerts.less index 3539e21064..ab0ab9aa13 100644 --- a/src/Umbraco.Web.UI.Client/src/less/alerts.less +++ b/src/Umbraco.Web.UI.Client/src/less/alerts.less @@ -54,6 +54,15 @@ border-color: @errorBorder; color: @errorText; } + +.alert-warning() { + background-color: @warningBackground; + border-color: @warningBorder; + color: @warningText; +} +.alert-warning { + .alert-warning() +} .alert-danger h4, .alert-error h4 { color: @errorText; @@ -110,6 +119,13 @@ padding: 6px 16px 6px 12px; margin-bottom: 6px; + .show-validation-type-warning & { + .alert-warning(); + &.alert-error::after { + border-top-color: @warningBackground; + } + } + &::after { content:''; position: absolute; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation-item.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation-item.less index 5e9772fb26..5fd743aaf0 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation-item.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation-item.less @@ -86,6 +86,16 @@ } } } + + .show-validation.show-validation-type-warning &.-has-error { + color: @yellow-d2; + &:hover { + color: @yellow-d2 !important; + } + &::before { + background-color: @yellow-d2; + } + } } &__action:active, @@ -122,14 +132,6 @@ line-height: 16px; display: block; - &.-type-alert { - background-color: @red; - } - - &.-type-warning { - background-color: @yellow-d2; - } - &:empty { height: 12px; min-width: 12px; @@ -137,6 +139,11 @@ &.--error-badge { display: none; font-weight: 900; + background-color: @red; + + .show-validation-type-warning & { + background-color: @yellow-d2; + } } } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less index bd787e2329..9dd40a4386 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less @@ -48,6 +48,10 @@ &.--error { border-color: @formErrorBorder !important; } + + .show-validation-type-warning &.--error { + border-color: @formWarningBorder !important; + } } .umb-nested-content__item.ui-sortable-placeholder { @@ -292,4 +296,4 @@ .umb-textarea, .umb-textstring { width:100%; } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-tabs.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-tabs.less index 15b317aa45..1b249f1c3a 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-tabs.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-tabs.less @@ -86,6 +86,13 @@ background-color: @red !important; border-color: @errorBorder; } +.show-validation.show-validation-type-warning .umb-tab--error > .umb-tab-button, +.show-validation.show-validation-type-warning .umb-tab--error > .umb-tab-button:hover, +.show-validation.show-validation-type-warning .umb-tab--error > .umb-tab-button:focus { + color: @white !important; + background-color: @yellow-d2 !important; + border-color: @warningBorder; +} .show-validation .umb-tab--error .umb-tab-button:before { content: "\e25d"; diff --git a/src/Umbraco.Web.UI.Client/src/less/forms.less b/src/Umbraco.Web.UI.Client/src/less/forms.less index 3782fca695..60561f9acc 100644 --- a/src/Umbraco.Web.UI.Client/src/less/forms.less +++ b/src/Umbraco.Web.UI.Client/src/less/forms.less @@ -506,10 +506,20 @@ input[type="checkbox"][readonly] { .formFieldState(@formErrorText, @formErrorText, @formErrorBackground); } +// ValidationError as a warning +.show-validation.show-validation-type-warning.ng-invalid .control-group.error, +.show-validation.show-validation-type-warning.ng-invalid .umb-editor-header__name-wrapper { + .formFieldState(@formWarningText, @formWarningText, @formWarningBackground); +} + //val-highlight directive styling .highlight-error { color: @formErrorText !important; border-color: @red-l1 !important; + .show-validation-type-warning & { + color: @formWarningText !important; + border-color: @yellow-d2 !important; + } } // FORM ACTIONS diff --git a/src/Umbraco.Web.UI.Client/src/less/variables.less b/src/Umbraco.Web.UI.Client/src/less/variables.less index cab0745a42..90cf24cf2d 100644 --- a/src/Umbraco.Web.UI.Client/src/less/variables.less +++ b/src/Umbraco.Web.UI.Client/src/less/variables.less @@ -291,8 +291,8 @@ @btnSuccessBackground: @ui-btn-positive;// updated 2019 @btnSuccessBackgroundHighlight: @ui-btn-positive-hover;// updated 2019 -@btnWarningBackground: @orange; -@btnWarningBackgroundHighlight: lighten(@orange, 10%); +@btnWarningBackground: @yellow-d2; +@btnWarningBackgroundHighlight: lighten(@yellow-d2, 10%); @btnDangerBackground: @red; @btnDangerBackgroundHighlight: @red-l1; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklistentryeditors/labelblock/labelblock.editor.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklistentryeditors/labelblock/labelblock.editor.less index 613a47b926..837fd3f564 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklistentryeditors/labelblock/labelblock.editor.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklistentryeditors/labelblock/labelblock.editor.less @@ -42,6 +42,9 @@ ng-form.ng-invalid-val-server-match-content > .umb-block-list__block > .umb-block-list__block--content > div > & { color: @formErrorText; + .show-validation-type-warning & { + color: @formWarningText; + } } ng-form.ng-invalid-val-server-match-content > .umb-block-list__block:not(.--active) > .umb-block-list__block--content > div > & { > span { @@ -61,6 +64,9 @@ padding: 2px; line-height: 10px; background-color: @formErrorText; + .show-validation-type-warning & { + background-color: @formWarningText; + } font-weight: 900; animation-duration: 1.4s; From 021c0b82c185d82194360ed36f0d91984f0dd7e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 20 Apr 2021 11:35:40 +0200 Subject: [PATCH 014/182] final corrections --- .../components/content/edit.controller.js | 8 +++++++- src/Umbraco.Web.UI.Client/src/less/alerts.less | 1 + .../src/less/components/overlays.less | 3 +++ .../src/less/components/umb-list.less | 5 ++++- src/Umbraco.Web.UI.Client/src/less/variables.less | 2 +- .../src/views/content/overlays/save.html | 4 ++-- .../blocklist/umb-block-list-property-editor.less | 15 ++++++++++----- 7 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js index 61433c2b62..196c885b4e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js @@ -765,7 +765,13 @@ }, function (err) { clearDirtyState($scope.content.variants); - model.submitButtonState = "error"; + //model.submitButtonState = "error"; + // Because this is the "save"-action, then we actually save though there was a validation error, therefor we will show success and display the validation errors politely. + if(err && err.data && err.data.ModelState && Object.keys(err.data.ModelState).length > 0) { + model.submitButtonState = "success"; + } else { + model.submitButtonState = "error"; + } //re-map the dialog model since we've re-bound the properties dialog.variants = $scope.content.variants; handleHttpException(err); diff --git a/src/Umbraco.Web.UI.Client/src/less/alerts.less b/src/Umbraco.Web.UI.Client/src/less/alerts.less index ab0ab9aa13..94dcef6f25 100644 --- a/src/Umbraco.Web.UI.Client/src/less/alerts.less +++ b/src/Umbraco.Web.UI.Client/src/less/alerts.less @@ -121,6 +121,7 @@ .show-validation-type-warning & { .alert-warning(); + font-weight: bold; &.alert-error::after { border-top-color: @warningBackground; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/overlays.less b/src/Umbraco.Web.UI.Client/src/less/components/overlays.less index 035bf02f91..12cce286d6 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/overlays.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/overlays.less @@ -267,6 +267,9 @@ .umb-overlay .text-error { color: @formErrorText; } +.umb-overlay .text-warning { + color: @formWarningText; +} .umb-overlay .text-success { color: @formSuccessText; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-list.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-list.less index 57ba73305a..c281f7f5ea 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-list.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-list.less @@ -26,7 +26,10 @@ a.umb-list-item:focus { } .umb-list-item--error { - color: @red; + color: @formErrorText; +} +.umb-list-item--warning { + color: @formWarningText; } .umb-list-item:hover .umb-list-checkbox, diff --git a/src/Umbraco.Web.UI.Client/src/less/variables.less b/src/Umbraco.Web.UI.Client/src/less/variables.less index 90cf24cf2d..9d114b093e 100644 --- a/src/Umbraco.Web.UI.Client/src/less/variables.less +++ b/src/Umbraco.Web.UI.Client/src/less/variables.less @@ -480,7 +480,7 @@ @formWarningBorder: darken(spin(@warningBackground, -10), 3%); @formErrorText: @errorBackground; -@formErrorBackground: lighten(@errorBackground, 55%); +@formErrorBackground: @errorBackground; @formErrorBorder: @red; @formSuccessText: @successBackground; diff --git a/src/Umbraco.Web.UI.Client/src/views/content/overlays/save.html b/src/Umbraco.Web.UI.Client/src/views/content/overlays/save.html index fa9ab8c437..d414f30dbf 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/overlays/save.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/overlays/save.html @@ -16,7 +16,7 @@
    -
    +
    - {{saveVariantSelectorForm.saveVariantSelector.errorMsg}} + {{saveVariantSelectorForm.saveVariantSelector.errorMsg}} diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umb-block-list-property-editor.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umb-block-list-property-editor.less index 019a772fdd..fbb7e1b32e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umb-block-list-property-editor.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umb-block-list-property-editor.less @@ -23,10 +23,6 @@ > .umb-block-list__block--actions { opacity: 0; transition: opacity 120ms; - - .--error { - color: @formErrorBorder !important; - } } &:hover, @@ -100,6 +96,12 @@ ng-form.ng-invalid-val-server-match-settings > .umb-block-list__block > .umb-blo &:hover { color: @ui-action-discreet-type-hover; } + &.--error { + color: @errorBackground; + .show-validation-type-warning & { + color: @warningBackground; + } + } > .__error-badge { position: absolute; top: -2px; @@ -113,7 +115,10 @@ ng-form.ng-invalid-val-server-match-settings > .umb-block-list__block > .umb-blo font-weight: bold; padding: 2px; line-height: 8px; - background-color: @red; + background-color: @errorBackground; + .show-validation-type-warning & { + background-color: @warningBackground; + } display: none; font-weight: 900; } From 1a5b88525b59af3594a7f21a3e029b557f5b3944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 22 Apr 2021 10:28:53 +0200 Subject: [PATCH 015/182] Media Picker v3 (#9461) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * set input file accept * Use PreValue file extensions for limiting the files to be chosen in file input * Current state for Warren to review * This should fix up what you need Niels * update csproj * use empty string if fileExtensions is undefined * public interface * initial work * local crops * translations * translation correction * fix misspeling * some progress * filter media picker * align media card grid items correctly * responsive media cropper * always be able to scale 3 times smallest scale * making image cropper property editor responsive * scroll to scale * adjust slider look * rearrange parts of mediaentryeditor * test helper * styling * move controls inside umb-image-crop * seperate umg-cropper-gravity styling * corrected layout * more ui refinement * keep the idea of mandatory out for now. * remove double ; * removed testing code * JSON Property Value Convertor now has an array of property editors to exclude * Property Value Convertor for Media Picker 3 aka Media Picker with Local Crops * Experimenting on best approach to retrieve local crop in razor view when iterating over picked media items * Update ValueConvertor to use ImageCropperValue as part of the model for views as alot of existing CropUrls can then use it * Update extension methods to take an ImageCropperValue model (localCropData) * Forgot to update CSProj for new ValueConvertor * New GetCropUrl @Url.GetCropUrl(crop.Alias, media.LocalCrops) as oppposed to @Url.GetCropUrl(media.LocalCrops, cropAlias:crop.Alias, useCropDimensions: true) * Remove dupe item in CSProj * Use a contains as an opposed to Array.IndexOf * various corrections, SingleMode based on max 1, remove double checkerBackground, enforce validation for Crops, changed error indication * mediapicker v3 * correct version * fixing file ext label text color * clipboard features for MediaPicker v3 * highlight not allowed types * highlight trashed as an error * Media Types Video, Sound, Document and Vector Image * Rename to Audio and VectorGraphics * Add (SVG) in the name for Vector Graphics * adding CSV to Documents * remove this commented code. * remove this commented code * number range should not go below 0, at-least as default until we make that configurable. * use min not ng-min * description for local crops * Error/Limits highlighting reactive * visual adjustments * Enabling opening filtered folders + corrected select hover states * Varous fixes to resolve issues with unit tests. * Refactor MediaType Documents to only contain Article file type * mark as build-in * predefined MediaPicker3 DataTypes, renaming v2 to "old" * set scale bar current value after min and max has been set * added missing } * update when focal point is dragged * adjusted styling for Image Cropper property editor * correcting comment * remove todo - message for trashed media items works * Changed parameter ordering * Introduced new extension method on MediaWithCrops to get croppings urls in with full path * Reintroducing Single Item Mode * use Multiple instead of SingleMode * renaming and adding multiple to preconfigured datatypes * Change existing media picker to use the Clipboard type MEDIA, enabling shared functionality. * clean up unused clipboard parts * adjusted to new amount * correcting test * Fix unit test * Move MediaWithCrops to separate file and move to Core.Models * parseContentForPaste * clean up * ensure crops is an array. * actively enable focal points, so we dont set focal points that aren't used. * only accept files that matches file extensions from Umbraco Settings * Cleanup * Add references from MediaPicker3 to media * corrections from various feedback * remove comment * correct wording * use windowResizeListener Co-authored-by: Warren Buckley Co-authored-by: Niels Lyngsø Co-authored-by: Mads Rasmussen Co-authored-by: Andy Butland Co-authored-by: Bjarke Berg Co-authored-by: Sebastiaan Janssen Co-authored-by: Elitsa Marinovska --- src/Umbraco.Core/Constants-Conventions.cs | 20 + src/Umbraco.Core/Constants-DataTypes.cs | 93 +++- src/Umbraco.Core/Constants-Icons.cs | 22 +- src/Umbraco.Core/Constants-PropertyEditors.cs | 7 +- .../Constants-PropertyTypeGroups.cs | 24 +- .../Migrations/Install/DatabaseDataCreator.cs | 113 ++++- src/Umbraco.Core/Models/DataTypeExtensions.cs | 4 + src/Umbraco.Core/Models/MediaWithCrops.cs | 15 + .../Persistence/Dtos/ContentTypeDto.cs | 2 +- .../Persistence/Dtos/PropertyTypeDto.cs | 2 +- .../ValueConverters/JsonValueConverter.cs | 8 +- src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../Composing/TypeLoaderTests.cs | 2 +- .../DataTypeDefinitionRepositoryTest.cs | 2 +- .../Repositories/MediaTypeRepositoryTest.cs | 16 +- .../PublishedContent/PublishedMediaTests.cs | 29 +- .../Services/MediaServiceTests.cs | 4 +- .../Entities/MockedContentTypes.cs | 8 +- .../components/forms/validwhen.directive.js | 12 + .../imaging/umbimagecrop.directive.js | 287 +++++++----- .../imaging/umbimagegravity.directive.js | 129 +++--- .../components/umbmediagrid.directive.js | 27 +- .../upload/umbpropertyfileupload.directive.js | 7 +- .../upload/umbsinglefileupload.directive.js | 15 +- .../validation/valservermatch.directive.js | 4 +- .../src/common/services/clipboard.service.js | 6 + .../common/services/cropperhelper.service.js | 46 +- src/Umbraco.Web.UI.Client/src/less/belle.less | 5 + .../src/less/components/umb-file-icon.less | 2 +- .../src/less/components/umb-media-grid.less | 52 +-- .../src/less/components/umb-range-slider.less | 30 +- .../src/less/mixins.less | 2 +- .../src/less/property-editors.less | 223 ++++++--- .../src/main.controller.js | 24 +- .../mediaentryeditor.controller.js | 183 ++++++++ .../mediaentryeditor/mediaentryeditor.html | 118 +++++ .../mediaentryeditor/mediaentryeditor.less | 122 +++++ .../mediapicker/mediapicker.controller.js | 70 ++- .../mediapicker/mediapicker.html | 104 +++-- .../components/imaging/umb-image-crop.html | 45 +- .../components/imaging/umb-image-gravity.html | 11 +- .../mediacard/umb-media-card-grid.less | 137 ++++++ .../components/mediacard/umb-media-card.html | 47 ++ .../components/mediacard/umb-media-card.less | 186 ++++++++ .../mediacard/umbMediaCard.component.js | 97 ++++ .../src/views/components/umb-media-grid.html | 65 +-- .../upload/umb-property-file-upload.html | 4 +- .../src/views/media/media.edit.controller.js | 64 +-- .../views/prevalueeditors/numberrange.html | 3 +- .../treesourcetypepicker.controller.js | 5 + .../umb-block-list-property-editor.less | 2 +- .../fileupload/fileupload.controller.js | 9 +- .../fileupload/fileupload.html | 3 +- .../imagecropper/imagecropper.controller.js | 6 +- .../imagecropper/imagecropper.html | 8 +- .../mediapicker/mediapicker.controller.js | 144 ++++-- .../mediapicker3/mediapicker3.html | 1 + .../prevalue/mediapicker3.crops.controller.js | 110 +++++ .../prevalue/mediapicker3.crops.html | 96 ++++ .../prevalue/mediapicker3.crops.less | 40 ++ .../umb-media-picker3-property-editor.html | 71 +++ .../umb-media-picker3-property-editor.less | 13 + ...umbMediaPicker3PropertyEditor.component.js | 431 ++++++++++++++++++ ...3PropertyEditor.createButton.controller.js | 18 + src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 12 + src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 12 + .../Umbraco/config/lang/en_us.xml | 12 + src/Umbraco.Web/Editors/MediaController.cs | 27 +- .../ImageCropperTemplateCoreExtensions.cs | 11 + .../ImageCropperTemplateExtensions.cs | 11 +- .../FileExtensionConfigItem.cs | 13 + .../FileUploadConfiguration.cs | 14 + .../FileUploadConfigurationEditor.cs | 12 + .../FileUploadPropertyEditor.cs | 4 + .../PropertyEditors/IFileExtensionConfig.cs | 13 + .../IFileExtensionConfigItem.cs | 11 + .../MediaPicker3Configuration.cs | 60 +++ .../MediaPicker3ConfigurationEditor.cs | 27 ++ .../MediaPicker3PropertyEditor.cs | 64 +++ .../MediaPickerWithCropsValueConverter.cs | 119 +++++ src/Umbraco.Web/Umbraco.Web.csproj | 9 + src/Umbraco.Web/UrlHelperRenderExtensions.cs | 26 ++ 82 files changed, 3344 insertions(+), 569 deletions(-) create mode 100644 src/Umbraco.Core/Models/MediaWithCrops.cs create mode 100644 src/Umbraco.Web.UI.Client/src/common/directives/components/forms/validwhen.directive.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediaentryeditor/mediaentryeditor.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediaentryeditor/mediaentryeditor.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediaentryeditor/mediaentryeditor.less create mode 100644 src/Umbraco.Web.UI.Client/src/views/components/mediacard/umb-media-card-grid.less create mode 100644 src/Umbraco.Web.UI.Client/src/views/components/mediacard/umb-media-card.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/components/mediacard/umb-media-card.less create mode 100644 src/Umbraco.Web.UI.Client/src/views/components/mediacard/umbMediaCard.component.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/mediapicker3.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/prevalue/mediapicker3.crops.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/prevalue/mediapicker3.crops.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/prevalue/mediapicker3.crops.less create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umb-media-picker3-property-editor.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umb-media-picker3-property-editor.less create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umbMediaPicker3PropertyEditor.component.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umbMediaPicker3PropertyEditor.createButton.controller.js create mode 100644 src/Umbraco.Web/PropertyEditors/FileExtensionConfigItem.cs create mode 100644 src/Umbraco.Web/PropertyEditors/FileUploadConfiguration.cs create mode 100644 src/Umbraco.Web/PropertyEditors/FileUploadConfigurationEditor.cs create mode 100644 src/Umbraco.Web/PropertyEditors/IFileExtensionConfig.cs create mode 100644 src/Umbraco.Web/PropertyEditors/IFileExtensionConfigItem.cs create mode 100644 src/Umbraco.Web/PropertyEditors/MediaPicker3Configuration.cs create mode 100644 src/Umbraco.Web/PropertyEditors/MediaPicker3ConfigurationEditor.cs create mode 100644 src/Umbraco.Web/PropertyEditors/MediaPicker3PropertyEditor.cs create mode 100644 src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerWithCropsValueConverter.cs diff --git a/src/Umbraco.Core/Constants-Conventions.cs b/src/Umbraco.Core/Constants-Conventions.cs index c1d7103a1c..c8233c8d34 100644 --- a/src/Umbraco.Core/Constants-Conventions.cs +++ b/src/Umbraco.Core/Constants-Conventions.cs @@ -118,6 +118,26 @@ namespace Umbraco.Core /// public const string Image = "Image"; + /// + /// MediaType alias for a video. + /// + public const string Video = "Video"; + + /// + /// MediaType alias for an audio. + /// + public const string Audio = "Audio"; + + /// + /// MediaType alias for an article. + /// + public const string Article = "Article"; + + /// + /// MediaType alias for vector graphics. + /// + public const string VectorGraphics = "VectorGraphics"; + /// /// MediaType alias indicating allowing auto-selection. /// diff --git a/src/Umbraco.Core/Constants-DataTypes.cs b/src/Umbraco.Core/Constants-DataTypes.cs index 673da8f9a3..f1af0ba99e 100644 --- a/src/Umbraco.Core/Constants-DataTypes.cs +++ b/src/Umbraco.Core/Constants-DataTypes.cs @@ -25,6 +25,10 @@ namespace Umbraco.Core public const int DropDownSingle = -39; public const int DropDownMultiple = -42; public const int Upload = -90; + public const int UploadVideo = -100; + public const int UploadAudio = -101; + public const int UploadArticle = -102; + public const int UploadVectorGraphics = -103; public const int DefaultContentListView = -95; public const int DefaultMediaListView = -96; @@ -42,7 +46,7 @@ namespace Umbraco.Core /// Defines the identifiers for Umbraco data types as constants for easy centralized access/management. /// public static class Guids - { + { /// /// Guid for Content Picker as string @@ -88,6 +92,49 @@ namespace Umbraco.Core public static readonly Guid MultipleMediaPickerGuid = new Guid(MultipleMediaPicker); + /// + /// Guid for Media Picker v3 as string + /// + public const string MediaPicker3 = "4309A3EA-0D78-4329-A06C-C80B036AF19A"; + + /// + /// Guid for Media Picker v3 + /// + public static readonly Guid MediaPicker3Guid = new Guid(MediaPicker3); + + /// + /// Guid for Media Picker v3 multiple as string + /// + public const string MediaPicker3Multiple = "1B661F40-2242-4B44-B9CB-3990EE2B13C0"; + + /// + /// Guid for Media Picker v3 multiple + /// + public static readonly Guid MediaPicker3MultipleGuid = new Guid(MediaPicker3Multiple); + + + /// + /// Guid for Media Picker v3 single-image as string + /// + public const string MediaPicker3SingleImage = "AD9F0CF2-BDA2-45D5-9EA1-A63CFC873FD3"; + + /// + /// Guid for Media Picker v3 single-image + /// + public static readonly Guid MediaPicker3SingleImageGuid = new Guid(MediaPicker3SingleImage); + + + /// + /// Guid for Media Picker v3 multi-image as string + /// + public const string MediaPicker3MultipleImages = "0E63D883-B62B-4799-88C3-157F82E83ECC"; + + /// + /// Guid for Media Picker v3 multi-image + /// + public static readonly Guid MediaPicker3MultipleImagesGuid = new Guid(MediaPicker3MultipleImages); + + /// /// Guid for Related Links as string /// @@ -307,6 +354,46 @@ namespace Umbraco.Core /// public static readonly Guid UploadGuid = new Guid(Upload); + /// + /// Guid for UploadVideo as string + /// + public const string UploadVideo = "70575fe7-9812-4396-bbe1-c81a76db71b5"; + + /// + /// Guid for UploadVideo + /// + public static readonly Guid UploadVideoGuid = new Guid(UploadVideo); + + /// + /// Guid for UploadAudio as string + /// + public const string UploadAudio = "8f430dd6-4e96-447e-9dc0-cb552c8cd1f3"; + + /// + /// Guid for UploadAudio + /// + public static readonly Guid UploadAudioGuid = new Guid(UploadAudio); + + /// + /// Guid for UploadArticle as string + /// + public const string UploadArticle = "bc1e266c-dac4-4164-bf08-8a1ec6a7143d"; + + /// + /// Guid for UploadArticle + /// + public static readonly Guid UploadArticleGuid = new Guid(UploadArticle); + + /// + /// Guid for UploadVectorGraphics as string + /// + public const string UploadVectorGraphics = "215cb418-2153-4429-9aef-8c0f0041191b"; + + /// + /// Guid for UploadVectorGraphics + /// + public static readonly Guid UploadVectorGraphicsGuid = new Guid(UploadVectorGraphics); + /// /// Guid for Label as string @@ -367,8 +454,8 @@ namespace Umbraco.Core /// Guid for Label decimal /// public static readonly Guid LabelDecimalGuid = new Guid(LabelDecimal); - - + + } } } diff --git a/src/Umbraco.Core/Constants-Icons.cs b/src/Umbraco.Core/Constants-Icons.cs index d5cc37c9a5..e15c1e162b 100644 --- a/src/Umbraco.Core/Constants-Icons.cs +++ b/src/Umbraco.Core/Constants-Icons.cs @@ -59,6 +59,26 @@ /// public const string MediaFile = "icon-document"; + /// + /// System media video icon + /// + public const string MediaVideo = "icon-video"; + + /// + /// System media audio icon + /// + public const string MediaAudio = "icon-sound-waves"; + + /// + /// System media article icon + /// + public const string MediaArticle = "icon-article"; + + /// + /// System media vector icon + /// + public const string MediaVectorGraphics = "icon-picture"; + /// /// System media folder icon /// @@ -93,7 +113,7 @@ /// System packages icon /// public const string Packages = "icon-box"; - + /// /// System property editor icon /// diff --git a/src/Umbraco.Core/Constants-PropertyEditors.cs b/src/Umbraco.Core/Constants-PropertyEditors.cs index 87739469d1..f69570dc08 100644 --- a/src/Umbraco.Core/Constants-PropertyEditors.cs +++ b/src/Umbraco.Core/Constants-PropertyEditors.cs @@ -95,12 +95,17 @@ namespace Umbraco.Core /// ListView. /// public const string ListView = "Umbraco.ListView"; - + /// /// Media Picker. /// public const string MediaPicker = "Umbraco.MediaPicker"; + /// + /// Media Picker v.3. + /// + public const string MediaPicker3 = "Umbraco.MediaPicker3"; + /// /// Multiple Media Picker. /// diff --git a/src/Umbraco.Core/Constants-PropertyTypeGroups.cs b/src/Umbraco.Core/Constants-PropertyTypeGroups.cs index d3402e69f8..20ada8c0f4 100644 --- a/src/Umbraco.Core/Constants-PropertyTypeGroups.cs +++ b/src/Umbraco.Core/Constants-PropertyTypeGroups.cs @@ -8,7 +8,7 @@ public static class PropertyTypeGroups { /// - /// Guid for a Image PropertyTypeGroup object. + /// Guid for an Image PropertyTypeGroup object. /// public const string Image = "79ED4D07-254A-42CF-8FA9-EBE1C116A596"; @@ -18,7 +18,27 @@ public const string File = "50899F9C-023A-4466-B623-ABA9049885FE"; /// - /// Guid for a Image PropertyTypeGroup object. + /// Guid for a Video PropertyTypeGroup object. + /// + public const string Video = "2F0A61B6-CF92-4FF4-B437-751AB35EB254"; + + /// + /// Guid for an Audio PropertyTypeGroup object. + /// + public const string Audio = "335FB495-0A87-4E82-B902-30EB367B767C"; + + /// + /// Guid for an Article PropertyTypeGroup object. + /// + public const string Article = "9AF3BD65-F687-4453-9518-5F180D1898EC"; + + /// + /// Guid for a VectorGraphics PropertyTypeGroup object. + /// + public const string VectorGraphics = "F199B4D7-9E84-439F-8531-F87D9AF37711"; + + /// + /// Guid for a Membership PropertyTypeGroup object. /// public const string Membership = "0756729D-D665-46E3-B84A-37ACEAA614F8"; } diff --git a/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs b/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs index 44de611348..264733e5b9 100644 --- a/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs +++ b/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs @@ -107,7 +107,11 @@ namespace Umbraco.Core.Migrations.Install InsertDataTypeNodeDto(Constants.DataTypes.LabelDateTime, 37, Constants.DataTypes.Guids.LabelDateTime, "Label (datetime)"); InsertDataTypeNodeDto(Constants.DataTypes.LabelTime, 38, Constants.DataTypes.Guids.LabelTime, "Label (time)"); InsertDataTypeNodeDto(Constants.DataTypes.LabelDecimal, 39, Constants.DataTypes.Guids.LabelDecimal, "Label (decimal)"); - _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Constants.DataTypes.Upload, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Constants.DataTypes.Upload}", SortOrder = 34, UniqueId = Constants.DataTypes.Guids.UploadGuid, Text = "Upload", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Constants.DataTypes.Upload, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Constants.DataTypes.Upload}", SortOrder = 34, UniqueId = Constants.DataTypes.Guids.UploadGuid, Text = "Upload File", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Constants.DataTypes.UploadVideo, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Constants.DataTypes.UploadVideo}", SortOrder = 35, UniqueId = Constants.DataTypes.Guids.UploadVideoGuid, Text = "Upload Video", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Constants.DataTypes.UploadAudio, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Constants.DataTypes.UploadAudio}", SortOrder = 36, UniqueId = Constants.DataTypes.Guids.UploadAudioGuid, Text = "Upload Audio", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Constants.DataTypes.UploadArticle, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Constants.DataTypes.UploadArticle}", SortOrder = 37, UniqueId = Constants.DataTypes.Guids.UploadArticleGuid, Text = "Upload Article", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Constants.DataTypes.UploadVectorGraphics, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Constants.DataTypes.UploadVectorGraphics}", SortOrder = 38, UniqueId = Constants.DataTypes.Guids.UploadVectorGraphicsGuid, Text = "Upload Vector Graphics", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Constants.DataTypes.Textarea, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Constants.DataTypes.Textarea}", SortOrder = 33, UniqueId = Constants.DataTypes.Guids.TextareaGuid, Text = "Textarea", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Constants.DataTypes.Textbox, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Constants.DataTypes.Textbox}", SortOrder = 32, UniqueId = Constants.DataTypes.Guids.TextstringGuid, Text = "Textstring", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Constants.DataTypes.RichtextEditor, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Constants.DataTypes.RichtextEditor}", SortOrder = 4, UniqueId = Constants.DataTypes.Guids.RichtextEditorGuid, Text = "Richtext editor", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); @@ -126,6 +130,10 @@ namespace Umbraco.Core.Migrations.Install _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1031, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1031", SortOrder = 2, UniqueId = new Guid("f38bd2d7-65d0-48e6-95dc-87ce06ec2d3d"), Text = Constants.Conventions.MediaTypes.Folder, NodeObjectType = Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }); _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1032, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1032", SortOrder = 2, UniqueId = new Guid("cc07b313-0843-4aa8-bbda-871c8da728c8"), Text = Constants.Conventions.MediaTypes.Image, NodeObjectType = Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }); _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1033, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1033", SortOrder = 2, UniqueId = new Guid("4c52d8ab-54e6-40cd-999c-7a5f24903e4d"), Text = Constants.Conventions.MediaTypes.File, NodeObjectType = Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }); + _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1034, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1034", SortOrder = 2, UniqueId = new Guid("f6c515bb-653c-4bdc-821c-987729ebe327"), Text = Constants.Conventions.MediaTypes.Video, NodeObjectType = Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }); + _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1035, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1035", SortOrder = 2, UniqueId = new Guid("a5ddeee0-8fd8-4cee-a658-6f1fcdb00de3"), Text = Constants.Conventions.MediaTypes.Audio, NodeObjectType = Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }); + _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1036, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1036", SortOrder = 2, UniqueId = new Guid("a43e3414-9599-4230-a7d3-943a21b20122"), Text = Constants.Conventions.MediaTypes.Article, NodeObjectType = Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }); + _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1037, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1037", SortOrder = 2, UniqueId = new Guid("c4b1efcf-a9d5-41c4-9621-e9d273b52a9c"), Text = "Vector Graphics (SVG)", NodeObjectType = Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }); _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Constants.DataTypes.Tags, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Constants.DataTypes.Tags}", SortOrder = 2, UniqueId = new Guid("b6b73142-b9c1-4bf8-a16d-e1c23320b549"), Text = "Tags", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Constants.DataTypes.ImageCropper, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Constants.DataTypes.ImageCropper}", SortOrder = 2, UniqueId = new Guid("1df9f033-e6d4-451f-b8d2-e0cbc50a836f"), Text = "Image Cropper", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1044, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1044", SortOrder = 0, UniqueId = new Guid("d59be02f-1df9-4228-aa1e-01917d806cda"), Text = Constants.Conventions.MemberTypes.DefaultAlias, NodeObjectType = Constants.ObjectTypes.MemberType, CreateDate = DateTime.Now }); @@ -133,9 +141,15 @@ namespace Umbraco.Core.Migrations.Install //New UDI pickers with newer Ids _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1046, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1046", SortOrder = 2, UniqueId = new Guid("FD1E0DA5-5606-4862-B679-5D0CF3A52A59"), Text = "Content Picker", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1047, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1047", SortOrder = 2, UniqueId = new Guid("1EA2E01F-EBD8-4CE1-8D71-6B1149E63548"), Text = "Member Picker", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1048, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1048", SortOrder = 2, UniqueId = new Guid("135D60E0-64D9-49ED-AB08-893C9BA44AE5"), Text = "Media Picker", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1049, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1049", SortOrder = 2, UniqueId = new Guid("9DBBCBBB-2327-434A-B355-AF1B84E5010A"), Text = "Multiple Media Picker", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1048, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1048", SortOrder = 2, UniqueId = new Guid("135D60E0-64D9-49ED-AB08-893C9BA44AE5"), Text = "Media Picker (old)", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1049, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1049", SortOrder = 2, UniqueId = new Guid("9DBBCBBB-2327-434A-B355-AF1B84E5010A"), Text = "Multiple Media Picker (old)", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1050, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1050", SortOrder = 2, UniqueId = new Guid("B4E3535A-1753-47E2-8568-602CF8CFEE6F"), Text = "Multi URL Picker", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + + _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1051, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1051", SortOrder = 2, UniqueId = Constants.DataTypes.Guids.MediaPicker3Guid, Text = "Media Picker 3", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1052, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1052", SortOrder = 2, UniqueId = Constants.DataTypes.Guids.MediaPicker3MultipleGuid, Text = "Multiple Media Picker 3", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1053, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1053", SortOrder = 2, UniqueId = Constants.DataTypes.Guids.MediaPicker3SingleImageGuid, Text = "Image Media Picker 3", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1054, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1054", SortOrder = 2, UniqueId = Constants.DataTypes.Guids.MediaPicker3MultipleImagesGuid, Text = "Multiple Image Media Picker 3", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + } private void CreateLockData() @@ -160,6 +174,10 @@ namespace Umbraco.Core.Migrations.Install _database.Insert(Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 532, NodeId = 1031, Alias = Constants.Conventions.MediaTypes.Folder, Icon = Constants.Icons.MediaFolder, Thumbnail = Constants.Icons.MediaFolder, IsContainer = false, AllowAtRoot = true, Variations = (byte) ContentVariation.Nothing }); _database.Insert(Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 533, NodeId = 1032, Alias = Constants.Conventions.MediaTypes.Image, Icon = Constants.Icons.MediaImage, Thumbnail = Constants.Icons.MediaImage, AllowAtRoot = true, Variations = (byte) ContentVariation.Nothing }); _database.Insert(Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 534, NodeId = 1033, Alias = Constants.Conventions.MediaTypes.File, Icon = Constants.Icons.MediaFile, Thumbnail = Constants.Icons.MediaFile, AllowAtRoot = true, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 540, NodeId = 1034, Alias = Constants.Conventions.MediaTypes.Video, Icon = Constants.Icons.MediaVideo, Thumbnail = Constants.Icons.MediaVideo, AllowAtRoot = true, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 541, NodeId = 1035, Alias = Constants.Conventions.MediaTypes.Audio, Icon = Constants.Icons.MediaAudio, Thumbnail = Constants.Icons.MediaAudio, AllowAtRoot = true, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 542, NodeId = 1036, Alias = Constants.Conventions.MediaTypes.Article, Icon = Constants.Icons.MediaArticle, Thumbnail = Constants.Icons.MediaArticle, AllowAtRoot = true, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 543, NodeId = 1037, Alias = Constants.Conventions.MediaTypes.VectorGraphics, Icon = Constants.Icons.MediaVectorGraphics, Thumbnail = Constants.Icons.MediaVectorGraphics, AllowAtRoot = true, Variations = (byte) ContentVariation.Nothing }); _database.Insert(Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 531, NodeId = 1044, Alias = Constants.Conventions.MemberTypes.DefaultAlias, Icon = Constants.Icons.Member, Thumbnail = Constants.Icons.Member, Variations = (byte) ContentVariation.Nothing }); } @@ -207,20 +225,44 @@ namespace Umbraco.Core.Migrations.Install { _database.Insert(Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 3, ContentTypeNodeId = 1032, Text = "Image", SortOrder = 1, UniqueId = new Guid(Constants.PropertyTypeGroups.Image) }); _database.Insert(Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 4, ContentTypeNodeId = 1033, Text = "File", SortOrder = 1, UniqueId = new Guid(Constants.PropertyTypeGroups.File) }); + _database.Insert(Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 52, ContentTypeNodeId = 1034, Text = "Video", SortOrder = 1, UniqueId = new Guid(Constants.PropertyTypeGroups.Video) }); + _database.Insert(Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 53, ContentTypeNodeId = 1035, Text = "Audio", SortOrder = 1, UniqueId = new Guid(Constants.PropertyTypeGroups.Audio) }); + _database.Insert(Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 54, ContentTypeNodeId = 1036, Text = "Article", SortOrder = 1, UniqueId = new Guid(Constants.PropertyTypeGroups.Article) }); + _database.Insert(Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 55, ContentTypeNodeId = 1037, Text = "Vector Graphics", SortOrder = 1, UniqueId = new Guid(Constants.PropertyTypeGroups.VectorGraphics) }); //membership property group _database.Insert(Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 11, ContentTypeNodeId = 1044, Text = "Membership", SortOrder = 1, UniqueId = new Guid(Constants.PropertyTypeGroups.Membership) }); } private void CreatePropertyTypeData() { - _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 6, UniqueId = 6.ToGuid(), DataTypeId = Constants.DataTypes.ImageCropper, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Constants.Conventions.Media.File, Name = "Upload image", SortOrder = 0, Mandatory = true, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 6, UniqueId = 6.ToGuid(), DataTypeId = Constants.DataTypes.ImageCropper, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Constants.Conventions.Media.File, Name = "Image", SortOrder = 0, Mandatory = true, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 7, UniqueId = 7.ToGuid(), DataTypeId = Constants.DataTypes.LabelInt, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Constants.Conventions.Media.Width, Name = "Width", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in pixels", Variations = (byte) ContentVariation.Nothing }); _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 8, UniqueId = 8.ToGuid(), DataTypeId = Constants.DataTypes.LabelInt, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Constants.Conventions.Media.Height, Name = "Height", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in pixels", Variations = (byte) ContentVariation.Nothing }); _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 9, UniqueId = 9.ToGuid(), DataTypeId = Constants.DataTypes.LabelBigint, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in bytes", Variations = (byte) ContentVariation.Nothing }); _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 10, UniqueId = 10.ToGuid(), DataTypeId = -92, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); - _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 24, UniqueId = 24.ToGuid(), DataTypeId = Constants.DataTypes.Upload, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Constants.Conventions.Media.File, Name = "Upload file", SortOrder = 0, Mandatory = true, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 24, UniqueId = 24.ToGuid(), DataTypeId = Constants.DataTypes.Upload, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Constants.Conventions.Media.File, Name = "File", SortOrder = 0, Mandatory = true, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 25, UniqueId = 25.ToGuid(), DataTypeId = -92, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 26, UniqueId = 26.ToGuid(), DataTypeId = Constants.DataTypes.LabelBigint, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in bytes", Variations = (byte) ContentVariation.Nothing }); + + _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 40, UniqueId = 40.ToGuid(), DataTypeId = Constants.DataTypes.UploadVideo, ContentTypeId = 1034, PropertyTypeGroupId = 52, Alias = Constants.Conventions.Media.File, Name = "Video", SortOrder = 0, Mandatory = true, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 41, UniqueId = 41.ToGuid(), DataTypeId = -92, ContentTypeId = 1034, PropertyTypeGroupId = 52, Alias = Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 42, UniqueId = 42.ToGuid(), DataTypeId = Constants.DataTypes.LabelBigint, ContentTypeId = 1034, PropertyTypeGroupId = 52, Alias = Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in bytes", Variations = (byte) ContentVariation.Nothing }); + + _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 43, UniqueId = 43.ToGuid(), DataTypeId = Constants.DataTypes.UploadAudio, ContentTypeId = 1035, PropertyTypeGroupId = 53, Alias = Constants.Conventions.Media.File, Name = "Audio", SortOrder = 0, Mandatory = true, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 44, UniqueId = 44.ToGuid(), DataTypeId = -92, ContentTypeId = 1035, PropertyTypeGroupId = 53, Alias = Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 45, UniqueId = 45.ToGuid(), DataTypeId = Constants.DataTypes.LabelBigint, ContentTypeId = 1035, PropertyTypeGroupId = 53, Alias = Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in bytes", Variations = (byte) ContentVariation.Nothing }); + + _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 46, UniqueId = 46.ToGuid(), DataTypeId = Constants.DataTypes.UploadArticle, ContentTypeId = 1036, PropertyTypeGroupId = 54, Alias = Constants.Conventions.Media.File, Name = "Article", SortOrder = 0, Mandatory = true, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 47, UniqueId = 47.ToGuid(), DataTypeId = -92, ContentTypeId = 1036, PropertyTypeGroupId = 54, Alias = Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 48, UniqueId = 48.ToGuid(), DataTypeId = Constants.DataTypes.LabelBigint, ContentTypeId = 1036, PropertyTypeGroupId = 54, Alias = Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in bytes", Variations = (byte) ContentVariation.Nothing }); + + _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 49, UniqueId = 49.ToGuid(), DataTypeId = Constants.DataTypes.UploadVectorGraphics, ContentTypeId = 1037, PropertyTypeGroupId = 55, Alias = Constants.Conventions.Media.File, Name = "Vector Graphics", SortOrder = 0, Mandatory = true, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 50, UniqueId = 50.ToGuid(), DataTypeId = -92, ContentTypeId = 1037, PropertyTypeGroupId = 55, Alias = Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 51, UniqueId = 51.ToGuid(), DataTypeId = Constants.DataTypes.LabelBigint, ContentTypeId = 1037, PropertyTypeGroupId = 55, Alias = Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in bytes", Variations = (byte) ContentVariation.Nothing }); + + + + //membership property types _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 28, UniqueId = 28.ToGuid(), DataTypeId = Constants.DataTypes.Textarea, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.Comments, Name = Constants.Conventions.Member.CommentsLabel, SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); _database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 29, UniqueId = 29.ToGuid(), DataTypeId = Constants.DataTypes.LabelInt, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.FailedPasswordAttempts, Name = Constants.Conventions.Member.FailedPasswordAttemptsLabel, SortOrder = 1, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); @@ -244,6 +286,10 @@ namespace Umbraco.Core.Migrations.Install _database.Insert(Constants.DatabaseSchema.Tables.ContentChildType, "Id", false, new ContentTypeAllowedContentTypeDto { Id = 1031, AllowedId = 1031 }); _database.Insert(Constants.DatabaseSchema.Tables.ContentChildType, "Id", false, new ContentTypeAllowedContentTypeDto { Id = 1031, AllowedId = 1032 }); _database.Insert(Constants.DatabaseSchema.Tables.ContentChildType, "Id", false, new ContentTypeAllowedContentTypeDto { Id = 1031, AllowedId = 1033 }); + _database.Insert(Constants.DatabaseSchema.Tables.ContentChildType, "Id", false, new ContentTypeAllowedContentTypeDto { Id = 1031, AllowedId = 1034 }); + _database.Insert(Constants.DatabaseSchema.Tables.ContentChildType, "Id", false, new ContentTypeAllowedContentTypeDto { Id = 1031, AllowedId = 1035 }); + _database.Insert(Constants.DatabaseSchema.Tables.ContentChildType, "Id", false, new ContentTypeAllowedContentTypeDto { Id = 1031, AllowedId = 1036 }); + _database.Insert(Constants.DatabaseSchema.Tables.ContentChildType, "Id", false, new ContentTypeAllowedContentTypeDto { Id = 1031, AllowedId = 1037 }); } private void CreateDataTypeData() @@ -307,6 +353,63 @@ namespace Umbraco.Core.Migrations.Install _database.Insert(Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = 1049, EditorAlias = Constants.PropertyEditors.Aliases.MediaPicker, DbType = "Ntext", Configuration = "{\"multiPicker\":1}" }); _database.Insert(Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = 1050, EditorAlias = Constants.PropertyEditors.Aliases.MultiUrlPicker, DbType = "Ntext" }); + + _database.Insert(Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto + { + NodeId = Constants.DataTypes.UploadVideo, + EditorAlias = Constants.PropertyEditors.Aliases.UploadField, + DbType = "Nvarchar", + Configuration = "{\"fileExtensions\":[{\"id\":0, \"value\":\"mp4\"}, {\"id\":1, \"value\":\"webm\"}, {\"id\":2, \"value\":\"ogv\"}]}" + }); + + _database.Insert(Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto + { + NodeId = Constants.DataTypes.UploadAudio, + EditorAlias = Constants.PropertyEditors.Aliases.UploadField, + DbType = "Nvarchar", + Configuration = "{\"fileExtensions\":[{\"id\":0, \"value\":\"mp3\"}, {\"id\":1, \"value\":\"weba\"}, {\"id\":2, \"value\":\"oga\"}, {\"id\":3, \"value\":\"opus\"}]}" + }); + + _database.Insert(Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto + { + NodeId = Constants.DataTypes.UploadArticle, + EditorAlias = Constants.PropertyEditors.Aliases.UploadField, + DbType = "Nvarchar", + Configuration = "{\"fileExtensions\":[{\"id\":0, \"value\":\"pdf\"}, {\"id\":1, \"value\":\"docx\"}, {\"id\":2, \"value\":\"doc\"}]}" + }); + + _database.Insert(Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto + { + NodeId = Constants.DataTypes.UploadVectorGraphics, + EditorAlias = Constants.PropertyEditors.Aliases.UploadField, + DbType = "Nvarchar", + Configuration = "{\"fileExtensions\":[{\"id\":0, \"value\":\"svg\"}]}" + }); + + _database.Insert(Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { + NodeId = 1051, + EditorAlias = Constants.PropertyEditors.Aliases.MediaPicker3, + DbType = "Ntext", + Configuration = "{\"multiple\": false, \"validationLimit\":{\"min\":0,\"max\":1}}" + }); + _database.Insert(Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { + NodeId = 1052, + EditorAlias = Constants.PropertyEditors.Aliases.MediaPicker3, + DbType = "Ntext", + Configuration = "{\"multiple\": true}" + }); + _database.Insert(Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { + NodeId = 1053, + EditorAlias = Constants.PropertyEditors.Aliases.MediaPicker3, + DbType = "Ntext", + Configuration = "{\"filter\":\"" + Constants.Conventions.MediaTypes.Image + "\", \"multiple\": false, \"validationLimit\":{\"min\":0,\"max\":1}}" + }); + _database.Insert(Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { + NodeId = 1054, + EditorAlias = Constants.PropertyEditors.Aliases.MediaPicker3, + DbType = "Ntext", + Configuration = "{\"filter\":\"" + Constants.Conventions.MediaTypes.Image + "\", \"multiple\": true}" + }); } private void CreateRelationTypeData() diff --git a/src/Umbraco.Core/Models/DataTypeExtensions.cs b/src/Umbraco.Core/Models/DataTypeExtensions.cs index f460edbde7..913aa4773e 100644 --- a/src/Umbraco.Core/Models/DataTypeExtensions.cs +++ b/src/Umbraco.Core/Models/DataTypeExtensions.cs @@ -62,6 +62,10 @@ namespace Umbraco.Core.Models Constants.DataTypes.Guids.TextstringGuid, Constants.DataTypes.Guids.TextareaGuid, Constants.DataTypes.Guids.UploadGuid, + Constants.DataTypes.Guids.UploadArticleGuid, + Constants.DataTypes.Guids.UploadAudioGuid, + Constants.DataTypes.Guids.UploadVectorGraphicsGuid, + Constants.DataTypes.Guids.UploadVideoGuid, Constants.DataTypes.Guids.LabelStringGuid, Constants.DataTypes.Guids.LabelDecimalGuid, Constants.DataTypes.Guids.LabelDateTimeGuid, diff --git a/src/Umbraco.Core/Models/MediaWithCrops.cs b/src/Umbraco.Core/Models/MediaWithCrops.cs new file mode 100644 index 0000000000..ef3205bd94 --- /dev/null +++ b/src/Umbraco.Core/Models/MediaWithCrops.cs @@ -0,0 +1,15 @@ +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.PropertyEditors.ValueConverters; + +namespace Umbraco.Core.Models +{ + /// + /// Model used in Razor Views for rendering + /// + public class MediaWithCrops + { + public IPublishedContent MediaItem { get; set; } + + public ImageCropperValue LocalCrops { get; set; } + } +} diff --git a/src/Umbraco.Core/Persistence/Dtos/ContentTypeDto.cs b/src/Umbraco.Core/Persistence/Dtos/ContentTypeDto.cs index e7a14a26e2..68036dab4b 100644 --- a/src/Umbraco.Core/Persistence/Dtos/ContentTypeDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/ContentTypeDto.cs @@ -11,7 +11,7 @@ namespace Umbraco.Core.Persistence.Dtos public const string TableName = Constants.DatabaseSchema.Tables.ContentType; [Column("pk")] - [PrimaryKeyColumn(IdentitySeed = 535)] + [PrimaryKeyColumn(IdentitySeed = 700)] public int PrimaryKey { get; set; } [Column("nodeId")] diff --git a/src/Umbraco.Core/Persistence/Dtos/PropertyTypeDto.cs b/src/Umbraco.Core/Persistence/Dtos/PropertyTypeDto.cs index 572201c94a..f22e4453f4 100644 --- a/src/Umbraco.Core/Persistence/Dtos/PropertyTypeDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/PropertyTypeDto.cs @@ -11,7 +11,7 @@ namespace Umbraco.Core.Persistence.Dtos internal class PropertyTypeDto { [Column("id")] - [PrimaryKeyColumn(IdentitySeed = 50)] + [PrimaryKeyColumn(IdentitySeed = 100)] public int Id { get; set; } [Column("dataTypeId")] diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/JsonValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/JsonValueConverter.cs index 270c8c3b0b..694ebfde27 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/JsonValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/JsonValueConverter.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Umbraco.Core.Composing; @@ -18,6 +19,8 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters { private readonly PropertyEditorCollection _propertyEditors; + string[] ExcludedPropertyEditors = new string[] { Constants.PropertyEditors.Aliases.MediaPicker3 }; + /// /// Initializes a new instance of the class. /// @@ -28,13 +31,16 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters /// /// It is a converter for any value type that is "JSON" + /// Unless it's in the Excluded Property Editors list + /// The new MediaPicker 3 stores JSON but we want to use its own ValueConvertor /// /// /// public override bool IsConverter(IPublishedPropertyType propertyType) { return _propertyEditors.TryGet(propertyType.EditorAlias, out var editor) - && editor.GetValueEditor().ValueType.InvariantEquals(ValueTypes.Json); + && editor.GetValueEditor().ValueType.InvariantEquals(ValueTypes.Json) + && ExcludedPropertyEditors.Contains(propertyType.EditorAlias) == false; } public override Type GetPropertyValueType(IPublishedPropertyType propertyType) diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 2ea5292d73..0a453ad75f 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -156,6 +156,7 @@ + diff --git a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs index b0c57b685b..d2bbf3e865 100644 --- a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs @@ -268,7 +268,7 @@ AnotherContentFinder public void GetDataEditors() { var types = _typeLoader.GetDataEditors(); - Assert.AreEqual(40, types.Count()); + Assert.AreEqual(41, types.Count()); } /// diff --git a/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs index ca8ee29ee3..339b3d4931 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs @@ -279,7 +279,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(dataTypeDefinitions, Is.Not.Null); Assert.That(dataTypeDefinitions.Any(), Is.True); Assert.That(dataTypeDefinitions.Any(x => x == null), Is.False); - Assert.That(dataTypeDefinitions.Length, Is.EqualTo(29)); + Assert.That(dataTypeDefinitions.Length, Is.EqualTo(37)); } } diff --git a/src/Umbraco.Tests/Persistence/Repositories/MediaTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MediaTypeRepositoryTest.cs index bb3286daed..e048886dbe 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MediaTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MediaTypeRepositoryTest.cs @@ -50,7 +50,7 @@ namespace Umbraco.Tests.Persistence.Repositories containerRepository.Save(container2); - var contentType = (IMediaType)MockedContentTypes.CreateVideoMediaType(); + var contentType = (IMediaType)MockedContentTypes.CreateNewMediaType(); contentType.ParentId = container2.Id; repository.Save(contentType); @@ -133,7 +133,7 @@ namespace Umbraco.Tests.Persistence.Repositories containerRepository.Save(container); - var contentType = MockedContentTypes.CreateVideoMediaType(); + var contentType = MockedContentTypes.CreateNewMediaType(); contentType.ParentId = container.Id; repository.Save(contentType); @@ -155,7 +155,7 @@ namespace Umbraco.Tests.Persistence.Repositories containerRepository.Save(container); - IMediaType contentType = MockedContentTypes.CreateVideoMediaType(); + IMediaType contentType = MockedContentTypes.CreateNewMediaType(); contentType.ParentId = container.Id; repository.Save(contentType); @@ -183,7 +183,7 @@ namespace Umbraco.Tests.Persistence.Repositories var repository = CreateRepository(provider); // Act - var contentType = MockedContentTypes.CreateVideoMediaType(); + var contentType = MockedContentTypes.CreateNewMediaType(); repository.Save(contentType); @@ -210,7 +210,7 @@ namespace Umbraco.Tests.Persistence.Repositories { var repository = CreateRepository(provider); - var videoMediaType = MockedContentTypes.CreateVideoMediaType(); + var videoMediaType = MockedContentTypes.CreateNewMediaType(); repository.Save(videoMediaType); @@ -249,7 +249,7 @@ namespace Umbraco.Tests.Persistence.Repositories var repository = CreateRepository(provider); // Act - var mediaType = MockedContentTypes.CreateVideoMediaType(); + var mediaType = MockedContentTypes.CreateNewMediaType(); repository.Save(mediaType); @@ -378,7 +378,7 @@ namespace Umbraco.Tests.Persistence.Repositories { var repository = CreateRepository(provider); - var mediaType = MockedContentTypes.CreateVideoMediaType(); + var mediaType = MockedContentTypes.CreateNewMediaType(); repository.Save(mediaType); @@ -406,7 +406,7 @@ namespace Umbraco.Tests.Persistence.Repositories { var repository = CreateRepository(provider); - var mediaType = MockedContentTypes.CreateVideoMediaType(); + var mediaType = MockedContentTypes.CreateNewMediaType(); repository.Save(mediaType); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs index f801d02c5b..bf84503837 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs @@ -1,29 +1,27 @@ -using System.Web; -using System.Xml.Linq; -using System.Xml.XPath; +using Examine; using NUnit.Framework; -using Umbraco.Core; -using Umbraco.Core.Models; -using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.TestHelpers.Entities; -using Umbraco.Tests.UmbracoExamine; -using Umbraco.Web; using System.Linq; using System.Threading; +using System.Web; using System.Xml; -using Examine; +using System.Xml.Linq; +using System.Xml.XPath; +using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.Strings; -using Umbraco.Examine; -using Current = Umbraco.Web.Composing.Current; -using Umbraco.Tests.Testing; using Umbraco.Core.Composing; +using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; +using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; +using Umbraco.Core.Strings; +using Umbraco.Examine; using Umbraco.Tests.LegacyXmlPublishedCache; +using Umbraco.Tests.TestHelpers.Entities; +using Umbraco.Tests.Testing; using Umbraco.Tests.Testing.Objects.Accessors; +using Umbraco.Tests.UmbracoExamine; +using Umbraco.Web; namespace Umbraco.Tests.PublishedContent { @@ -94,6 +92,7 @@ namespace Umbraco.Tests.PublishedContent Name = "Rich Text", DataTypeId = -87 //tiny mce }); + var existing = ServiceContext.MediaTypeService.GetAll(); ServiceContext.MediaTypeService.Save(mType); var media = MockedMedia.CreateMediaImage(mType, -1); media.Properties["content"].SetValue("
    This is some content
    "); diff --git a/src/Umbraco.Tests/Services/MediaServiceTests.cs b/src/Umbraco.Tests/Services/MediaServiceTests.cs index 52f26ecb4d..d5cec11211 100644 --- a/src/Umbraco.Tests/Services/MediaServiceTests.cs +++ b/src/Umbraco.Tests/Services/MediaServiceTests.cs @@ -163,7 +163,7 @@ namespace Umbraco.Tests.Services { // Arrange var mediaService = ServiceContext.MediaService; - var mediaType = MockedContentTypes.CreateVideoMediaType(); + var mediaType = MockedContentTypes.CreateNewMediaType(); ServiceContext.MediaTypeService.Save(mediaType); var media = mediaService.CreateMedia(string.Empty, -1, "video"); @@ -175,7 +175,7 @@ namespace Umbraco.Tests.Services public void Ensure_Content_Xml_Created() { var mediaService = ServiceContext.MediaService; - var mediaType = MockedContentTypes.CreateVideoMediaType(); + var mediaType = MockedContentTypes.CreateNewMediaType(); ServiceContext.MediaTypeService.Save(mediaType); var media = mediaService.CreateMedia("Test", -1, "video"); diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs index e3bb012dae..1b85787fee 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs @@ -378,13 +378,13 @@ namespace Umbraco.Tests.TestHelpers.Entities return contentType; } - public static MediaType CreateVideoMediaType() + public static MediaType CreateNewMediaType() { var mediaType = new MediaType(-1) { - Alias = "video", - Name = "Video", - Description = "ContentType used for videos", + Alias = "newMediaType", + Name = "New Media Type", + Description = "ContentType used for a new format", Icon = ".sprTreeDoc3", Thumbnail = "doc.png", SortOrder = 1, diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/validwhen.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/validwhen.directive.js new file mode 100644 index 0000000000..63681a380a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/validwhen.directive.js @@ -0,0 +1,12 @@ +angular.module("umbraco.directives").directive('validWhen', function () { + return { + require: 'ngModel', + restrict: 'A', + link: function (scope, element, attr, ngModel) { + + attr.$observe("validWhen", function (newValue) { + ngModel.$setValidity("validWhen", newValue === "true"); + }); + } + }; +}); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagecrop.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagecrop.directive.js index f1f2cb38e8..744e4280db 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagecrop.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagecrop.directive.js @@ -6,10 +6,14 @@ **/ angular.module("umbraco.directives") .directive('umbImageCrop', - function ($timeout, cropperHelper) { + function ($timeout, $window, cropperHelper) { + + const MAX_SCALE = 4; + return { restrict: 'E', replace: true, + transclude: true, templateUrl: 'views/components/imaging/umb-image-crop.html', scope: { src: '=', @@ -17,24 +21,29 @@ angular.module("umbraco.directives") height: '@', crop: "=", center: "=", - maxSize: '@' + maxSize: '@?', + alias: '@?', + forceUpdate: '@?' }, link: function (scope, element, attrs) { + var unsubscribe = []; let sliderRef = null; - scope.width = 400; - scope.height = 320; + scope.loaded = false; + scope.width = 0; + scope.height = 0; scope.dimensions = { + element: {}, image: {}, cropper: {}, viewport: {}, - margin: 20, + margin: {}, scale: { - min: 0, - max: 3, + min: 1, + max: MAX_SCALE, current: 1 } }; @@ -45,10 +54,10 @@ angular.module("umbraco.directives") "tooltips": [false], "format": { to: function (value) { - return parseFloat(parseFloat(value).toFixed(3)); //Math.round(value); + return parseFloat(parseFloat(value).toFixed(3)); }, from: function (value) { - return parseFloat(parseFloat(value).toFixed(3)); //Math.round(value); + return parseFloat(parseFloat(value).toFixed(3)); } }, "range": { @@ -59,19 +68,24 @@ angular.module("umbraco.directives") scope.setup = function (slider) { sliderRef = slider; - - // Set slider handle position - sliderRef.noUiSlider.set(scope.dimensions.scale.current); - - // Update slider range min/max - sliderRef.noUiSlider.updateOptions({ - "range": { - "min": scope.dimensions.scale.min, - "max": scope.dimensions.scale.max - } - }); + updateSlider(); }; + function updateSlider() { + if(sliderRef) { + // Update slider range min/max + sliderRef.noUiSlider.updateOptions({ + "range": { + "min": scope.dimensions.scale.min, + "max": scope.dimensions.scale.max + } + }); + + // Set slider handle position + sliderRef.noUiSlider.set(scope.dimensions.scale.current); + } + } + scope.slide = function (values) { if (values) { scope.dimensions.scale.current = parseFloat(values); @@ -84,77 +98,108 @@ angular.module("umbraco.directives") } }; + function onScroll(event) { + // cross-browser wheel delta + var delta = Math.max(-50, Math.min(50, (event.wheelDelta || -event.detail))); + + if(sliderRef) { + var currentScale =sliderRef.noUiSlider.get(); + + var newScale = Math.min(Math.max(currentScale + delta*.001*scope.dimensions.image.ratio, scope.dimensions.scale.min), scope.dimensions.scale.max); + sliderRef.noUiSlider.set(newScale); + scope.$evalAsync(() => { + scope.dimensions.scale.current = newScale; + }); + + if(event.preventDefault) { + event.preventDefault(); + } + } + } + + //live rendering of viewport and image styles - scope.style = function () { - return { - 'height': (parseInt(scope.dimensions.viewport.height, 10)) + 'px', - 'width': (parseInt(scope.dimensions.viewport.width, 10)) + 'px' - }; + function updateStyles() { + scope.maskStyle = { + 'height': (parseInt(scope.dimensions.cropper.height, 10)) + 'px', + 'width': (parseInt(scope.dimensions.cropper.width, 10)) + 'px', + 'top': (parseInt(scope.dimensions.margin.top, 10)) + 'px', + 'left': (parseInt(scope.dimensions.margin.left, 10)) + 'px' + } }; + updateStyles(); //elements var $viewport = element.find(".viewport"); var $image = element.find("img"); var $overlay = element.find(".overlay"); - var $container = element.find(".crop-container"); + + $overlay.bind("focus", function () { + $overlay.bind("DOMMouseScroll mousewheel onmousewheel", onScroll); + }); + $overlay.bind("blur", function () { + $overlay.unbind("DOMMouseScroll mousewheel onmousewheel", onScroll); + }); + //default constraints for drag n drop - var constraints = { left: { max: scope.dimensions.margin, min: scope.dimensions.margin }, top: { max: scope.dimensions.margin, min: scope.dimensions.margin } }; + var constraints = { left: { max: 0, min: 0 }, top: { max: 0, min: 0 } }; scope.constraints = constraints; //set constaints for cropping drag and drop var setConstraints = function () { - constraints.left.min = scope.dimensions.margin + scope.dimensions.cropper.width - scope.dimensions.image.width; - constraints.top.min = scope.dimensions.margin + scope.dimensions.cropper.height - scope.dimensions.image.height; + constraints.left.min = scope.dimensions.cropper.width - scope.dimensions.image.width; + constraints.top.min = scope.dimensions.cropper.height - scope.dimensions.image.height; }; - var setDimensions = function (originalImage) { - originalImage.width("auto"); - originalImage.height("auto"); + var setDimensions = function () { - var image = {}; - image.originalWidth = originalImage.width(); - image.originalHeight = originalImage.height(); - - image.width = image.originalWidth; - image.height = image.originalHeight; - image.left = originalImage[0].offsetLeft; - image.top = originalImage[0].offsetTop; - - scope.dimensions.image = image; + scope.dimensions.image.width = scope.dimensions.image.originalWidth; + scope.dimensions.image.height = scope.dimensions.image.originalHeight; //unscaled editor size - //var viewPortW = $viewport.width(); - //var viewPortH = $viewport.height(); - var _viewPortW = parseInt(scope.width, 10); - var _viewPortH = parseInt(scope.height, 10); + var _cropW = parseInt(scope.width, 10); + var _cropH = parseInt(scope.height, 10); - //if we set a constraint we will scale it down if needed - if (scope.maxSize) { - var ratioCalculation = cropperHelper.scaleToMaxSize( - _viewPortW, - _viewPortH, - scope.maxSize); + var ratioCalculation = cropperHelper.scaleToMaxSize( + _cropW, + _cropH, + scope.dimensions.viewport.width - 40, + scope.dimensions.viewport.height - 40); - //so if we have a max size, override the thumb sizes - _viewPortW = ratioCalculation.width; - _viewPortH = ratioCalculation.height; - } + //so if we have a max size, override the thumb sizes + _cropW = ratioCalculation.width; + _cropH = ratioCalculation.height; - scope.dimensions.viewport.width = _viewPortW + 2 * scope.dimensions.margin; - scope.dimensions.viewport.height = _viewPortH + 2 * scope.dimensions.margin; - scope.dimensions.cropper.width = _viewPortW; // scope.dimensions.viewport.width - 2 * scope.dimensions.margin; - scope.dimensions.cropper.height = _viewPortH; // scope.dimensions.viewport.height - 2 * scope.dimensions.margin; + // set margins: + scope.dimensions.margin.left = (scope.dimensions.viewport.width - _cropW) * 0.5; + scope.dimensions.margin.top = (scope.dimensions.viewport.height - _cropH) * 0.5; + + scope.dimensions.cropper.width = _cropW; + scope.dimensions.cropper.height = _cropH; + updateStyles(); }; //resize to a given ratio var resizeImageToScale = function (ratio) { - //do stuff - var size = cropperHelper.calculateSizeToRatio(scope.dimensions.image.originalWidth, scope.dimensions.image.originalHeight, ratio); - scope.dimensions.image.width = size.width; - scope.dimensions.image.height = size.height; + + var prevWidth = scope.dimensions.image.width; + var prevHeight = scope.dimensions.image.height; + + scope.dimensions.image.width = scope.dimensions.image.originalWidth * ratio; + scope.dimensions.image.height = scope.dimensions.image.originalHeight * ratio; + + var difW = (scope.dimensions.image.width - prevWidth); + var difH = (scope.dimensions.image.height - prevHeight); + + // normalized focus point: + var focusNormX = (-scope.dimensions.image.left + scope.dimensions.cropper.width*.5) / prevWidth; + var focusNormY = (-scope.dimensions.image.top + scope.dimensions.cropper.height*.5) / prevHeight; + + scope.dimensions.image.left = scope.dimensions.image.left - difW * focusNormX; + scope.dimensions.image.top = scope.dimensions.image.top - difH * focusNormY; setConstraints(); validatePosition(scope.dimensions.image.left, scope.dimensions.image.top); @@ -163,10 +208,10 @@ angular.module("umbraco.directives") //resize the image to a predefined crop coordinate var resizeImageToCrop = function () { scope.dimensions.image = cropperHelper.convertToStyle( - scope.crop, + runtimeCrop, { width: scope.dimensions.image.originalWidth, height: scope.dimensions.image.originalHeight }, scope.dimensions.cropper, - scope.dimensions.margin); + 0); var ratioCalculation = cropperHelper.calculateAspectRatioFit( scope.dimensions.image.originalWidth, @@ -178,25 +223,19 @@ angular.module("umbraco.directives") scope.dimensions.scale.current = scope.dimensions.image.ratio; // Update min and max based on original width/height + // Here we update the slider to use the scala of the current setup, i dont know why its made in this way but this is how it is. scope.dimensions.scale.min = ratioCalculation.ratio; - scope.dimensions.scale.max = 2; + // TODO: Investigate wether we can limit users to not scale bigger than the amount of pixels in the source: + //scope.dimensions.scale.max = ratioCalculation.ratio * Math.min(MAX_SCALE, scope.dimensions.image.originalWidth/scope.dimensions.cropper.width); + scope.dimensions.scale.max = ratioCalculation.ratio * MAX_SCALE; + + updateSlider(); }; var validatePosition = function (left, top) { - if (left > constraints.left.max) { - left = constraints.left.max; - } - if (left <= constraints.left.min) { - left = constraints.left.min; - } - - if (top > constraints.top.max) { - top = constraints.top.max; - } - if (top <= constraints.top.min) { - top = constraints.top.min; - } + left = Math.min(Math.max(left, constraints.left.min), constraints.left.max); + top = Math.min(Math.max(top, constraints.top.min), constraints.top.max); if (scope.dimensions.image.left !== left) { scope.dimensions.image.left = left; @@ -209,36 +248,54 @@ angular.module("umbraco.directives") //sets scope.crop to the recalculated % based crop - var calculateCropBox = function () { - scope.crop = cropperHelper.pixelsToCoordinates(scope.dimensions.image, scope.dimensions.cropper.width, scope.dimensions.cropper.height, scope.dimensions.margin); + function calculateCropBox() { + runtimeCrop = cropperHelper.pixelsToCoordinates(scope.dimensions.image, scope.dimensions.cropper.width, scope.dimensions.cropper.height, 0); }; + function saveCropBox() { + scope.crop = Utilities.copy(runtimeCrop); + } //Drag and drop positioning, using jquery ui draggable - var onStartDragPosition, top, left; + //var onStartDragPosition, top, left; + var dragStartPosition = {}; $overlay.draggable({ + start: function (event, ui) { + dragStartPosition.left = scope.dimensions.image.left; + dragStartPosition.top = scope.dimensions.image.top; + }, drag: function (event, ui) { scope.$apply(function () { - validatePosition(ui.position.left, ui.position.top); + validatePosition(dragStartPosition.left + (ui.position.left - ui.originalPosition.left), dragStartPosition.top + (ui.position.top - ui.originalPosition.top)); }); }, stop: function (event, ui) { scope.$apply(function () { //make sure that every validates one more time... - validatePosition(ui.position.left, ui.position.top); + validatePosition(dragStartPosition.left + (ui.position.left - ui.originalPosition.left), dragStartPosition.top + (ui.position.top - ui.originalPosition.top)); calculateCropBox(); - scope.dimensions.image.rnd = Math.random(); + saveCropBox(); }); } }); - var init = function (image) { - scope.loaded = false; + var runtimeCrop; + var init = function () { - //set dimensions on image, viewport, cropper etc - setDimensions(image); + // store original size: + scope.dimensions.image.originalWidth = $image.width(); + scope.dimensions.image.originalHeight = $image.height(); + // runtime Crop, should not be saved until we have interactions: + runtimeCrop = Utilities.copy(scope.crop); + + onViewportSizeChanged(); + + scope.loaded = true; + }; + + function setCrop() { //create a default crop if we haven't got one already var createDefaultCrop = !scope.crop; if (createDefaultCrop) { @@ -275,41 +332,67 @@ angular.module("umbraco.directives") resizeImageToCrop(); } } + } - //sets constaints for the cropper + + function onViewportSizeChanged() { + scope.dimensions.viewport.width = $viewport.width(); + scope.dimensions.viewport.height = $viewport.height(); + + setDimensions(); + setCrop(); setConstraints(); - scope.loaded = true; - }; + } // Watchers - scope.$watchCollection('[width, height]', function (newValues, oldValues) { + unsubscribe.push(scope.$watchCollection('[width, height, alias, forceUpdate]', function (newValues, oldValues) { // We have to reinit the whole thing if // one of the external params changes if (newValues !== oldValues) { - setDimensions($image); + runtimeCrop = Utilities.copy(scope.crop); + setDimensions(); + setCrop(); setConstraints(); } - }); + })); - var throttledResizing = _.throttle(function () { + var throttledScale = _.throttle(() => scope.$evalAsync(() => { resizeImageToScale(scope.dimensions.scale.current); calculateCropBox(); - }, 15); + saveCropBox(); + }), 16); // Happens when we change the scale - scope.$watch("dimensions.scale.current", function (newValue, oldValue) { + unsubscribe.push(scope.$watch("dimensions.scale.current", function (newValue, oldValue) { if (scope.loaded) { - throttledResizing(); + throttledScale(); } - }); + })); + // Init + + //if we have a max-size we will use it, to keep this backwards compatible. + // I dont see this max size begin usefull, as we should aim for responsive UI. + if (scope.maxSize) { + element.css("max-width", parseInt(scope.maxSize, 10) + "px"); + element.css("max-height", parseInt(scope.maxSize, 10) + "px"); + } + $image.on("load", function () { $timeout(function () { - init($image); + init(); }); }); + + windowResizeListener.register(onViewportSizeChanged); + + scope.$on('$destroy', function () { + $image.prop("src", ""); + windowResizeListener.unregister(onViewportSizeChanged); + unsubscribe.forEach(u => u()); + }) } }; }); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagegravity.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagegravity.directive.js index fd9a236f87..277848811b 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagegravity.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagegravity.directive.js @@ -13,8 +13,8 @@ top: 0 }; - var htmlImage = null; //DOM element reference - var htmlOverlay = null; //DOM element reference + var imageElement = null; //DOM element reference + var focalPointElement = null; //DOM element reference var draggable = null; vm.loaded = false; @@ -22,33 +22,33 @@ vm.$onChanges = onChanges; vm.$postLink = postLink; vm.$onDestroy = onDestroy; - vm.style = style; + vm.style = {}; + vm.overlayStyle = {}; vm.setFocalPoint = setFocalPoint; /** Sets the css style for the Dot */ - function style() { - - if (vm.dimensions.width <= 0 || vm.dimensions.height <= 0) { - //this initializes the dimensions since when the image element first loads - //there will be zero dimensions - setDimensions(); - } - - return { + function updateStyle() { + vm.style = { 'top': vm.dimensions.top + 'px', 'left': vm.dimensions.left + 'px' }; + vm.overlayStyle = { + 'width': vm.dimensions.width + 'px', + 'height': vm.dimensions.height + 'px' + }; + }; - function setFocalPoint (event) { + function setFocalPoint(event) { $scope.$emit("imageFocalPointStart"); - var offsetX = event.offsetX - 10; - var offsetY = event.offsetY - 10; + // We do this to get the right position, no matter the focalPoint was clicked. + var viewportPosition = imageElement[0].getBoundingClientRect(); + var offsetX = event.clientX - viewportPosition.left; + var offsetY = event.clientY - viewportPosition.top; calculateGravity(offsetX, offsetY); - - lazyEndEvent(); + $scope.$emit("imageFocalPointStop"); }; /** Initializes the component */ @@ -61,33 +61,30 @@ /** Called when the component has linked everything and the DOM is available */ function postLink() { //elements - htmlImage = $element.find("img"); - htmlOverlay = $element.find(".overlay"); + imageElement = $element.find("img"); + focalPointElement = $element.find(".focalPoint"); //Drag and drop positioning, using jquery ui draggable - draggable = htmlOverlay.draggable({ + draggable = focalPointElement.draggable({ containment: "parent", start: function () { - $scope.$apply(function () { - $scope.$emit("imageFocalPointStart"); - }); + $scope.$emit("imageFocalPointStart"); }, - stop: function () { - $scope.$apply(function () { - var offsetX = htmlOverlay[0].offsetLeft; - var offsetY = htmlOverlay[0].offsetTop; - calculateGravity(offsetX, offsetY); - }); + stop: function (event, ui) { + + var offsetX = ui.position.left; + var offsetY = ui.position.top; + + $scope.$evalAsync(calculateGravity(offsetX, offsetY)); + + $scope.$emit("imageFocalPointStop"); - lazyEndEvent(); } }); - $(window).on('resize.umbImageGravity', function () { - $scope.$apply(function () { - resized(); - }); - }); + window.addEventListener('resize.umbImageGravity', onResizeHandler); + window.addEventListener('resize', onResizeHandler); + //if any ancestor directive emits this event, we need to resize $scope.$on("editors.content.splitViewChanged", function () { @@ -95,12 +92,12 @@ }); //listen for the image DOM element loading - htmlImage.on("load", function () { + imageElement.on("load", function () { $timeout(function () { vm.isCroppable = true; vm.hasDimensions = true; - + if (vm.src) { if (vm.src.endsWith(".svg")) { vm.isCroppable = false; @@ -117,6 +114,8 @@ } setDimensions(); + updateStyle(); + vm.loaded = true; if (vm.onImageLoaded) { vm.onImageLoaded({ @@ -129,16 +128,19 @@ } function onDestroy() { - $(window).off('resize.umbImageGravity'); - if (htmlOverlay) { + window.removeEventListener('resize.umbImageGravity', onResizeHandler); + window.removeEventListener('resize', onResizeHandler); + /* + if (focalPointElement) { // TODO: This should be destroyed but this will throw an exception: // "cannot call methods on draggable prior to initialization; attempted to call method 'destroy'" // I've tried lots of things and cannot get this to work, we weren't destroying before so hopefully // there's no mem leaks? - //htmlOverlay.draggable("destroy"); + focalPointElement.draggable("destroy"); } - if (htmlImage) { - htmlImage.off("load"); + */ + if (imageElement) { + imageElement.off("load"); } } @@ -146,14 +148,21 @@ function resized() { $timeout(function () { setDimensions(); + updateStyle(); }); + /* // Make sure we can find the offset values for the overlay(dot) before calculating // fixes issue with resize event when printing the page (ex. hitting ctrl+p inside the rte) - if (htmlOverlay.is(':visible')) { - var offsetX = htmlOverlay[0].offsetLeft; - var offsetY = htmlOverlay[0].offsetTop; + if (focalPointElement.is(':visible')) { + var offsetX = focalPointElement[0].offsetLeft; + var offsetY = focalPointElement[0].offsetTop; calculateGravity(offsetX, offsetY); } + */ + } + + function onResizeHandler() { + $scope.$evalAsync(resized); } /** Watches the one way binding changes */ @@ -163,17 +172,18 @@ && !Utilities.equals(changes.center.currentValue, changes.center.previousValue)) { //when center changes update the dimensions setDimensions(); + updateStyle(); } } /** Sets the width/height/left/top dimentions based on the image size and the "center" value */ function setDimensions() { - if (vm.isCroppable && htmlImage && vm.center) { - vm.dimensions.width = htmlImage.width(); - vm.dimensions.height = htmlImage.height(); - vm.dimensions.left = vm.center.left * vm.dimensions.width - 10; - vm.dimensions.top = vm.center.top * vm.dimensions.height - 10; + if (vm.isCroppable && imageElement && vm.center) { + vm.dimensions.width = imageElement.width(); + vm.dimensions.height = imageElement.height(); + vm.dimensions.left = vm.center.left * vm.dimensions.width; + vm.dimensions.top = vm.center.top * vm.dimensions.height; } return vm.dimensions.width; @@ -185,31 +195,22 @@ * @param {any} offsetY */ function calculateGravity(offsetX, offsetY) { - vm.onValueChanged({ - left: (offsetX + 10) / vm.dimensions.width, - top: (offsetY + 10) / vm.dimensions.height + left: Math.min(Math.max(offsetX, 0), vm.dimensions.width) / vm.dimensions.width, + top: Math.min(Math.max(offsetY, 0), vm.dimensions.height) / vm.dimensions.height }); - - //vm.center.left = (offsetX + 10) / scope.dimensions.width; - //vm.center.top = (offsetY + 10) / scope.dimensions.height; }; - var lazyEndEvent = _.debounce(function () { - $scope.$apply(function () { - $scope.$emit("imageFocalPointStop"); - }); - }, 2000); - } var umbImageGravityComponent = { templateUrl: 'views/components/imaging/umb-image-gravity.html', bindings: { src: "<", - center: "<", + center: "<", onImageLoaded: "&?", - onValueChanged: "&" + onValueChanged: "&", + disableFocalPoint: " 0) { setFlexValues(scope.items); } @@ -188,7 +188,7 @@ Use this directive to generate a thumbnail grid of media items. } } } - + /** * Returns wether a item should be selectable or not. */ @@ -203,9 +203,9 @@ Use this directive to generate a thumbnail grid of media items. } else { return scope.onlyFolders !== "true"; } - + return false; - + } function setOriginalSize(item, maxHeight) { @@ -255,7 +255,7 @@ Use this directive to generate a thumbnail grid of media items. } } - + function setFlexValues(mediaItems) { var flexSortArray = mediaItems; @@ -292,8 +292,11 @@ Use this directive to generate a thumbnail grid of media items. mediaItem.flexStyle = flexStyle; } } - + scope.clickItem = function(item, $event, $index) { + if (item.isFolder === true && item.filtered) { + scope.clickItemName(item, $event, $index); + } if (scope.onClick) { scope.onClick(item, $event, $index); $event.stopPropagation(); @@ -312,7 +315,7 @@ Use this directive to generate a thumbnail grid of media items. scope.onDetailsHover(item, $event, hover); } }; - + var unbindItemsWatcher = scope.$watch('items', function(newValue, oldValue) { if (Utilities.isArray(newValue)) { activate(); @@ -333,7 +336,7 @@ Use this directive to generate a thumbnail grid of media items. //change sort scope.setSort = function (col) { if (scope.sortColumn === col) { - scope.sortReverse = !scope.sortReverse; + scope.sortReverse = !scope.sortReverse; } else { scope.sortColumn = col; @@ -345,9 +348,9 @@ Use this directive to generate a thumbnail grid of media items. } } scope.sortDirection = scope.sortReverse ? "desc" : "asc"; - + } - // sort function + // sort function scope.sortBy = function (item) { if (scope.sortColumn === "updateDate") { return [-item['isFolder'],item['updateDate']]; diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbpropertyfileupload.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbpropertyfileupload.directive.js index db1e38adc6..5492fee1a0 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbpropertyfileupload.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/upload/umbpropertyfileupload.directive.js @@ -85,7 +85,7 @@ /** Called when the component has linked all elements, this is when the form controller is available */ function postLink() { - + } function initialize() { @@ -186,7 +186,7 @@ }); } } - + } } @@ -325,7 +325,8 @@ */ onFilesChanged: "&", onInit: "&", - required: "=" + required: "=", + acceptFileExt: ""; + return { restrict: "E", scope: { - rebuild: "=" + rebuild: "=", + acceptFileExt: "
    ", - link: function (scope, el, attrs) { + template: "
    "+innerTemplate+"
    ", + link: function (scope, el) { scope.$watch("rebuild", function (newVal, oldVal) { if (newVal && newVal !== oldVal) { //recompile it! - el.html(""); + el.html(innerTemplate); $compile(el.contents())(scope); } }); @@ -30,4 +35,4 @@ function umbSingleFileUpload($compile) { }; } -angular.module('umbraco.directives').directive("umbSingleFileUpload", umbSingleFileUpload); \ No newline at end of file +angular.module('umbraco.directives').directive("umbSingleFileUpload", umbSingleFileUpload); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valservermatch.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valservermatch.directive.js index 5f8600c8c0..b07ab55436 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valservermatch.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valservermatch.directive.js @@ -2,8 +2,8 @@ * @ngdoc directive * @name umbraco.directives.directive:valServerMatch * @restrict A - * @description A custom validator applied to a form/ng-form within an umbProperty that validates server side validation data - * contained within the serverValidationManager. The data can be matched on "exact", "prefix", "suffix" or "contains" matches against + * @description A custom validator applied to a form/ng-form within an umbProperty that validates server side validation data + * contained within the serverValidationManager. The data can be matched on "exact", "prefix", "suffix" or "contains" matches against * a property validation key. The attribute value can be in multiple value types: * - STRING = The property validation key to have an exact match on. If matched, then the form will have a valServerMatch validator applied. * - OBJECT = A dictionary where the key is the match type: "contains", "prefix", "suffix" and the value is either: diff --git a/src/Umbraco.Web.UI.Client/src/common/services/clipboard.service.js b/src/Umbraco.Web.UI.Client/src/common/services/clipboard.service.js index 83fd3d08c2..901e5fa93c 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/clipboard.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/clipboard.service.js @@ -17,6 +17,7 @@ function clipboardService($window, notificationsService, eventsService, localSto TYPES.ELEMENT_TYPE = "elementType"; TYPES.BLOCK = "block"; TYPES.RAW = "raw"; + TYPES.MEDIA = "media"; var clearPropertyResolvers = {}; var pastePropertyResolvers = {}; @@ -70,6 +71,9 @@ function clipboardService($window, notificationsService, eventsService, localSto propMethod(data[p], TYPES.RAW); } } + clipboardTypeResolvers[TYPES.MEDIA] = function(data, propMethod) { + // no resolving needed for this type currently. + } var STORAGE_KEY = "umbClipboardService"; @@ -147,6 +151,8 @@ function clipboardService($window, notificationsService, eventsService, localSto return entry.type === type && ( + allowedAliases === null + || (entry.alias && allowedAliases.filter(allowedAlias => allowedAlias === entry.alias).length > 0) || (entry.aliases && entry.aliases.filter(entryAlias => allowedAliases.filter(allowedAlias => allowedAlias === entryAlias).length > 0).length === entry.aliases.length) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/cropperhelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/cropperhelper.service.js index 256a1461db..1f860f237c 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/cropperhelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/cropperhelper.service.js @@ -44,24 +44,23 @@ function cropperHelper(umbRequestHelper, $http) { return { width:srcWidth*ratio, height:srcHeight*ratio, ratio: ratio}; }, - scaleToMaxSize : function(srcWidth, srcHeight, maxSize) { - - var retVal = {height: srcHeight, width: srcWidth}; + scaleToMaxSize : function(srcWidth, srcHeight, maxWidth, maxHeight) { - if(srcWidth > maxSize ||srcHeight > maxSize){ - var ratio = [maxSize / srcWidth, maxSize / srcHeight ]; - ratio = Math.min(ratio[0], ratio[1]); - - retVal.height = srcHeight * ratio; - retVal.width = srcWidth * ratio; - } - - return retVal; + // fallback to maxHeight: + maxHeight = maxHeight || maxWidth; + + // get smallest ratio, if ratio exceeds 1 we will not scale(hence we parse 1 as the maximum allowed ratio) + var ratio = Math.min(maxWidth / srcWidth, maxHeight / srcHeight, 1); + + return { + width: srcWidth * ratio, + height:srcHeight * ratio + }; }, //returns a ng-style object with top,left,width,height pixel measurements //expects {left,right,top,bottom} - {width,height}, {width,height}, int - //offset is just to push the image position a number of pixels from top,left + //offset is just to push the image position a number of pixels from top,left convertToStyle : function(coordinates, originalSize, viewPort, offset){ var coordinates_px = service.coordinatesToPixels(coordinates, originalSize, offset); @@ -85,14 +84,14 @@ function cropperHelper(umbRequestHelper, $http) { return style; }, - + coordinatesToPixels : function(coordinates, originalSize, offset){ var coordinates_px = { x1: Math.floor(coordinates.x1 * originalSize.width), y1: Math.floor(coordinates.y1 * originalSize.height), x2: Math.floor(coordinates.x2 * originalSize.width), - y2: Math.floor(coordinates.y2 * originalSize.height) + y2: Math.floor(coordinates.y2 * originalSize.height) }; return coordinates_px; @@ -106,25 +105,18 @@ function cropperHelper(umbRequestHelper, $http) { var x2_px = image.width - (x1_px + width); var y2_px = image.height - (y1_px + height); - //crop coordinates in % var crop = {}; - crop.x1 = x1_px / image.width; - crop.y1 = y1_px / image.height; - crop.x2 = x2_px / image.width; - crop.y2 = y2_px / image.height; - - for(var coord in crop){ - if(crop[coord] < 0){ - crop[coord] = 0; - } - } + crop.x1 = Math.max(x1_px / image.width, 0); + crop.y1 = Math.max(y1_px / image.height, 0); + crop.x2 = Math.max(x2_px / image.width, 0); + crop.y2 = Math.max(y2_px / image.height, 0); return crop; }, alignToCoordinates : function(image, center, viewport){ - + var min_left = (image.width) - (viewport.width); var min_top = (image.height) - (viewport.height); diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index 359c3dd427..6f95608d7a 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -215,6 +215,11 @@ @import "../views/propertyeditors/blocklist/blocklistentryeditors/unsupportedblock/unsupportedblock.editor.less"; @import "../views/propertyeditors/blocklist/blocklistentryeditors/labelblock/labelblock.editor.less"; @import "../views/propertyeditors/blocklist/blocklistentryeditors/inlineblock/inlineblock.editor.less"; +@import "../views/components/mediacard/umb-media-card-grid.less"; +@import "../views/components/mediacard/umb-media-card.less"; +@import "../views/propertyeditors/mediapicker3/prevalue/mediapicker3.crops.less"; +@import "../views/propertyeditors/mediapicker3/umb-media-picker3-property-editor.less"; +@import "../views/common/infiniteeditors/mediaentryeditor/mediaentryeditor.less"; // Utilities diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-file-icon.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-file-icon.less index febee80a97..ffe87277e6 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-file-icon.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-file-icon.less @@ -20,7 +20,7 @@ > span { position: absolute; - color: @white; + color: @ui-active-type; background: @ui-active; padding: 1px 3px; font-size: 10px; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-media-grid.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-media-grid.less index 5f79d65de1..71be01e6ff 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-media-grid.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-media-grid.less @@ -34,21 +34,6 @@ } -.umb-media-grid__item.-unselectable { - &::before { - content: ""; - position: absolute; - z-index: 1; - top: 0; - left: 0; - right: 0; - bottom: 0; - border-radius: @baseBorderRadius; - background-color: rgba(230, 230, 230, .8); - pointer-events: none; - } -} - .umb-media-grid__item.-selectable, .umb-media-grid__item.-folder {// If folders isnt selectable, they opens if clicked, therefor... cursor: pointer; @@ -59,21 +44,12 @@ } .umb-media-grid__item.-folder { - &.-selectable { .media-grid-item-edit:hover .umb-media-grid__item-name, .media-grid-item-edit:focus .umb-media-grid__item-name { text-decoration: underline; } } - - &.-unselectable { - &:hover, &:focus { - .umb-media-grid__item-name { - text-decoration: underline; - } - } - } } @@ -85,8 +61,7 @@ } .umb-media-grid__item.-selected, .umb-media-grid__item.-selectable:hover { - &::before { - content: ""; + .umb-media-grid__item-select { position: absolute; z-index:2; top: -2px; @@ -100,15 +75,21 @@ } } .umb-media-grid__item.-selectable:hover { - &::before { + .umb-media-grid__item-select { opacity: .33; } } .umb-media-grid__item.-selected:hover { - &::before { + .umb-media-grid__item-select { opacity: .75; } } +.umb-media-grid__item.-filtered:not(.-folder) { + cursor:not-allowed; + * { + pointer-events: none; + } +} .umb-media-grid__item-file-icon { transform: translate(-50%,-50%); @@ -189,14 +170,25 @@ } } -.umb-media-grid__item-name { - cursor: pointer; +.umb-media-grid__item-overlay { + cursor: pointer; + + &:hover .umb-media-grid__item-name{ + text-decoration: underline; + } +} + +.umb-media-grid__item-overlay:not(.-selected) { + &:hover + .umb-media-grid__item-select { + display: none; + } } .umb-media-grid__item-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-range-slider.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-range-slider.less index 6ae92ffa4e..cc5c17ba70 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-range-slider.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-range-slider.less @@ -2,19 +2,35 @@ .umb-range-slider.noUi-target { background: linear-gradient(to bottom, @grayLighter 0%, @grayLighter 100%); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + box-shadow: none; border-radius: 20px; - height: 10px; - border: none; + height: 8px; + border: 1px solid @inputBorder; + &:focus, &:focus-within { + border-color: @inputBorderFocus; + } +} +.umb-range-slider .noUi-connects { + cursor: pointer; + height: 20px; + top: -6px; +} +.umb-range-slider .noUi-tooltip { + padding: 2px 6px; } - .umb-range-slider .noUi-handle { + outline: none; + cursor: grab; border-radius: 100px; border: none; box-shadow: none; width: 20px !important; height: 20px !important; - background-color: @blueMid; + right: -10px !important; // half the handle width + background-color: @blueExtraDark; +} +.umb-range-slider .noUi-horizontal .noUi-handle { + top: -7px; } .umb-range-slider .noUi-handle::before { @@ -25,10 +41,6 @@ display: none; } -.umb-range-slider .noUi-handle { - right: -10px !important; // half the handle width -} - .umb-range-slider .noUi-marker-large.noUi-marker-horizontal { height: 10px; } diff --git a/src/Umbraco.Web.UI.Client/src/less/mixins.less b/src/Umbraco.Web.UI.Client/src/less/mixins.less index 9739a90dae..b046ca69d9 100644 --- a/src/Umbraco.Web.UI.Client/src/less/mixins.less +++ b/src/Umbraco.Web.UI.Client/src/less/mixins.less @@ -405,7 +405,7 @@ } } -.checkeredBackground(@backgroundColor: @gray-9, @fillColor: @black, @fillOpacity: 0.25) { +.checkeredBackground(@backgroundColor: @white, @fillColor: @black, @fillOpacity: 0.1) { background-image: url('data:image/svg+xml;charset=utf-8,\ \ \ diff --git a/src/Umbraco.Web.UI.Client/src/less/property-editors.less b/src/Umbraco.Web.UI.Client/src/less/property-editors.less index f5e652aa3d..328ba2229b 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -463,9 +463,16 @@ .umb-cropper{ position: relative; + width: 100%; } -.umb-cropper img, .umb-cropper-gravity img{ +.umb-cropper .crop-container { + position: relative; + width: 100%; + padding-bottom: 9 / 16 * 100%; +} + +.umb-cropper img { position: relative; max-width: 100%; height: auto; @@ -477,75 +484,72 @@ max-width: none; } - .umb-cropper .overlay, .umb-cropper-gravity .overlay { - top: 0; - left: 0; + .umb-cropper .overlay { + position: absolute; + top: 0 !important; + bottom: 0; + left: 0 !important; + right: 0; cursor: move; z-index: @zindexCropperOverlay; - position: absolute; + border: 1px solid @inputBorder; + outline: none; + + &:focus { + border-color: @inputBorderFocus; + } } -.umb-cropper .viewport{ +.umb-cropper .viewport { + position: absolute; overflow: hidden; - position: relative; - margin: auto; - max-width: 100%; - height: auto; - } - -.umb-cropper-gravity .viewport{ - overflow: hidden; - position: relative; width: 100%; height: 100%; -} + .checkeredBackground(); + contain: strict; + > img { + position: absolute; + } + } -.umb-cropper .viewport:after { - content: ""; +.umb-cropper .viewport .__mask { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: @zindexCropperOverlay - 1; - opacity: .75; - box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2); + box-shadow: 0 0 0 2000px rgba(255, 255, 255, .8); +} +.umb-cropper .viewport .__mask-info { + position: absolute; + bottom: -20px; + height: 20px; + right: 0; + z-index: @zindexCropperOverlay - 1; + font-size: 12px; + opacity: 0.7; + padding: 0px 6px; } -.umb-cropper-gravity .overlay{ - width: 14px; - height: 14px; - text-align: center; - border-radius: 20px; - background: @pinkLight; - border: 3px solid @white; - opacity: 0.8; -} - -.umb-cropper-gravity .overlay i { - font-size: 26px; - line-height: 26px; - opacity: 0.8 !important; -} - -.umb-cropper .crop-container { - text-align: center; +.umb-cropper .crop-controls-wrapper { + display: flex; + height: 50px; + align-items: center; + background-color: #fff; + .btn:last-of-type { + margin-right: 10px; + } } .umb-cropper .crop-slider-wrapper { - padding: 10px; - border-top: 1px solid @gray-10; - margin-top: 10px; + flex: auto; display: flex; align-items: center; justify-content: center; flex-wrap: wrap; - @media (min-width: 769px) { - padding: 10px 50px 10px 50px; - } - i { color: @gray-3; flex: 0 0 25px; @@ -558,11 +562,20 @@ } .crop-slider { - padding: 50px 15px 40px 15px; - width: 66.6%; + width: calc(100% - 100px); } } +.umb-cropper .crop-controls-wrapper__icon-left { + margin-right: 10px; + +} +.umb-cropper .crop-controls-wrapper__icon-right { + margin-left: 10px; + font-size: 22px; +} + +/* .umb-cropper-gravity .viewport, .umb-cropper-gravity, .umb-cropper-imageholder { display: inline-block; max-width: 100%; @@ -572,30 +585,51 @@ float: left; } + .umb-cropper-imageholder umb-image-gravity { + display:block; + } + */ + + .umb-crop-thumbnail-container { + img { + max-width: unset; + } + } + .cropList { display: inline-block; position: relative; vertical-align: top; + flex:0; } - .gravity-container { - border: 1px solid @gray-8; + .umb-cropper-gravity .gravity-container { + border: 1px solid @inputBorder; + box-sizing: border-box; line-height: 0; + width: 100%; + height: 100%; + overflow: hidden; + .checkeredBackground(); + contain: content; + + &:focus, &:focus-within { + border-color: @inputBorderFocus; + } .viewport { - max-width: 600px; - .checkeredBackground(); + position: relative; + width: 100%; + height: 100%; + + display: flex; + justify-content: center; + align-items: center; img { display: block; - margin-left: auto; - margin-right: auto; - } - - img { - display: block; - margin-left: auto; - margin-right: auto; + max-width: 100%; + max-height: 100%; } &:hover { @@ -604,6 +638,62 @@ } } + + .umb-cropper-gravity img { + position: relative; + max-width: 100%; + height: auto; + top: 0; + left: 0; + } + + .umb-cropper-gravity .overlayViewport { + position: absolute; + top:0; + bottom:0; + left:0; + right:0; + contain: strict; + + display: flex; + justify-content: center; + align-items: center; + } + .umb-cropper-gravity .overlay { + position: relative; + display: block; + max-width: 100%; + max-height: 100%; + cursor: crosshair; + } + .umb-cropper-gravity .overlay .focalPoint { + position: absolute; + top: 0; + left: 0; + cursor: move; + z-index: @zindexCropperOverlay; + + width: 14px; + height: 14px; + // this element should have no width or height as its preventing the jQuery draggable-plugin to go all the way to the sides: + margin-left: -10px; + margin-top: -10px; + margin-right: -10px; + margin-bottom: -10px; + + text-align: center; + border-radius: 20px; + background: @pinkLight; + border: 3px solid @white; + opacity: 0.8; + } + + .umb-cropper-gravity .overlay .focalPoint i { + font-size: 26px; + line-height: 26px; + opacity: 0.8 !important; + } + .imagecropper { display: flex; align-items: flex-start; @@ -611,24 +701,13 @@ @media (max-width: 768px) { flex-direction: column; - float: left; - max-width: 100%; - } - - .viewport img { - .checkeredBackground(); } + } .imagecropper .umb-cropper__container { position: relative; - margin-bottom: 10px; - max-width: 100%; - border: 1px solid @gray-10; - - @media (min-width: 769px) { - width: 600px; - } + width: 100%; } .imagecropper .umb-cropper__container .button-drawer { diff --git a/src/Umbraco.Web.UI.Client/src/main.controller.js b/src/Umbraco.Web.UI.Client/src/main.controller.js index d21331f106..7d9431fcae 100644 --- a/src/Umbraco.Web.UI.Client/src/main.controller.js +++ b/src/Umbraco.Web.UI.Client/src/main.controller.js @@ -1,18 +1,18 @@ -/** +/** * @ngdoc controller - * @name Umbraco.MainController + * @name Umbraco.MainController * @function - * - * @description + * + * @description * The main application controller - * + * */ -function MainController($scope, $location, appState, treeService, notificationsService, - userService, historyService, updateChecker, navigationService, eventsService, +function MainController($scope, $location, appState, treeService, notificationsService, + userService, historyService, updateChecker, navigationService, eventsService, tmhDynamicLocale, localStorageService, editorService, overlayService, assetsService, tinyMceAssets) { - + //the null is important because we do an explicit bool check on this in the view - $scope.authenticated = null; + $scope.authenticated = null; $scope.touchDevice = appState.getGlobalState("touchDevice"); $scope.infiniteMode = false; $scope.overlay = {}; @@ -27,14 +27,14 @@ function MainController($scope, $location, appState, treeService, notificationsS assetsService.loadJs(tinyJsAsset, $scope); }); - // There are a number of ways to detect when a focus state should be shown when using the tab key and this seems to be the simplest solution. + // There are a number of ways to detect when a focus state should be shown when using the tab key and this seems to be the simplest solution. // For more information about this approach, see https://hackernoon.com/removing-that-ugly-focus-ring-and-keeping-it-too-6c8727fefcd2 function handleFirstTab(evt) { if (evt.keyCode === 9) { enableTabbingActive(); } } - + function enableTabbingActive() { $scope.tabbingActive = true; $scope.$digest(); @@ -185,7 +185,7 @@ function MainController($scope, $location, appState, treeService, notificationsS evts.push(eventsService.on("appState.overlay", function (name, args) { $scope.overlay = args; })); - + // events for tours evts.push(eventsService.on("appState.tour.start", function (name, args) { $scope.tour = args; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediaentryeditor/mediaentryeditor.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediaentryeditor/mediaentryeditor.controller.js new file mode 100644 index 0000000000..6c8a038536 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediaentryeditor/mediaentryeditor.controller.js @@ -0,0 +1,183 @@ +angular.module("umbraco") + .controller("Umbraco.Editors.MediaEntryEditorController", + function ($scope, localizationService, entityResource, editorService, overlayService, eventsService, mediaHelper) { + + var unsubscribe = []; + var vm = this; + + vm.loading = true; + vm.model = $scope.model; + vm.mediaEntry = vm.model.mediaEntry; + vm.currentCrop = null; + + localizationService.localizeMany([ + vm.model.createFlow ? "general_cancel" : "general_close", + vm.model.createFlow ? "general_create" : "buttons_submitChanges" + ]).then(function (data) { + vm.closeLabel = data[0]; + vm.submitLabel = data[1]; + }); + + vm.title = ""; + + function init() { + + updateMedia(); + + unsubscribe.push(eventsService.on("editors.media.saved", function(name, args) { + // if this media item uses the updated media type we want to reload the media file + if(args && args.media && args.media.key === vm.mediaEntry.mediaKey) { + updateMedia(); + } + })); + } + + function updateMedia() { + + vm.loading = true; + entityResource.getById(vm.mediaEntry.mediaKey, "Media").then(function (mediaEntity) { + vm.media = mediaEntity; + vm.imageSrc = mediaHelper.resolveFileFromEntity(mediaEntity, true); + vm.loading = false; + vm.hasDimensions = false; + vm.isCroppable = false; + + localizationService.localize("mediaPicker_editMediaEntryLabel", [vm.media.name, vm.model.documentName]).then(function (data) { + vm.title = data; + }); + }, function () { + localizationService.localize("mediaPicker_deletedItem").then(function (localized) { + vm.media = { + name: localized, + icon: "icon-picture", + trashed: true + }; + vm.loading = false; + vm.hasDimensions = false; + vm.isCroppable = false; + }); + }); + } + + vm.onImageLoaded = onImageLoaded; + function onImageLoaded(isCroppable, hasDimensions) { + vm.isCroppable = isCroppable; + vm.hasDimensions = hasDimensions; + }; + + + vm.repickMedia = repickMedia; + function repickMedia() { + vm.model.propertyEditor.changeMediaFor(vm.model.mediaEntry, onMediaReplaced); + } + + function onMediaReplaced() { + + // mark we have changes: + vm.imageCropperForm.$setDirty(); + + // un-select crop: + vm.currentCrop = null; + + // + updateMedia(); + } + + vm.openMedia = openMedia; + function openMedia() { + + var mediaEditor = { + id: vm.mediaEntry.mediaKey, + submit: function () { + editorService.close(); + }, + close: function () { + editorService.close(); + } + }; + editorService.mediaEditor(mediaEditor); + } + + + vm.focalPointChanged = function(left, top) { + //update the model focalpoint value + vm.mediaEntry.focalPoint = { + left: left, + top: top + }; + + //set form to dirty to track changes + setDirty(); + } + + + + vm.selectCrop = selectCrop; + function selectCrop(targetCrop) { + vm.currentCrop = targetCrop; + setDirty(); + // TODO: start watchin values of crop, first when changed set to dirty. + }; + + vm.deselectCrop = deselectCrop; + function deselectCrop() { + vm.currentCrop = null; + }; + + vm.resetCrop = resetCrop; + function resetCrop() { + if (vm.currentCrop) { + $scope.$evalAsync( () => { + vm.model.propertyEditor.resetCrop(vm.currentCrop); + vm.forceUpdateCrop = Math.random(); + }); + } + } + + function setDirty() { + vm.imageCropperForm.$setDirty(); + } + + + vm.submitAndClose = function () { + if (vm.model && vm.model.submit) { + vm.model.submit(vm.model); + } + } + + vm.close = function () { + if (vm.model && vm.model.close) { + if (vm.model.createFlow === true || vm.imageCropperForm.$dirty === true) { + var labels = vm.model.createFlow === true ? ["mediaPicker_confirmCancelMediaEntryCreationHeadline", "mediaPicker_confirmCancelMediaEntryCreationMessage"] : ["prompt_discardChanges", "mediaPicker_confirmCancelMediaEntryHasChanges"]; + localizationService.localizeMany(labels).then(function (localizations) { + const confirm = { + title: localizations[0], + view: "default", + content: localizations[1], + submitButtonLabelKey: "general_discard", + submitButtonStyle: "danger", + closeButtonLabelKey: "prompt_stay", + submit: function () { + overlayService.close(); + vm.model.close(vm.model); + }, + close: function () { + overlayService.close(); + } + }; + overlayService.open(confirm); + }); + } else { + vm.model.close(vm.model); + } + + } + } + + init(); + $scope.$on("$destroy", function () { + unsubscribe.forEach(x => x()); + }); + + } + ); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediaentryeditor/mediaentryeditor.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediaentryeditor/mediaentryeditor.html new file mode 100644 index 0000000000..afa3451899 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediaentryeditor/mediaentryeditor.html @@ -0,0 +1,118 @@ +
    + + + + + + + + +
    + +
    + This item is in the Recycle Bin +
    + +
    +
    + + + + +
    + +
    + +
    + + + + + +
    + +
    + + + + + + + +
    + + +
    +
    + +
    +
    + +
    + + + + + + + + + + + + + + + + +
    +
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediaentryeditor/mediaentryeditor.less b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediaentryeditor/mediaentryeditor.less new file mode 100644 index 0000000000..1de962f7e1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediaentryeditor/mediaentryeditor.less @@ -0,0 +1,122 @@ +.umb-media-entry-editor { + + .umb-cropper-imageholder { + position: relative; + width: 100%; + height: 100%; + } + .umb-cropper-gravity { + height: 100%; + } + .umb-cropper__container { + width: 100%; + height: 100%; + } + .umb-cropper { + height: 100%; + } + .umb-cropper .crop-container { + padding-bottom: 0; + height: calc(100% - 50px) + } + .umb-cropper .crop-controls-wrapper { + justify-content: center; + } + .umb-cropper .crop-slider-wrapper { + max-width: 500px; + } +} + +.umb-media-entry-editor__pane { + display: flex; + flex-flow: row-reverse; + height: 100%; + width: 100%; +} + +.umb-media-entry-editor__crops { + background-color: white; + overflow: auto; + + > button { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: relative; + text-align: center; + padding: 4px 10px 0 10px; + border-bottom: 1px solid @gray-9; + box-sizing: border-box; + height: 120px; + width: 120px; + color: @ui-active-type; + + &:hover { + color: @ui-active-type-hover; + text-decoration: none; + } + + &:active { + .box-shadow(~"inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05)"); + } + + &::before { + content: ""; + position: absolute; + width: 0px; + max-height: 50px; + height: (100% - 16px); + top: auto; + bottom: auto; + background-color: @ui-light-active-border; + left: 0; + border-radius: 0 3px 3px 0; + opacity: 0; + transition: all .2s linear; + } + + &.--is-active { + color: @ui-light-active-type; + + &::before { + opacity: 1; + width: 4px; + } + } + &.--is-defined { + + } + + > .__icon { + font-size: 24px; + display: block; + text-align: center; + margin-bottom: 7px; + } + + > .__text { + font-size: 12px; + line-height: 1em; + margin-top: 4px; + } + } +} + +.umb-media-entry-editor__imagecropper { + flex: auto; + height: 100%; +} + +.umb-media-entry-editor__imageholder { + display: block; + position: relative; + height: calc(100% - 50px); +} +.umb-media-entry-editor__imageholder-actions { + background-color: white; + height: 50px; + display: flex; + justify-content: center; +} + diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js index fec2e632c5..029dedf214 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js @@ -1,7 +1,7 @@ //used for the media picker dialog angular.module("umbraco") .controller("Umbraco.Editors.MediaPickerController", - function ($scope, $timeout, mediaResource, entityResource, userService, mediaHelper, mediaTypeHelper, eventsService, treeService, localStorageService, localizationService, editorService, umbSessionStorage, notificationsService) { + function ($scope, $timeout, mediaResource, entityResource, userService, mediaHelper, mediaTypeHelper, eventsService, treeService, localStorageService, localizationService, editorService, umbSessionStorage, notificationsService, clipboardService) { var vm = this; @@ -19,6 +19,8 @@ angular.module("umbraco") vm.enterSubmitFolder = enterSubmitFolder; vm.focalPointChanged = focalPointChanged; vm.changePagination = changePagination; + vm.onNavigationChanged = onNavigationChanged; + vm.clickClearClipboard = clickClearClipboard; vm.clickHandler = clickHandler; vm.clickItemName = clickItemName; @@ -27,7 +29,10 @@ angular.module("umbraco") vm.selectLayout = selectLayout; vm.showMediaList = false; + vm.navigation = []; + var dialogOptions = $scope.model; + vm.clipboardItems = dialogOptions.clipboardItems; $scope.disableFolderSelect = (dialogOptions.disableFolderSelect && dialogOptions.disableFolderSelect !== "0") ? true : false; $scope.disableFocalPoint = (dialogOptions.disableFocalPoint && dialogOptions.disableFocalPoint !== "0") ? true : false; @@ -100,10 +105,32 @@ angular.module("umbraco") function setTitle() { if (!$scope.model.title) { - localizationService.localize("defaultdialogs_selectMedia") + localizationService.localizeMany(["defaultdialogs_selectMedia", "mediaPicker_tabClipboard"]) .then(function (data) { - $scope.model.title = data; + $scope.model.title = data[0]; + + + vm.navigation = [{ + "alias": "empty", + "name": data[0], + "icon": "icon-umb-media", + "active": true, + "view": "" + }]; + + if(vm.clipboardItems) { + vm.navigation.push({ + "alias": "clipboard", + "name": data[1], + "icon": "icon-paste-in", + "view": "", + "disabled": vm.clipboardItems.length === 0 + }); + } + + vm.activeTab = vm.navigation[0]; }); + } } @@ -149,7 +176,7 @@ angular.module("umbraco") .then(function (node) { $scope.target = node; // Moving directly to existing node's folder - gotoFolder({ id: node.parentId }).then(function() { + gotoFolder({ id: node.parentId }).then(function () { selectMedia(node); $scope.target.url = mediaHelper.resolveFileFromEntity(node); $scope.target.thumbnail = mediaHelper.resolveFileFromEntity(node, true); @@ -169,10 +196,10 @@ angular.module("umbraco") function upload(v) { var fileSelect = $(".umb-file-dropzone .file-select"); - if (fileSelect.length === 0){ + if (fileSelect.length === 0) { localizationService.localize('media_uploadNotAllowed').then(function (message) { notificationsService.warning(message); }); } - else{ + else { fileSelect.trigger("click"); } } @@ -395,6 +422,19 @@ angular.module("umbraco") }); }; + function onNavigationChanged(tab) { + vm.activeTab.active = false; + vm.activeTab = tab; + vm.activeTab.active = true; + }; + + function clickClearClipboard() { + vm.onNavigationChanged(vm.navigation[0]); + vm.navigation[1].disabled = true; + vm.clipboardItems = []; + dialogOptions.clickClearClipboard(); + }; + var debounceSearchMedia = _.debounce(function () { $scope.$apply(function () { if (vm.searchOptions.filter) { @@ -504,13 +544,7 @@ angular.module("umbraco") var allowedTypes = dialogOptions.filter ? dialogOptions.filter.split(",") : null; for (var i = 0; i < data.length; i++) { - if (data[i].metaData.MediaPath !== null) { - data[i].thumbnail = mediaHelper.resolveFileFromEntity(data[i], true); - data[i].image = mediaHelper.resolveFileFromEntity(data[i], false); - } - if (data[i].metaData.UpdateDate !== null){ - data[i].updateDate = data[i].metaData.UpdateDate; - } + setDefaultData(data[i]); data[i].filtered = allowedTypes && allowedTypes.indexOf(data[i].metaData.ContentTypeAlias) < 0; } @@ -523,6 +557,16 @@ angular.module("umbraco") }); } + function setDefaultData(item) { + if (item.metaData.MediaPath !== null) { + item.thumbnail = mediaHelper.resolveFileFromEntity(item, true); + item.image = mediaHelper.resolveFileFromEntity(item, false); + } + if (item.metaData.UpdateDate !== null) { + item.updateDate = item.metaData.UpdateDate; + } + } + function preSelectMedia() { for (var folderIndex = 0; folderIndex < $scope.images.length; folderIndex++) { var folderImage = $scope.images[folderIndex]; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html index df0c8e3cef..d1f0699b13 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html @@ -3,13 +3,15 @@ - +
    @@ -19,21 +21,20 @@
    @@ -49,20 +50,20 @@
    -
    +
    + layouts="vm.layout.layouts" + active-layout="vm.layout.activeLayout" + on-layout-select="vm.selectLayout(layout)">
    + layouts="vm.layout.layouts" + active-layout="vm.layout.activeLayout" + on-layout-select="vm.selectLayout(layout)">
    @@ -86,31 +87,29 @@ + class="umb-breadcrumbs__add-ancestor" + ng-show="model.showFolderInput" + ng-model="model.newFolderName" + ng-keydown="enterSubmitFolder($event)" + ng-blur="vm.submitFolder()" + focus-when="{{model.showFolderInput}}" />
  • - + - - +
    - @@ -145,11 +142,30 @@
    - - - + + + +
    + + +
    + +
    + + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/imaging/umb-image-crop.html b/src/Umbraco.Web.UI.Client/src/views/components/imaging/umb-image-crop.html index 933551bbff..af692f8322 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/imaging/umb-image-crop.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/imaging/umb-image-crop.html @@ -1,25 +1,36 @@
    -
    - -
    -
    +
    + +
    +
    {{width}}px x {{height}}px
    +
    +
    +
    -
    - +
    +
    + -
    - - +
    + + +
    + +
    - - +
    - +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/imaging/umb-image-gravity.html b/src/Umbraco.Web.UI.Client/src/views/components/imaging/umb-image-gravity.html index edd840a47f..10aa6a774a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/imaging/umb-image-gravity.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/imaging/umb-image-gravity.html @@ -1,12 +1,17 @@
    - +
    - + -
    +
    +
    + +
    +
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/mediacard/umb-media-card-grid.less b/src/Umbraco.Web.UI.Client/src/views/components/mediacard/umb-media-card-grid.less new file mode 100644 index 0000000000..f7e5764335 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/mediacard/umb-media-card-grid.less @@ -0,0 +1,137 @@ +.umb-media-card-grid { + /* Grid Setup */ + display: grid; + grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); + grid-auto-rows: minmax(100px, auto); + grid-gap: 10px; + + justify-items: center; + align-items: center; +} +.umb-media-card-grid__cell { + position: relative; + display: flex; + width: 100%; + height: 100%; + align-items: center; + justify-content: center; +} + +.umb-media-card-grid--inline-create-button { + position: absolute; + height: 100%; + z-index: 1; + opacity: 0; + outline: none; + left: 0; + width: 12px; + margin-left: -7px; + padding-left: 6px; + margin-right: -6px; + transition: opacity 240ms; + + &::before { + content: ''; + position: absolute; + background: @blueMid; + background: linear-gradient(0deg, rgba(@blueMid,0) 0%, rgba(@blueMid,1) 50%, rgba(@blueMid,0) 100%); + border-left: 1px solid white; + border-right: 1px solid white; + border-radius: 2px; + left: 0; + top: 0; + bottom: 0; + width: 2px; + animation: umb-media-card-grid--inline-create-button_before 400ms ease-in-out alternate infinite; + transform: scaleX(.99); + transition: transform 240ms ease-out; + + @keyframes umb-media-card-grid--inline-create-button_before { + 0% { opacity: 1; } + 100% { opacity: 0.5; } + } + } + + > .__plus { + position: absolute; + display: flex; + justify-content: center; + align-items: center; + pointer-events: none; // lets stop avoiding the mouse values in JS move event. + box-sizing: border-box; + width: 28px; + height: 28px; + margin-left: -18px; + margin-top: -18px - 8px; + border-radius: 3em; + font-size: 14px; + border: 2px solid @blueMid; + color: @blueMid; + background-color: rgba(255, 255, 255, .96); + box-shadow: 0 0 0 2px rgba(255, 255, 255, .96); + transform: scale(0); + transition: transform 240ms ease-in; + + animation: umb-media-card-grid--inline-create-button__plus 400ms ease-in-out alternate infinite; + + @keyframes umb-media-card-grid--inline-create-button__plus { + 0% { color: rgba(@blueMid, 1); } + 100% { color: rgba(@blueMid, 0.8); } + } + + } + + &:focus { + > .__plus { + border-color: @ui-outline; + } + } + + &:hover, &:focus { + opacity: 1; + + &::before { + transform: scaleX(1); + } + > .__plus { + transform: scale(1); + transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275); + + } + } +} + +.umb-media-card-grid__create-button { + position: relative; + width: 100%; + padding-bottom: 100%; + + border: 1px dashed @ui-action-discreet-border; + color: @ui-action-discreet-type; + font-weight: bold; + box-sizing: border-box; + border-radius: @baseBorderRadius; + + > div { + position: absolute; + height: 100%; + width: 100%; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + } +} + +.umb-media-card-grid__create-button:hover { + color: @ui-action-discreet-type-hover; + border-color: @ui-action-discreet-border-hover; + text-decoration: none; +} + +.umb-media-card-grid__create-button.--disabled, +.umb-media-card-grid__create-button.--disabled:hover { + color: @gray-7; + border-color: @gray-7; + cursor: default; +} diff --git a/src/Umbraco.Web.UI.Client/src/views/components/mediacard/umb-media-card.html b/src/Umbraco.Web.UI.Client/src/views/components/mediacard/umb-media-card.html new file mode 100644 index 0000000000..01ce31415e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/mediacard/umb-media-card.html @@ -0,0 +1,47 @@ + +
    + +
    + +

    + + +

    + +

    + + +

    + + + {{vm.media.name}} + + + + + + + + +
    + + + + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/mediacard/umb-media-card.less b/src/Umbraco.Web.UI.Client/src/views/components/mediacard/umb-media-card.less new file mode 100644 index 0000000000..de3840b4d7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/mediacard/umb-media-card.less @@ -0,0 +1,186 @@ +.umb-media-card, +umb-media-card { + position: relative; + display: inline-block; + width: 100%; + //background-color: white; + border-radius: @baseBorderRadius; + //box-shadow: 0 1px 2px rgba(0,0,0,.2); + overflow: hidden; + + transition: box-shadow 120ms; + + cursor: pointer; + + .umb-outline(); + + &:hover { + box-shadow: 0 1px 3px rgba(@ui-action-type-hover, .5); + } + + &.--isOpen { + &::after { + content: ""; + position: absolute; + border: 2px solid @ui-active-border; + border-radius: @baseBorderRadius; + top:0; + bottom: 0; + left: 0; + right: 0; + } + } + + &.--hasError { + border: 2px solid @errorBackground; + } + + &.--sortable-placeholder { + &::after { + content: ""; + position: absolute; + background-color:rgba(@ui-drop-area-color, .05); + border: 2px solid rgba(@ui-drop-area-color, .1); + border-radius: @baseBorderRadius; + box-shadow: 0 0 4px rgba(@ui-drop-area-color, 0.05); + top:0; + bottom: 0; + left: 0; + right: 0; + animation: umb-block-card--sortable-placeholder 400ms ease-in-out alternate infinite; + @keyframes umb-block-card--sortable-placeholder { + 0% { opacity: 1; } + 100% { opacity: 0.5; } + } + } + box-shadow: none; + } + + .__status { + + position: absolute; + top: 0; + left: 0; + right: 0; + padding: 2px; + + &.--error { + background-color: @errorBackground; + color: @errorText; + } + } + + .__showcase { + position: relative; + max-width: 100%; + min-height: 120px; + max-height: 240px; + text-align: center; + //padding-bottom: 10/16*100%; + //background-color: @gray-12; + + img { + object-fit: contain; + max-height: 240px; + } + + umb-file-icon { + width: 100%; + padding-bottom: 100%; + display: block; + .umb-file-icon { + position: absolute; + top: 0; + bottom: 0; + left: 10px; + right: 10px; + display: flex; + align-items: center; + justify-content: center; + } + } + } + + .__info { + position: absolute; + text-align: left; + bottom: 0; + width: 100%; + background-color: #fff; + padding-top: 6px; + padding-bottom: 7px;// 7 + 1 to compentiate for the -1 substraction in margin-bottom. + + opacity: 0; + transition: opacity 120ms; + + &.--error { + opacity: 1; + background-color: @errorBackground; + .__name, .__subname { + color: @errorText; + } + } + + .__name { + font-weight: bold; + font-size: 13px; + color: @ui-action-type; + margin-left: 16px; + margin-bottom: -1px; + + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + .__subname { + color: @gray-4; + font-size: 12px; + margin-left: 16px; + margin-top: 1px; + margin-bottom: -1px; + line-height: 1.5em; + } + } + + &:hover, &:focus, &:focus-within { + .__info { + opacity: 1; + } + .__info:not(.--error) { + .__name { + color: @ui-action-type-hover; + } + } + } + + .__actions { + position: absolute; + top: 10px; + right: 10px; + font-size: 0; + background-color: rgba(255, 255, 255, .96); + border-radius: 16px; + padding-left: 5px; + padding-right: 5px; + + opacity: 0; + transition: opacity 120ms; + .__action { + position: relative; + display: inline-block; + padding: 5px; + font-size: 18px; + + color: @ui-action-discreet-type; + &:hover { + color: @ui-action-discreet-type-hover; + } + } + } + &:hover, &:focus, &:focus-within { + .__actions { + opacity: 1; + } + } + +} diff --git a/src/Umbraco.Web.UI.Client/src/views/components/mediacard/umbMediaCard.component.js b/src/Umbraco.Web.UI.Client/src/views/components/mediacard/umbMediaCard.component.js new file mode 100644 index 0000000000..24b20367aa --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/mediacard/umbMediaCard.component.js @@ -0,0 +1,97 @@ +(function () { + "use strict"; + + angular + .module("umbraco") + .component("umbMediaCard", { + templateUrl: "views/components/mediacard/umb-media-card.html", + controller: MediaCardController, + controllerAs: "vm", + transclude: true, + bindings: { + mediaKey: " { + if(newValue !== oldValue) { + vm.updateThumbnail(); + } + })); + + function checkErrorState() { + + vm.notAllowed = (vm.media &&vm.allowedTypes && vm.allowedTypes.length > 0 && vm.allowedTypes.indexOf(vm.media.metaData.ContentTypeAlias) === -1); + + if ( + vm.hasError === true || vm.notAllowed === true || (vm.media && vm.media.trashed === true) + ) { + $element.addClass("--hasError") + vm.mediaCardForm.$setValidity('error', false) + } else { + $element.removeClass("--hasError") + vm.mediaCardForm.$setValidity('error', true) + } + } + + vm.$onInit = function () { + + unsubscribe.push($scope.$watchGroup(["vm.media.trashed", "vm.hasError"], checkErrorState)); + + vm.updateThumbnail(); + + unsubscribe.push(eventsService.on("editors.media.saved", function(name, args) { + // if this media item uses the updated media type we want to reload the media file + if(args && args.media && args.media.key === vm.mediaKey) { + vm.updateThumbnail(); + } + })); + } + + + vm.$onDestroy = function () { + unsubscribe.forEach(x => x()); + } + + vm.updateThumbnail = function () { + + if(vm.mediaKey && vm.mediaKey !== "") { + vm.loading = true; + + entityResource.getById(vm.mediaKey, "Media").then(function (mediaEntity) { + vm.media = mediaEntity; + checkErrorState(); + vm.thumbnail = mediaHelper.resolveFileFromEntity(mediaEntity, true); + + vm.loading = false; + }, function () { + localizationService.localize("mediaPicker_deletedItem").then(function (localized) { + vm.media = { + name: localized, + icon: "icon-picture", + trashed: true + }; + vm.loading = false; + $element.addClass("--hasError") + vm.mediaCardForm.$setValidity('error', false) + }); + }); + } + + } + + } + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-media-grid.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-media-grid.html index f41390bce3..9754056267 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-media-grid.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-media-grid.html @@ -6,39 +6,42 @@ ng-click="clickItem(item, $event, $index)" ng-repeat="item in items | filter:filterBy" ng-style="item.flexStyle" - ng-class="{'-selected': item.selected, '-file': !item.thumbnail, '-folder': item.isFolder, '-svg': item.extension == 'svg', '-selectable': item.selectable, '-unselectable': !item.selectable}"> -
    - -
    - -
    {{item.name}}
    -
    - - -
    - - - {{item.name}} - - - {{item.name}} - - - {{item.name}} - - - - + ng-class="{'-selected': item.selected, '-file': !item.thumbnail, '-folder': item.isFolder, '-svg': item.extension == 'svg', '-selectable': item.selectable, '-unselectable': !item.selectable, '-filtered': item.filtered}"> + +
    + +
    {{item.name}}
    + + +
    + + +
    + + + {{item.name}} + + + {{item.name}} + + + {{item.name}} + + + + + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/upload/umb-property-file-upload.html b/src/Umbraco.Web.UI.Client/src/views/components/upload/umb-property-file-upload.html index 41e24a6cda..fadc0ac3b1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/upload/umb-property-file-upload.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/upload/umb-property-file-upload.html @@ -6,9 +6,9 @@

    Click to upload

    - +
    - +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/media/media.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/media/media.edit.controller.js index f41f22a1a9..88d112e2d6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/media.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/media/media.edit.controller.js @@ -2,29 +2,29 @@ * @ngdoc controller * @name Umbraco.Editors.Media.EditController * @function - * + * * @description * The controller for the media editor */ -function mediaEditController($scope, $routeParams, $location, $http, $q, appState, mediaResource, - entityResource, navigationService, notificationsService, localizationService, - serverValidationManager, contentEditingHelper, fileManager, formHelper, +function mediaEditController($scope, $routeParams, $location, $http, $q, appState, mediaResource, + entityResource, navigationService, notificationsService, localizationService, + serverValidationManager, contentEditingHelper, fileManager, formHelper, editorState, umbRequestHelper, eventsService) { - + var evts = []; var nodeId = null; var create = false; var infiniteMode = $scope.model && $scope.model.infiniteMode; - // when opening the editor through infinite editing get the + // when opening the editor through infinite editing get the // node id from the model instead of the route param if(infiniteMode && $scope.model.id) { nodeId = $scope.model.id; } else { nodeId = $routeParams.id; } - - // when opening the editor through infinite editing get the + + // when opening the editor through infinite editing get the // create option from the model instead of the route param if(infiniteMode) { create = $scope.model.create; @@ -72,22 +72,22 @@ function mediaEditController($scope, $routeParams, $location, $http, $q, appStat } function init() { - + var content = $scope.content; - + // we need to check whether an app is present in the current data, if not we will present the default app. var isAppPresent = false; - + // on first init, we dont have any apps. but if we are re-initializing, we do, but ... if ($scope.app) { - + // lets check if it still exists as part of our apps array. (if not we have made a change to our docType, even just a re-save of the docType it will turn into new Apps.) content.apps.forEach(app => { if (app === $scope.app) { isAppPresent = true; } }); - + // if we did reload our DocType, but still have the same app we will try to find it by the alias. if (isAppPresent === false) { content.apps.forEach(app => { @@ -98,9 +98,9 @@ function mediaEditController($scope, $routeParams, $location, $http, $q, appStat } }); } - + } - + // if we still dont have a app, lets show the first one: if (isAppPresent === false) { content.apps[0].active = true; @@ -108,16 +108,16 @@ function mediaEditController($scope, $routeParams, $location, $http, $q, appStat } editorState.set($scope.content); - + bindEvents(); } - + function bindEvents() { //bindEvents can be called more than once and we don't want to have multiple bound events for (var e in evts) { eventsService.unsubscribe(evts[e]); } - + evts.push(eventsService.on("editors.mediaType.saved", function(name, args) { // if this media item uses the updated media type we need to reload the media item if(args && args.mediaType && args.mediaType.key === $scope.content.contentType.key) { @@ -131,7 +131,7 @@ function mediaEditController($scope, $routeParams, $location, $http, $q, appStat })); } $scope.page.submitButtonLabelKey = "buttons_save"; - + /** Syncs the content item to it's tree node - this occurs on first load and after saving */ function syncTreeNode(content, path, initialLoad) { @@ -149,7 +149,7 @@ function mediaEditController($scope, $routeParams, $location, $http, $q, appStat //it's a child item, just sync the ui node to the parent navigationService.syncTree({ tree: "media", path: path.substring(0, path.lastIndexOf(",")).split(","), forceReload: initialLoad !== true }); - //if this is a child of a list view and it's the initial load of the editor, we need to get the tree node + //if this is a child of a list view and it's the initial load of the editor, we need to get the tree node // from the server so that we can load in the actions menu. umbRequestHelper.resourcePromise( $http.get(content.treeNodeUrl), @@ -176,7 +176,7 @@ function mediaEditController($scope, $routeParams, $location, $http, $q, appStat $scope.save = function () { if (formHelper.submitForm({ scope: $scope })) { - + $scope.page.saveButtonState = "busy"; mediaResource.save($scope.content, create, fileManager.getFiles()) @@ -200,12 +200,16 @@ function mediaEditController($scope, $routeParams, $location, $http, $q, appStat editorState.set($scope.content); syncTreeNode($scope.content, data.path); - + $scope.page.saveButtonState = "success"; init(); } + eventsService.emit("editors.media.saved", {media: data}); + + return data; + }, function(err) { formHelper.resetForm({ scope: $scope, hasErrors: true }); @@ -213,16 +217,16 @@ function mediaEditController($scope, $routeParams, $location, $http, $q, appStat err: err, rebindCallback: contentEditingHelper.reBindChangedProperties($scope.content, err.data) }); - + editorState.set($scope.content); $scope.page.saveButtonState = "error"; }); } else { - showValidationNotification(); + showValidationNotification(); } - + }; function loadMedia() { @@ -231,7 +235,7 @@ function mediaEditController($scope, $routeParams, $location, $http, $q, appStat .then(function (data) { $scope.content = data; - + if (data.isChildOfListView && data.trashed === false) { $scope.page.listViewPath = ($routeParams.page) ? "/media/media/edit/" + data.parentId + "?page=" + $routeParams.page @@ -247,9 +251,9 @@ function mediaEditController($scope, $routeParams, $location, $http, $q, appStat serverValidationManager.notifyAndClearAllSubscriptions(); if(!infiniteMode) { - syncTreeNode($scope.content, data.path, true); + syncTreeNode($scope.content, data.path, true); } - + if ($scope.content.parentId && $scope.content.parentId !== -1 && $scope.content.parentId !== -21) { //We fetch all ancestors of the node to generate the footer breadcrump navigation entityResource.getAncestors(nodeId, "media") @@ -279,7 +283,7 @@ function mediaEditController($scope, $routeParams, $location, $http, $q, appStat $scope.appChanged = function (app) { $scope.app = app; - + // setup infinite mode if(infiniteMode) { $scope.page.submitButtonLabelKey = "buttons_saveAndClose"; @@ -296,7 +300,7 @@ function mediaEditController($scope, $routeParams, $location, $http, $q, appStat $location.path($scope.page.listViewPath.split("?")[0]); } }; - + //ensure to unregister from all events! $scope.$on('$destroy', function () { for (var e in evts) { diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/numberrange.html b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/numberrange.html index d9d8cad982..6e67c94793 100644 --- a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/numberrange.html +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/numberrange.html @@ -4,6 +4,7 @@ type="number" ng-model="model.value.min" placeholder="0" + min="0" ng-max="model.value.max" fix-number /> @@ -11,7 +12,7 @@ type="number" ng-model="model.value.max" placeholder="∞" - ng-min="model.value.min" + ng-min="model.value.min || 0" fix-number /> diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesourcetypepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesourcetypepicker.controller.js index dcc9add395..d02e626bfa 100644 --- a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesourcetypepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesourcetypepicker.controller.js @@ -99,6 +99,11 @@ function TreeSourceTypePickerController($scope, contentTypeResource, mediaTypeRe eventsService.unsubscribe(evts[e]); } }); + + if ($scope.model.config.itemType) { + currentItemType = $scope.model.config.itemType; + init(); + } } angular.module('umbraco').controller("Umbraco.PrevalueEditors.TreeSourceTypePickerController", TreeSourceTypePickerController); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umb-block-list-property-editor.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umb-block-list-property-editor.less index 019a772fdd..66ef23c744 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umb-block-list-property-editor.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umb-block-list-property-editor.less @@ -10,7 +10,7 @@ .umb-block-list__wrapper { position: relative; - max-width: 1024px; + .umb-property-editor--limit-width(); > .ui-sortable > .ui-sortable-helper > .umb-block-list__block > .umb-block-list__block--content > * { box-shadow: 0px 5px 10px 0 rgba(0,0,0,.2); } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.controller.js index c485f4bbc6..4f1016e680 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.controller.js @@ -11,11 +11,14 @@ * */ function fileUploadController($scope, fileManager) { - + $scope.fileChanged = onFileChanged; //declare a special method which will be called whenever the value has changed from the server $scope.model.onValueChanged = onValueChanged; + + $scope.fileExtensionsString = $scope.model.config.fileExtensions ? $scope.model.config.fileExtensions.map(x => "."+x.value).join(",") : ""; + /** * Called when the file selection value changes * @param {any} value @@ -38,12 +41,12 @@ files: [] }); } - + }; angular.module("umbraco") .controller('Umbraco.PropertyEditors.FileUploadController', fileUploadController) - .run(function (mediaHelper, umbRequestHelper, assetsService) { + .run(function (mediaHelper) { if (mediaHelper && mediaHelper.registerFileResolver) { //NOTE: The 'entity' can be either a normal media entity or an "entity" returned from the entityResource diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.html index 522278e99e..36509e8947 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.html @@ -4,6 +4,7 @@ property-alias="{{model.alias}}" value="model.value" required="model.validation.mandatory" - on-files-selected="fileChanged(value)"> + on-files-selected="fileChanged(value)" + accept-file-ext="fileExtensionsString">
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js index 4df8f7e596..e9d9950bdd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js @@ -1,6 +1,6 @@ angular.module('umbraco') .controller("Umbraco.PropertyEditors.ImageCropperController", - function ($scope, fileManager, $timeout) { + function ($scope, fileManager, $timeout, mediaHelper) { var config = Utilities.copy($scope.model.config); @@ -18,6 +18,8 @@ angular.module('umbraco') //declare a special method which will be called whenever the value has changed from the server $scope.model.onValueChanged = onValueChanged; + var umbracoSettings = Umbraco.Sys.ServerVariables.umbracoSettings; + $scope.acceptFileExt = mediaHelper.formatFileTypes(umbracoSettings.imageFileTypes); /** * Called when the umgImageGravity component updates the focal point value * @param {any} left @@ -150,7 +152,7 @@ angular.module('umbraco') // we have a crop open already - close the crop (this will discard any changes made) close(); - // the crop editor needs a digest cycle to close down properly, otherwise its state + // the crop editor needs a digest cycle to close down properly, otherwise its state // is reused for the new crop... and that's really bad $timeout(function () { crop(targetCrop); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html index 241d61660e..9dc1a3b91a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html @@ -13,11 +13,12 @@ on-files-selected="filesSelected(value, files)" on-files-changed="filesChanged(files)" on-init="fileUploaderInit(value, files)" - hide-selection="true"> + hide-selection="true" + accept-file-ext="acceptFileExt">
    -
    +
    @@ -25,7 +26,6 @@ width="{{currentCrop.width}}" crop="currentCrop.coordinates" center="model.value.focalPoint" - max-size="450" src="imageSrc">
    @@ -49,7 +49,7 @@
    - +
    • diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js index ca46f30bb7..c6320a7cf2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js @@ -1,7 +1,7 @@ //this controller simply tells the dialogs service to open a mediaPicker window //with a specified callback, this callback will receive an object with a selection on it angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerController", - function ($scope, entityResource, mediaHelper, $timeout, userService, localizationService, editorService, overlayService) { + function ($scope, entityResource, mediaHelper, $timeout, userService, localizationService, editorService, overlayService, clipboardService) { var vm = this; @@ -10,6 +10,7 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl vm.add = add; vm.remove = remove; + vm.copyItem = copyItem; vm.editItem = editItem; vm.showAdd = showAdd; @@ -53,7 +54,7 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl // it's prone to someone "fixing" it at some point without knowing the effects. Rather use toString() // compares and be completely sure it works. var found = medias.find(m => m.udi.toString() === id.toString() || m.id.toString() === id.toString()); - + var mediaItem = found || { name: vm.labels.deletedItem, @@ -67,33 +68,36 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl return mediaItem; }); - medias.forEach(media => { - if (!media.extension && media.id && media.metaData) { - media.extension = mediaHelper.getFileExtension(media.metaData.MediaPath); - } - - // if there is no thumbnail, try getting one if the media is not a placeholder item - if (!media.thumbnail && media.id && media.metaData) { - media.thumbnail = mediaHelper.resolveFileFromEntity(media, true); - } - - vm.mediaItems.push(media); - - if ($scope.model.config.idType === "udi") { - selectedIds.push(media.udi); - } else { - selectedIds.push(media.id); - } - }); + medias.forEach(media => appendMedia(media)); sync(); }); } } + function appendMedia(media) { + if (!media.extension && media.id && media.metaData) { + media.extension = mediaHelper.getFileExtension(media.metaData.MediaPath); + } + + // if there is no thumbnail, try getting one if the media is not a placeholder item + if (!media.thumbnail && media.id && media.metaData) { + media.thumbnail = mediaHelper.resolveFileFromEntity(media, true); + } + + vm.mediaItems.push(media); + + if ($scope.model.config.idType === "udi") { + selectedIds.push(media.udi); + } else { + selectedIds.push(media.id); + } + } + function sync() { $scope.model.value = selectedIds.join(); removeAllEntriesAction.isDisabled = selectedIds.length === 0; + copyAllEntriesAction.isDisabled = removeAllEntriesAction.isDisabled; } function setDirty() { @@ -103,9 +107,9 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl } function reloadUpdatedMediaItems(updatedMediaNodes) { - // because the images can be edited through the media picker we need to + // because the images can be edited through the media picker we need to // reload. We only reload the images that is already picked but has been updated. - // We have to get the entities from the server because the media + // We have to get the entities from the server because the media // can be edited without being selected vm.mediaItems.forEach(media => { if (updatedMediaNodes.indexOf(media.udi) !== -1) { @@ -129,7 +133,7 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl ]; localizationService.localizeMany(labelKeys) - .then(function(data) { + .then(function (data) { vm.labels.deletedItem = data[0]; vm.labels.trashed = data[1]; @@ -143,7 +147,7 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl else { $scope.model.config.startNodeId = userData.startMediaIds.length !== 1 ? -1 : userData.startMediaIds[0]; $scope.model.config.startNodeIsVirtual = userData.startMediaIds.length !== 1; - } + } } // only allow users to add and edit media if they have access to the media section @@ -163,6 +167,50 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl setDirty(); } + function copyAllEntries() { + if($scope.mediaItems.length > 0) { + + // gather aliases + var aliases = $scope.mediaItems.map(mediaEntity => mediaEntity.metaData.ContentTypeAlias); + + // remove duplicate aliases + aliases = aliases.filter((item, index) => aliases.indexOf(item) === index); + + var data = $scope.mediaItems.map(mediaEntity => { return {"mediaKey": mediaEntity.key }}); + + localizationService.localize("clipboard_labelForArrayOfItems", [$scope.model.label]).then(function(localizedLabel) { + clipboardService.copyArray(clipboardService.TYPES.MEDIA, aliases, data, localizedLabel, "icon-thumbnail-list", $scope.model.id); + }); + } + } + + function copyItem(mediaItem) { + + var mediaEntry = {}; + mediaEntry.mediaKey = mediaItem.key; + + clipboardService.copy(clipboardService.TYPES.MEDIA, mediaItem.metaData.ContentTypeAlias, mediaEntry, mediaItem.name, mediaItem.icon, mediaItem.udi); + } + + function pasteFromClipboard(pasteEntry, pasteType) { + + if (pasteEntry === undefined) { + return; + } + + pasteEntry = clipboardService.parseContentForPaste(pasteEntry, pasteType); + + entityResource.getById(pasteEntry.mediaKey, "Media").then(function (mediaEntity) { + + if(disableFolderSelect === true && mediaEntity.metaData.ContentTypeAlias === "Folder") { + return; + } + + appendMedia(mediaEntity); + sync(); + }); + } + function editItem(item) { var mediaEditor = { id: item.id, @@ -174,7 +222,7 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl if (model && model.mediaNode) { entityResource.getById(model.mediaNode.id, "Media") .then(function (mediaEntity) { - // if an image is selecting more than once + // if an image is selecting more than once // we need to update all the media items vm.mediaItems.forEach(media => { if (media.id === model.mediaNode.id) { @@ -200,6 +248,22 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl multiPicker: multiPicker, onlyImages: onlyImages, disableFolderSelect: disableFolderSelect, + clickPasteItem: function(item, mouseEvent) { + if (Array.isArray(item.data)) { + var indexIncrementor = 0; + item.data.forEach(function (entry) { + if (pasteFromClipboard(entry, item.type)) { + indexIncrementor++; + } + }); + } else { + pasteFromClipboard(item.data, item.type); + } + if(!(mouseEvent.ctrlKey || mouseEvent.metaKey)) { + editorService.close(); + } + setDirty(); + }, submit: function (model) { editorService.close(); @@ -231,6 +295,21 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl } } + + var allowedTypes = null; + if(onlyImages) { + allowedTypes = ["Image"]; // Media Type Image Alias. + } + + mediaPicker.clickClearClipboard = function ($event) { + clipboardService.clearEntriesOfType(clipboardService.TYPES.Media, allowedTypes); + }; + + mediaPicker.clipboardItems = clipboardService.retriveEntriesOfType(clipboardService.TYPES.MEDIA, allowedTypes); + mediaPicker.clipboardItems.sort( (a, b) => { + return b.date - a.date + }); + editorService.mediaPicker(mediaPicker); } @@ -262,6 +341,14 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl }); } + var copyAllEntriesAction = { + labelKey: 'clipboard_labelForCopyAllEntries', + labelTokens: ['Media'], + icon: "documents", + method: copyAllEntries, + isDisabled: true + } + var removeAllEntriesAction = { labelKey: 'clipboard_labelForRemoveAllEntries', labelTokens: [], @@ -269,9 +356,10 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl method: removeAllEntries, isDisabled: true }; - + if (multiPicker === true) { var propertyActions = [ + copyAllEntriesAction, removeAllEntriesAction ]; @@ -289,12 +377,12 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl cancel: ".unsortable", update: function () { setDirty(); - $timeout(function() { + $timeout(function () { // TODO: Instead of doing this with a timeout would be better to use a watch like we do in the // content picker. Then we don't have to worry about setting ids, render models, models, we just set one and let the // watch do all the rest. selectedIds = vm.mediaItems.map(media => $scope.model.config.idType === "udi" ? media.udi : media.id); - + sync(); }); } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/mediapicker3.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/mediapicker3.html new file mode 100644 index 0000000000..5e67aafe3e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/mediapicker3.html @@ -0,0 +1 @@ + diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/prevalue/mediapicker3.crops.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/prevalue/mediapicker3.crops.controller.js new file mode 100644 index 0000000000..922370a032 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/prevalue/mediapicker3.crops.controller.js @@ -0,0 +1,110 @@ +angular.module("umbraco").controller("Umbraco.PropertyEditors.MediaPicker3.CropConfigurationController", + function ($scope) { + + var unsubscribe = []; + + if (!$scope.model.value) { + $scope.model.value = []; + } + + $scope.setFocus = false; + + $scope.remove = function (crop, evt) { + evt.preventDefault(); + const i = $scope.model.value.indexOf(crop); + if (i > -1) { + $scope.model.value.splice(i, 1); + } + }; + + $scope.edit = function (crop, evt) { + evt.preventDefault(); + crop.editMode = true; + }; + + $scope.addNewCrop = function (evt) { + evt.preventDefault(); + + var crop = {}; + crop.editMode = true; + + $scope.model.value.push(crop); + $scope.validate(crop); + } + $scope.setChanges = function (crop) { + $scope.validate(crop); + if( + crop.hasWidthError !== true && + crop.hasHeightError !== true && + crop.hasAliasError !== true + ) { + crop.editMode = false; + window.dispatchEvent(new Event('resize.umbImageGravity')); + } + }; + $scope.useForAlias = function (crop) { + if (crop.alias == null || crop.alias === "") { + crop.alias = (crop.label || "").toCamelCase(); + } + }; + $scope.validate = function(crop) { + $scope.validateWidth(crop); + $scope.validateHeight(crop); + $scope.validateAlias(crop); + } + $scope.validateWidth = function (crop) { + crop.hasWidthError = !(Utilities.isNumber(crop.width) && crop.width > 0); + }; + $scope.validateHeight = function (crop) { + crop.hasHeightError = !(Utilities.isNumber(crop.height) && crop.height > 0); + }; + $scope.validateAlias = function (crop, $event) { + var exists = $scope.model.value.find( x => crop !== x && crop.alias === x.alias); + if (exists !== undefined || crop.alias === "") { + // alias is not valid + crop.hasAliasError = true; + } else { + // everything was good: + crop.hasAliasError = false; + } + + }; + + $scope.confirmChanges = function (crop, event) { + if (event.keyCode == 13) { + $scope.setChanges(crop, event); + event.preventDefault(); + } + }; + $scope.focusNextField = function (event) { + if (event.keyCode == 13) { + + var el = event.target; + + var inputs = Array.from(document.querySelectorAll("input:not(disabled)")); + var inputIndex = inputs.indexOf(el); + if (inputIndex > -1) { + var nextIndex = inputs.indexOf(el) +1; + + if(inputs.length > nextIndex) { + inputs[nextIndex].focus(); + event.preventDefault(); + } + } + } + }; + + $scope.sortableOptions = { + axis: 'y', + containment: 'parent', + cursor: 'move', + tolerance: 'pointer' + }; + + $scope.$on("$destroy", function () { + for (const subscription of unsubscribe) { + subscription(); + } + }); + + }); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/prevalue/mediapicker3.crops.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/prevalue/mediapicker3.crops.html new file mode 100644 index 0000000000..46b9ddb15f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/prevalue/mediapicker3.crops.html @@ -0,0 +1,96 @@ +
      + +
      +
      +
      +
      + Label +
      +
      + Alias +
      +
      + Width +
      +
      + Height +
      +
      + Actions +
      +
      +
      +
      + +
      + +
      {{crop.label}}
      +
      {{crop.alias}}
      +
      {{crop.width}}px
      +
      {{crop.height}}px
      +
      + + +
      + + +
      + +
      + +
      + +
      + +
      + +
      + +
      + +
      +
      + +
      + +
      +
      +
      + + + +
      diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/prevalue/mediapicker3.crops.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/prevalue/mediapicker3.crops.less new file mode 100644 index 0000000000..5f5a2d4689 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/prevalue/mediapicker3.crops.less @@ -0,0 +1,40 @@ +.umb-mediapicker3-crops { + + input.ng-invalid.ng-touched { + border-color:@formErrorBorder; + color:@formErrorBorder + } + + .umb-table button { + position: relative; + color: @ui-action-discreet-type; + margin-right: 10px; + font-size: 14px; + &:hover { + color: @ui-action-discreet-type-hover; + } + } + +} + +.umb-mediapicker3-crops__add { + + margin-top:10px; + + display: flex; + align-items: center; + justify-content: center; + background: transparent; + border: 1px dashed @ui-action-discreet-border; + color: @ui-action-discreet-type; + font-weight: bold; + padding: 5px 15px; + box-sizing: border-box; + width: 100%; +} + +.umb-mediapicker3-crops__add:hover { + color: @ui-action-discreet-type-hover; + border-color: @ui-action-discreet-border-hover; + text-decoration: none; +} diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umb-media-picker3-property-editor.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umb-media-picker3-property-editor.html new file mode 100644 index 0000000000..aa9f50b7df --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umb-media-picker3-property-editor.html @@ -0,0 +1,71 @@ +
      + + + +
      + +
      + +
      + + + + +
      + + +
      +
      + +
      +
      + + + +
      + + + + +
      +
      + Minimum %0% entries, needs %1% more. +
      + > +
      +
      +
      + Maximum %0% entries, %1% too many. +
      + +
      + +
      diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umb-media-picker3-property-editor.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umb-media-picker3-property-editor.less new file mode 100644 index 0000000000..d02c0b055c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umb-media-picker3-property-editor.less @@ -0,0 +1,13 @@ +.umb-mediapicker3 { + + .umb-media-card-grid { + padding: 20px; + border: 1px solid @inputBorder; + box-sizing: border-box; + .umb-property-editor--limit-width(); + + &.--singleMode { + max-width: 202px; + } + } +} diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umbMediaPicker3PropertyEditor.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umbMediaPicker3PropertyEditor.component.js new file mode 100644 index 0000000000..675381d46e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umbMediaPicker3PropertyEditor.component.js @@ -0,0 +1,431 @@ +(function () { + "use strict"; + + + /** + * @ngdoc directive + * @name umbraco.directives.directive:umbMediaPicker3PropertyEditor + * @function + * + * @description + * The component for the Media Picker property editor. + */ + angular + .module("umbraco") + .component("umbMediaPicker3PropertyEditor", { + templateUrl: "views/propertyeditors/MediaPicker3/umb-media-picker3-property-editor.html", + controller: MediaPicker3Controller, + controllerAs: "vm", + bindings: { + model: "=" + }, + require: { + propertyForm: "^form", + umbProperty: "?^umbProperty", + umbVariantContent: '?^^umbVariantContent', + umbVariantContentEditors: '?^^umbVariantContentEditors', + umbElementEditorContent: '?^^umbElementEditorContent' + } + }); + + function MediaPicker3Controller($scope, editorService, clipboardService, localizationService, overlayService, userService, entityResource) { + + var unsubscribe = []; + + // Property actions: + var copyAllMediasAction = null; + var removeAllMediasAction = null; + + var vm = this; + + vm.loading = true; + + vm.supportCopy = clipboardService.isSupported(); + + + vm.labels = {}; + + localizationService.localizeMany(["grid_addElement", "content_createEmpty"]).then(function (data) { + vm.labels.grid_addElement = data[0]; + vm.labels.content_createEmpty = data[1]; + }); + + vm.$onInit = function() { + + vm.validationLimit = vm.model.config.validationLimit || {}; + // If single-mode we only allow 1 item as the maximum: + if(vm.model.config.multiple === false) { + vm.validationLimit.max = 1; + } + vm.model.config.crops = vm.model.config.crops || []; + vm.singleMode = vm.validationLimit.max === 1; + vm.allowedTypes = vm.model.config.filter ? vm.model.config.filter.split(",") : null; + + copyAllMediasAction = { + labelKey: "clipboard_labelForCopyAllEntries", + labelTokens: [vm.model.label], + icon: "documents", + method: requestCopyAllMedias, + isDisabled: true + }; + + removeAllMediasAction = { + labelKey: 'clipboard_labelForRemoveAllEntries', + labelTokens: [], + icon: 'trash', + method: requestRemoveAllMedia, + isDisabled: true + }; + + var propertyActions = []; + if(vm.supportCopy) { + propertyActions.push(copyAllMediasAction); + } + propertyActions.push(removeAllMediasAction); + + if (vm.umbProperty) { + vm.umbProperty.setPropertyActions(propertyActions); + } + + if(vm.model.value === null || !Array.isArray(vm.model.value)) { + vm.model.value = []; + } + + vm.model.value.forEach(mediaEntry => updateMediaEntryData(mediaEntry)); + + userService.getCurrentUser().then(function (userData) { + + if (!vm.model.config.startNodeId) { + if (vm.model.config.ignoreUserStartNodes === true) { + vm.model.config.startNodeId = -1; + vm.model.config.startNodeIsVirtual = true; + } else { + vm.model.config.startNodeId = userData.startMediaIds.length !== 1 ? -1 : userData.startMediaIds[0]; + vm.model.config.startNodeIsVirtual = userData.startMediaIds.length !== 1; + } + } + + // only allow users to add and edit media if they have access to the media section + var hasAccessToMedia = userData.allowedSections.indexOf("media") !== -1; + vm.allowEdit = hasAccessToMedia; + vm.allowAdd = hasAccessToMedia; + + vm.loading = false; + }); + + }; + + function setDirty() { + if (vm.propertyForm) { + vm.propertyForm.$setDirty(); + } + } + + vm.addMediaAt = addMediaAt; + function addMediaAt(createIndex, $event) { + var mediaPicker = { + startNodeId: vm.model.config.startNodeId, + startNodeIsVirtual: vm.model.config.startNodeIsVirtual, + dataTypeKey: vm.model.dataTypeKey, + multiPicker: vm.singleMode !== true, + clickPasteItem: function(item, mouseEvent) { + + if (Array.isArray(item.data)) { + var indexIncrementor = 0; + item.data.forEach(function (entry) { + if (requestPasteFromClipboard(createIndex + indexIncrementor, entry, item.type)) { + indexIncrementor++; + } + }); + } else { + requestPasteFromClipboard(createIndex, item.data, item.type); + } + if(!(mouseEvent.ctrlKey || mouseEvent.metaKey)) { + mediaPicker.close(); + } + }, + submit: function (model) { + editorService.close(); + + var indexIncrementor = 0; + model.selection.forEach((entry) => { + var mediaEntry = {}; + mediaEntry.key = String.CreateGuid(); + mediaEntry.mediaKey = entry.key; + updateMediaEntryData(mediaEntry); + vm.model.value.splice(createIndex + indexIncrementor, 0, mediaEntry); + indexIncrementor++; + }); + + setDirty(); + }, + close: function () { + editorService.close(); + } + } + + if(vm.model.config.filter) { + mediaPicker.filter = vm.model.config.filter; + } + + mediaPicker.clickClearClipboard = function ($event) { + clipboardService.clearEntriesOfType(clipboardService.TYPES.Media, vm.allowedTypes || null); + }; + + mediaPicker.clipboardItems = clipboardService.retriveEntriesOfType(clipboardService.TYPES.MEDIA, vm.allowedTypes || null); + mediaPicker.clipboardItems.sort( (a, b) => { + return b.date - a.date + }); + + editorService.mediaPicker(mediaPicker); + } + + // To be used by infinite editor. (defined here cause we need configuration from property editor) + function changeMediaFor(mediaEntry, onSuccess) { + var mediaPicker = { + startNodeId: vm.model.config.startNodeId, + startNodeIsVirtual: vm.model.config.startNodeIsVirtual, + dataTypeKey: vm.model.dataTypeKey, + multiPicker: false, + submit: function (model) { + editorService.close(); + + model.selection.forEach((entry) => {// only one. + mediaEntry.mediaKey = entry.key; + }); + + // reset focal and crops: + mediaEntry.crops = null; + mediaEntry.focalPoint = null; + updateMediaEntryData(mediaEntry); + + if(onSuccess) { + onSuccess(); + } + }, + close: function () { + editorService.close(); + } + } + + if(vm.model.config.filter) { + mediaPicker.filter = vm.model.config.filter; + } + + editorService.mediaPicker(mediaPicker); + } + + function resetCrop(cropEntry) { + Object.assign(cropEntry, vm.model.config.crops.find( c => c.alias === cropEntry.alias)); + cropEntry.coordinates = null; + setDirty(); + } + + function updateMediaEntryData(mediaEntry) { + + mediaEntry.crops = mediaEntry.crops || []; + mediaEntry.focalPoint = mediaEntry.focalPoint || { + left: 0.5, + top: 0.5 + }; + + // Copy config and only transfer coordinates. + var newCrops = Utilities.copy(vm.model.config.crops); + newCrops.forEach(crop => { + var oldCrop = mediaEntry.crops.filter(x => x.alias === crop.alias).shift(); + if (oldCrop && oldCrop.height === crop.height && oldCrop.width === crop.width) { + crop.coordinates = oldCrop.coordinates; + } + }); + mediaEntry.crops = newCrops; + + } + + vm.removeMedia = removeMedia; + function removeMedia(media) { + var index = vm.model.value.indexOf(media); + if(index !== -1) { + vm.model.value.splice(index, 1); + } + } + function deleteAllMedias() { + vm.model.value = []; + } + + vm.activeMediaEntry = null; + function setActiveMedia(mediaEntryOrNull) { + vm.activeMediaEntry = mediaEntryOrNull; + } + + vm.editMedia = editMedia; + function editMedia(mediaEntry, options, $event) { + + if($event) + $event.stopPropagation(); + + options = options || {}; + + setActiveMedia(mediaEntry); + + var documentInfo = getDocumentNameAndIcon(); + + // make a clone to avoid editing model directly. + var mediaEntryClone = Utilities.copy(mediaEntry); + + var mediaEditorModel = { + $parentScope: $scope, // pass in a $parentScope, this maintains the scope inheritance in infinite editing + $parentForm: vm.propertyForm, // pass in a $parentForm, this maintains the FormController hierarchy with the infinite editing view (if it contains a form) + createFlow: options.createFlow === true, + documentName: documentInfo.name, + mediaEntry: mediaEntryClone, + propertyEditor: { + changeMediaFor: changeMediaFor, + resetCrop: resetCrop + }, + enableFocalPointSetter: vm.model.config.enableLocalFocalPoint || false, + view: "views/common/infiniteeditors/mediaEntryEditor/mediaEntryEditor.html", + size: "large", + submit: function(model) { + vm.model.value[vm.model.value.indexOf(mediaEntry)] = mediaEntryClone; + setActiveMedia(null) + editorService.close(); + }, + close: function(model) { + if(model.createFlow === true) { + // This means that the user cancelled the creation and we should remove the media item. + // TODO: remove new media item. + } + setActiveMedia(null) + editorService.close(); + } + }; + + // open property settings editor + editorService.open(mediaEditorModel); + } + + var getDocumentNameAndIcon = function() { + // get node name + var contentNodeName = "?"; + var contentNodeIcon = null; + if(vm.umbVariantContent) { + contentNodeName = vm.umbVariantContent.editor.content.name; + if(vm.umbVariantContentEditors) { + contentNodeIcon = vm.umbVariantContentEditors.content.icon.split(" ")[0]; + } else if (vm.umbElementEditorContent) { + contentNodeIcon = vm.umbElementEditorContent.model.documentType.icon.split(" ")[0]; + } + } else if (vm.umbElementEditorContent) { + contentNodeName = vm.umbElementEditorContent.model.documentType.name; + contentNodeIcon = vm.umbElementEditorContent.model.documentType.icon.split(" ")[0]; + } + + return { + name: contentNodeName, + icon: contentNodeIcon + } + } + + var requestCopyAllMedias = function() { + var mediaKeys = vm.model.value.map(x => x.mediaKey) + entityResource.getByIds(mediaKeys, "Media").then(function (entities) { + + // gather aliases + var aliases = entities.map(mediaEntity => mediaEntity.metaData.ContentTypeAlias); + + // remove duplicate aliases + aliases = aliases.filter((item, index) => aliases.indexOf(item) === index); + + var documentInfo = getDocumentNameAndIcon(); + + localizationService.localize("clipboard_labelForArrayOfItemsFrom", [vm.model.label, documentInfo.name]).then(function(localizedLabel) { + clipboardService.copyArray(clipboardService.TYPES.MEDIA, aliases, vm.model.value, localizedLabel, documentInfo.icon || "icon-thumbnail-list", vm.model.id); + }); + }); + } + + vm.copyMedia = copyMedia; + function copyMedia(mediaEntry) { + entityResource.getById(mediaEntry.mediaKey, "Media").then(function (mediaEntity) { + clipboardService.copy(clipboardService.TYPES.MEDIA, mediaEntity.metaData.ContentTypeAlias, mediaEntry, mediaEntity.name, mediaEntity.icon, mediaEntry.key); + }); + } + function requestPasteFromClipboard(createIndex, pasteEntry, pasteType) { + + if (pasteEntry === undefined) { + return false; + } + + pasteEntry = clipboardService.parseContentForPaste(pasteEntry, pasteType); + + pasteEntry.key = String.CreateGuid(); + updateMediaEntryData(pasteEntry); + vm.model.value.splice(createIndex, 0, pasteEntry); + + + return true; + + } + + function requestRemoveAllMedia() { + localizationService.localizeMany(["mediaPicker_confirmRemoveAllMediaEntryMessage", "general_remove"]).then(function (data) { + overlayService.confirmDelete({ + title: data[1], + content: data[0], + close: function () { + overlayService.close(); + }, + submit: function () { + deleteAllMedias(); + overlayService.close(); + } + }); + }); + } + + + vm.sortableOptions = { + cursor: "grabbing", + handle: "umb-media-card", + cancel: "input,textarea,select,option", + classes: ".umb-media-card--dragging", + distance: 5, + tolerance: "pointer", + scroll: true, + update: function (ev, ui) { + setDirty(); + } + }; + + + function onAmountOfMediaChanged() { + + // enable/disable property actions + if (copyAllMediasAction) { + copyAllMediasAction.isDisabled = vm.model.value.length === 0; + } + if (removeAllMediasAction) { + removeAllMediasAction.isDisabled = vm.model.value.length === 0; + } + + // validate limits: + if (vm.propertyForm && vm.validationLimit) { + + var isMinRequirementGood = vm.validationLimit.min === null || vm.model.value.length >= vm.validationLimit.min; + vm.propertyForm.minCount.$setValidity("minCount", isMinRequirementGood); + + var isMaxRequirementGood = vm.validationLimit.max === null || vm.model.value.length <= vm.validationLimit.max; + vm.propertyForm.maxCount.$setValidity("maxCount", isMaxRequirementGood); + } + } + + unsubscribe.push($scope.$watch(() => vm.model.value.length, onAmountOfMediaChanged)); + + $scope.$on("$destroy", function () { + for (const subscription of unsubscribe) { + subscription(); + } + }); + } + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umbMediaPicker3PropertyEditor.createButton.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umbMediaPicker3PropertyEditor.createButton.controller.js new file mode 100644 index 0000000000..b561784d9f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umbMediaPicker3PropertyEditor.createButton.controller.js @@ -0,0 +1,18 @@ +(function () { + "use strict"; + + angular + .module("umbraco") + .controller("Umbraco.PropertyEditors.MediaPicker3PropertyEditor.CreateButtonController", + function Controller($scope) { + + var vm = this; + vm.plusPosY = 0; + + vm.onMouseMove = function($event) { + vm.plusPosY = $event.offsetY; + } + + }); + +})(); diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index 737181c668..4abcdf8a40 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -1100,6 +1100,17 @@ Mange hilsner fra Umbraco robotten Du har valgt et medie som er slettet eller lagt i papirkurven Du har valgt medier som er slettede eller lagt i papirkurven Slettet + Åben i mediebiblioteket + Skift medie + Nulstil medie beskæring + Rediger %0% på %1% + Annuller indsættelse? + + Du har foretaget ændringer til bruge af dette media. Er du sikker på at du vil annullere? + Fjern? + Fjern brugen af alle medier? + Udklipsholder + Ikke tilladt indtast eksternt link @@ -1845,6 +1856,7 @@ Mange hilsner fra Umbraco robotten Kopier %0% %0% fra %1% + Samling af %0% Fjern alle elementer Ryd udklipsholder diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 3f6c985a0f..cbb6902d74 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -1353,6 +1353,17 @@ To manage your website, simply open the Umbraco backoffice and start adding cont You have picked a media item currently deleted or in the recycle bin You have picked media items currently deleted or in the recycle bin Trashed + Open in Media Library + Change Media Item + Reset media crop + Edit %0% on %1% + Discard creation? + + You have made changes to this content. Are you sure you want to discard them? + Remove? + Remove all medias? + Clipboard + Not allowed enter external link @@ -2377,6 +2388,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Copy %0% %0% from %1% + Collection of %0% Remove all items Clear clipboard diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index 87b58e5063..590a248393 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -1363,6 +1363,17 @@ To manage your website, simply open the Umbraco backoffice and start adding cont You have picked a media item currently deleted or in the recycle bin You have picked media items currently deleted or in the recycle bin Trashed + Open in Media Library + Change Media Item + Reset media crop + Edit %0% on %1% + Discard creation? + + You have made changes to this content. Are you sure you want to discard them? + Remove? + Remove all medias? + Clipboard + Not allowed enter external link @@ -2396,6 +2407,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Copy %0% %0% from %1% + Collection of %0% Remove all items Clear clipboard diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index 8d13ccd4d7..7160a87351 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -704,7 +704,32 @@ namespace Umbraco.Web.Editors if (result.FormData["contentTypeAlias"] == Constants.Conventions.MediaTypes.AutoSelect) { - if (Current.Configs.Settings().Content.ImageFileTypes.Contains(ext)) + var mediaTypes = Services.MediaTypeService.GetAll(); + // Look up MediaTypes + foreach (var mediaTypeItem in mediaTypes) + { + var fileProperty = mediaTypeItem.CompositionPropertyTypes.FirstOrDefault(x => x.Alias == "umbracoFile"); + if (fileProperty != null) { + var dataTypeKey = fileProperty.DataTypeKey; + var dataType = Services.DataTypeService.GetDataType(dataTypeKey); + + if (dataType != null && dataType.Configuration is IFileExtensionsConfig fileExtensionsConfig) { + var fileExtensions = fileExtensionsConfig.FileExtensions; + if (fileExtensions != null) + { + if (fileExtensions.Where(x => x.Value == ext).Count() != 0) + { + mediaType = mediaTypeItem.Alias; + break; + } + } + } + } + + } + + // If media type is still File then let's check if it's an image. + if (mediaType == Constants.Conventions.MediaTypes.File && Current.Configs.Settings().Content.ImageFileTypes.Contains(ext)) { mediaType = Constants.Conventions.MediaTypes.Image; } diff --git a/src/Umbraco.Web/ImageCropperTemplateCoreExtensions.cs b/src/Umbraco.Web/ImageCropperTemplateCoreExtensions.cs index f39b267e18..766cb1e99f 100644 --- a/src/Umbraco.Web/ImageCropperTemplateCoreExtensions.cs +++ b/src/Umbraco.Web/ImageCropperTemplateCoreExtensions.cs @@ -28,6 +28,11 @@ namespace Umbraco.Web return mediaItem.GetCropUrl(imageUrlGenerator, cropAlias: cropAlias, useCropDimensions: true); } + public static string GetCropUrl(this IPublishedContent mediaItem, string cropAlias, IImageUrlGenerator imageUrlGenerator, ImageCropperValue imageCropperValue) + { + return mediaItem.Url().GetCropUrl(imageUrlGenerator, imageCropperValue, cropAlias: cropAlias, useCropDimensions: true); + } + /// /// Gets the ImageProcessor URL by the crop alias using the specified property containing the image cropper Json data on the IPublishedContent item. /// @@ -375,5 +380,11 @@ namespace Umbraco.Web return imageUrlGenerator.GetImageUrl(options); } + + public static string GetLocalCropUrl(this MediaWithCrops mediaWithCrops, string alias, IImageUrlGenerator imageUrlGenerator, string cacheBusterValue) + { + return mediaWithCrops.LocalCrops.Src + mediaWithCrops.LocalCrops.GetCropUrl(alias, imageUrlGenerator, cacheBusterValue: cacheBusterValue); + + } } } diff --git a/src/Umbraco.Web/ImageCropperTemplateExtensions.cs b/src/Umbraco.Web/ImageCropperTemplateExtensions.cs index dad2f9e3f3..51845946f1 100644 --- a/src/Umbraco.Web/ImageCropperTemplateExtensions.cs +++ b/src/Umbraco.Web/ImageCropperTemplateExtensions.cs @@ -1,7 +1,5 @@ using System; -using Newtonsoft.Json.Linq; using System.Globalization; -using System.Text; using Newtonsoft.Json; using Umbraco.Core; using Umbraco.Core.Composing; @@ -32,6 +30,8 @@ namespace Umbraco.Web /// public static string GetCropUrl(this IPublishedContent mediaItem, string cropAlias) => ImageCropperTemplateCoreExtensions.GetCropUrl(mediaItem, cropAlias, Current.ImageUrlGenerator); + public static string GetCropUrl(this IPublishedContent mediaItem, string cropAlias, ImageCropperValue imageCropperValue) => ImageCropperTemplateCoreExtensions.GetCropUrl(mediaItem, cropAlias, Current.ImageUrlGenerator, imageCropperValue); + /// /// Gets the ImageProcessor URL by the crop alias using the specified property containing the image cropper Json data on the IPublishedContent item. /// @@ -118,6 +118,13 @@ namespace Umbraco.Web ImageCropRatioMode? ratioMode = null, bool upScale = true) => ImageCropperTemplateCoreExtensions.GetCropUrl(mediaItem, Current.ImageUrlGenerator, width, height, propertyAlias, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions, ratioMode, upScale); + public static string GetLocalCropUrl(this MediaWithCrops mediaWithCrops, + string alias, + string cacheBusterValue = null) + => ImageCropperTemplateCoreExtensions.GetLocalCropUrl(mediaWithCrops, alias, Current.ImageUrlGenerator, cacheBusterValue); + + + /// /// Gets the ImageProcessor URL from the image path. /// diff --git a/src/Umbraco.Web/PropertyEditors/FileExtensionConfigItem.cs b/src/Umbraco.Web/PropertyEditors/FileExtensionConfigItem.cs new file mode 100644 index 0000000000..859b3b35eb --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/FileExtensionConfigItem.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; + +namespace Umbraco.Web.PropertyEditors +{ + public class FileExtensionConfigItem : IFileExtensionConfigItem + { + [JsonProperty("id")] + public int Id { get; set; } + + [JsonProperty("value")] + public string Value { get; set; } + } +} diff --git a/src/Umbraco.Web/PropertyEditors/FileUploadConfiguration.cs b/src/Umbraco.Web/PropertyEditors/FileUploadConfiguration.cs new file mode 100644 index 0000000000..55f947797a --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/FileUploadConfiguration.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Web.PropertyEditors +{ + /// + /// Represents the configuration for the file upload address value editor. + /// + public class FileUploadConfiguration : IFileExtensionsConfig + { + [ConfigurationField("fileExtensions", "Accepted file extensions", "multivalues")] + public List FileExtensions { get; set; } = new List(); + } +} diff --git a/src/Umbraco.Web/PropertyEditors/FileUploadConfigurationEditor.cs b/src/Umbraco.Web/PropertyEditors/FileUploadConfigurationEditor.cs new file mode 100644 index 0000000000..abbd19a793 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/FileUploadConfigurationEditor.cs @@ -0,0 +1,12 @@ +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Web.PropertyEditors +{ + /// + /// Represents the configuration editor for the file upload value editor. + /// + public class FileUploadConfigurationEditor : ConfigurationEditor + { + + } +} diff --git a/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs index 052af18aa1..a105d490be 100644 --- a/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs @@ -32,6 +32,10 @@ namespace Umbraco.Web.PropertyEditors _uploadAutoFillProperties = new UploadAutoFillProperties(_mediaFileSystem, logger, contentSection); } + + /// + protected override IConfigurationEditor CreateConfigurationEditor() => new FileUploadConfigurationEditor(); + /// /// Creates the corresponding property value editor. /// diff --git a/src/Umbraco.Web/PropertyEditors/IFileExtensionConfig.cs b/src/Umbraco.Web/PropertyEditors/IFileExtensionConfig.cs new file mode 100644 index 0000000000..c4934540c7 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/IFileExtensionConfig.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using Umbraco.Web.PropertyEditors; + +namespace Umbraco.Core.PropertyEditors +{ + /// + /// Marker interface for any editor configuration that supports defining file extensions + /// + public interface IFileExtensionsConfig + { + List FileExtensions { get; set; } + } +} diff --git a/src/Umbraco.Web/PropertyEditors/IFileExtensionConfigItem.cs b/src/Umbraco.Web/PropertyEditors/IFileExtensionConfigItem.cs new file mode 100644 index 0000000000..682e881565 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/IFileExtensionConfigItem.cs @@ -0,0 +1,11 @@ +using Newtonsoft.Json; + +namespace Umbraco.Web.PropertyEditors +{ + public interface IFileExtensionConfigItem + { + int Id { get; set; } + + string Value { get; set; } + } +} diff --git a/src/Umbraco.Web/PropertyEditors/MediaPicker3Configuration.cs b/src/Umbraco.Web/PropertyEditors/MediaPicker3Configuration.cs new file mode 100644 index 0000000000..4c3c6564a5 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/MediaPicker3Configuration.cs @@ -0,0 +1,60 @@ +using Newtonsoft.Json; +using Umbraco.Core; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Web.PropertyEditors +{ + /// + /// Represents the configuration for the media picker value editor. + /// + public class MediaPicker3Configuration : IIgnoreUserStartNodesConfig + { + [ConfigurationField("filter", "Accepted types", "treesourcetypepicker", + Description = "Limit to specific types")] + public string Filter { get; set; } + + [ConfigurationField("multiple", "Pick multiple items", "boolean", Description = "Outputs a IEnumerable")] + public bool Multiple { get; set; } + + [ConfigurationField("validationLimit", "Amount", "numberrange", Description = "Set a required range of medias")] + public NumberRange ValidationLimit { get; set; } = new NumberRange(); + + public class NumberRange + { + [JsonProperty("min")] + public int? Min { get; set; } + + [JsonProperty("max")] + public int? Max { get; set; } + } + + [ConfigurationField("startNodeId", "Start node", "mediapicker")] + public Udi StartNodeId { get; set; } + + [ConfigurationField(Core.Constants.DataTypes.ReservedPreValueKeys.IgnoreUserStartNodes, + "Ignore User Start Nodes", "boolean", + Description = "Selecting this option allows a user to choose nodes that they normally don't have access to.")] + public bool IgnoreUserStartNodes { get; set; } + + [ConfigurationField("enableLocalFocalPoint", "Enable Focal Point", "boolean")] + public bool EnableLocalFocalPoint { get; set; } + + [ConfigurationField("crops", "Image Crops", "views/propertyeditors/MediaPicker3/prevalue/mediapicker3.crops.html", Description = "Local crops, stored on document")] + public CropConfiguration[] Crops { get; set; } + + public class CropConfiguration + { + [JsonProperty("alias")] + public string Alias { get; set; } + + [JsonProperty("label")] + public string Label { get; set; } + + [JsonProperty("width")] + public int Width { get; set; } + + [JsonProperty("height")] + public int Height { get; set; } + } + } +} diff --git a/src/Umbraco.Web/PropertyEditors/MediaPicker3ConfigurationEditor.cs b/src/Umbraco.Web/PropertyEditors/MediaPicker3ConfigurationEditor.cs new file mode 100644 index 0000000000..37063aa153 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/MediaPicker3ConfigurationEditor.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Web.PropertyEditors +{ + /// + /// Represents the configuration editor for the media picker value editor. + /// + public class MediaPicker3ConfigurationEditor : ConfigurationEditor + { + /// + /// Initializes a new instance of the class. + /// + public MediaPicker3ConfigurationEditor() + { + // configure fields + // this is not part of ContentPickerConfiguration, + // but is required to configure the UI editor (when editing the configuration) + + Field(nameof(MediaPicker3Configuration.StartNodeId)) + .Config = new Dictionary { { "idType", "udi" } }; + + Field(nameof(MediaPicker3Configuration.Filter)) + .Config = new Dictionary { { "itemType", "media" } }; + } + } +} diff --git a/src/Umbraco.Web/PropertyEditors/MediaPicker3PropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/MediaPicker3PropertyEditor.cs new file mode 100644 index 0000000000..526b4830c8 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/MediaPicker3PropertyEditor.cs @@ -0,0 +1,64 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Umbraco.Core; +using Umbraco.Core.Logging; +using Umbraco.Core.Models.Editors; +using Umbraco.Core.PropertyEditors; +using Umbraco.Web.PropertyEditors.ValueConverters; + +namespace Umbraco.Web.PropertyEditors +{ + /// + /// Represents a media picker property editor. + /// + [DataEditor( + Constants.PropertyEditors.Aliases.MediaPicker3, + EditorType.PropertyValue, + "Media Picker v3", + "mediapicker3", + ValueType = ValueTypes.Json, + Group = Constants.PropertyEditors.Groups.Media, + Icon = Constants.Icons.MediaImage)] + public class MediaPicker3PropertyEditor : DataEditor + { + /// + /// Initializes a new instance of the class. + /// + public MediaPicker3PropertyEditor(ILogger logger) + : base(logger) + { + } + + /// + protected override IConfigurationEditor CreateConfigurationEditor() => new MediaPicker3ConfigurationEditor(); + + protected override IDataValueEditor CreateValueEditor() => new MediaPicker3PropertyValueEditor(Attribute); + + internal class MediaPicker3PropertyValueEditor : DataValueEditor, IDataValueReference + { + /// + /// Note: no FromEditor() and ToEditor() methods + /// We do not want to transform the way the data is stored in the DB and would like to keep a raw JSON string + /// + public MediaPicker3PropertyValueEditor(DataEditorAttribute attribute) : base(attribute) + { + } + + public IEnumerable GetReferences(object value) + { + var rawJson = value == null ? string.Empty : value is string str ? str : value.ToString(); + + if (rawJson.IsNullOrWhiteSpace()) + yield break; + + var mediaWithCropsDtos = JsonConvert.DeserializeObject(rawJson); + + foreach (var mediaWithCropsDto in mediaWithCropsDtos) + { + yield return new UmbracoEntityReference(GuidUdi.Create(Constants.UdiEntityType.Media, mediaWithCropsDto.MediaKey)); + } + } + + } + } +} diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerWithCropsValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerWithCropsValueConverter.cs new file mode 100644 index 0000000000..f9b2ad75e1 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerWithCropsValueConverter.cs @@ -0,0 +1,119 @@ +using Newtonsoft.Json; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.Serialization; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.PropertyEditors.ValueConverters; +using Umbraco.Web.PublishedCache; + +namespace Umbraco.Web.PropertyEditors.ValueConverters +{ + [DefaultPropertyValueConverter] + public class MediaPickerWithCropsValueConverter : PropertyValueConverterBase + { + + private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; + + public MediaPickerWithCropsValueConverter(IPublishedSnapshotAccessor publishedSnapshotAccessor) + { + _publishedSnapshotAccessor = publishedSnapshotAccessor ?? throw new ArgumentNullException(nameof(publishedSnapshotAccessor)); + } + + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Snapshot; + + /// + /// Enusre this property value convertor is for the New Media Picker with Crops aka MediaPicker 3 + /// + public override bool IsConverter(IPublishedPropertyType propertyType) => propertyType.EditorAlias.Equals(Core.Constants.PropertyEditors.Aliases.MediaPicker3); + + /// + /// Check if the raw JSON value is not an empty array + /// + public override bool? IsValue(object value, PropertyValueLevel level) => value?.ToString() != "[]"; + + /// + /// What C# model type does the raw JSON return for Models & Views + /// + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) + { + // Check do we want to return IPublishedContent collection still or a NEW model ? + var isMultiple = IsMultipleDataType(propertyType.DataType); + return isMultiple + ? typeof(IEnumerable) + : typeof(MediaWithCrops); + } + + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) => source?.ToString(); + + public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + { + var mediaItems = new List(); + var isMultiple = IsMultipleDataType(propertyType.DataType); + if (inter == null) + { + return isMultiple ? mediaItems: null; + } + + var dtos = JsonConvert.DeserializeObject>(inter.ToString()); + + foreach(var media in dtos) + { + var item = _publishedSnapshotAccessor.PublishedSnapshot.Media.GetById(media.MediaKey); + if (item != null) + { + mediaItems.Add(new MediaWithCrops + { + MediaItem = item, + LocalCrops = new ImageCropperValue + { + Crops = media.Crops, + FocalPoint = media.FocalPoint, + Src = item.Url() + } + }); + } + } + + return isMultiple ? mediaItems : FirstOrDefault(mediaItems); + } + + /// + /// Is the media picker configured to pick multiple media items + /// + /// + /// + private bool IsMultipleDataType(PublishedDataType dataType) + { + var config = dataType.ConfigurationAs(); + return config.Multiple; + } + + private object FirstOrDefault(IList mediaItems) + { + return mediaItems.Count == 0 ? null : mediaItems[0]; + } + + + /// + /// Model/DTO that represents the JSON that the MediaPicker3 stores + /// + [DataContract] + internal class MediaWithCropsDto + { + [DataMember(Name = "key")] + public Guid Key { get; set; } + + [DataMember(Name = "mediaKey")] + public Guid MediaKey { get; set; } + + [DataMember(Name = "crops")] + public IEnumerable Crops { get; set; } + + [DataMember(Name = "focalPoint")] + public ImageCropperValue.ImageCropperFocalPoint FocalPoint { get; set; } + } + } +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index a6cbefa825..ff988cf5bf 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -254,9 +254,17 @@ + + + + + + + + @@ -266,6 +274,7 @@ + diff --git a/src/Umbraco.Web/UrlHelperRenderExtensions.cs b/src/Umbraco.Web/UrlHelperRenderExtensions.cs index 0f5b0557f4..592c88945b 100644 --- a/src/Umbraco.Web/UrlHelperRenderExtensions.cs +++ b/src/Umbraco.Web/UrlHelperRenderExtensions.cs @@ -262,6 +262,32 @@ namespace Umbraco.Web return htmlEncode ? new HtmlString(HttpUtility.HtmlEncode(url)) : new HtmlString(url); } + public static IHtmlString GetCropUrl(this UrlHelper urlHelper, + ImageCropperValue imageCropperValue, + string cropAlias, + int? width = null, + int? height = null, + int? quality = null, + ImageCropMode? imageCropMode = null, + ImageCropAnchor? imageCropAnchor = null, + bool preferFocalPoint = false, + bool useCropDimensions = true, + string cacheBusterValue = null, + string furtherOptions = null, + ImageCropRatioMode? ratioMode = null, + bool upScale = true, + bool htmlEncode = true) + { + if (imageCropperValue == null) return EmptyHtmlString; + + var imageUrl = imageCropperValue.Src; + var url = imageUrl.GetCropUrl(imageCropperValue, width, height, cropAlias, quality, imageCropMode, + imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBusterValue, furtherOptions, ratioMode, + upScale); + return htmlEncode ? new HtmlString(HttpUtility.HtmlEncode(url)) : new HtmlString(url); + } + + #endregion /// From 0dfefb72cb90bb8f2e35a2a78ecbbe17396b6ef1 Mon Sep 17 00:00:00 2001 From: Arkadiusz Biel Date: Sat, 31 Oct 2020 22:09:39 +0000 Subject: [PATCH 016/182] use v8 branch as default for API Docs --- src/ApiDocs/docfx.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ApiDocs/docfx.json b/src/ApiDocs/docfx.json index 76677d45d5..8283b351c4 100644 --- a/src/ApiDocs/docfx.json +++ b/src/ApiDocs/docfx.json @@ -50,6 +50,7 @@ ], "globalMetadata": { "_appTitle": "Umbraco c# Api docs", + "branch": "v8/contrib", "_enableSearch": true, "_disableContribution": false }, From c885831e02a8c2e4b312dec48945ca5d491130a6 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Thu, 22 Apr 2021 16:34:11 +0200 Subject: [PATCH 017/182] Revert "use v8 branch as default for API Docs" This reverts commit 0dfefb72cb90bb8f2e35a2a78ecbbe17396b6ef1. --- src/ApiDocs/docfx.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ApiDocs/docfx.json b/src/ApiDocs/docfx.json index 8283b351c4..76677d45d5 100644 --- a/src/ApiDocs/docfx.json +++ b/src/ApiDocs/docfx.json @@ -50,7 +50,6 @@ ], "globalMetadata": { "_appTitle": "Umbraco c# Api docs", - "branch": "v8/contrib", "_enableSearch": true, "_disableContribution": false }, From 4369747c720d2b5fa7e3c8b7c3617e3ba29a4421 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 22 Apr 2021 16:55:18 +0200 Subject: [PATCH 018/182] skip client side validation --- .../components/content/edit.controller.js | 87 +++++++++---------- 1 file changed, 39 insertions(+), 48 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js index 196c885b4e..bce797d5c8 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js @@ -442,7 +442,6 @@ // This is a helper method to reduce the amount of code repitition for actions: Save, Publish, SendToPublish function performSave(args) { - //Used to check validility of nested form - coming from Content Apps mostly //Set them all to be invalid var fieldsToRollback = checkValidility(); @@ -476,8 +475,6 @@ return $q.when(data); }, function (err) { - - syncTreeNode($scope.content, $scope.content.path); if($scope.contentForm.$invalid !== true) { @@ -739,54 +736,48 @@ clearNotifications($scope.content); // TODO: Add "..." to save button label if there are more than one variant to publish - currently it just adds the elipses if there's more than 1 variant if (hasVariants($scope.content)) { - - //before we launch the dialog we want to execute all client side validations first - if (formHelper.submitForm({ scope: $scope, action: "openSaveDialog", skipValidation:true, keepServerValidation:true })) { - var dialog = { - parentScope: $scope, - view: "views/content/overlays/save.html", - variants: $scope.content.variants, //set a model property for the dialog - skipFormValidation: true, //when submitting the overlay form, skip any client side validation - submitButtonLabelKey: "buttons_save", - submit: function (model) { - model.submitButtonState = "busy"; + var dialog = { + parentScope: $scope, + view: "views/content/overlays/save.html", + variants: $scope.content.variants, //set a model property for the dialog + skipFormValidation: true, //when submitting the overlay form, skip any client side validation + submitButtonLabelKey: "buttons_save", + submit: function (model) { + model.submitButtonState = "busy"; + clearNotifications($scope.content); + //we need to return this promise so that the dialog can handle the result and wire up the validation response + return performSave({ + saveMethod: $scope.saveMethod(), + action: "save", + showNotifications: false, + skipValidation: true + }).then(function (data) { + //show all notifications manually here since we disabled showing them automatically in the save method + formHelper.showNotifications(data); clearNotifications($scope.content); - //we need to return this promise so that the dialog can handle the result and wire up the validation response - return performSave({ - saveMethod: $scope.saveMethod(), - action: "save", - showNotifications: false - }).then(function (data) { - //show all notifications manually here since we disabled showing them automatically in the save method - formHelper.showNotifications(data); - clearNotifications($scope.content); - overlayService.close(); - return $q.when(data); - }, - function (err) { - clearDirtyState($scope.content.variants); - //model.submitButtonState = "error"; - // Because this is the "save"-action, then we actually save though there was a validation error, therefor we will show success and display the validation errors politely. - if(err && err.data && err.data.ModelState && Object.keys(err.data.ModelState).length > 0) { - model.submitButtonState = "success"; - } else { - model.submitButtonState = "error"; - } - //re-map the dialog model since we've re-bound the properties - dialog.variants = $scope.content.variants; - handleHttpException(err); - }); - }, - close: function (oldModel) { overlayService.close(); - } - }; + return $q.when(data); + }, + function (err) { + clearDirtyState($scope.content.variants); + //model.submitButtonState = "error"; + // Because this is the "save"-action, then we actually save though there was a validation error, therefor we will show success and display the validation errors politely. + if(err && err.data && err.data.ModelState && Object.keys(err.data.ModelState).length > 0) { + model.submitButtonState = "success"; + } else { + model.submitButtonState = "error"; + } + //re-map the dialog model since we've re-bound the properties + dialog.variants = $scope.content.variants; + handleHttpException(err); + }); + }, + close: function (oldModel) { + overlayService.close(); + } + }; - overlayService.open(dialog); - } - else { - showValidationNotification(); - } + overlayService.open(dialog); } else { //ensure the flags are set From 306c82f871a0346b04322b604ceaf569a3861fbd Mon Sep 17 00:00:00 2001 From: nzdev <834725+nzdev@users.noreply.github.com> Date: Fri, 23 Apr 2021 17:03:32 +1200 Subject: [PATCH 019/182] Change log to Info --- .../PublishedCache/NuCache/PublishedSnapshotService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index a592fed4be..58892057d2 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -225,7 +225,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var okMedia = false; if (_syncBootStateAccessor.GetSyncBootState() == SyncBootState.ColdBoot) { - _logger.Warn("Sync Service is in a Cold Boot state. Skip LoadCachesOnStartup as the Sync Service will trigger a full reload"); + _logger.Info("Sync Service is in a Cold Boot state. Skip LoadCachesOnStartup as the Sync Service will trigger a full reload"); _isReady = true; return; } From bc25e82767bd1ae44c3cd613b0e20fd97238ce99 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Fri, 23 Apr 2021 13:32:42 +0200 Subject: [PATCH 020/182] https://github.com/umbraco/Umbraco-CMS/issues/10179 Execute AuthenticateAsync and AuthorizeAsync on nested requests --- .../Extensions/ControllerContextExtensions.cs | 31 +++++++++++++++++++ .../Trees/ApplicationTreeController.cs | 6 ++-- .../FeatureAuthorizeRequirement.cs | 4 +++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.BackOffice/Extensions/ControllerContextExtensions.cs b/src/Umbraco.Web.BackOffice/Extensions/ControllerContextExtensions.cs index 98efd7a92d..658d5bfc0d 100644 --- a/src/Umbraco.Web.BackOffice/Extensions/ControllerContextExtensions.cs +++ b/src/Umbraco.Web.BackOffice/Extensions/ControllerContextExtensions.cs @@ -2,8 +2,12 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Authorization.Policy; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.DependencyInjection; namespace Umbraco.Cms.Web.BackOffice.Extensions { @@ -15,8 +19,35 @@ namespace Umbraco.Cms.Web.BackOffice.Extensions /// Whether the user is authenticated or not. internal static async Task InvokeAuthorizationFiltersForRequest(this ControllerContext controllerContext, ActionContext actionContext) { + var actionDescriptor = controllerContext.ActionDescriptor; + var metadataCollection = new EndpointMetadataCollection(actionDescriptor.EndpointMetadata.Union(new []{actionDescriptor})); + + var authorizeData = metadataCollection.GetOrderedMetadata(); + var policyProvider = controllerContext.HttpContext.RequestServices.GetRequiredService(); + var policy = await AuthorizationPolicy.CombineAsync(policyProvider, authorizeData); + if (policy is not null) + { + var policyEvaluator = controllerContext.HttpContext.RequestServices.GetRequiredService(); + var authenticateResult = await policyEvaluator.AuthenticateAsync(policy, controllerContext.HttpContext); + + if (!authenticateResult.Succeeded) + { + return false; + } + + // TODO this is super hacky, but we rely on the FeatureAuthorizeHandler can still handle endpoints + // (The way before .NET 5). The .NET 5 way would need to use han http context, for the "inner" request + // with the nested controller + var resource = new Endpoint(null,metadataCollection, null); + var authorizeResult = await policyEvaluator.AuthorizeAsync(policy, authenticateResult, controllerContext.HttpContext, resource); + if (!authorizeResult.Succeeded) + { + return false; + } + } + var filters = actionDescriptor.FilterDescriptors; var filterGrouping = new FilterGrouping(filters, controllerContext.HttpContext.RequestServices); diff --git a/src/Umbraco.Web.BackOffice/Trees/ApplicationTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/ApplicationTreeController.cs index 0769ad79de..077207913b 100644 --- a/src/Umbraco.Web.BackOffice/Trees/ApplicationTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/ApplicationTreeController.cs @@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Primitives; +using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models.Trees; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Trees; @@ -18,7 +19,6 @@ using Umbraco.Cms.Web.Common.Filters; using Umbraco.Cms.Web.Common.ModelBinders; using Umbraco.Extensions; using static Umbraco.Cms.Core.Constants.Web.Routing; -using Constants = Umbraco.Cms.Core.Constants; namespace Umbraco.Cms.Web.BackOffice.Trees { @@ -150,11 +150,11 @@ namespace Umbraco.Cms.Web.BackOffice.Trees foreach (var t in trees) { var nodeResult = await TryGetRootNode(t, queryStrings); - if (!(nodeResult.Result is null)) + if (nodeResult != null && nodeResult.Result is not null) { return nodeResult.Result; } - var node = nodeResult.Value; + var node = nodeResult?.Value; if (node != null) { diff --git a/src/Umbraco.Web.Common/Authorization/FeatureAuthorizeRequirement.cs b/src/Umbraco.Web.Common/Authorization/FeatureAuthorizeRequirement.cs index 5845df902c..93605192aa 100644 --- a/src/Umbraco.Web.Common/Authorization/FeatureAuthorizeRequirement.cs +++ b/src/Umbraco.Web.Common/Authorization/FeatureAuthorizeRequirement.cs @@ -8,5 +8,9 @@ namespace Umbraco.Cms.Web.Common.Authorization /// public class FeatureAuthorizeRequirement : IAuthorizationRequirement { + public FeatureAuthorizeRequirement() + { + } + } } From 22afe5e341372540357ed2380f125e6b1b9e2913 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 26 Apr 2021 15:40:46 +1000 Subject: [PATCH 021/182] Adds cold boot restart to load test proj --- src/Umbraco.TestData/LoadTestComponent.cs | 35 ++++++++++++++ src/Umbraco.TestData/LoadTestComposer.cs | 29 +++++++++++ src/Umbraco.TestData/LoadTestController.cs | 51 +++++--------------- src/Umbraco.TestData/Umbraco.TestData.csproj | 2 + 4 files changed, 77 insertions(+), 40 deletions(-) create mode 100644 src/Umbraco.TestData/LoadTestComponent.cs create mode 100644 src/Umbraco.TestData/LoadTestComposer.cs diff --git a/src/Umbraco.TestData/LoadTestComponent.cs b/src/Umbraco.TestData/LoadTestComponent.cs new file mode 100644 index 0000000000..97c006520d --- /dev/null +++ b/src/Umbraco.TestData/LoadTestComponent.cs @@ -0,0 +1,35 @@ +using System.Web.Mvc; +using System.Web.Routing; +using Umbraco.Core.Composing; +using System.Configuration; + +// see https://github.com/Shazwazza/UmbracoScripts/tree/master/src/LoadTesting + +namespace Umbraco.TestData +{ + public class LoadTestComponent : IComponent + { + public void Initialize() + { + if (ConfigurationManager.AppSettings["Umbraco.TestData.Enabled"] != "true") + return; + + + + RouteTable.Routes.MapRoute( + name: "LoadTest", + url: "LoadTest/{action}", + defaults: new + { + controller = "LoadTest", + action = "Index" + }, + namespaces: new[] { "Umbraco.TestData" } + ); + } + + public void Terminate() + { + } + } +} diff --git a/src/Umbraco.TestData/LoadTestComposer.cs b/src/Umbraco.TestData/LoadTestComposer.cs new file mode 100644 index 0000000000..2c5e404642 --- /dev/null +++ b/src/Umbraco.TestData/LoadTestComposer.cs @@ -0,0 +1,29 @@ +using Umbraco.Core.Composing; +using System.Configuration; +using Umbraco.Web.PublishedCache.NuCache; + +// see https://github.com/Shazwazza/UmbracoScripts/tree/master/src/LoadTesting + +namespace Umbraco.TestData +{ + public class LoadTestComposer : ComponentComposer, IUserComposer + { + public override void Compose(Composition composition) + { + base.Compose(composition); + + if (ConfigurationManager.AppSettings["Umbraco.TestData.Enabled"] != "true") + return; + + composition.Register(typeof(LoadTestController), Lifetime.Request); + + if (ConfigurationManager.AppSettings["Umbraco.TestData.IgnoreLocalDb"] == "true") + { + composition.Register(factory => new PublishedSnapshotServiceOptions + { + IgnoreLocalDb = true + }); + } + } + } +} diff --git a/src/Umbraco.TestData/LoadTestController.cs b/src/Umbraco.TestData/LoadTestController.cs index 97665dd084..8e1faf56d1 100644 --- a/src/Umbraco.TestData/LoadTestController.cs +++ b/src/Umbraco.TestData/LoadTestController.cs @@ -6,10 +6,9 @@ using Umbraco.Core.Services; using Umbraco.Core.Models; using System.Web; using System.Web.Hosting; -using System.Web.Routing; using System.Diagnostics; -using Umbraco.Core.Composing; -using System.Configuration; +using Umbraco.Core.IO; +using System.IO; // see https://github.com/Shazwazza/UmbracoScripts/tree/master/src/LoadTesting @@ -261,6 +260,15 @@ namespace Umbraco.TestData HttpRuntime.UnloadAppDomain(); } + public ActionResult ColdBootRestart() + { + Directory.Delete(IOHelper.MapPath("~/App_Data/TEMP/DistCache"), true); + + DoRestart(); + + return Content("Cold Boot Restarted."); + } + public ActionResult Restart() { DoRestart(); @@ -331,41 +339,4 @@ namespace Umbraco.TestData return t; } } - - public class TestComponent : IComponent - { - public void Initialize() - { - if (ConfigurationManager.AppSettings["Umbraco.TestData.Enabled"] != "true") - return; - - RouteTable.Routes.MapRoute( - name: "LoadTest", - url: "LoadTest/{action}", - defaults: new - { - controller = "LoadTest", - action = "Index" - }, - namespaces: new[] { "Umbraco.TestData" } - ); - } - - public void Terminate() - { - } - } - - public class TestComposer : ComponentComposer, IUserComposer - { - public override void Compose(Composition composition) - { - base.Compose(composition); - - if (ConfigurationManager.AppSettings["Umbraco.TestData.Enabled"] != "true") - return; - - composition.Register(typeof(LoadTestController), Lifetime.Request); - } - } } diff --git a/src/Umbraco.TestData/Umbraco.TestData.csproj b/src/Umbraco.TestData/Umbraco.TestData.csproj index a3753cc17b..113d209ef4 100644 --- a/src/Umbraco.TestData/Umbraco.TestData.csproj +++ b/src/Umbraco.TestData/Umbraco.TestData.csproj @@ -41,6 +41,8 @@ + + From 053a56a45bc8d6b9917513a84d9953039790f31a Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 26 Apr 2021 18:07:23 +1000 Subject: [PATCH 022/182] Removes the callbacks from DatabaseServerMessenger, moves logic for cold boot into the components responsible for what needs to happen in cold boot. This is still not ideal but we are stuck with backwards compat. In netcore the initialization will be fixed up (if not already). Ensure examine rebuilds don't occur until the first http request is done instead of on a timer which could be problematic with cold boots. --- .../Sync/DatabaseServerMessenger.cs | 143 ++++++++---------- .../Sync/DatabaseServerMessengerOptions.cs | 10 +- .../Sync/ISyncBootStateAccessor.cs | 9 +- .../Sync/NonRuntimeLevelBootStateAccessor.cs | 2 + src/Umbraco.Core/Sync/SyncBootState.cs | 14 +- .../PublishedContent/NuCacheChildrenTests.cs | 2 +- .../PublishedContent/NuCacheTests.cs | 2 +- .../Scoping/ScopedNuCacheTests.cs | 2 +- .../ContentTypeServiceVariantsTests.cs | 2 +- .../TestHelpers/TestSyncBootStateAccessor.cs | 3 + ...aseServerRegistrarAndMessengerComponent.cs | 75 +-------- ...baseServerRegistrarAndMessengerComposer.cs | 38 +++++ .../NuCache/PublishedSnapshotService.cs | 24 +-- .../Search/ExamineFinalComponent.cs | 58 +++++-- src/Umbraco.Web/Umbraco.Web.csproj | 3 +- 15 files changed, 198 insertions(+), 189 deletions(-) create mode 100644 src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComposer.cs diff --git a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs index ebc77dbdca..2b52a0948e 100644 --- a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs +++ b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs @@ -39,9 +39,9 @@ namespace Umbraco.Core.Sync private int _lastId = -1; private DateTime _lastSync; private DateTime _lastPruned; - private bool _initialized; private bool _syncing; private bool _released; + private readonly Lazy _getSyncBootState; public DatabaseServerMessengerOptions Options { get; } @@ -59,6 +59,7 @@ namespace Umbraco.Core.Sync _lastPruned = _lastSync = DateTime.UtcNow; _syncIdle = new ManualResetEvent(true); _distCacheFilePath = new Lazy(() => GetDistCacheFilePath(globalSettings)); + _getSyncBootState = new Lazy(BootInternal); } protected ILogger Logger { get; } @@ -75,7 +76,7 @@ namespace Umbraco.Core.Sync { // we don't care if there's servers listed or not, // if distributed call is enabled we will make the call - return _initialized && DistributedEnabled; + return _getSyncBootState.IsValueCreated && DistributedEnabled; } protected override void DeliverRemote( @@ -118,6 +119,12 @@ namespace Umbraco.Core.Sync /// Callers MUST ensure thread-safety. /// protected void Boot() + { + var bootState = GetSyncBootState(); + Booting?.Invoke(this, bootState); + } + + private SyncBootState BootInternal() { // weight:10, must release *before* the published snapshot service, because once released // the service will *not* be able to properly handle our notifications anymore @@ -139,7 +146,7 @@ namespace Umbraco.Core.Sync // properly releasing MainDom - a timeout here means that one refresher // is taking too much time processing, however when it's done we will // not update lastId and stop everything - var idle =_syncIdle.WaitOne(5000); + var idle = _syncIdle.WaitOne(5000); if (idle == false) { Logger.Warn("The wait lock timed out, application is shutting down. The current instruction batch will be re-processed."); @@ -147,17 +154,23 @@ namespace Umbraco.Core.Sync }, weight); + SyncBootState bootState = SyncBootState.Unknown; + if (registered == false) - return; + { + return bootState; + } ReadLastSynced(); // get _lastId using (var scope = ScopeProvider.CreateScope()) { EnsureInstructions(scope.Database); // reset _lastId if instructions are missing - Initialize(scope.Database); // boot + bootState = Initialize(scope.Database); // boot scope.Complete(); } + + return bootState; } /// @@ -167,36 +180,11 @@ namespace Umbraco.Core.Sync /// Thread safety: this is NOT thread safe. Because it is NOT meant to run multi-threaded. /// Callers MUST ensure thread-safety. /// - private void Initialize(IUmbracoDatabase database) + private SyncBootState Initialize(IUmbracoDatabase database) { - lock (_locko) - { - if (_released) return; - var coldboot = IsColdBoot(database); + // could occur if shutting down immediately once starting up and before we've initialized + if (_released) return SyncBootState.Unknown; - if (coldboot) - { - // go get the last id in the db and store it - // note: do it BEFORE initializing otherwise some instructions might get lost - // when doing it before, some instructions might run twice - not an issue - var maxId = database.ExecuteScalar("SELECT MAX(id) FROM umbracoCacheInstruction"); - - //if there is a max currently, or if we've never synced - if (maxId > 0 || _lastId < 0) - SaveLastSynced(maxId); - - // execute initializing callbacks - if (Options.InitializingCallbacks != null) - foreach (var callback in Options.InitializingCallbacks) - callback(); - } - - _initialized = true; - } - } - - private bool IsColdBoot(IUmbracoDatabase database) - { var coldboot = false; if (_lastId < 0) // never synced before { @@ -206,27 +194,48 @@ namespace Umbraco.Core.Sync + " The server will build its caches and indexes, and then adjust its last synced Id to the latest found in" + " the database and maintain cache updates based on that Id."); - coldboot = true; - } - else + coldboot = true; + } + else + { + //check for how many instructions there are to process, each row contains a count of the number of instructions contained in each + //row so we will sum these numbers to get the actual count. + var count = database.ExecuteScalar("SELECT SUM(instructionCount) FROM umbracoCacheInstruction WHERE id > @lastId", new { lastId = _lastId }); + if (count > Options.MaxProcessingInstructionCount) { - //check for how many instructions there are to process, each row contains a count of the number of instructions contained in each - //row so we will sum these numbers to get the actual count. - var count = database.ExecuteScalar("SELECT SUM(instructionCount) FROM umbracoCacheInstruction WHERE id > @lastId", new {lastId = _lastId}); - if (count > Options.MaxProcessingInstructionCount) - { - //too many instructions, proceed to cold boot - Logger.Warn( - "The instruction count ({InstructionCount}) exceeds the specified MaxProcessingInstructionCount ({MaxProcessingInstructionCount})." - + " The server will skip existing instructions, rebuild its caches and indexes entirely, adjust its last synced Id" - + " to the latest found in the database and maintain cache updates based on that Id.", - count, Options.MaxProcessingInstructionCount); + //too many instructions, proceed to cold boot + Logger.Warn( + "The instruction count ({InstructionCount}) exceeds the specified MaxProcessingInstructionCount ({MaxProcessingInstructionCount})." + + " The server will skip existing instructions, rebuild its caches and indexes entirely, adjust its last synced Id" + + " to the latest found in the database and maintain cache updates based on that Id.", + count, Options.MaxProcessingInstructionCount); coldboot = true; } } - return coldboot; + if (coldboot) + { + // go get the last id in the db and store it + // note: do it BEFORE initializing otherwise some instructions might get lost + // when doing it before, some instructions might run twice - not an issue + var maxId = database.ExecuteScalar("SELECT MAX(id) FROM umbracoCacheInstruction"); + + //if there is a max currently, or if we've never synced + if (maxId > 0 || _lastId < 0) + SaveLastSynced(maxId); + + // execute initializing callbacks + if (Options.InitializingCallbacks != null) + { + foreach (var callback in Options.InitializingCallbacks) + { + callback(); + } + } + } + + return coldboot ? SyncBootState.ColdBoot : SyncBootState.WarmBoot; } /// @@ -358,7 +367,7 @@ namespace Umbraco.Core.Sync } catch (JsonException ex) { - Logger.Error(ex, "Failed to deserialize instructions ({DtoId}: '{DtoInstructions}').", + Logger.Error(ex, "Failed to deserialize instructions ({DtoId}: '{DtoInstructions}').", dto.Id, dto.Instructions); @@ -416,11 +425,11 @@ namespace Umbraco.Core.Sync //} catch (Exception ex) { - Logger.Error ( - ex, - "DISTRIBUTED CACHE IS NOT UPDATED. Failed to execute instructions ({DtoId}: '{DtoInstructions}'). Instruction is being skipped/ignored", - dto.Id, - dto.Instructions); + Logger.Error( + ex, + "DISTRIBUTED CACHE IS NOT UPDATED. Failed to execute instructions ({DtoId}: '{DtoInstructions}'). Instruction is being skipped/ignored", + dto.Id, + dto.Instructions); //we cannot throw here because this invalid instruction will just keep getting processed over and over and errors // will be thrown over and over. The only thing we can do is ignore and move on. @@ -536,6 +545,8 @@ namespace Umbraco.Core.Sync + "/D" + AppDomain.CurrentDomain.Id // eg 22 + "] " + Guid.NewGuid().ToString("N").ToUpper(); // make it truly unique + public event EventHandler Booting; + private string GetDistCacheFilePath(IGlobalSettings globalSettings) { var fileName = HttpRuntime.AppDomainAppId.ReplaceNonAlphanumericChars(string.Empty) + "-lastsynced.txt"; @@ -554,29 +565,7 @@ namespace Umbraco.Core.Sync #endregion - public SyncBootState GetSyncBootState() - { - try - { - ReadLastSynced(); // get _lastId - using (var scope = ScopeProvider.CreateScope()) - { - EnsureInstructions(scope.Database); - bool isColdBoot = IsColdBoot(scope.Database); - - if (isColdBoot) - { - return SyncBootState.ColdBoot; - } - return SyncBootState.HasSyncState; - } - } - catch(Exception ex) - { - Logger.Warn("Error determining Sync Boot State", ex); - return SyncBootState.Unknown; - } - } + public SyncBootState GetSyncBootState() => _getSyncBootState.Value; #region Notify refreshers diff --git a/src/Umbraco.Core/Sync/DatabaseServerMessengerOptions.cs b/src/Umbraco.Core/Sync/DatabaseServerMessengerOptions.cs index 6bfd6bff4a..a769709d09 100644 --- a/src/Umbraco.Core/Sync/DatabaseServerMessengerOptions.cs +++ b/src/Umbraco.Core/Sync/DatabaseServerMessengerOptions.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; namespace Umbraco.Core.Sync { @@ -24,13 +25,8 @@ namespace Umbraco.Core.Sync /// public int MaxProcessingInstructionCount { get; set; } - /// - /// A list of callbacks that will be invoked if the lastsynced.txt file does not exist. - /// - /// - /// These callbacks will typically be for eg rebuilding the xml cache file, or examine indexes, based on - /// the data in the database to get this particular server node up to date. - /// + [Obsolete("This should not be used. If initialization calls need to be invoked on a cold boot, use the ISyncBootStateAccessor.Booting event.")] + [EditorBrowsable(EditorBrowsableState.Never)] public IEnumerable InitializingCallbacks { get; set; } /// diff --git a/src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs b/src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs index 4b8500f2d9..1598215d2c 100644 --- a/src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs +++ b/src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Umbraco.Core.Sync { @@ -16,5 +12,10 @@ namespace Umbraco.Core.Sync /// /// SyncBootState GetSyncBootState(); + + /// + /// Raised when the boot state is known + /// + event EventHandler Booting; } } diff --git a/src/Umbraco.Core/Sync/NonRuntimeLevelBootStateAccessor.cs b/src/Umbraco.Core/Sync/NonRuntimeLevelBootStateAccessor.cs index 70cec6cc96..cc0b7dfaf1 100644 --- a/src/Umbraco.Core/Sync/NonRuntimeLevelBootStateAccessor.cs +++ b/src/Umbraco.Core/Sync/NonRuntimeLevelBootStateAccessor.cs @@ -11,6 +11,8 @@ namespace Umbraco.Core.Sync /// public class NonRuntimeLevelBootStateAccessor : ISyncBootStateAccessor { + public event EventHandler Booting; + public SyncBootState GetSyncBootState() { return SyncBootState.Unknown; diff --git a/src/Umbraco.Core/Sync/SyncBootState.cs b/src/Umbraco.Core/Sync/SyncBootState.cs index 4abc53abba..45f7a87331 100644 --- a/src/Umbraco.Core/Sync/SyncBootState.cs +++ b/src/Umbraco.Core/Sync/SyncBootState.cs @@ -1,24 +1,20 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Umbraco.Core.Sync +namespace Umbraco.Core.Sync { public enum SyncBootState { /// - /// Unknown state. Treat as HasSyncState + /// Unknown state. Treat as WarmBoot /// Unknown = 0, + /// /// Cold boot. No Sync state /// ColdBoot = 1, + /// /// Warm boot. Sync state present /// - HasSyncState = 2 + WarmBoot = 2 } } diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs index 068a161268..36f6b9d479 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs @@ -157,7 +157,7 @@ namespace Umbraco.Tests.PublishedContent Mock.Of(), Mock.Of(), new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }), - new TestSyncBootStateAccessor(SyncBootState.HasSyncState)); + new TestSyncBootStateAccessor(SyncBootState.WarmBoot)); // invariant is the current default _variationAccesor.VariationContext = new VariationContext(); diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs index 652891c476..7cb4d97ea7 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs @@ -203,7 +203,7 @@ namespace Umbraco.Tests.PublishedContent Mock.Of(), Mock.Of(), new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }), - new TestSyncBootStateAccessor(SyncBootState.HasSyncState)); + new TestSyncBootStateAccessor(SyncBootState.WarmBoot)); // invariant is the current default _variationAccesor.VariationContext = new VariationContext(); diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index e204bd7b5f..4453bfbbdf 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -100,7 +100,7 @@ namespace Umbraco.Tests.Scoping Factory.GetInstance(), Mock.Of(), new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }), - new TestSyncBootStateAccessor(SyncBootState.HasSyncState)); + new TestSyncBootStateAccessor(SyncBootState.WarmBoot)); } protected UmbracoContext GetUmbracoContextNu(string url, int templateId = 1234, RouteData routeData = null, bool setSingleton = false, IUmbracoSettingsSection umbracoSettings = null, IEnumerable urlProviders = null) diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs index 9cd4bb63e8..7e3bc60411 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs @@ -72,7 +72,7 @@ namespace Umbraco.Tests.Services Factory.GetInstance(), Mock.Of(), new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }), - new TestSyncBootStateAccessor(SyncBootState.HasSyncState)); + new TestSyncBootStateAccessor(SyncBootState.WarmBoot)); } public class LocalServerMessenger : ServerMessengerBase diff --git a/src/Umbraco.Tests/TestHelpers/TestSyncBootStateAccessor.cs b/src/Umbraco.Tests/TestHelpers/TestSyncBootStateAccessor.cs index e5f6989381..3bff19ff2d 100644 --- a/src/Umbraco.Tests/TestHelpers/TestSyncBootStateAccessor.cs +++ b/src/Umbraco.Tests/TestHelpers/TestSyncBootStateAccessor.cs @@ -15,6 +15,9 @@ namespace Umbraco.Tests.TestHelpers { _syncBootState = syncBootState; } + + public event EventHandler Booting; + public SyncBootState GetSyncBootState() { return _syncBootState; diff --git a/src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs b/src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs index 26ba0db324..ef13e166a5 100644 --- a/src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs +++ b/src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs @@ -4,77 +4,13 @@ using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Logging; using Umbraco.Core.Services; -using Umbraco.Core.Services.Changes; using Umbraco.Core.Sync; using Umbraco.Examine; -using Umbraco.Web.Cache; using Umbraco.Web.Routing; using Umbraco.Web.Scheduling; -using Umbraco.Web.Search; -using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Web.Compose { - /// - /// Ensures that servers are automatically registered in the database, when using the database server registrar. - /// - /// - /// At the moment servers are automatically registered upon first request and then on every - /// request but not more than once per (configurable) period. This really is "for information & debug" purposes so - /// we can look at the table and see what servers are registered - but the info is not used anywhere. - /// Should we actually want to use this, we would need a better and more deterministic way of figuring - /// out the "server address" ie the address to which server-to-server requests should be sent - because it - /// probably is not the "current request address" - especially in multi-domains configurations. - /// - [RuntimeLevel(MinLevel = RuntimeLevel.Run)] - - // during Initialize / Startup, we end up checking Examine, which needs to be initialized beforehand - // TODO: should not be a strong dependency on "examine" but on an "indexing component" - [ComposeAfter(typeof(ExamineComposer))] - - public sealed class DatabaseServerRegistrarAndMessengerComposer : ComponentComposer, ICoreComposer - { - public static DatabaseServerMessengerOptions GetDefaultOptions(IFactory factory) - { - var logger = factory.GetInstance(); - var indexRebuilder = factory.GetInstance(); - - return new DatabaseServerMessengerOptions - { - //These callbacks will be executed if the server has not been synced - // (i.e. it is a new server or the lastsynced.txt file has been removed) - InitializingCallbacks = new Action[] - { - //rebuild the xml cache file if the server is not synced - () => - { - // rebuild the published snapshot caches entirely, if the server is not synced - // this is equivalent to DistributedCache RefreshAll... but local only - // (we really should have a way to reuse RefreshAll... locally) - // note: refresh all content & media caches does refresh content types too - var svc = Current.PublishedSnapshotService; - svc.Notify(new[] { new DomainCacheRefresher.JsonPayload(0, DomainChangeTypes.RefreshAll) }); - svc.Notify(new[] { new ContentCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) }, out _, out _); - svc.Notify(new[] { new MediaCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) }, out _); - }, - - //rebuild indexes if the server is not synced - // NOTE: This will rebuild ALL indexes including the members, if developers want to target specific - // indexes then they can adjust this logic themselves. - () => { ExamineComponent.RebuildIndexes(indexRebuilder, logger, false, 5000); } - } - }; - } - - public override void Compose(Composition composition) - { - base.Compose(composition); - - composition.SetDatabaseServerMessengerOptions(GetDefaultOptions); - composition.SetServerMessenger(); - composition.Register(factory=> factory.GetInstance() as BatchedDatabaseServerMessenger, Lifetime.Singleton); - } - } public sealed class DatabaseServerRegistrarAndMessengerComponent : IComponent { @@ -88,14 +24,17 @@ namespace Umbraco.Web.Compose private readonly BackgroundTaskRunner _processTaskRunner; private bool _started; private IBackgroundTask[] _tasks; - private IndexRebuilder _indexRebuilder; - public DatabaseServerRegistrarAndMessengerComponent(IRuntimeState runtime, IServerRegistrar serverRegistrar, IServerMessenger serverMessenger, IServerRegistrationService registrationService, ILogger logger, IndexRebuilder indexRebuilder) + public DatabaseServerRegistrarAndMessengerComponent( + IRuntimeState runtime, + IServerRegistrar serverRegistrar, + IServerMessenger serverMessenger, + IServerRegistrationService registrationService, + ILogger logger) { _runtime = runtime; _logger = logger; _registrationService = registrationService; - _indexRebuilder = indexRebuilder; // create task runner for DatabaseServerRegistrar _registrar = serverRegistrar as DatabaseServerRegistrar; @@ -118,7 +57,9 @@ namespace Umbraco.Web.Compose { //We will start the whole process when a successful request is made if (_registrar != null || _messenger != null) + { UmbracoModule.RouteAttempt += RegisterBackgroundTasksOnce; + } // must come last, as it references some _variables _messenger?.Startup(); diff --git a/src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComposer.cs b/src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComposer.cs new file mode 100644 index 0000000000..96e538652f --- /dev/null +++ b/src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComposer.cs @@ -0,0 +1,38 @@ +using Umbraco.Core; +using Umbraco.Core.Composing; +using Umbraco.Core.Sync; +using Umbraco.Web.Search; + +namespace Umbraco.Web.Compose +{ + /// + /// Ensures that servers are automatically registered in the database, when using the database server registrar. + /// + /// + /// At the moment servers are automatically registered upon first request and then on every + /// request but not more than once per (configurable) period. This really is "for information & debug" purposes so + /// we can look at the table and see what servers are registered - but the info is not used anywhere. + /// Should we actually want to use this, we would need a better and more deterministic way of figuring + /// out the "server address" ie the address to which server-to-server requests should be sent - because it + /// probably is not the "current request address" - especially in multi-domains configurations. + /// + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] + // TODO: This is legacy, we no longer need to do this but we don't want to change the behavior now + [ComposeAfter(typeof(ExamineComposer))] + public sealed class DatabaseServerRegistrarAndMessengerComposer : ComponentComposer, ICoreComposer + { + public static DatabaseServerMessengerOptions GetDefaultOptions(IFactory factory) + { + return new DatabaseServerMessengerOptions(); + } + + public override void Compose(Composition composition) + { + base.Compose(composition); + + composition.SetDatabaseServerMessengerOptions(GetDefaultOptions); + composition.SetServerMessenger(); + composition.Register(factory => factory.GetInstance() as BatchedDatabaseServerMessenger, Lifetime.Singleton); + } + } +} diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index f3298cda54..b35a3d0edb 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -152,7 +152,10 @@ namespace Umbraco.Web.PublishedCache.NuCache _domainStore = new SnapDictionary(); - LoadCachesOnStartup(); + _syncBootStateAccessor.Booting += (sender, args) => + { + LoadCachesOnStartup(args); + }; } Guid GetUid(ContentStore store, int id) => store.LiveSnapshot.Get(id)?.Uid ?? default; @@ -219,32 +222,31 @@ namespace Umbraco.Web.PublishedCache.NuCache /// Populates the stores /// /// This is called inside of a lock for _storesLock - private void LoadCachesOnStartup() + private void LoadCachesOnStartup(SyncBootState bootState) { + // TODO: This is super ugly that this does this as part of the ctor. + // In netcore this will be different, the initialization will happen + // outside of the ctor. + var okContent = false; var okMedia = false; - if (_syncBootStateAccessor.GetSyncBootState() == SyncBootState.ColdBoot) - { - _logger.Info("Sync Service is in a Cold Boot state. Skip LoadCachesOnStartup as the Sync Service will trigger a full reload"); - _isReady = true; - return; - } + try { - if (_localContentDbExists) + if (bootState != SyncBootState.ColdBoot && _localContentDbExists) { okContent = LockAndLoadContent(scope => LoadContentFromLocalDbLocked(true)); if (!okContent) _logger.Warn("Loading content from local db raised warnings, will reload from database."); } - if (_localMediaDbExists) + if (bootState != SyncBootState.ColdBoot && _localMediaDbExists) { okMedia = LockAndLoadMedia(scope => LoadMediaFromLocalDbLocked(true)); if (!okMedia) _logger.Warn("Loading media from local db raised warnings, will reload from database."); } - + if (!okContent) LockAndLoadContent(scope => LoadContentFromDatabaseLocked(scope, true)); diff --git a/src/Umbraco.Web/Search/ExamineFinalComponent.cs b/src/Umbraco.Web/Search/ExamineFinalComponent.cs index 95000b2b46..22dcb43cc0 100644 --- a/src/Umbraco.Web/Search/ExamineFinalComponent.cs +++ b/src/Umbraco.Web/Search/ExamineFinalComponent.cs @@ -3,6 +3,8 @@ using Umbraco.Core.Logging; using Umbraco.Examine; using Umbraco.Core.Composing; using Umbraco.Core; +using Umbraco.Core.Sync; +using Umbraco.Web.Routing; namespace Umbraco.Web.Search { @@ -16,27 +18,65 @@ namespace Umbraco.Web.Search private readonly IExamineManager _examineManager; BackgroundIndexRebuilder _indexRebuilder; private readonly IMainDom _mainDom; - - public ExamineFinalComponent(IProfilingLogger logger, IExamineManager examineManager, BackgroundIndexRebuilder indexRebuilder, IMainDom mainDom) + private readonly ISyncBootStateAccessor _syncBootStateAccessor; + private readonly object _locker = new object(); + private bool _initialized = false; + + public ExamineFinalComponent(IProfilingLogger logger, IExamineManager examineManager, BackgroundIndexRebuilder indexRebuilder, IMainDom mainDom, ISyncBootStateAccessor syncBootStateAccessor) { _logger = logger; _examineManager = examineManager; _indexRebuilder = indexRebuilder; _mainDom = mainDom; + _syncBootStateAccessor = syncBootStateAccessor; + + // must add the handler in the ctor because it will be too late in Initialize + // TODO: All of this boot synchronization for cold boot logic needs should be fixed in netcore + _syncBootStateAccessor.Booting += _syncBootStateAccessor_Booting; + } + + /// + /// Once the boot state is known we can see if we require rebuilds + /// + /// + /// + private void _syncBootStateAccessor_Booting(object sender, SyncBootState e) + { + UmbracoModule.RouteAttempt += UmbracoModule_RouteAttempt; + } + + private void UmbracoModule_RouteAttempt(object sender, RoutableAttemptEventArgs e) + { + UmbracoModule.RouteAttempt -= UmbracoModule_RouteAttempt; + + if (!_initialized) + { + lock (_locker) + { + // double check lock, we must only do this once + if (!_initialized) + { + if (!_mainDom.IsMainDom) return; + + var bootState = _syncBootStateAccessor.GetSyncBootState(); + + _examineManager.ConfigureIndexes(_mainDom, _logger); + + // if it's a cold boot, rebuild all indexes including non-empty ones + _indexRebuilder.RebuildIndexes(bootState != SyncBootState.ColdBoot, 0); + } + } + } + } public void Initialize() - { - if (!_mainDom.IsMainDom) return; - - _examineManager.ConfigureIndexes(_mainDom, _logger); - - // TODO: Instead of waiting 5000 ms, we could add an event handler on to fulfilling the first request, then start? - _indexRebuilder.RebuildIndexes(true, 5000); + { } public void Terminate() { + _syncBootStateAccessor.Booting -= _syncBootStateAccessor_Booting; } } } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index a6cbefa825..48cc00afc9 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -132,6 +132,7 @@ + @@ -1306,4 +1307,4 @@ - + \ No newline at end of file From 1a3165507ef32aca8a9ec22501ff1bca6dcc2df2 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 26 Apr 2021 18:26:49 +1000 Subject: [PATCH 023/182] adds note --- src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs b/src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs index 1598215d2c..249b038cae 100644 --- a/src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs +++ b/src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs @@ -16,6 +16,6 @@ namespace Umbraco.Core.Sync /// /// Raised when the boot state is known /// - event EventHandler Booting; + event EventHandler Booting; // TODO: This should be removed in netcore } } From 4ade2264d2da7ae38112a6aaad6b101c6f56c87e Mon Sep 17 00:00:00 2001 From: Anders Bjerner Date: Thu, 22 Apr 2021 19:25:59 +0200 Subject: [PATCH 024/182] Minor Danish translations --- src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index 39a6fee671..9e564635fa 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -804,12 +804,12 @@ Vis genveje Brug listevisning Tillad på rodniveau - Comment/Uncomment lines - Remove line - Copy Lines Up - Copy Lines Down - Move Lines Up - Move Lines Down + Lommentér/Udkommentér linjer + Slet linje + Kopiér linjer op + Kopiér linjer ned + Flyt linjer op + Flyt linjer ned Generelt Editor Skift tillad sprogvarianter From 51a720b16c550d0fffe62f79a86f65a77128f779 Mon Sep 17 00:00:00 2001 From: Mole Date: Tue, 27 Apr 2021 09:07:10 +0200 Subject: [PATCH 025/182] Add references to Umbraco.Code as PrivateAsset (#10148) Co-authored-by: Bjarke Berg --- src/Umbraco.Core/Models/Mapping/UserMapDefinition.cs | 2 ++ src/Umbraco.Core/Umbraco.Core.csproj | 3 +++ src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj | 3 +++ src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj | 3 +++ .../Umbraco.Persistence.SqlCe.csproj | 3 +++ .../Umbraco.PublishedCache.NuCache.csproj | 3 +++ src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj | 3 +++ src/Umbraco.Web.Common/Umbraco.Web.Common.csproj | 3 +++ src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj | 3 +++ src/Umbraco.Web.Website/Umbraco.Web.Website.csproj | 5 +++++ 10 files changed, 31 insertions(+) diff --git a/src/Umbraco.Core/Models/Mapping/UserMapDefinition.cs b/src/Umbraco.Core/Models/Mapping/UserMapDefinition.cs index 76f9e7f64f..85da93fb31 100644 --- a/src/Umbraco.Core/Models/Mapping/UserMapDefinition.cs +++ b/src/Umbraco.Core/Models/Mapping/UserMapDefinition.cs @@ -104,6 +104,7 @@ namespace Umbraco.Cms.Core.Models.Mapping // Umbraco.Code.MapAll -SecurityStamp -Avatar -ProviderUserKey -RawPasswordValue // Umbraco.Code.MapAll -RawPasswordAnswerValue -Comments -IsApproved -IsLockedOut -LastLoginDate // Umbraco.Code.MapAll -LastPasswordChangeDate -LastLockoutDate -FailedPasswordAttempts + // Umbraco.Code.MapAll -PasswordConfiguration private void Map(UserInvite source, IUser target, MapperContext context) { target.Email = source.Email; @@ -122,6 +123,7 @@ namespace Umbraco.Cms.Core.Models.Mapping // Umbraco.Code.MapAll -ProviderUserKey -RawPasswordValue -RawPasswordAnswerValue -PasswordQuestion -Comments // Umbraco.Code.MapAll -IsApproved -IsLockedOut -LastLoginDate -LastPasswordChangeDate -LastLockoutDate // Umbraco.Code.MapAll -FailedPasswordAttempts + // Umbraco.Code.MapAll -PasswordConfiguration private void Map(UserSave source, IUser target, MapperContext context) { target.Name = source.Name; diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 59bf33d110..7abbff9815 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -25,6 +25,9 @@ + + all + diff --git a/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj b/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj index 329c11f879..30baa3c9de 100644 --- a/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj +++ b/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj @@ -48,6 +48,9 @@ runtime; build; native; contentfiles; analyzers all + + all + diff --git a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj index 90554b3401..9df72edf84 100644 --- a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj +++ b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj @@ -50,6 +50,9 @@ + + all + diff --git a/src/Umbraco.Persistence.SqlCe/Umbraco.Persistence.SqlCe.csproj b/src/Umbraco.Persistence.SqlCe/Umbraco.Persistence.SqlCe.csproj index 93622540e4..090c47fe0c 100644 --- a/src/Umbraco.Persistence.SqlCe/Umbraco.Persistence.SqlCe.csproj +++ b/src/Umbraco.Persistence.SqlCe/Umbraco.Persistence.SqlCe.csproj @@ -15,6 +15,9 @@ + + all + diff --git a/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj b/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj index 9aea12912c..b99f767b8e 100644 --- a/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj +++ b/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj @@ -16,6 +16,9 @@ + + all + diff --git a/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj b/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj index 01dffccaeb..9d12be6c79 100644 --- a/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj +++ b/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj @@ -22,6 +22,9 @@ + + all + diff --git a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj index bb2ecbc346..125d0f1585 100644 --- a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj +++ b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj @@ -34,6 +34,9 @@ + + all + 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 bb98df64d6..e55b6dec1f 100644 --- a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj +++ b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj @@ -75,6 +75,9 @@ + + all + diff --git a/src/Umbraco.Web.Website/Umbraco.Web.Website.csproj b/src/Umbraco.Web.Website/Umbraco.Web.Website.csproj index 7ebf12e6d9..8fa48bd194 100644 --- a/src/Umbraco.Web.Website/Umbraco.Web.Website.csproj +++ b/src/Umbraco.Web.Website/Umbraco.Web.Website.csproj @@ -27,6 +27,11 @@ + + + all + + <_Parameter1>Umbraco.Tests.UnitTests From eaa46fe1902ab6bd8527a761b7b1dfde850c76e0 Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Tue, 27 Apr 2021 17:10:30 +1000 Subject: [PATCH 026/182] Allows for structured logging with the IProfilingLogger (#9958) * Allows for structured logging with the IProfilingLogger * Review fixes Co-authored-by: Bjarke Berg --- src/Umbraco.Core/Logging/DisposableTimer.cs | 99 +++++++++++++++++--- src/Umbraco.Core/Logging/IProfilingLogger.cs | 14 +-- src/Umbraco.Core/Logging/ProfilingLogger.cs | 44 ++++----- 3 files changed, 107 insertions(+), 50 deletions(-) diff --git a/src/Umbraco.Core/Logging/DisposableTimer.cs b/src/Umbraco.Core/Logging/DisposableTimer.cs index 50bfd537cd..58e951ab3b 100644 --- a/src/Umbraco.Core/Logging/DisposableTimer.cs +++ b/src/Umbraco.Core/Logging/DisposableTimer.cs @@ -16,13 +16,24 @@ namespace Umbraco.Cms.Core.Logging private readonly IDisposable _profilerStep; private readonly string _endMessage; private string _failMessage; + private readonly object[] _endMessageArgs; + private readonly object[] _failMessageArgs; private Exception _failException; private bool _failed; private readonly string _timingId; // internal - created by profiling logger - internal DisposableTimer(ILogger logger, LogLevel level, IProfiler profiler, Type loggerType, - string startMessage, string endMessage, string failMessage = null, + internal DisposableTimer( + ILogger logger, + LogLevel level, + IProfiler profiler, + Type loggerType, + string startMessage, + string endMessage, + string failMessage = null, + object[] startMessageArgs = null, + object[] endMessageArgs = null, + object[] failMessageArgs = null, int thresholdMilliseconds = 0) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); @@ -30,6 +41,8 @@ namespace Umbraco.Cms.Core.Logging _loggerType = loggerType ?? throw new ArgumentNullException(nameof(loggerType)); _endMessage = endMessage; _failMessage = failMessage; + _endMessageArgs = endMessageArgs; + _failMessageArgs = failMessageArgs; _thresholdMilliseconds = thresholdMilliseconds < 0 ? 0 : thresholdMilliseconds; _timingId = Guid.NewGuid().ToString("N").Substring(0, 7); // keep it short-ish @@ -38,10 +51,30 @@ namespace Umbraco.Cms.Core.Logging switch (_level) { case LogLevel.Debug: - logger.LogDebug("{StartMessage} [Timing {TimingId}]", startMessage, _timingId); + if (startMessageArgs == null) + { + logger.LogDebug("{StartMessage} [Timing {TimingId}]", startMessage, _timingId); + } + else + { + var args = new object[startMessageArgs.Length + 1]; + startMessageArgs.CopyTo(args, 0); + args[startMessageArgs.Length] = _timingId; + logger.LogDebug(startMessage + " [Timing {TimingId}]", args); + } break; case LogLevel.Information: - logger.LogInformation("{StartMessage} [Timing {TimingId}]", startMessage, _timingId); + if (startMessageArgs == null) + { + logger.LogInformation("{StartMessage} [Timing {TimingId}]", startMessage, _timingId); + } + else + { + var args = new object[startMessageArgs.Length + 1]; + startMessageArgs.CopyTo(args, 0); + args[startMessageArgs.Length] = _timingId; + logger.LogDebug(startMessage + " [Timing {TimingId}]", args); + } break; default: throw new ArgumentOutOfRangeException(nameof(level)); @@ -85,19 +118,55 @@ namespace Umbraco.Cms.Core.Logging { if (_failed) { - _logger.LogError(_failException, "{FailMessage} ({Duration}ms) [Timing {TimingId}]", _failMessage, Stopwatch.ElapsedMilliseconds, _timingId); + if (_failMessageArgs is null) + { + _logger.LogError(_failException, "{FailMessage} ({Duration}ms) [Timing {TimingId}]", _failMessage, Stopwatch.ElapsedMilliseconds, _timingId); + } + else + { + var args = new object[_failMessageArgs.Length + 2]; + _failMessageArgs.CopyTo(args, 0); + args[_failMessageArgs.Length - 1] = Stopwatch.ElapsedMilliseconds; + args[_failMessageArgs.Length] = _timingId; + _logger.LogError(_failException, _failMessage + " ({Duration}ms) [Timing {TimingId}]", args); + } } - else switch (_level) + else { - case LogLevel.Debug: - _logger.LogDebug("{EndMessage} ({Duration}ms) [Timing {TimingId}]", _endMessage, Stopwatch.ElapsedMilliseconds, _timingId); - break; - case LogLevel.Information: - _logger.LogInformation("{EndMessage} ({Duration}ms) [Timing {TimingId}]", _endMessage, Stopwatch.ElapsedMilliseconds, _timingId); - break; - // filtered in the ctor - //default: - // throw new Exception(); + switch (_level) + { + case LogLevel.Debug: + if (_endMessageArgs == null) + { + _logger.LogDebug("{EndMessage} ({Duration}ms) [Timing {TimingId}]", _endMessage, Stopwatch.ElapsedMilliseconds, _timingId); + } + else + { + var args = new object[_endMessageArgs.Length + 2]; + _endMessageArgs.CopyTo(args, 0); + args[args.Length - 1] = Stopwatch.ElapsedMilliseconds; + args[args.Length] = _timingId; + _logger.LogDebug(_endMessage + " ({Duration}ms) [Timing {TimingId}]", args); + } + break; + case LogLevel.Information: + if (_endMessageArgs == null) + { + _logger.LogInformation("{EndMessage} ({Duration}ms) [Timing {TimingId}]", _endMessage, Stopwatch.ElapsedMilliseconds, _timingId); + } + else + { + var args = new object[_endMessageArgs.Length + 2]; + _endMessageArgs.CopyTo(args, 0); + args[_endMessageArgs.Length - 1] = Stopwatch.ElapsedMilliseconds; + args[_endMessageArgs.Length] = _timingId; + _logger.LogInformation(_endMessage + " ({Duration}ms) [Timing {TimingId}]", args); + } + break; + // filtered in the ctor + //default: + // throw new Exception(); + } } } } diff --git a/src/Umbraco.Core/Logging/IProfilingLogger.cs b/src/Umbraco.Core/Logging/IProfilingLogger.cs index fd51bd2da3..4a0714fd0c 100644 --- a/src/Umbraco.Core/Logging/IProfilingLogger.cs +++ b/src/Umbraco.Core/Logging/IProfilingLogger.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Umbraco.Cms.Core.Logging { @@ -10,31 +10,31 @@ namespace Umbraco.Cms.Core.Logging /// /// Profiles an action and log as information messages. /// - DisposableTimer TraceDuration(string startMessage); + DisposableTimer TraceDuration(string startMessage, object[] startMessageArgs = null); /// /// Profiles an action and log as information messages. /// - DisposableTimer TraceDuration(string startMessage, string completeMessage, string failMessage = null); + DisposableTimer TraceDuration(string startMessage, string completeMessage, string failMessage = null, object[] startMessageArgs = null, object[] endMessageArgs = null, object[] failMessageArgs = null); /// /// Profiles an action and log as information messages. /// - DisposableTimer TraceDuration(Type loggerType, string startMessage, string completeMessage, string failMessage = null); + DisposableTimer TraceDuration(Type loggerType, string startMessage, string completeMessage, string failMessage = null, object[] startMessageArgs = null, object[] endMessageArgs = null, object[] failMessageArgs = null); /// /// Profiles an action and log as debug messages. /// - DisposableTimer DebugDuration(string startMessage); + DisposableTimer DebugDuration(string startMessage, object[] startMessageArgs = null); /// /// Profiles an action and log as debug messages. /// - DisposableTimer DebugDuration(string startMessage, string completeMessage, string failMessage = null, int thresholdMilliseconds = 0); + DisposableTimer DebugDuration(string startMessage, string completeMessage, string failMessage = null, int thresholdMilliseconds = 0, object[] startMessageArgs = null, object[] endMessageArgs = null, object[] failMessageArgs = null); /// /// Profiles an action and log as debug messages. /// - DisposableTimer DebugDuration(Type loggerType, string startMessage, string completeMessage, string failMessage = null, int thresholdMilliseconds = 0); + DisposableTimer DebugDuration(Type loggerType, string startMessage, string completeMessage, string failMessage = null, int thresholdMilliseconds = 0, object[] startMessageArgs = null, object[] endMessageArgs = null, object[] failMessageArgs = null); } } diff --git a/src/Umbraco.Core/Logging/ProfilingLogger.cs b/src/Umbraco.Core/Logging/ProfilingLogger.cs index 6a6bcfd850..9c66c95d24 100644 --- a/src/Umbraco.Core/Logging/ProfilingLogger.cs +++ b/src/Umbraco.Core/Logging/ProfilingLogger.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.Extensions.Logging; namespace Umbraco.Cms.Core.Logging @@ -27,41 +27,29 @@ namespace Umbraco.Cms.Core.Logging Profiler = profiler ?? throw new ArgumentNullException(nameof(profiler)); } - public DisposableTimer TraceDuration(string startMessage) - { - return TraceDuration(startMessage, "Completed."); - } + public DisposableTimer TraceDuration(string startMessage, object[] startMessageArgs = null) + => TraceDuration(startMessage, "Completed.", startMessageArgs: startMessageArgs); - public DisposableTimer TraceDuration(string startMessage, string completeMessage, string failMessage = null) - { - return new DisposableTimer(Logger, LogLevel.Information, Profiler, typeof(T), startMessage, completeMessage, failMessage); - } + public DisposableTimer TraceDuration(string startMessage, string completeMessage, string failMessage = null, object[] startMessageArgs = null, object[] endMessageArgs = null, object[] failMessageArgs = null) + => new DisposableTimer(Logger, LogLevel.Information, Profiler, typeof(T), startMessage, completeMessage, failMessage); - public DisposableTimer TraceDuration(Type loggerType, string startMessage, string completeMessage, string failMessage = null) - { - return new DisposableTimer(Logger, LogLevel.Information, Profiler, loggerType, startMessage, completeMessage, failMessage); - } + public DisposableTimer TraceDuration(Type loggerType, string startMessage, string completeMessage, string failMessage = null, object[] startMessageArgs = null, object[] endMessageArgs = null, object[] failMessageArgs = null) + => new DisposableTimer(Logger, LogLevel.Information, Profiler, loggerType, startMessage, completeMessage, failMessage); - public DisposableTimer DebugDuration(string startMessage) - { - return Logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug) - ? DebugDuration(startMessage, "Completed.") + public DisposableTimer DebugDuration(string startMessage, object[] startMessageArgs = null) + => Logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug) + ? DebugDuration(startMessage, "Completed.", startMessageArgs: startMessageArgs) : null; - } - public DisposableTimer DebugDuration(string startMessage, string completeMessage, string failMessage = null, int thresholdMilliseconds = 0) - { - return Logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug) - ? new DisposableTimer(Logger, LogLevel.Debug, Profiler, typeof(T), startMessage, completeMessage, failMessage, thresholdMilliseconds) + public DisposableTimer DebugDuration(string startMessage, string completeMessage, string failMessage = null, int thresholdMilliseconds = 0, object[] startMessageArgs = null, object[] endMessageArgs = null, object[] failMessageArgs = null) + => Logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug) + ? new DisposableTimer(Logger, LogLevel.Debug, Profiler, typeof(T), startMessage, completeMessage, failMessage, startMessageArgs, endMessageArgs, failMessageArgs, thresholdMilliseconds) : null; - } - public DisposableTimer DebugDuration(Type loggerType, string startMessage, string completeMessage, string failMessage = null, int thresholdMilliseconds = 0) - { - return Logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug) - ? new DisposableTimer(Logger, LogLevel.Debug, Profiler, loggerType, startMessage, completeMessage, failMessage, thresholdMilliseconds) + public DisposableTimer DebugDuration(Type loggerType, string startMessage, string completeMessage, string failMessage = null, int thresholdMilliseconds = 0, object[] startMessageArgs = null, object[] endMessageArgs = null, object[] failMessageArgs = null) + => Logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug) + ? new DisposableTimer(Logger, LogLevel.Debug, Profiler, loggerType, startMessage, completeMessage, failMessage, startMessageArgs, endMessageArgs, failMessageArgs, thresholdMilliseconds) : null; - } #region ILogger From c9ebaadf2300768677d4000e723a6ae34ef126d7 Mon Sep 17 00:00:00 2001 From: Mole Date: Tue, 27 Apr 2021 09:52:17 +0200 Subject: [PATCH 027/182] Netcore: File systems rework (#10181) * Allow IMediaFileSystem to be replace in the DI, or registered with inner filesystem * Remove GetFileSystem from Filesystems It was only used by tests. * Make MediaFileSystem inherit from PhysicalFileSystem directly * Remove FileSystemWrapper * Remove inner filesystem from MediaFileSystem * Add MediaFileManager and bare minimum to make it testable * Remove MediaFileSystem * Fix unit tests using MediaFileManager * Remove IFileSystem and rely only on FileSystem * Hide dangerous methods in FileSystems and do some cleaning * Apply stylecop warnings to MediaFileManager * Add FilesystemsCreator to Tests.Common This allows you to create an instance if FileSystems with your own specified IFileSystem for testing purposes outside our own test suite. * Allow the stylesheet filesystem to be replaced. * Fix tests * Don't save stylesheetWrapper in a temporary var * refactor(FileSystems): change how stylesheet filesystem is registered * fix(FileSystems): unable to overwrite media filesystem SetMediaFileSystem added the MediaManager as a Singleton instead of replacing the existing instance. * fix(FileSystems): calling AddFileSystems replaces MediaManager When calling AddFileSystems after SetMediaFileSystem the MediaManager gets replaced by the default PhysicalFileSystem, so instead of calling SetMediaFileSystem in AddFileSystems we now call TrySetMediaFileSystem instead. This method will not replace any existing instance of the MediaManager if there's already a MediaManager registered. * Use SetMediaFileSystem instead of TrySet, and rename AddFilesystems to ConfigureFileSystems Also don't call AddFileSystems again in ConfigureFilesystems * Don't wrap CSS filesystem twice * Add CreateShadowWrapperInternal to avoid casting * Throw UnauthorizedAccessException isntead of InvalidOperationException * Remove ResetShadowId Co-authored-by: Rasmus John Pedersen --- .../Events/QueuingEventDispatcher.cs | 8 +- .../Extensions/ContentExtensions.cs | 16 +- src/Umbraco.Core/IO/FileSystemExtensions.cs | 28 -- src/Umbraco.Core/IO/FileSystemWrapper.cs | 118 -------- src/Umbraco.Core/IO/FileSystems.cs | 261 +++++++++++------- src/Umbraco.Core/IO/IFileSystems.cs | 33 --- src/Umbraco.Core/IO/IMediaFileSystem.cs | 66 ----- src/Umbraco.Core/IO/IMediaPathScheme.cs | 6 +- src/Umbraco.Core/IO/MediaFileManager.cs | 224 +++++++++++++++ src/Umbraco.Core/IO/MediaFileSystem.cs | 126 --------- .../CombinedGuidsMediaPathScheme.cs | 4 +- .../OriginalMediaPathScheme.cs | 8 +- .../TwoGuidsMediaPathScheme.cs | 4 +- .../MediaPathSchemes/UniqueMediaPathScheme.cs | 4 +- .../Media/UploadAutoFillProperties.cs | 8 +- .../Models/Mapping/UserMapDefinition.cs | 12 +- src/Umbraco.Core/Models/UserExtensions.cs | 6 +- .../UmbracoBuilder.FileSystems.cs | 21 +- .../UmbracoBuilder.Uniques.cs | 47 +++- .../Implement/PartialViewMacroRepository.cs | 2 +- .../Implement/PartialViewRepository.cs | 2 +- .../Implement/ScriptRepository.cs | 2 +- .../Implement/StylesheetRepository.cs | 2 +- .../Implement/TemplateRepository.cs | 2 +- .../FileUploadPropertyEditor.cs | 22 +- .../FileUploadPropertyValueEditor.cs | 18 +- .../ImageCropperPropertyEditor.cs | 24 +- .../ImageCropperPropertyValueEditor.cs | 18 +- .../RichTextEditorPastedImages.cs | 17 +- src/Umbraco.Infrastructure/Scoping/Scope.cs | 16 +- .../Scoping/ScopeProvider.cs | 12 +- .../Services/Implement/MediaService.cs | 16 +- .../TestHelpers/FileSystemsCreator.cs | 37 +++ .../Umbraco.Core/IO/FileSystemsTests.cs | 41 +-- .../Umbraco.Core/IO/ShadowFileSystemTests.cs | 33 +-- .../Repositories/ContentTypeRepositoryTest.cs | 2 +- .../Repositories/DocumentRepositoryTest.cs | 2 +- .../PartialViewRepositoryTests.cs | 9 +- .../Repositories/ScriptRepositoryTest.cs | 11 +- .../Repositories/StylesheetRepositoryTest.cs | 12 +- .../Repositories/TemplateRepositoryTest.cs | 2 +- .../Scoping/ScopeFileSystemsTests.cs | 43 ++- .../Umbraco.Core/Components/ComponentTests.cs | 8 +- .../Scoping/ScopeEventDispatcherTests.cs | 8 +- .../Scoping/ScopeUnitTests.cs | 10 +- src/Umbraco.Tests/Models/MediaXmlTest.cs | 6 +- .../PublishedContentTestBase.cs | 5 +- .../PublishedContent/PublishedContentTests.cs | 3 +- .../Routing/MediaUrlProviderTests.cs | 8 +- .../TestHelpers/TestObjects-Mocks.cs | 28 +- src/Umbraco.Tests/TestHelpers/TestObjects.cs | 6 +- src/Umbraco.Tests/Testing/UmbracoTestBase.cs | 5 +- .../Controllers/CodeFileController.cs | 4 +- .../Controllers/CurrentUserController.cs | 8 +- .../Controllers/ImagesController.cs | 8 +- .../Controllers/LogController.cs | 8 +- .../Controllers/MediaController.cs | 8 +- .../Controllers/UsersController.cs | 22 +- .../Trees/PartialViewMacrosTreeController.cs | 2 +- .../Trees/PartialViewsTreeController.cs | 2 +- .../Trees/ScriptsTreeController.cs | 2 +- .../Trees/StylesheetsTreeController.cs | 2 +- src/Umbraco.Web/Composing/Current.cs | 2 +- 63 files changed, 740 insertions(+), 760 deletions(-) delete mode 100644 src/Umbraco.Core/IO/FileSystemWrapper.cs delete mode 100644 src/Umbraco.Core/IO/IFileSystems.cs delete mode 100644 src/Umbraco.Core/IO/IMediaFileSystem.cs create mode 100644 src/Umbraco.Core/IO/MediaFileManager.cs delete mode 100644 src/Umbraco.Core/IO/MediaFileSystem.cs create mode 100644 src/Umbraco.Tests.Common/TestHelpers/FileSystemsCreator.cs diff --git a/src/Umbraco.Core/Events/QueuingEventDispatcher.cs b/src/Umbraco.Core/Events/QueuingEventDispatcher.cs index de82fc1f24..e79cd67cd8 100644 --- a/src/Umbraco.Core/Events/QueuingEventDispatcher.cs +++ b/src/Umbraco.Core/Events/QueuingEventDispatcher.cs @@ -11,11 +11,11 @@ namespace Umbraco.Cms.Core.Events /// public class QueuingEventDispatcher : QueuingEventDispatcherBase { - private readonly IMediaFileSystem _mediaFileSystem; - public QueuingEventDispatcher(IMediaFileSystem mediaFileSystem) + private readonly MediaFileManager _mediaFileManager; + public QueuingEventDispatcher(MediaFileManager mediaFileManager) : base(true) { - _mediaFileSystem = mediaFileSystem; + _mediaFileManager = mediaFileManager; } protected override void ScopeExitCompleted() @@ -33,7 +33,7 @@ namespace Umbraco.Cms.Core.Events // but then where should it be (without making things too complicated)? var delete = e.Args as IDeletingMediaFilesEventArgs; if (delete != null && delete.MediaFilesToDelete.Count > 0) - _mediaFileSystem.DeleteMediaFiles(delete.MediaFilesToDelete); + _mediaFileManager.DeleteMediaFiles(delete.MediaFilesToDelete); } } diff --git a/src/Umbraco.Core/Extensions/ContentExtensions.cs b/src/Umbraco.Core/Extensions/ContentExtensions.cs index 7794d60b15..5aaad5e303 100644 --- a/src/Umbraco.Core/Extensions/ContentExtensions.cs +++ b/src/Umbraco.Core/Extensions/ContentExtensions.cs @@ -204,7 +204,7 @@ namespace Umbraco.Extensions /// /// Sets the posted file value of a property. /// - public static void SetValue(this IContentBase content, IMediaFileSystem mediaFileSystem, IShortStringHelper shortStringHelper, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IJsonSerializer serializer, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null) + public static void SetValue(this IContentBase content, MediaFileManager mediaFileManager, IShortStringHelper shortStringHelper, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IJsonSerializer serializer, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null) { if (filename == null || filestream == null) return; @@ -212,10 +212,10 @@ namespace Umbraco.Extensions if (string.IsNullOrWhiteSpace(filename)) return; filename = filename.ToLower(); - SetUploadFile(content, mediaFileSystem, contentTypeBaseServiceProvider, serializer, propertyTypeAlias, filename, filestream, culture, segment); + SetUploadFile(content, mediaFileManager, contentTypeBaseServiceProvider, serializer, propertyTypeAlias, filename, filestream, culture, segment); } - private static void SetUploadFile(this IContentBase content, IMediaFileSystem mediaFileSystem, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IJsonSerializer serializer, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null) + private static void SetUploadFile(this IContentBase content, MediaFileManager mediaFileManager, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IJsonSerializer serializer, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null) { var property = GetProperty(content, contentTypeBaseServiceProvider, propertyTypeAlias); @@ -229,11 +229,11 @@ namespace Umbraco.Extensions // the property value is a JSON serialized image crop data set - grab the "src" property as the file source svalue = serializer.DeserializeSubset(svalue, "src"); } - oldpath = mediaFileSystem.GetRelativePath(svalue); + oldpath = mediaFileManager.FileSystem.GetRelativePath(svalue); } - var filepath = mediaFileSystem.StoreFile(content, property.PropertyType, filename, filestream, oldpath); - property.SetValue(mediaFileSystem.GetUrl(filepath), culture, segment); + var filepath = mediaFileManager.StoreFile(content, property.PropertyType, filename, filestream, oldpath); + property.SetValue(mediaFileManager.FileSystem.GetUrl(filepath), culture, segment); } // gets or creates a property for a content item. @@ -269,13 +269,13 @@ namespace Umbraco.Extensions /// the "folder number" that was assigned to the previous file referenced by the property, /// if any. /// - public static string StoreFile(this IContentBase content, IMediaFileSystem mediaFileSystem, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, string propertyTypeAlias, string filename, Stream filestream, string filepath) + public static string StoreFile(this IContentBase content, MediaFileManager mediaFileManager, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, string propertyTypeAlias, string filename, Stream filestream, string filepath) { var contentType = contentTypeBaseServiceProvider.GetContentTypeOf(content); var propertyType = contentType .CompositionPropertyTypes.FirstOrDefault(x => x.Alias.InvariantEquals(propertyTypeAlias)); if (propertyType == null) throw new ArgumentException("Invalid property type alias " + propertyTypeAlias + "."); - return mediaFileSystem.StoreFile(content, propertyType, filename, filestream, filepath); + return mediaFileManager.StoreFile(content, propertyType, filename, filestream, filepath); } #endregion diff --git a/src/Umbraco.Core/IO/FileSystemExtensions.cs b/src/Umbraco.Core/IO/FileSystemExtensions.cs index e688ca22da..6f1f47007f 100644 --- a/src/Umbraco.Core/IO/FileSystemExtensions.cs +++ b/src/Umbraco.Core/IO/FileSystemExtensions.cs @@ -66,33 +66,5 @@ namespace Umbraco.Extensions } fs.DeleteFile(tempFile); } - - /// - /// Unwraps a filesystem. - /// - /// - /// A filesystem can be wrapped in a (public) or a (internal), - /// and this method deals with the various wrappers and - /// - public static IFileSystem Unwrap(this IFileSystem filesystem) - { - var unwrapping = true; - while (unwrapping) - { - switch (filesystem) - { - case FileSystemWrapper wrapper: - filesystem = wrapper.InnerFileSystem; - break; - case ShadowWrapper shadow: - filesystem = shadow.InnerFileSystem; - break; - default: - unwrapping = false; - break; - } - } - return filesystem; - } } } diff --git a/src/Umbraco.Core/IO/FileSystemWrapper.cs b/src/Umbraco.Core/IO/FileSystemWrapper.cs deleted file mode 100644 index 34276131c7..0000000000 --- a/src/Umbraco.Core/IO/FileSystemWrapper.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; - -namespace Umbraco.Cms.Core.IO -{ - /// - /// All custom file systems that are based upon another IFileSystem should inherit from FileSystemWrapper - /// - /// - /// An IFileSystem is generally used as a base file system, for example like a PhysicalFileSystem or an S3FileSystem. - /// Then, other custom file systems are wrapped upon these files systems like MediaFileSystem, etc... All of the custom - /// file systems must inherit from FileSystemWrapper. - /// - /// This abstract class just wraps the 'real' IFileSystem object passed in to its constructor. - /// - public abstract class FileSystemWrapper : IFileSystem - { - protected FileSystemWrapper(IFileSystem innerFileSystem) - { - InnerFileSystem = innerFileSystem; - } - - internal IFileSystem InnerFileSystem { get; set; } - - public virtual IEnumerable GetDirectories(string path) - { - return InnerFileSystem.GetDirectories(path); - } - - public virtual void DeleteDirectory(string path) - { - InnerFileSystem.DeleteDirectory(path); - } - - public virtual void DeleteDirectory(string path, bool recursive) - { - InnerFileSystem.DeleteDirectory(path, recursive); - } - - public virtual bool DirectoryExists(string path) - { - return InnerFileSystem.DirectoryExists(path); - } - - public virtual void AddFile(string path, Stream stream) - { - InnerFileSystem.AddFile(path, stream); - } - - public virtual void AddFile(string path, Stream stream, bool overrideExisting) - { - InnerFileSystem.AddFile(path, stream, overrideExisting); - } - - public virtual IEnumerable GetFiles(string path) - { - return InnerFileSystem.GetFiles(path); - } - - public virtual IEnumerable GetFiles(string path, string filter) - { - return InnerFileSystem.GetFiles(path, filter); - } - - public virtual Stream OpenFile(string path) - { - return InnerFileSystem.OpenFile(path); - } - - public virtual void DeleteFile(string path) - { - InnerFileSystem.DeleteFile(path); - } - - public virtual bool FileExists(string path) - { - return InnerFileSystem.FileExists(path); - } - - public virtual string GetRelativePath(string fullPathOrUrl) - { - return InnerFileSystem.GetRelativePath(fullPathOrUrl); - } - - public virtual string GetFullPath(string path) - { - return InnerFileSystem.GetFullPath(path); - } - - public virtual string GetUrl(string path) - { - return InnerFileSystem.GetUrl(path); - } - - public virtual DateTimeOffset GetLastModified(string path) - { - return InnerFileSystem.GetLastModified(path); - } - - public virtual DateTimeOffset GetCreated(string path) - { - return InnerFileSystem.GetCreated(path); - } - - public virtual long GetSize(string path) - { - return InnerFileSystem.GetSize(path); - } - - public virtual bool CanAddPhysical => InnerFileSystem.CanAddPhysical; - - public virtual void AddFile(string path, string physicalPath, bool overrideIfExists = true, bool copy = false) - { - InnerFileSystem.AddFile(path, physicalPath, overrideIfExists, copy); - } - } -} diff --git a/src/Umbraco.Core/IO/FileSystems.cs b/src/Umbraco.Core/IO/FileSystems.cs index 52ee2fe0d3..1b4db6acec 100644 --- a/src/Umbraco.Core/IO/FileSystems.cs +++ b/src/Umbraco.Core/IO/FileSystems.cs @@ -1,23 +1,25 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; +using System.ComponentModel; using System.Threading; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Hosting; -using Umbraco.Extensions; namespace Umbraco.Cms.Core.IO { - public class FileSystems : IFileSystems + /// + /// Provides the system filesystems. + /// + public sealed class FileSystems { - private readonly IServiceProvider _container; private readonly ILogger _logger; private readonly ILoggerFactory _loggerFactory; private readonly IIOHelper _ioHelper; + private GlobalSettings _globalSettings; + private readonly IHostingEnvironment _hostingEnvironment; - private readonly ConcurrentDictionary> _filesystems = new ConcurrentDictionary>(); // wrappers for shadow support private ShadowWrapper _macroPartialFileSystem; @@ -38,93 +40,175 @@ namespace Umbraco.Cms.Core.IO #region Constructor // DI wants a public ctor - public FileSystems(IServiceProvider container, ILogger logger, ILoggerFactory loggerFactory, IIOHelper ioHelper, IOptions globalSettings, IHostingEnvironment hostingEnvironment) + public FileSystems( + ILoggerFactory loggerFactory, + IIOHelper ioHelper, + IOptions globalSettings, + IHostingEnvironment hostingEnvironment) { - _container = container; - _logger = logger; + _logger = loggerFactory.CreateLogger(); _loggerFactory = loggerFactory; _ioHelper = ioHelper; _globalSettings = globalSettings.Value; _hostingEnvironment = hostingEnvironment; } - // for tests only, totally unsafe - internal void Reset() + // Ctor for tests, allows you to set the various filesystems + internal FileSystems( + ILoggerFactory loggerFactory, + IIOHelper ioHelper, + IOptions globalSettings, + IHostingEnvironment hostingEnvironment, + IFileSystem macroPartialFileSystem, + IFileSystem partialViewsFileSystem, + IFileSystem stylesheetFileSystem, + IFileSystem scriptsFileSystem, + IFileSystem mvcViewFileSystem) : this(loggerFactory, ioHelper, globalSettings, hostingEnvironment) { - _shadowWrappers.Clear(); - _filesystems.Clear(); - Volatile.Write(ref _wkfsInitialized, false); - _shadowCurrentId = null; + _macroPartialFileSystem = CreateShadowWrapperInternal(macroPartialFileSystem, "macro-partials"); + _partialViewsFileSystem = CreateShadowWrapperInternal(partialViewsFileSystem, "partials"); + _stylesheetsFileSystem = CreateShadowWrapperInternal(stylesheetFileSystem, "css"); + _scriptsFileSystem = CreateShadowWrapperInternal(scriptsFileSystem, "scripts"); + _mvcViewsFileSystem = CreateShadowWrapperInternal(mvcViewFileSystem, "view"); + // Set initialized to true so the filesystems doesn't get overwritten. + _wkfsInitialized = true; + } - // for tests only, totally unsafe - internal static void ResetShadowId() - { - _shadowCurrentId = null; - } - - // set by the scope provider when taking control of filesystems + /// + /// Used be Scope provider to take control over the filesystems, should never be used for anything else. + /// + [EditorBrowsable(EditorBrowsableState.Never)] public Func IsScoped { get; set; } = () => false; #endregion #region Well-Known FileSystems - /// + /// + /// Gets the macro partials filesystem. + /// public IFileSystem MacroPartialsFileSystem { get { - if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); + if (Volatile.Read(ref _wkfsInitialized) == false) + { + EnsureWellKnownFileSystems(); + } + return _macroPartialFileSystem; } } - /// + /// + /// Gets the partial views filesystem. + /// public IFileSystem PartialViewsFileSystem { get { - if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); + if (Volatile.Read(ref _wkfsInitialized) == false) + { + EnsureWellKnownFileSystems(); + } + return _partialViewsFileSystem; } } - /// + /// + /// Gets the stylesheets filesystem. + /// public IFileSystem StylesheetsFileSystem { get { - if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); + if (Volatile.Read(ref _wkfsInitialized) == false) + { + EnsureWellKnownFileSystems(); + } + return _stylesheetsFileSystem; } } - /// + /// + /// Gets the scripts filesystem. + /// public IFileSystem ScriptsFileSystem { get { - if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); + if (Volatile.Read(ref _wkfsInitialized) == false) + { + EnsureWellKnownFileSystems(); + } + return _scriptsFileSystem; } } - /// + /// + /// Gets the MVC views filesystem. + /// public IFileSystem MvcViewsFileSystem { get { - if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); + if (Volatile.Read(ref _wkfsInitialized) == false) + { + EnsureWellKnownFileSystems(); + } + return _mvcViewsFileSystem; } } - private void EnsureWellKnownFileSystems() + /// + /// Sets the stylesheet filesystem. + /// + /// + /// Be careful when using this, the root path and root url must be correct for this to work. + /// + /// The . + /// If the is null + /// Throws exception if the StylesheetFileSystem has already been initialized. + /// Throws exception if full path can't be resolved successfully. + public void SetStylesheetFilesystem(IFileSystem fileSystem) { - LazyInitializer.EnsureInitialized(ref _wkfsObject, ref _wkfsInitialized, ref _wkfsLock, CreateWellKnownFileSystems); + if (fileSystem == null) + { + throw new ArgumentNullException(nameof(fileSystem)); + } + + if (_stylesheetsFileSystem != null) + { + throw new InvalidOperationException( + "The StylesheetFileSystem cannot be changed when it's already been initialized."); + } + + // Verify that _rootUrl/_rootPath is correct + // We have to do this because there's a tight coupling + // to the VirtualPath we get with CodeFileDisplay from the frontend. + try + { + var rootPath = fileSystem.GetFullPath("/css/"); + } + catch (UnauthorizedAccessException exception) + { + throw new UnauthorizedAccessException( + "Can't register the stylesheet filesystem, " + + "this is most likely caused by using a PhysicalFileSystem with an incorrect " + + "rootPath/rootUrl. RootPath must be \\wwwroot\\css" + + " and rootUrl must be /css", exception); + } + + _stylesheetsFileSystem = CreateShadowWrapperInternal(fileSystem, "css"); } + private void EnsureWellKnownFileSystems() => LazyInitializer.EnsureInitialized(ref _wkfsObject, ref _wkfsInitialized, ref _wkfsLock, CreateWellKnownFileSystems); + // need to return something to LazyInitializer.EnsureInitialized // but it does not really matter what we return - here, null private object CreateWellKnownFileSystems() @@ -134,20 +218,28 @@ namespace Umbraco.Cms.Core.IO //TODO this is fucked, why do PhysicalFileSystem has a root url? Mvc views cannot be accessed by url! var macroPartialFileSystem = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, logger, _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.MacroPartials), _hostingEnvironment.ToAbsolute(Constants.SystemDirectories.MacroPartials)); var partialViewsFileSystem = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, logger, _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.PartialViews), _hostingEnvironment.ToAbsolute(Constants.SystemDirectories.PartialViews)); - var stylesheetsFileSystem = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, logger, _hostingEnvironment.MapPathWebRoot(_globalSettings.UmbracoCssPath), _hostingEnvironment.ToAbsolute(_globalSettings.UmbracoCssPath)); var scriptsFileSystem = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, logger, _hostingEnvironment.MapPathWebRoot(_globalSettings.UmbracoScriptsPath), _hostingEnvironment.ToAbsolute(_globalSettings.UmbracoScriptsPath)); var mvcViewsFileSystem = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, logger, _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.MvcViews), _hostingEnvironment.ToAbsolute(Constants.SystemDirectories.MvcViews)); _macroPartialFileSystem = new ShadowWrapper(macroPartialFileSystem, _ioHelper, _hostingEnvironment, _loggerFactory, "macro-partials", IsScoped); _partialViewsFileSystem = new ShadowWrapper(partialViewsFileSystem, _ioHelper, _hostingEnvironment, _loggerFactory, "partials", IsScoped); - _stylesheetsFileSystem = new ShadowWrapper(stylesheetsFileSystem, _ioHelper, _hostingEnvironment, _loggerFactory, "css", IsScoped); _scriptsFileSystem = new ShadowWrapper(scriptsFileSystem, _ioHelper, _hostingEnvironment, _loggerFactory, "scripts", IsScoped); _mvcViewsFileSystem = new ShadowWrapper(mvcViewsFileSystem, _ioHelper, _hostingEnvironment, _loggerFactory, "views", IsScoped); + if (_stylesheetsFileSystem == null) + { + var stylesheetsFileSystem = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, logger, + _hostingEnvironment.MapPathWebRoot(_globalSettings.UmbracoCssPath), + _hostingEnvironment.ToAbsolute(_globalSettings.UmbracoCssPath)); + + _stylesheetsFileSystem = new ShadowWrapper(stylesheetsFileSystem, _ioHelper, _hostingEnvironment, _loggerFactory, "css", IsScoped); + + _shadowWrappers.Add(_stylesheetsFileSystem); + } + // TODO: do we need a lock here? _shadowWrappers.Add(_macroPartialFileSystem); _shadowWrappers.Add(_partialViewsFileSystem); - _shadowWrappers.Add(_stylesheetsFileSystem); _shadowWrappers.Add(_scriptsFileSystem); _shadowWrappers.Add(_mvcViewsFileSystem); @@ -156,64 +248,6 @@ namespace Umbraco.Cms.Core.IO #endregion - #region Providers - - private readonly Dictionary _paths = new Dictionary(); - - // internal for tests - internal IReadOnlyDictionary Paths => _paths; - private GlobalSettings _globalSettings; - private readonly IHostingEnvironment _hostingEnvironment; - - /// - /// Gets a strongly-typed filesystem. - /// - /// The type of the filesystem. - /// A strongly-typed filesystem of the specified type. - /// - /// Note that any filesystem created by this method *after* shadowing begins, will *not* be - /// shadowing (and an exception will be thrown by the ShadowWrapper). - /// - public TFileSystem GetFileSystem(IFileSystem supporting) - where TFileSystem : FileSystemWrapper - { - if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); - - return (TFileSystem) _filesystems.GetOrAdd(typeof(TFileSystem), _ => new Lazy(() => - { - var typeofTFileSystem = typeof(TFileSystem); - - // path must be unique and not collide with paths used in CreateWellKnownFileSystems - // for our well-known 'media' filesystem we can use the short 'media' path - // for others, put them under 'x/' and use ... something - string path; - if (typeofTFileSystem == typeof(MediaFileSystem)) - { - path = "media"; - } - else - { - lock (_paths) - { - if (!_paths.TryGetValue(typeofTFileSystem, out path)) - { - path = Guid.NewGuid().ToString("N").Substring(0, 6); - while (_paths.ContainsValue(path)) // this can't loop forever, right? - path = Guid.NewGuid().ToString("N").Substring(0, 6); - _paths[typeofTFileSystem] = path; - } - } - - path = "x/" + path; - } - - var shadowWrapper = CreateShadowWrapper(supporting, path); - return _container.CreateInstance(shadowWrapper); - })).Value; - } - - #endregion - #region Shadow // note @@ -221,9 +255,17 @@ namespace Umbraco.Cms.Core.IO // global shadow for the entire application, so great care should be taken to ensure that the // application is *not* doing anything else when using a shadow. + /// + /// Shadows the filesystem, should never be used outside the Scope class. + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] public ICompletable Shadow() { - if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); + if (Volatile.Read(ref _wkfsInitialized) == false) + { + EnsureWellKnownFileSystems(); + } var id = ShadowWrapper.CreateShadowId(_hostingEnvironment); return new ShadowFileSystems(this, id); // will invoke BeginShadow and EndShadow @@ -235,14 +277,18 @@ namespace Umbraco.Cms.Core.IO { // if we throw here, it means that something very wrong happened. if (_shadowCurrentId != null) + { throw new InvalidOperationException("Already shadowing."); + } _shadowCurrentId = id; _logger.LogDebug("Shadow '{ShadowId}'", _shadowCurrentId); - foreach (var wrapper in _shadowWrappers) + foreach (ShadowWrapper wrapper in _shadowWrappers) + { wrapper.Shadow(_shadowCurrentId); + } } } @@ -252,14 +298,19 @@ namespace Umbraco.Cms.Core.IO { // if we throw here, it means that something very wrong happened. if (_shadowCurrentId == null) + { throw new InvalidOperationException("Not shadowing."); + } + if (id != _shadowCurrentId) + { throw new InvalidOperationException("Not the current shadow."); + } _logger.LogDebug("UnShadow '{ShadowId}' {Status}", id, completed ? "complete" : "abort"); var exceptions = new List(); - foreach (var wrapper in _shadowWrappers) + foreach (ShadowWrapper wrapper in _shadowWrappers) { try { @@ -275,17 +326,31 @@ namespace Umbraco.Cms.Core.IO _shadowCurrentId = null; if (exceptions.Count > 0) + { throw new AggregateException(completed ? "Failed to apply all changes (see exceptions)." : "Failed to abort (see exceptions).", exceptions); + } } } - private ShadowWrapper CreateShadowWrapper(IFileSystem filesystem, string shadowPath) + /// + /// Creates a shadow wrapper for a filesystem, should never be used outside UmbracoBuilder or testing + /// + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public IFileSystem CreateShadowWrapper(IFileSystem filesystem, string shadowPath) => CreateShadowWrapperInternal(filesystem, shadowPath); + + private ShadowWrapper CreateShadowWrapperInternal(IFileSystem filesystem, string shadowPath) { lock (_shadowLocker) { var wrapper = new ShadowWrapper(filesystem, _ioHelper, _hostingEnvironment, _loggerFactory, shadowPath,() => IsScoped()); if (_shadowCurrentId != null) + { wrapper.Shadow(_shadowCurrentId); + } + _shadowWrappers.Add(wrapper); return wrapper; } diff --git a/src/Umbraco.Core/IO/IFileSystems.cs b/src/Umbraco.Core/IO/IFileSystems.cs deleted file mode 100644 index 3a169e33a3..0000000000 --- a/src/Umbraco.Core/IO/IFileSystems.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Umbraco.Cms.Core.IO -{ - /// - /// Provides the system filesystems. - /// - public interface IFileSystems - { - /// - /// Gets the macro partials filesystem. - /// - IFileSystem MacroPartialsFileSystem { get; } - - /// - /// Gets the partial views filesystem. - /// - IFileSystem PartialViewsFileSystem { get; } - - /// - /// Gets the stylesheets filesystem. - /// - IFileSystem StylesheetsFileSystem { get; } - - /// - /// Gets the scripts filesystem. - /// - IFileSystem ScriptsFileSystem { get; } - - /// - /// Gets the MVC views filesystem. - /// - IFileSystem MvcViewsFileSystem { get; } - } -} diff --git a/src/Umbraco.Core/IO/IMediaFileSystem.cs b/src/Umbraco.Core/IO/IMediaFileSystem.cs deleted file mode 100644 index eced74482e..0000000000 --- a/src/Umbraco.Core/IO/IMediaFileSystem.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using Umbraco.Cms.Core.Models; - -namespace Umbraco.Cms.Core.IO -{ - /// - /// Provides methods allowing the manipulation of media files. - /// - public interface IMediaFileSystem : IFileSystem - { - /// - /// Delete media files. - /// - /// Files to delete (filesystem-relative paths). - void DeleteMediaFiles(IEnumerable files); - - /// - /// Gets the file path of a media file. - /// - /// The file name. - /// The unique identifier of the content/media owning the file. - /// The unique identifier of the property type owning the file. - /// The filesystem-relative path to the media file. - /// With the old media path scheme, this CREATES a new media path each time it is invoked. - string GetMediaPath(string filename, Guid cuid, Guid puid); - - /// - /// Gets the file path of a media file. - /// - /// The file name. - /// A previous file path. - /// The unique identifier of the content/media owning the file. - /// The unique identifier of the property type owning the file. - /// The filesystem-relative path to the media file. - /// In the old, legacy, number-based scheme, we try to re-use the media folder - /// specified by . Else, we CREATE a new one. Each time we are invoked. - string GetMediaPath(string filename, string prevpath, Guid cuid, Guid puid); - - /// - /// Stores a media file associated to a property of a content item. - /// - /// The content item owning the media file. - /// The property type owning the media file. - /// The media file name. - /// A stream containing the media bytes. - /// An optional filesystem-relative filepath to the previous media file. - /// The filesystem-relative filepath to the media file. - /// - /// The file is considered "owned" by the content/propertyType. - /// If an is provided then that file (and associated thumbnails if any) is deleted - /// before the new file is saved, and depending on the media path scheme, the folder may be reused for the new file. - /// - string StoreFile(IContentBase content, IPropertyType propertyType, string filename, Stream filestream, string oldpath); - - /// - /// Copies a media file as a new media file, associated to a property of a content item. - /// - /// The content item owning the copy of the media file. - /// The property type owning the copy of the media file. - /// The filesystem-relative path to the source media file. - /// The filesystem-relative path to the copy of the media file. - string CopyFile(IContentBase content, IPropertyType propertyType, string sourcepath); - } -} diff --git a/src/Umbraco.Core/IO/IMediaPathScheme.cs b/src/Umbraco.Core/IO/IMediaPathScheme.cs index bd48b21a16..2b2696ec5d 100644 --- a/src/Umbraco.Core/IO/IMediaPathScheme.cs +++ b/src/Umbraco.Core/IO/IMediaPathScheme.cs @@ -10,13 +10,13 @@ namespace Umbraco.Cms.Core.IO /// /// Gets a media file path. /// - /// The media filesystem. + /// The media filesystem. /// The (content, media) item unique identifier. /// The property type unique identifier. /// The file name. /// A previous filename. /// The filesystem-relative complete file path. - string GetFilePath(IMediaFileSystem fileSystem, Guid itemGuid, Guid propertyGuid, string filename, string previous = null); + string GetFilePath(MediaFileManager fileManager, Guid itemGuid, Guid propertyGuid, string filename, string previous = null); /// /// Gets the directory that can be deleted when the file is deleted. @@ -28,6 +28,6 @@ namespace Umbraco.Cms.Core.IO /// The directory, and anything below it, will be deleted. /// Can return null (or empty) when no directory should be deleted. /// - string GetDeleteDirectory(IMediaFileSystem fileSystem, string filepath); + string GetDeleteDirectory(MediaFileManager fileSystem, string filepath); } } diff --git a/src/Umbraco.Core/IO/MediaFileManager.cs b/src/Umbraco.Core/IO/MediaFileManager.cs new file mode 100644 index 0000000000..de89d7ca87 --- /dev/null +++ b/src/Umbraco.Core/IO/MediaFileManager.cs @@ -0,0 +1,224 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Strings; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Core.IO +{ + public sealed class MediaFileManager + { + private readonly IMediaPathScheme _mediaPathScheme; + private readonly ILogger _logger; + private readonly IShortStringHelper _shortStringHelper; + + /// + /// Gets the media filesystem. + /// + public IFileSystem FileSystem { get; } + + public MediaFileManager( + IFileSystem fileSystem, + IMediaPathScheme mediaPathScheme, + ILogger logger, + IShortStringHelper shortStringHelper) + { + _mediaPathScheme = mediaPathScheme; + _logger = logger; + _shortStringHelper = shortStringHelper; + FileSystem = fileSystem; + } + + /// + /// Delete media files. + /// + /// Files to delete (filesystem-relative paths). + public void DeleteMediaFiles(IEnumerable files) + { + files = files.Distinct(); + + // kinda try to keep things under control + var options = new ParallelOptions { MaxDegreeOfParallelism = 20 }; + + Parallel.ForEach(files, options, file => + { + try + { + if (file.IsNullOrWhiteSpace()) + { + return; + } + + if (FileSystem.FileExists(file) == false) + { + return; + } + + FileSystem.DeleteFile(file); + + var directory = _mediaPathScheme.GetDeleteDirectory(this, file); + if (!directory.IsNullOrWhiteSpace()) + { + FileSystem.DeleteDirectory(directory, true); + } + } + catch (Exception e) + { + _logger.LogError(e, "Failed to delete media file '{File}'.", file); + } + }); + } + + #region Media Path + + /// + /// Gets the file path of a media file. + /// + /// The file name. + /// The unique identifier of the content/media owning the file. + /// The unique identifier of the property type owning the file. + /// The filesystem-relative path to the media file. + /// With the old media path scheme, this CREATES a new media path each time it is invoked. + public string GetMediaPath(string filename, Guid cuid, Guid puid) + { + filename = Path.GetFileName(filename); + if (filename == null) + { + throw new ArgumentException("Cannot become a safe filename.", nameof(filename)); + } + + filename = _shortStringHelper.CleanStringForSafeFileName(filename.ToLowerInvariant()); + + return _mediaPathScheme.GetFilePath(this, cuid, puid, filename); + } + + /// + /// Gets the file path of a media file. + /// + /// The file name. + /// A previous file path. + /// The unique identifier of the content/media owning the file. + /// The unique identifier of the property type owning the file. + /// The filesystem-relative path to the media file. + /// In the old, legacy, number-based scheme, we try to re-use the media folder + /// specified by . Else, we CREATE a new one. Each time we are invoked. + public string GetMediaPath(string filename, string prevpath, Guid cuid, Guid puid) + { + filename = Path.GetFileName(filename); + if (filename == null) + { + throw new ArgumentException("Cannot become a safe filename.", nameof(filename)); + } + + filename = _shortStringHelper.CleanStringForSafeFileName(filename.ToLowerInvariant()); + + return _mediaPathScheme.GetFilePath(this, cuid, puid, filename, prevpath); + } + + #endregion + + #region Associated Media Files + + /// + /// Stores a media file associated to a property of a content item. + /// + /// The content item owning the media file. + /// The property type owning the media file. + /// The media file name. + /// A stream containing the media bytes. + /// An optional filesystem-relative filepath to the previous media file. + /// The filesystem-relative filepath to the media file. + /// + /// The file is considered "owned" by the content/propertyType. + /// If an is provided then that file (and associated thumbnails if any) is deleted + /// before the new file is saved, and depending on the media path scheme, the folder may be reused for the new file. + /// + public string StoreFile(IContentBase content, IPropertyType propertyType, string filename, Stream filestream, string oldpath) + { + if (content == null) + { + throw new ArgumentNullException(nameof(content)); + } + + if (propertyType == null) + { + throw new ArgumentNullException(nameof(propertyType)); + } + + if (filename == null) + { + throw new ArgumentNullException(nameof(filename)); + } + + if (string.IsNullOrWhiteSpace(filename)) + { + throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(filename)); + } + + if (filestream == null) + { + throw new ArgumentNullException(nameof(filestream)); + } + + // clear the old file, if any + if (string.IsNullOrWhiteSpace(oldpath) == false) + { + FileSystem.DeleteFile(oldpath); + } + + // get the filepath, store the data + // use oldpath as "prevpath" to try and reuse the folder, in original number-based scheme + var filepath = GetMediaPath(filename, oldpath, content.Key, propertyType.Key); + FileSystem.AddFile(filepath, filestream); + return filepath; + } + + /// + /// Copies a media file as a new media file, associated to a property of a content item. + /// + /// The content item owning the copy of the media file. + /// The property type owning the copy of the media file. + /// The filesystem-relative path to the source media file. + /// The filesystem-relative path to the copy of the media file. + public string CopyFile(IContentBase content, IPropertyType propertyType, string sourcepath) + { + if (content == null) + { + throw new ArgumentNullException(nameof(content)); + } + + if (propertyType == null) + { + throw new ArgumentNullException(nameof(propertyType)); + } + + if (sourcepath == null) + { + throw new ArgumentNullException(nameof(sourcepath)); + } + + if (string.IsNullOrWhiteSpace(sourcepath)) + { + throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(sourcepath)); + } + + // ensure we have a file to copy + if (FileSystem.FileExists(sourcepath) == false) + { + return null; + } + + // get the filepath + var filename = Path.GetFileName(sourcepath); + var filepath = GetMediaPath(filename, content.Key, propertyType.Key); + FileSystem.CopyFile(sourcepath, filepath); + return filepath; + } + + #endregion + } +} diff --git a/src/Umbraco.Core/IO/MediaFileSystem.cs b/src/Umbraco.Core/IO/MediaFileSystem.cs deleted file mode 100644 index 6d598941c5..0000000000 --- a/src/Umbraco.Core/IO/MediaFileSystem.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Strings; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Core.IO -{ - /// - /// A custom file system provider for media - /// - public class MediaFileSystem : FileSystemWrapper, IMediaFileSystem - { - private readonly IMediaPathScheme _mediaPathScheme; - private readonly ILogger _logger; - private readonly IShortStringHelper _shortStringHelper; - - /// - /// Initializes a new instance of the class. - /// - public MediaFileSystem(IFileSystem innerFileSystem, IMediaPathScheme mediaPathScheme, ILogger logger, IShortStringHelper shortStringHelper) - : base(innerFileSystem) - { - _mediaPathScheme = mediaPathScheme; - _logger = logger; - _shortStringHelper = shortStringHelper; - } - - /// - public void DeleteMediaFiles(IEnumerable files) - { - files = files.Distinct(); - - // kinda try to keep things under control - var options = new ParallelOptions { MaxDegreeOfParallelism = 20 }; - - Parallel.ForEach(files, options, file => - { - try - { - if (file.IsNullOrWhiteSpace()) return; - if (FileExists(file) == false) return; - DeleteFile(file); - - var directory = _mediaPathScheme.GetDeleteDirectory(this, file); - if (!directory.IsNullOrWhiteSpace()) - DeleteDirectory(directory, true); - } - catch (Exception e) - { - _logger.LogError(e, "Failed to delete media file '{File}'.", file); - } - }); - } - - #region Media Path - - /// - public string GetMediaPath(string filename, Guid cuid, Guid puid) - { - filename = Path.GetFileName(filename); - if (filename == null) throw new ArgumentException("Cannot become a safe filename.", nameof(filename)); - filename = _shortStringHelper.CleanStringForSafeFileName(filename.ToLowerInvariant()); - - return _mediaPathScheme.GetFilePath(this, cuid, puid, filename); - } - - /// - public string GetMediaPath(string filename, string prevpath, Guid cuid, Guid puid) - { - filename = Path.GetFileName(filename); - if (filename == null) throw new ArgumentException("Cannot become a safe filename.", nameof(filename)); - filename = _shortStringHelper.CleanStringForSafeFileName(filename.ToLowerInvariant()); - - return _mediaPathScheme.GetFilePath(this, cuid, puid, filename, prevpath); - } - - #endregion - - #region Associated Media Files - - /// - public string StoreFile(IContentBase content, IPropertyType propertyType, string filename, Stream filestream, string oldpath) - { - if (content == null) throw new ArgumentNullException(nameof(content)); - if (propertyType == null) throw new ArgumentNullException(nameof(propertyType)); - if (filename == null) throw new ArgumentNullException(nameof(filename)); - if (string.IsNullOrWhiteSpace(filename)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(filename)); - if (filestream == null) throw new ArgumentNullException(nameof(filestream)); - - // clear the old file, if any - if (string.IsNullOrWhiteSpace(oldpath) == false) - DeleteFile(oldpath); - - // get the filepath, store the data - // use oldpath as "prevpath" to try and reuse the folder, in original number-based scheme - var filepath = GetMediaPath(filename, oldpath, content.Key, propertyType.Key); - AddFile(filepath, filestream); - return filepath; - } - - /// - public string CopyFile(IContentBase content, IPropertyType propertyType, string sourcepath) - { - if (content == null) throw new ArgumentNullException(nameof(content)); - if (propertyType == null) throw new ArgumentNullException(nameof(propertyType)); - if (sourcepath == null) throw new ArgumentNullException(nameof(sourcepath)); - if (string.IsNullOrWhiteSpace(sourcepath)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(sourcepath)); - - // ensure we have a file to copy - if (FileExists(sourcepath) == false) return null; - - // get the filepath - var filename = Path.GetFileName(sourcepath); - var filepath = GetMediaPath(filename, content.Key, propertyType.Key); - this.CopyFile(sourcepath, filepath); - return filepath; - } - - #endregion - } -} diff --git a/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs b/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs index a23468d5ac..f3c15a13d6 100644 --- a/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs +++ b/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs @@ -12,7 +12,7 @@ namespace Umbraco.Cms.Core.IO.MediaPathSchemes public class CombinedGuidsMediaPathScheme : IMediaPathScheme { /// - public string GetFilePath(IMediaFileSystem fileSystem, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) + public string GetFilePath(MediaFileManager fileManager, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) { // assumes that cuid and puid keys can be trusted - and that a single property type // for a single content cannot store two different files with the same name @@ -23,6 +23,6 @@ namespace Umbraco.Cms.Core.IO.MediaPathSchemes } /// - public string GetDeleteDirectory(IMediaFileSystem fileSystem, string filepath) => Path.GetDirectoryName(filepath); + public string GetDeleteDirectory(MediaFileManager fileSystem, string filepath) => Path.GetDirectoryName(filepath); } } diff --git a/src/Umbraco.Core/IO/MediaPathSchemes/OriginalMediaPathScheme.cs b/src/Umbraco.Core/IO/MediaPathSchemes/OriginalMediaPathScheme.cs index ea23bf0145..d1cea0c46b 100644 --- a/src/Umbraco.Core/IO/MediaPathSchemes/OriginalMediaPathScheme.cs +++ b/src/Umbraco.Core/IO/MediaPathSchemes/OriginalMediaPathScheme.cs @@ -20,7 +20,7 @@ namespace Umbraco.Cms.Core.IO.MediaPathSchemes private bool _folderCounterInitialized; /// - public string GetFilePath(IMediaFileSystem fileSystem, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) + public string GetFilePath(MediaFileManager fileManager, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) { string directory; if (previous != null) @@ -33,11 +33,11 @@ namespace Umbraco.Cms.Core.IO.MediaPathSchemes var pos = previous.IndexOf(sep, StringComparison.Ordinal); var s = pos > 0 ? previous.Substring(0, pos) : null; - directory = pos > 0 && int.TryParse(s, out _) ? s : GetNextDirectory(fileSystem); + directory = pos > 0 && int.TryParse(s, out _) ? s : GetNextDirectory(fileManager.FileSystem); } else { - directory = GetNextDirectory(fileSystem); + directory = GetNextDirectory(fileManager.FileSystem); } if (directory == null) @@ -47,7 +47,7 @@ namespace Umbraco.Cms.Core.IO.MediaPathSchemes } /// - public string GetDeleteDirectory(IMediaFileSystem fileSystem, string filepath) + public string GetDeleteDirectory(MediaFileManager fileSystem, string filepath) { return Path.GetDirectoryName(filepath); } diff --git a/src/Umbraco.Core/IO/MediaPathSchemes/TwoGuidsMediaPathScheme.cs b/src/Umbraco.Core/IO/MediaPathSchemes/TwoGuidsMediaPathScheme.cs index 2fffd4e7d6..203c8aaed8 100644 --- a/src/Umbraco.Core/IO/MediaPathSchemes/TwoGuidsMediaPathScheme.cs +++ b/src/Umbraco.Core/IO/MediaPathSchemes/TwoGuidsMediaPathScheme.cs @@ -12,13 +12,13 @@ namespace Umbraco.Cms.Core.IO.MediaPathSchemes public class TwoGuidsMediaPathScheme : IMediaPathScheme { /// - public string GetFilePath(IMediaFileSystem fileSystem, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) + public string GetFilePath(MediaFileManager fileManager, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) { return Path.Combine(itemGuid.ToString("N"), propertyGuid.ToString("N"), filename).Replace('\\', '/'); } /// - public string GetDeleteDirectory(IMediaFileSystem fileSystem, string filepath) + public string GetDeleteDirectory(MediaFileManager fileManager, string filepath) { return Path.GetDirectoryName(filepath); } diff --git a/src/Umbraco.Core/IO/MediaPathSchemes/UniqueMediaPathScheme.cs b/src/Umbraco.Core/IO/MediaPathSchemes/UniqueMediaPathScheme.cs index faf94fb6e6..55a9cd4574 100644 --- a/src/Umbraco.Core/IO/MediaPathSchemes/UniqueMediaPathScheme.cs +++ b/src/Umbraco.Core/IO/MediaPathSchemes/UniqueMediaPathScheme.cs @@ -14,7 +14,7 @@ namespace Umbraco.Cms.Core.IO.MediaPathSchemes private const int DirectoryLength = 8; /// - public string GetFilePath(IMediaFileSystem fileSystem, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) + public string GetFilePath(MediaFileManager fileManager, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) { var combinedGuid = GuidUtils.Combine(itemGuid, propertyGuid); var directory = GuidUtils.ToBase32String(combinedGuid, DirectoryLength); @@ -32,6 +32,6 @@ namespace Umbraco.Cms.Core.IO.MediaPathSchemes /// race conditions. We'd need to implement locks in for /// this. /// - public string GetDeleteDirectory(IMediaFileSystem fileSystem, string filepath) => null; + public string GetDeleteDirectory(MediaFileManager fileManager, string filepath) => null; } } diff --git a/src/Umbraco.Core/Media/UploadAutoFillProperties.cs b/src/Umbraco.Core/Media/UploadAutoFillProperties.cs index dc5529d25f..32c8ddd98d 100644 --- a/src/Umbraco.Core/Media/UploadAutoFillProperties.cs +++ b/src/Umbraco.Core/Media/UploadAutoFillProperties.cs @@ -13,18 +13,18 @@ namespace Umbraco.Cms.Core.Media /// public class UploadAutoFillProperties { - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly ILogger _logger; private readonly IImageUrlGenerator _imageUrlGenerator; private readonly IImageDimensionExtractor _imageDimensionExtractor; public UploadAutoFillProperties( - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileManager, ILogger logger, IImageUrlGenerator imageUrlGenerator, IImageDimensionExtractor imageDimensionExtractor) { - _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); + _mediaFileManager = mediaFileManager ?? throw new ArgumentNullException(nameof(mediaFileManager)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _imageUrlGenerator = imageUrlGenerator ?? throw new ArgumentNullException(nameof(imageUrlGenerator)); _imageDimensionExtractor = imageDimensionExtractor ?? throw new ArgumentNullException(nameof(imageDimensionExtractor)); @@ -69,7 +69,7 @@ namespace Umbraco.Cms.Core.Media // if anything goes wrong, just reset the properties try { - using (var filestream = _mediaFileSystem.OpenFile(filepath)) + using (var filestream = _mediaFileManager.FileSystem.OpenFile(filepath)) { var extension = (Path.GetExtension(filepath) ?? "").TrimStart(Constants.CharArrays.Period); var size = _imageUrlGenerator.IsSupportedImageFormat(extension) ? (ImageSize?)_imageDimensionExtractor.GetDimensions(filestream) : null; diff --git a/src/Umbraco.Core/Models/Mapping/UserMapDefinition.cs b/src/Umbraco.Core/Models/Mapping/UserMapDefinition.cs index 85da93fb31..4a19fbe530 100644 --- a/src/Umbraco.Core/Models/Mapping/UserMapDefinition.cs +++ b/src/Umbraco.Core/Models/Mapping/UserMapDefinition.cs @@ -28,12 +28,12 @@ namespace Umbraco.Cms.Core.Models.Mapping private readonly ActionCollection _actions; private readonly AppCaches _appCaches; private readonly GlobalSettings _globalSettings; - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly IShortStringHelper _shortStringHelper; private readonly IImageUrlGenerator _imageUrlGenerator; public UserMapDefinition(ILocalizedTextService textService, IUserService userService, IEntityService entityService, ISectionService sectionService, - AppCaches appCaches, ActionCollection actions, IOptions globalSettings, IMediaFileSystem mediaFileSystem, IShortStringHelper shortStringHelper, + AppCaches appCaches, ActionCollection actions, IOptions globalSettings, MediaFileManager mediaFileManager, IShortStringHelper shortStringHelper, IImageUrlGenerator imageUrlGenerator) { _sectionService = sectionService; @@ -43,7 +43,7 @@ namespace Umbraco.Cms.Core.Models.Mapping _actions = actions; _appCaches = appCaches; _globalSettings = globalSettings.Value; - _mediaFileSystem = mediaFileSystem; + _mediaFileManager = mediaFileManager; _shortStringHelper = shortStringHelper; _imageUrlGenerator = imageUrlGenerator; } @@ -283,7 +283,7 @@ namespace Umbraco.Cms.Core.Models.Mapping private void Map(IUser source, UserDisplay target, MapperContext context) { target.AvailableCultures = _textService.GetSupportedCultures().ToDictionary(x => x.Name, x => x.DisplayName); - target.Avatars = source.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileSystem, _imageUrlGenerator); + target.Avatars = source.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileManager, _imageUrlGenerator); target.CalculatedStartContentIds = GetStartNodes(source.CalculateContentStartNodeIds(_entityService, _appCaches), UmbracoObjectTypes.Document, "content/contentRoot", context); target.CalculatedStartMediaIds = GetStartNodes(source.CalculateMediaStartNodeIds(_entityService, _appCaches), UmbracoObjectTypes.Media, "media/mediaRoot", context); target.CreateDate = source.CreateDate; @@ -314,7 +314,7 @@ namespace Umbraco.Cms.Core.Models.Mapping //Loading in the user avatar's requires an external request if they don't have a local file avatar, this means that initial load of paging may incur a cost //Alternatively, if this is annoying the back office UI would need to be updated to request the avatars for the list of users separately so it doesn't look //like the load time is waiting. - target.Avatars = source.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileSystem, _imageUrlGenerator); + target.Avatars = source.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileManager, _imageUrlGenerator); target.Culture = source.GetUserCulture(_textService, _globalSettings).ToString(); target.Email = source.Email; target.EmailHash = source.Email.ToLowerInvariant().Trim().GenerateHash(); @@ -333,7 +333,7 @@ namespace Umbraco.Cms.Core.Models.Mapping private void Map(IUser source, UserDetail target, MapperContext context) { target.AllowedSections = source.AllowedSections; - target.Avatars = source.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileSystem, _imageUrlGenerator); + target.Avatars = source.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileManager, _imageUrlGenerator); target.Culture = source.GetUserCulture(_textService, _globalSettings).ToString(); target.Email = source.Email; target.EmailHash = source.Email.ToLowerInvariant().Trim().GenerateHash(); diff --git a/src/Umbraco.Core/Models/UserExtensions.cs b/src/Umbraco.Core/Models/UserExtensions.cs index 6fb74c3b86..dd96faf298 100644 --- a/src/Umbraco.Core/Models/UserExtensions.cs +++ b/src/Umbraco.Core/Models/UserExtensions.cs @@ -21,11 +21,11 @@ namespace Umbraco.Cms.Core.Models /// /// /// - /// + /// /// /// A list of 5 different sized avatar URLs /// - public static string[] GetUserAvatarUrls(this IUser user, IAppCache cache, IMediaFileSystem mediaFileSystem, IImageUrlGenerator imageUrlGenerator) + public static string[] GetUserAvatarUrls(this IUser user, IAppCache cache, MediaFileManager mediaFileManager, IImageUrlGenerator imageUrlGenerator) { // If FIPS is required, never check the Gravatar service as it only supports MD5 hashing. // Unfortunately, if the FIPS setting is enabled on Windows, using MD5 will throw an exception @@ -76,7 +76,7 @@ namespace Umbraco.Cms.Core.Models } //use the custom avatar - var avatarUrl = mediaFileSystem.GetUrl(user.Avatar); + var avatarUrl = mediaFileManager.FileSystem.GetUrl(user.Avatar); return new[] { imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(avatarUrl) { ImageCropMode = ImageCropMode.Crop, Width = 30, Height = 30 }), diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.FileSystems.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.FileSystems.cs index b00b9a4985..edb8033f3d 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.FileSystems.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.FileSystems.cs @@ -19,10 +19,7 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection * Create an implementation of IFileSystem and register it as the underlying filesystem for * MediaFileSystem with the following extension on composition. * - * composition.SetMediaFileSystem(factory => FactoryMethodToReturnYourImplementation()) - * - * Alternatively you can just register an Implementation of IMediaFileSystem, however the - * extension above ensures that your IFileSystem implementation is wrapped by the "ShadowWrapper". + * builder.SetMediaFileSystem(factory => FactoryMethodToReturnYourImplementation()) * * WHAT IS SHADOWING * ----------------- @@ -37,23 +34,17 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection internal static IUmbracoBuilder AddFileSystems(this IUmbracoBuilder builder) { // register FileSystems, which manages all filesystems - // it needs to be registered (not only the interface) because it provides additional - // functionality eg for scoping, and is injected in the scope provider - whereas the - // interface is really for end-users to get access to filesystems. - builder.Services.AddUnique(factory => factory.CreateInstance(factory)); - - // register IFileSystems, which gives access too all filesystems - builder.Services.AddUnique(factory => factory.GetRequiredService()); + builder.Services.AddUnique(); // register the scheme for media paths builder.Services.AddUnique(); builder.SetMediaFileSystem(factory => { - var ioHelper = factory.GetRequiredService(); - var hostingEnvironment = factory.GetRequiredService(); - var logger = factory.GetRequiredService>(); - var globalSettings = factory.GetRequiredService>().Value; + IIOHelper ioHelper = factory.GetRequiredService(); + IHostingEnvironment hostingEnvironment = factory.GetRequiredService(); + ILogger logger = factory.GetRequiredService>(); + GlobalSettings globalSettings = factory.GetRequiredService>().Value; var rootPath = hostingEnvironment.MapPathWebRoot(globalSettings.UmbracoMediaPath); var rootUrl = hostingEnvironment.ToAbsolute(globalSettings.UmbracoMediaPath); diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Uniques.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Uniques.cs index 5a6c8fe8f2..cbbaa6a3e0 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Uniques.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Uniques.cs @@ -1,5 +1,6 @@ using System; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Dictionary; using Umbraco.Cms.Core.IO; @@ -109,20 +110,48 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection } /// - /// Sets the underlying media filesystem. + /// Sets the filesystem used by the MediaFileManager /// /// A builder. - /// A filesystem factory. - /// - /// Using this helper will ensure that your IFileSystem implementation is wrapped by the ShadowWrapper - /// - public static void SetMediaFileSystem(this IUmbracoBuilder builder, Func filesystemFactory) - => builder.Services.AddUnique(factory => + /// Factory method to create an IFileSystem implementation used in the MediaFileManager + public static void SetMediaFileSystem(this IUmbracoBuilder builder, + Func filesystemFactory) => builder.Services.AddUnique( + provider => { - var fileSystems = factory.GetRequiredService(); - return fileSystems.GetFileSystem(filesystemFactory(factory)); + IFileSystem filesystem = filesystemFactory(provider); + // We need to use the Filesystems to create a shadow wrapper, + // because shadow wrapper requires the IsScoped delegate from the FileSystems. + // This is used by the scope provider when taking control of the filesystems. + FileSystems fileSystems = provider.GetRequiredService(); + IFileSystem shadow = fileSystems.CreateShadowWrapper(filesystem, "media"); + + return provider.CreateInstance(shadow); }); + /// + /// Register FileSystems with a method to configure the . + /// + /// A builder. + /// Method that configures the . + /// Throws exception if is null. + /// Throws exception if full path can't be resolved successfully. + public static void ConfigureFileSystems(this IUmbracoBuilder builder, + Action configure) + { + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } + + builder.Services.AddUnique( + provider => + { + FileSystems fileSystems = provider.CreateInstance(); + configure(provider, fileSystems); + return fileSystems; + }); + } + /// /// Sets the log viewer. /// diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewMacroRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewMacroRepository.cs index d13e4c6945..47123b790e 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewMacroRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewMacroRepository.cs @@ -6,7 +6,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement { internal class PartialViewMacroRepository : PartialViewRepository, IPartialViewMacroRepository { - public PartialViewMacroRepository(IFileSystems fileSystems, IIOHelper ioHelper) + public PartialViewMacroRepository(FileSystems fileSystems, IIOHelper ioHelper) : base(fileSystems.MacroPartialsFileSystem, ioHelper) { } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewRepository.cs index eee199e252..a1d2b218c4 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewRepository.cs @@ -13,7 +13,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement { private readonly IIOHelper _ioHelper; - public PartialViewRepository(IFileSystems fileSystems, IIOHelper ioHelper) + public PartialViewRepository(FileSystems fileSystems, IIOHelper ioHelper) : base(fileSystems.PartialViewsFileSystem) { _ioHelper = ioHelper; diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ScriptRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ScriptRepository.cs index 161b7a90a8..77b8ac9e20 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ScriptRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ScriptRepository.cs @@ -19,7 +19,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement private readonly IIOHelper _ioHelper; private readonly GlobalSettings _globalSettings; - public ScriptRepository(IFileSystems fileSystems, IIOHelper ioHelper, IOptions globalSettings) + public ScriptRepository(FileSystems fileSystems, IIOHelper ioHelper, IOptions globalSettings) : base(fileSystems.ScriptsFileSystem) { _ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper)); diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/StylesheetRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/StylesheetRepository.cs index 03c729b51c..ac255890bd 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/StylesheetRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/StylesheetRepository.cs @@ -21,7 +21,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement private readonly IIOHelper _ioHelper; private readonly GlobalSettings _globalSettings; - public StylesheetRepository(ILogger logger, IFileSystems fileSystems, IIOHelper ioHelper, IOptions globalSettings) + public StylesheetRepository(ILogger logger, FileSystems fileSystems, IIOHelper ioHelper, IOptions globalSettings) : base(fileSystems.StylesheetsFileSystem) { _logger = logger; diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs index ec54a6bef8..3aad92a012 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs @@ -30,7 +30,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement private readonly IFileSystem _viewsFileSystem; private readonly ViewHelper _viewHelper; - public TemplateRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, IFileSystems fileSystems, IIOHelper ioHelper, IShortStringHelper shortStringHelper) + public TemplateRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, FileSystems fileSystems, IIOHelper ioHelper, IShortStringHelper shortStringHelper) : base(scopeAccessor, cache, logger) { _ioHelper = ioHelper; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs index a0bbf2a7fe..db895444c4 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs @@ -30,7 +30,7 @@ namespace Umbraco.Cms.Core.PropertyEditors INotificationHandler, INotificationHandler, INotificationHandler { - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly ContentSettings _contentSettings; private readonly UploadAutoFillProperties _uploadAutoFillProperties; private readonly IDataTypeService _dataTypeService; @@ -40,7 +40,7 @@ namespace Umbraco.Cms.Core.PropertyEditors public FileUploadPropertyEditor( ILoggerFactory loggerFactory, - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileManager, IOptions contentSettings, IDataTypeService dataTypeService, ILocalizationService localizationService, @@ -51,7 +51,7 @@ namespace Umbraco.Cms.Core.PropertyEditors IContentService contentService) : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) { - _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); + _mediaFileManager = mediaFileManager ?? throw new ArgumentNullException(nameof(mediaFileManager)); _contentSettings = contentSettings.Value; _dataTypeService = dataTypeService; _localizationService = localizationService; @@ -66,7 +66,7 @@ namespace Umbraco.Cms.Core.PropertyEditors /// The corresponding property value editor. protected override IDataValueEditor CreateValueEditor() { - var editor = new FileUploadPropertyValueEditor(Attribute, _mediaFileSystem, _dataTypeService, _localizationService, _localizedTextService, ShortStringHelper, Options.Create(_contentSettings), JsonSerializer); + var editor = new FileUploadPropertyValueEditor(Attribute, _mediaFileManager, _dataTypeService, _localizationService, _localizedTextService, ShortStringHelper, Options.Create(_contentSettings), JsonSerializer); editor.Validators.Add(new UploadFileTypeValidator(_localizedTextService, Options.Create(_contentSettings))); return editor; } @@ -115,12 +115,12 @@ namespace Umbraco.Cms.Core.PropertyEditors //check if the published value contains data and return it var propVal = propertyValue.PublishedValue; if (propVal != null && propVal is string str1 && !str1.IsNullOrWhiteSpace()) - yield return _mediaFileSystem.GetRelativePath(str1); + yield return _mediaFileManager.FileSystem.GetRelativePath(str1); //check if the edited value contains data and return it propVal = propertyValue.EditedValue; if (propVal != null && propVal is string str2 && !str2.IsNullOrWhiteSpace()) - yield return _mediaFileSystem.GetRelativePath(str2); + yield return _mediaFileManager.FileSystem.GetRelativePath(str2); } } @@ -142,9 +142,9 @@ namespace Umbraco.Cms.Core.PropertyEditors continue; } - var sourcePath = _mediaFileSystem.GetRelativePath(str); - var copyPath = _mediaFileSystem.CopyFile(notification.Copy, property.PropertyType, sourcePath); - notification.Copy.SetValue(property.Alias, _mediaFileSystem.GetUrl(copyPath), propertyValue.Culture, propertyValue.Segment); + var sourcePath = _mediaFileManager.FileSystem.GetRelativePath(str); + var copyPath = _mediaFileManager.CopyFile(notification.Copy, property.PropertyType, sourcePath); + notification.Copy.SetValue(property.Alias, _mediaFileManager.FileSystem.GetUrl(copyPath), propertyValue.Culture, propertyValue.Segment); isUpdated = true; } } @@ -165,7 +165,7 @@ namespace Umbraco.Cms.Core.PropertyEditors private void DeleteContainedFiles(IEnumerable deletedEntities) { var filePathsToDelete = ContainedFilePaths(deletedEntities); - _mediaFileSystem.DeleteMediaFiles(filePathsToDelete); + _mediaFileManager.DeleteMediaFiles(filePathsToDelete); } public void Handle(MediaSavingNotification notification) @@ -194,7 +194,7 @@ namespace Umbraco.Cms.Core.PropertyEditors if (string.IsNullOrWhiteSpace(svalue)) _uploadAutoFillProperties.Reset(model, autoFillConfig, pvalue.Culture, pvalue.Segment); else - _uploadAutoFillProperties.Populate(model, autoFillConfig, _mediaFileSystem.GetRelativePath(svalue), pvalue.Culture, pvalue.Segment); + _uploadAutoFillProperties.Populate(model, autoFillConfig, _mediaFileManager.FileSystem.GetRelativePath(svalue), pvalue.Culture, pvalue.Segment); } } } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyValueEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyValueEditor.cs index 3f892dd000..b30a0b971d 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyValueEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyValueEditor.cs @@ -19,12 +19,12 @@ namespace Umbraco.Cms.Core.PropertyEditors /// internal class FileUploadPropertyValueEditor : DataValueEditor { - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly ContentSettings _contentSettings; public FileUploadPropertyValueEditor( DataEditorAttribute attribute, - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileManager, IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, @@ -33,7 +33,7 @@ namespace Umbraco.Cms.Core.PropertyEditors IJsonSerializer jsonSerializer) : base(dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer, attribute) { - _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); + _mediaFileManager = mediaFileManager ?? throw new ArgumentNullException(nameof(mediaFileManager)); _contentSettings = contentSettings.Value ?? throw new ArgumentNullException(nameof(contentSettings)); } @@ -59,7 +59,7 @@ namespace Umbraco.Cms.Core.PropertyEditors { var currentPath = currentValue as string; if (!currentPath.IsNullOrWhiteSpace()) - currentPath = _mediaFileSystem.GetRelativePath(currentPath); + currentPath = _mediaFileManager.FileSystem.GetRelativePath(currentPath); string editorFile = null; if (editorValue.Value != null) @@ -84,7 +84,7 @@ namespace Umbraco.Cms.Core.PropertyEditors // value is unchanged. if (string.IsNullOrWhiteSpace(editorFile) && string.IsNullOrWhiteSpace(currentPath) == false) { - _mediaFileSystem.DeleteFile(currentPath); + _mediaFileManager.FileSystem.DeleteFile(currentPath); return null; // clear } @@ -100,11 +100,11 @@ namespace Umbraco.Cms.Core.PropertyEditors // remove current file if replaced if (currentPath != filepath && string.IsNullOrWhiteSpace(currentPath) == false) - _mediaFileSystem.DeleteFile(currentPath); + _mediaFileManager.FileSystem.DeleteFile(currentPath); // update json and return if (editorFile == null) return null; - return filepath == null ? string.Empty : _mediaFileSystem.GetUrl(filepath); + return filepath == null ? string.Empty : _mediaFileManager.FileSystem.GetUrl(filepath); } @@ -118,14 +118,14 @@ namespace Umbraco.Cms.Core.PropertyEditors // get the filepath // in case we are using the old path scheme, try to re-use numbers (bah...) - var filepath = _mediaFileSystem.GetMediaPath(file.FileName, currentPath, cuid, puid); // fs-relative path + var filepath = _mediaFileManager.GetMediaPath(file.FileName, currentPath, cuid, puid); // fs-relative path using (var filestream = File.OpenRead(file.TempFilePath)) { // TODO: Here it would make sense to do the auto-fill properties stuff but the API doesn't allow us to do that right // since we'd need to be able to return values for other properties from these methods - _mediaFileSystem.AddFile(filepath, filestream, true); // must overwrite! + _mediaFileManager.FileSystem.AddFile(filepath, filestream, true); // must overwrite! } return filepath; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs index e138e98618..fe66a31e13 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs @@ -37,7 +37,7 @@ namespace Umbraco.Cms.Core.PropertyEditors INotificationHandler, INotificationHandler, INotificationHandler { - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly ContentSettings _contentSettings; private readonly IDataTypeService _dataTypeService; private readonly IIOHelper _ioHelper; @@ -50,7 +50,7 @@ namespace Umbraco.Cms.Core.PropertyEditors /// public ImageCropperPropertyEditor( ILoggerFactory loggerFactory, - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileManager, IOptions contentSettings, IDataTypeService dataTypeService, ILocalizationService localizationService, @@ -62,7 +62,7 @@ namespace Umbraco.Cms.Core.PropertyEditors IContentService contentService) : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) { - _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); + _mediaFileManager = mediaFileManager ?? throw new ArgumentNullException(nameof(mediaFileManager)); _contentSettings = contentSettings.Value ?? throw new ArgumentNullException(nameof(contentSettings)); _dataTypeService = dataTypeService ?? throw new ArgumentNullException(nameof(dataTypeService)); _ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper)); @@ -86,7 +86,7 @@ namespace Umbraco.Cms.Core.PropertyEditors /// Creates the corresponding property value editor. /// /// The corresponding property value editor. - protected override IDataValueEditor CreateValueEditor() => new ImageCropperPropertyValueEditor(Attribute, LoggerFactory.CreateLogger(), _mediaFileSystem, DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, _contentSettings, JsonSerializer); + protected override IDataValueEditor CreateValueEditor() => new ImageCropperPropertyValueEditor(Attribute, LoggerFactory.CreateLogger(), _mediaFileManager, DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, _contentSettings, JsonSerializer); /// /// Creates the corresponding preValue editor. @@ -151,11 +151,11 @@ namespace Umbraco.Cms.Core.PropertyEditors { //check if the published value contains data and return it var src = GetFileSrcFromPropertyValue(propertyValue.PublishedValue, out var _); - if (src != null) yield return _mediaFileSystem.GetRelativePath(src); + if (src != null) yield return _mediaFileManager.FileSystem.GetRelativePath(src); //check if the edited value contains data and return it src = GetFileSrcFromPropertyValue(propertyValue.EditedValue, out var _); - if (src != null) yield return _mediaFileSystem.GetRelativePath(src); + if (src != null) yield return _mediaFileManager.FileSystem.GetRelativePath(src); } } @@ -174,7 +174,7 @@ namespace Umbraco.Cms.Core.PropertyEditors deserializedValue = GetJObject(str, true); if (deserializedValue?["src"] == null) return null; var src = deserializedValue["src"].Value(); - return relative ? _mediaFileSystem.GetRelativePath(src) : src; + return relative ? _mediaFileManager.FileSystem.GetRelativePath(src) : src; } /// @@ -198,9 +198,9 @@ namespace Umbraco.Cms.Core.PropertyEditors { continue; } - var sourcePath = _mediaFileSystem.GetRelativePath(src); - var copyPath = _mediaFileSystem.CopyFile(notification.Copy, property.PropertyType, sourcePath); - jo["src"] = _mediaFileSystem.GetUrl(copyPath); + var sourcePath = _mediaFileManager.FileSystem.GetRelativePath(src); + var copyPath = _mediaFileManager.CopyFile(notification.Copy, property.PropertyType, sourcePath); + jo["src"] = _mediaFileManager.FileSystem.GetUrl(copyPath); notification.Copy.SetValue(property.Alias, jo.ToString(), propertyValue.Culture, propertyValue.Segment); isUpdated = true; } @@ -221,7 +221,7 @@ namespace Umbraco.Cms.Core.PropertyEditors private void DeleteContainedFiles(IEnumerable deletedEntities) { var filePathsToDelete = ContainedFilePaths(deletedEntities); - _mediaFileSystem.DeleteMediaFiles(filePathsToDelete); + _mediaFileManager.DeleteMediaFiles(filePathsToDelete); } public void Handle(MediaSavingNotification notification) @@ -282,7 +282,7 @@ namespace Umbraco.Cms.Core.PropertyEditors if (src == null) _autoFillProperties.Reset(model, autoFillConfig, pvalue.Culture, pvalue.Segment); else - _autoFillProperties.Populate(model, autoFillConfig, _mediaFileSystem.GetRelativePath(src), pvalue.Culture, pvalue.Segment); + _autoFillProperties.Populate(model, autoFillConfig, _mediaFileManager.FileSystem.GetRelativePath(src), pvalue.Culture, pvalue.Segment); } } } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyValueEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyValueEditor.cs index 5cbbdf8e31..19fceb21e5 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyValueEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyValueEditor.cs @@ -24,13 +24,13 @@ namespace Umbraco.Cms.Core.PropertyEditors internal class ImageCropperPropertyValueEditor : DataValueEditor // TODO: core vs web? { private readonly ILogger _logger; - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly ContentSettings _contentSettings; public ImageCropperPropertyValueEditor( DataEditorAttribute attribute, ILogger logger, - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileSystem, IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, @@ -40,7 +40,7 @@ namespace Umbraco.Cms.Core.PropertyEditors : base(dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer, attribute) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); + _mediaFileManager = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); _contentSettings = contentSettings ?? throw new ArgumentNullException(nameof(contentSettings)); } @@ -97,7 +97,7 @@ namespace Umbraco.Cms.Core.PropertyEditors _logger.LogWarning(ex, "Could not parse current db value to a JObject."); } if (string.IsNullOrWhiteSpace(currentPath) == false) - currentPath = _mediaFileSystem.GetRelativePath(currentPath); + currentPath = _mediaFileManager.FileSystem.GetRelativePath(currentPath); // get the new json and path JObject editorJson = null; @@ -130,7 +130,7 @@ namespace Umbraco.Cms.Core.PropertyEditors // value is unchanged. if (string.IsNullOrWhiteSpace(editorFile) && string.IsNullOrWhiteSpace(currentPath) == false) { - _mediaFileSystem.DeleteFile(currentPath); + _mediaFileManager.FileSystem.DeleteFile(currentPath); return null; // clear } @@ -146,11 +146,11 @@ namespace Umbraco.Cms.Core.PropertyEditors // remove current file if replaced if (currentPath != filepath && string.IsNullOrWhiteSpace(currentPath) == false) - _mediaFileSystem.DeleteFile(currentPath); + _mediaFileManager.FileSystem.DeleteFile(currentPath); // update json and return if (editorJson == null) return null; - editorJson["src"] = filepath == null ? string.Empty : _mediaFileSystem.GetUrl(filepath); + editorJson["src"] = filepath == null ? string.Empty : _mediaFileManager.FileSystem.GetUrl(filepath); return editorJson.ToString(); } @@ -163,14 +163,14 @@ namespace Umbraco.Cms.Core.PropertyEditors // get the filepath // in case we are using the old path scheme, try to re-use numbers (bah...) - var filepath = _mediaFileSystem.GetMediaPath(file.FileName, currentPath, cuid, puid); // fs-relative path + var filepath = _mediaFileManager.GetMediaPath(file.FileName, currentPath, cuid, puid); // fs-relative path using (var filestream = File.OpenRead(file.TempFilePath)) { // TODO: Here it would make sense to do the auto-fill properties stuff but the API doesn't allow us to do that right // since we'd need to be able to return values for other properties from these methods - _mediaFileSystem.AddFile(filepath, filestream, true); // must overwrite! + _mediaFileManager.FileSystem.AddFile(filepath, filestream, true); // must overwrite! } return filepath; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs b/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs index 94140d00f2..f92b2911dd 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs @@ -27,21 +27,30 @@ namespace Umbraco.Cms.Core.PropertyEditors private readonly IHostingEnvironment _hostingEnvironment; private readonly IMediaService _mediaService; private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly IShortStringHelper _shortStringHelper; private readonly IPublishedUrlProvider _publishedUrlProvider; private readonly IJsonSerializer _serializer; const string TemporaryImageDataAttribute = "data-tmpimg"; - public RichTextEditorPastedImages(IUmbracoContextAccessor umbracoContextAccessor, ILogger logger, IHostingEnvironment hostingEnvironment, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IMediaFileSystem mediaFileSystem, IShortStringHelper shortStringHelper, IPublishedUrlProvider publishedUrlProvider, IJsonSerializer serializer) + public RichTextEditorPastedImages( + IUmbracoContextAccessor umbracoContextAccessor, + ILogger logger, + IHostingEnvironment hostingEnvironment, + IMediaService mediaService, + IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, + MediaFileManager mediaFileManager, + IShortStringHelper shortStringHelper, + IPublishedUrlProvider publishedUrlProvider, + IJsonSerializer serializer) { _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _hostingEnvironment = hostingEnvironment; _mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider ?? throw new ArgumentNullException(nameof(contentTypeBaseServiceProvider)); - _mediaFileSystem = mediaFileSystem; + _mediaFileManager = mediaFileManager; _shortStringHelper = shortStringHelper; _publishedUrlProvider = publishedUrlProvider; _serializer = serializer; @@ -98,7 +107,7 @@ namespace Umbraco.Cms.Core.PropertyEditors if (fileStream == null) throw new InvalidOperationException("Could not acquire file stream"); using (fileStream) { - mediaFile.SetValue(_mediaFileSystem, _shortStringHelper, _contentTypeBaseServiceProvider, _serializer, Constants.Conventions.Media.File, safeFileName, fileStream); + mediaFile.SetValue(_mediaFileManager, _shortStringHelper, _contentTypeBaseServiceProvider, _serializer, Constants.Conventions.Media.File, safeFileName, fileStream); } _mediaService.Save(mediaFile, userId); diff --git a/src/Umbraco.Infrastructure/Scoping/Scope.cs b/src/Umbraco.Infrastructure/Scoping/Scope.cs index 386bd7c06e..9d58cab2b4 100644 --- a/src/Umbraco.Infrastructure/Scoping/Scope.cs +++ b/src/Umbraco.Infrastructure/Scoping/Scope.cs @@ -21,7 +21,7 @@ namespace Umbraco.Cms.Core.Scoping { private readonly ScopeProvider _scopeProvider; private readonly CoreDebugSettings _coreDebugSettings; - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly IEventAggregator _eventAggregator; private readonly ILogger _logger; @@ -55,7 +55,7 @@ namespace Umbraco.Cms.Core.Scoping private Scope( ScopeProvider scopeProvider, CoreDebugSettings coreDebugSettings, - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileManager, IEventAggregator eventAggregator, ILogger logger, FileSystems fileSystems, @@ -71,7 +71,7 @@ namespace Umbraco.Cms.Core.Scoping { _scopeProvider = scopeProvider; _coreDebugSettings = coreDebugSettings; - _mediaFileSystem = mediaFileSystem; + _mediaFileManager = mediaFileManager; _eventAggregator = eventAggregator; _logger = logger; @@ -163,7 +163,7 @@ namespace Umbraco.Cms.Core.Scoping public Scope( ScopeProvider scopeProvider, CoreDebugSettings coreDebugSettings, - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileManager, IEventAggregator eventAggregator, ILogger logger, FileSystems fileSystems, @@ -175,14 +175,14 @@ namespace Umbraco.Cms.Core.Scoping bool? scopeFileSystems = null, bool callContext = false, bool autoComplete = false) - : this(scopeProvider, coreDebugSettings, mediaFileSystem, eventAggregator, logger, fileSystems, null, scopeContext, detachable, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete) + : this(scopeProvider, coreDebugSettings, mediaFileManager, eventAggregator, logger, fileSystems, null, scopeContext, detachable, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete) { } // initializes a new scope in a nested scopes chain, with its parent public Scope( ScopeProvider scopeProvider, CoreDebugSettings coreDebugSettings, - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileManager, IEventAggregator eventAggregator, ILogger logger, FileSystems fileSystems, @@ -193,7 +193,7 @@ namespace Umbraco.Cms.Core.Scoping bool? scopeFileSystems = null, bool callContext = false, bool autoComplete = false) - : this(scopeProvider, coreDebugSettings, mediaFileSystem, eventAggregator, logger, fileSystems, parent, null, false, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete) + : this(scopeProvider, coreDebugSettings, mediaFileManager, eventAggregator, logger, fileSystems, parent, null, false, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete) { } public Guid InstanceId { get; } = Guid.NewGuid(); @@ -397,7 +397,7 @@ namespace Umbraco.Cms.Core.Scoping return ParentScope.Events; } - return _eventDispatcher ??= new QueuingEventDispatcher(_mediaFileSystem); + return _eventDispatcher ??= new QueuingEventDispatcher(_mediaFileManager); } } diff --git a/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs b/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs index c9e8b39947..cc365beb9b 100644 --- a/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs +++ b/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs @@ -29,19 +29,19 @@ namespace Umbraco.Cms.Core.Scoping private readonly IRequestCache _requestCache; private readonly FileSystems _fileSystems; private readonly CoreDebugSettings _coreDebugSettings; - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private static readonly AsyncLocal> s_scopeStack = new AsyncLocal>(); private static readonly AsyncLocal> s_scopeContextStack = new AsyncLocal>(); private static readonly string s_scopeItemKey = typeof(Scope).FullName; private static readonly string s_contextItemKey = typeof(ScopeProvider).FullName; private readonly IEventAggregator _eventAggregator; - public ScopeProvider(IUmbracoDatabaseFactory databaseFactory, FileSystems fileSystems, IOptions coreDebugSettings, IMediaFileSystem mediaFileSystem, ILogger logger, ILoggerFactory loggerFactory, IRequestCache requestCache, IEventAggregator eventAggregator) + public ScopeProvider(IUmbracoDatabaseFactory databaseFactory, FileSystems fileSystems, IOptions coreDebugSettings, MediaFileManager mediaFileManager, ILogger logger, ILoggerFactory loggerFactory, IRequestCache requestCache, IEventAggregator eventAggregator) { DatabaseFactory = databaseFactory; _fileSystems = fileSystems; _coreDebugSettings = coreDebugSettings.Value; - _mediaFileSystem = mediaFileSystem; + _mediaFileManager = mediaFileManager; _logger = logger; _loggerFactory = loggerFactory; _requestCache = requestCache; @@ -381,7 +381,7 @@ namespace Umbraco.Cms.Core.Scoping RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified, IEventDispatcher eventDispatcher = null, bool? scopeFileSystems = null) - => new Scope(this, _coreDebugSettings, _mediaFileSystem, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, true, null, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems); + => new Scope(this, _coreDebugSettings, _mediaFileManager, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, true, null, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems); /// public void AttachScope(IScope other, bool callContext = false) @@ -460,7 +460,7 @@ namespace Umbraco.Cms.Core.Scoping { IScopeContext ambientContext = AmbientContext; ScopeContext newContext = ambientContext == null ? new ScopeContext() : null; - var scope = new Scope(this, _coreDebugSettings, _mediaFileSystem, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, false, newContext, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete); + var scope = new Scope(this, _coreDebugSettings, _mediaFileManager, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, false, newContext, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete); // assign only if scope creation did not throw! PushAmbientScope(scope); if (newContext != null) @@ -470,7 +470,7 @@ namespace Umbraco.Cms.Core.Scoping return scope; } - var nested = new Scope(this, _coreDebugSettings, _mediaFileSystem, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, ambientScope, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete); + var nested = new Scope(this, _coreDebugSettings, _mediaFileManager, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, ambientScope, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete); PushAmbientScope(nested); return nested; } diff --git a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs index 831cbb1b01..3f12d94cf1 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs @@ -29,16 +29,16 @@ namespace Umbraco.Cms.Core.Services.Implement private readonly IEntityRepository _entityRepository; private readonly IShortStringHelper _shortStringHelper; - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; #region Constructors - public MediaService(IScopeProvider provider, IMediaFileSystem mediaFileSystem, ILoggerFactory loggerFactory, IEventMessagesFactory eventMessagesFactory, + public MediaService(IScopeProvider provider, MediaFileManager mediaFileManager, ILoggerFactory loggerFactory, IEventMessagesFactory eventMessagesFactory, IMediaRepository mediaRepository, IAuditRepository auditRepository, IMediaTypeRepository mediaTypeRepository, IEntityRepository entityRepository, IShortStringHelper shortStringHelper) : base(provider, loggerFactory, eventMessagesFactory) { - _mediaFileSystem = mediaFileSystem; + _mediaFileManager = mediaFileManager; _mediaRepository = mediaRepository; _auditRepository = auditRepository; _mediaTypeRepository = mediaTypeRepository; @@ -1183,12 +1183,12 @@ namespace Umbraco.Cms.Core.Services.Implement public Stream GetMediaFileContentStream(string filepath) { - if (_mediaFileSystem.FileExists(filepath) == false) + if (_mediaFileManager.FileSystem.FileExists(filepath) == false) return null; try { - return _mediaFileSystem.OpenFile(filepath); + return _mediaFileManager.FileSystem.OpenFile(filepath); } catch { @@ -1198,17 +1198,17 @@ namespace Umbraco.Cms.Core.Services.Implement public void SetMediaFileContent(string filepath, Stream stream) { - _mediaFileSystem.AddFile(filepath, stream, true); + _mediaFileManager.FileSystem.AddFile(filepath, stream, true); } public void DeleteMediaFile(string filepath) { - _mediaFileSystem.DeleteFile(filepath); + _mediaFileManager.FileSystem.DeleteFile(filepath); } public long GetMediaFileSize(string filepath) { - return _mediaFileSystem.GetSize(filepath); + return _mediaFileManager.FileSystem.GetSize(filepath); } #endregion diff --git a/src/Umbraco.Tests.Common/TestHelpers/FileSystemsCreator.cs b/src/Umbraco.Tests.Common/TestHelpers/FileSystemsCreator.cs new file mode 100644 index 0000000000..d9d5de1ebd --- /dev/null +++ b/src/Umbraco.Tests.Common/TestHelpers/FileSystemsCreator.cs @@ -0,0 +1,37 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.IO; + +namespace Umbraco.Cms.Tests.Common.TestHelpers +{ + public static class FileSystemsCreator + { + /// + /// Create an instance FileSystems where you can set the individual filesystems. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static FileSystems CreateTestFileSystems( + ILoggerFactory loggerFactory, + IIOHelper ioHelper, + IOptions globalSettings, + IHostingEnvironment hostingEnvironment, + IFileSystem macroPartialFileSystem, + IFileSystem partialViewsFileSystem, + IFileSystem stylesheetFileSystem, + IFileSystem scriptsFileSystem, + IFileSystem mvcViewFileSystem) => + new FileSystems(loggerFactory, ioHelper, globalSettings, hostingEnvironment, macroPartialFileSystem, + partialViewsFileSystem, stylesheetFileSystem, scriptsFileSystem, mvcViewFileSystem); + } +} diff --git a/src/Umbraco.Tests.Integration/Umbraco.Core/IO/FileSystemsTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Core/IO/FileSystemsTests.cs index 358a5fb350..ce361e59b6 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Core/IO/FileSystemsTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Core/IO/FileSystemsTests.cs @@ -19,52 +19,35 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.IO public class FileSystemsTests : UmbracoIntegrationTest { [Test] - public void Can_Get_MediaFileSystem() + public void Can_Get_MediaFileManager() { - IMediaFileSystem fileSystem = GetRequiredService(); + MediaFileManager fileSystem = GetRequiredService(); Assert.NotNull(fileSystem); } [Test] - public void Can_Get_IMediaFileSystem() + public void MediaFileManager_Is_Singleton() { - IMediaFileSystem fileSystem = GetRequiredService(); - Assert.NotNull(fileSystem); - } - - [Test] - public void IMediaFileSystem_Is_Singleton() - { - IMediaFileSystem fileSystem1 = GetRequiredService(); - IMediaFileSystem fileSystem2 = GetRequiredService(); - Assert.AreSame(fileSystem1, fileSystem2); - } - - [Test] - public void Can_Unwrap_MediaFileSystem() - { - IMediaFileSystem fileSystem = GetRequiredService(); - IFileSystem unwrapped = fileSystem.Unwrap(); - Assert.IsNotNull(unwrapped); - var physical = unwrapped as PhysicalFileSystem; - Assert.IsNotNull(physical); + MediaFileManager fileManager1 = GetRequiredService(); + MediaFileManager fileManager2 = GetRequiredService(); + Assert.AreSame(fileManager1, fileManager2); } [Test] public void Can_Delete_MediaFiles() { - IMediaFileSystem fs = GetRequiredService(); - var ms = new MemoryStream(Encoding.UTF8.GetBytes("test")); - string virtPath = fs.GetMediaPath("file.txt", Guid.NewGuid(), Guid.NewGuid()); - fs.AddFile(virtPath, ms); + MediaFileManager mediaFileManager = GetRequiredService(); + var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes("test")); + string virtualPath = mediaFileManager.GetMediaPath("file.txt", Guid.NewGuid(), Guid.NewGuid()); + mediaFileManager.FileSystem.AddFile(virtualPath, memoryStream); // ~/media/1234/file.txt exists IHostingEnvironment hostingEnvironment = GetRequiredService(); - string physPath = hostingEnvironment.MapPathWebRoot(Path.Combine("media", virtPath)); + string physPath = hostingEnvironment.MapPathWebRoot(Path.Combine("media", virtualPath)); Assert.IsTrue(File.Exists(physPath)); // ~/media/1234/file.txt is gone - fs.DeleteMediaFiles(new[] { virtPath }); + mediaFileManager.DeleteMediaFiles(new[] { virtualPath }); Assert.IsFalse(File.Exists(physPath)); IMediaPathScheme scheme = GetRequiredService(); diff --git a/src/Umbraco.Tests.Integration/Umbraco.Core/IO/ShadowFileSystemTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Core/IO/ShadowFileSystemTests.cs index 276d7a267e..751430a824 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Core/IO/ShadowFileSystemTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Core/IO/ShadowFileSystemTests.cs @@ -35,14 +35,12 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.IO public void SetUp() { ClearFiles(HostingEnvironment); - FileSystems.ResetShadowId(); } [TearDown] public void TearDown() { ClearFiles(HostingEnvironment); - FileSystems.ResetShadowId(); } private void ClearFiles(IHostingEnvironment hostingEnvironment) @@ -383,13 +381,6 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.IO Assert.IsFalse(File.Exists(path + "/ShadowTests/sub/sub/f2.txt")); } - class FS : FileSystemWrapper - { - public FS(IFileSystem innerFileSystem) - : base(innerFileSystem) - { } - } - [Test] public void ShadowScopeComplete() { @@ -403,11 +394,10 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.IO var phy = new PhysicalFileSystem(IOHelper, HostingEnvironment, Logger, path, "ignore"); - var container = Mock.Of(); var globalSettings = Options.Create(new GlobalSettings()); - var fileSystems = new FileSystems(container, Mock.Of>(), loggerFactory, IOHelper, globalSettings, HostingEnvironment) { IsScoped = () => scopedFileSystems }; - var fs = fileSystems.GetFileSystem(phy); - var sw = (ShadowWrapper) fs.InnerFileSystem; + var fileSystems = new FileSystems(loggerFactory, IOHelper, globalSettings, HostingEnvironment) { IsScoped = () => scopedFileSystems }; + var shadowPath = $"x/{Guid.NewGuid().ToString("N").Substring(0, 6)}"; + var sw = (ShadowWrapper)fileSystems.CreateShadowWrapper(phy, shadowPath); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) sw.AddFile("sub/f1.txt", ms); @@ -439,8 +429,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.IO var typedDir = dirs.FirstOrDefault(x => x.Replace('\\', '/').EndsWith("/x")); Assert.IsNotNull(typedDir); dirs = Directory.GetDirectories(typedDir); - var suid = fileSystems.Paths[typeof(FS)]; - var scopedDir = dirs.FirstOrDefault(x => x.Replace('\\', '/').EndsWith("/" + suid)); // this is where files go + var scopedDir = dirs.FirstOrDefault(x => x.Replace('\\', '/').EndsWith("/" + shadowPath)); // this is where files go Assert.IsNotNull(scopedDir); scope.Dispose(); scopedFileSystems = false; @@ -496,11 +485,10 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.IO var phy = new PhysicalFileSystem(IOHelper, HostingEnvironment, Logger, path, "ignore"); - var container = Mock.Of(); var globalSettings = Options.Create(new GlobalSettings()); - var fileSystems = new FileSystems(container, Mock.Of>(), NullLoggerFactory.Instance, IOHelper, globalSettings, HostingEnvironment) { IsScoped = () => scopedFileSystems }; - var fs = fileSystems.GetFileSystem( phy); - var sw = (ShadowWrapper) fs.InnerFileSystem; + var fileSystems = new FileSystems(NullLoggerFactory.Instance, IOHelper, globalSettings, HostingEnvironment) { IsScoped = () => scopedFileSystems }; + var shadowPath = $"x/{Guid.NewGuid().ToString("N").Substring(0, 6)}"; + var sw = fileSystems.CreateShadowWrapper(phy, shadowPath); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) sw.AddFile("sub/f1.txt", ms); @@ -548,11 +536,10 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.IO var phy = new PhysicalFileSystem(IOHelper, HostingEnvironment, Logger, path, "ignore"); - var container = Mock.Of(); var globalSettings = Options.Create(new GlobalSettings()); - var fileSystems = new FileSystems(container, Mock.Of>(), NullLoggerFactory.Instance, IOHelper, globalSettings, HostingEnvironment) { IsScoped = () => scopedFileSystems }; - var fs = fileSystems.GetFileSystem( phy); - var sw = (ShadowWrapper)fs.InnerFileSystem; + var fileSystems = new FileSystems(NullLoggerFactory.Instance, IOHelper, globalSettings, HostingEnvironment) { IsScoped = () => scopedFileSystems }; + var shadowPath = $"x/{Guid.NewGuid().ToString("N").Substring(0, 6)}"; + var sw = fileSystems.CreateShadowWrapper(phy, shadowPath); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) sw.AddFile("sub/f1.txt", ms); diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ContentTypeRepositoryTest.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ContentTypeRepositoryTest.cs index fd27dda811..a72e8c6c55 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ContentTypeRepositoryTest.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ContentTypeRepositoryTest.cs @@ -32,7 +32,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.Repos private ContentType _simpleContentType; private ContentType _textpageContentType; - private IFileSystems FileSystems => GetRequiredService(); + private FileSystems FileSystems => GetRequiredService(); private IUmbracoMapper Mapper => GetRequiredService(); diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentRepositoryTest.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentRepositoryTest.cs index 7857115241..8cf2c8c932 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentRepositoryTest.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentRepositoryTest.cs @@ -49,7 +49,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.Repos private IDataTypeService DataTypeService => GetRequiredService(); - private IFileSystems FileSystems => GetRequiredService(); + private FileSystems FileSystems => GetRequiredService(); private IConfigurationEditorJsonSerializer ConfigurationEditorJsonSerializer => GetRequiredService(); diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/PartialViewRepositoryTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/PartialViewRepositoryTests.cs index 44947b45cc..b0769f68ed 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/PartialViewRepositoryTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/PartialViewRepositoryTests.cs @@ -5,13 +5,15 @@ using System; using System.Collections.Generic; using System.IO; using Microsoft.Extensions.Logging; -using Moq; +using Microsoft.Extensions.Options; using NUnit.Framework; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement; +using Umbraco.Cms.Tests.Common.TestHelpers; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Cms.Tests.Integration.Testing; using Constants = Umbraco.Cms.Core.Constants; @@ -42,8 +44,9 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.Repos public void PathTests() { // unless noted otherwise, no changes / 7.2.8 - IFileSystems fileSystems = Mock.Of(); - Mock.Get(fileSystems).Setup(x => x.PartialViewsFileSystem).Returns(_fileSystem); + FileSystems fileSystems = FileSystemsCreator.CreateTestFileSystems(LoggerFactory, IOHelper, + GetRequiredService>(), HostingEnvironment, + null, _fileSystem, null, null, null); IScopeProvider provider = ScopeProvider; using (IScope scope = provider.CreateScope()) diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ScriptRepositoryTest.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ScriptRepositoryTest.cs index 35e53905a0..001fd89f03 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ScriptRepositoryTest.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ScriptRepositoryTest.cs @@ -7,7 +7,7 @@ using System.IO; using System.Linq; using System.Text; using Microsoft.Extensions.Logging; -using Moq; +using Microsoft.Extensions.Options; using NUnit.Framework; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Hosting; @@ -16,6 +16,7 @@ using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement; +using Umbraco.Cms.Tests.Common.TestHelpers; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Cms.Tests.Integration.Testing; @@ -27,16 +28,18 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.Repos { private IHostingEnvironment HostingEnvironment => GetRequiredService(); - private IFileSystems _fileSystems; + private FileSystems _fileSystems; private IFileSystem _fileSystem; [SetUp] public void SetUpFileSystem() { - _fileSystems = Mock.Of(); string path = GlobalSettings.UmbracoScriptsPath; _fileSystem = new PhysicalFileSystem(IOHelper, HostingEnvironment, LoggerFactory.CreateLogger(), HostingEnvironment.MapPathWebRoot(path), HostingEnvironment.ToAbsolute(path)); - Mock.Get(_fileSystems).Setup(x => x.ScriptsFileSystem).Returns(_fileSystem); + + _fileSystems = FileSystemsCreator.CreateTestFileSystems(LoggerFactory, IOHelper, GetRequiredService>(), + HostingEnvironment, + null, null, null, _fileSystem, null); using (Stream stream = CreateStream("Umbraco.Sys.registerNamespace(\"Umbraco.Utils\");")) { _fileSystem.AddFile("test-script.js", stream); diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/StylesheetRepositoryTest.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/StylesheetRepositoryTest.cs index 6f6d6e3e71..9b2acd17c1 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/StylesheetRepositoryTest.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/StylesheetRepositoryTest.cs @@ -8,7 +8,7 @@ using System.IO; using System.Linq; using System.Text; using Microsoft.Extensions.Logging; -using Moq; +using Microsoft.Extensions.Options; using NUnit.Framework; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Hosting; @@ -16,6 +16,7 @@ using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement; +using Umbraco.Cms.Tests.Common.TestHelpers; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Cms.Tests.Integration.Testing; @@ -25,7 +26,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.Repos [UmbracoTest(Database = UmbracoTestOptions.Database.None, Logger = UmbracoTestOptions.Logger.Console)] public class StylesheetRepositoryTest : UmbracoIntegrationTest { - private IFileSystems _fileSystems; + private FileSystems _fileSystems; private IFileSystem _fileSystem; private IHostingEnvironment HostingEnvironment => GetRequiredService(); @@ -33,10 +34,13 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.Repos [SetUp] public void SetUpFileSystem() { - _fileSystems = Mock.Of(); string path = HostingEnvironment.MapPathWebRoot(GlobalSettings.UmbracoCssPath); _fileSystem = new PhysicalFileSystem(IOHelper, HostingEnvironment, GetRequiredService>(), path, "/css"); - Mock.Get(_fileSystems).Setup(x => x.StylesheetsFileSystem).Returns(_fileSystem); + + _fileSystems = FileSystemsCreator.CreateTestFileSystems(LoggerFactory, IOHelper, GetRequiredService>(), + HostingEnvironment, + null, null, _fileSystem, null, null); + Stream stream = CreateStream("body {background:#EE7600; color:#FFF;}"); _fileSystem.AddFile("styles.css", stream); } diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs index 7b4150c897..646367c99a 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs @@ -36,7 +36,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.Repos { private IHostingEnvironment HostingEnvironment => GetRequiredService(); - private IFileSystems FileSystems => GetRequiredService(); + private FileSystems FileSystems => GetRequiredService(); private ITemplateRepository CreateRepository(IScopeProvider provider) => new TemplateRepository((IScopeAccessor)provider, AppCaches.Disabled, LoggerFactory.CreateLogger(), FileSystems, IOHelper, ShortStringHelper); diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/ScopeFileSystemsTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/ScopeFileSystemsTests.cs index f96852faeb..4fb20cde98 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/ScopeFileSystemsTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/ScopeFileSystemsTests.cs @@ -25,7 +25,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping [UmbracoTest(Database = UmbracoTestOptions.Database.NewEmptyPerTest)] public class ScopeFileSystemsTests : UmbracoIntegrationTest { - private IMediaFileSystem MediaFileSystem => GetRequiredService(); + private MediaFileManager MediaFileManager => GetRequiredService(); private IHostingEnvironment HostingEnvironment => GetRequiredService(); @@ -35,7 +35,6 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping [TearDown] public void Teardown() { - FileSystems.ResetShadowId(); ClearFiles(IOHelper); } @@ -47,12 +46,12 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping } [Test] - public void MediaFileSystem_does_not_write_to_physical_file_system_when_scoped_if_scope_does_not_complete() + public void MediaFileManager_does_not_write_to_physical_file_system_when_scoped_if_scope_does_not_complete() { string rootPath = HostingEnvironment.MapPathWebRoot(GlobalSettings.UmbracoMediaPath); string rootUrl = HostingEnvironment.ToAbsolute(GlobalSettings.UmbracoMediaPath); var physMediaFileSystem = new PhysicalFileSystem(IOHelper, HostingEnvironment, GetRequiredService>(), rootPath, rootUrl); - IMediaFileSystem mediaFileSystem = MediaFileSystem; + MediaFileManager mediaFileManager = MediaFileManager; Assert.IsFalse(physMediaFileSystem.FileExists("f1.txt")); @@ -60,28 +59,28 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping { using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) { - mediaFileSystem.AddFile("f1.txt", ms); + MediaFileManager.FileSystem.AddFile("f1.txt", ms); } - Assert.IsTrue(mediaFileSystem.FileExists("f1.txt")); + Assert.IsTrue(mediaFileManager.FileSystem.FileExists("f1.txt")); Assert.IsFalse(physMediaFileSystem.FileExists("f1.txt")); - Assert.IsTrue(mediaFileSystem.FileExists("f1.txt")); + Assert.IsTrue(mediaFileManager.FileSystem.FileExists("f1.txt")); Assert.IsFalse(physMediaFileSystem.FileExists("f1.txt")); } // After scope is disposed ensure shadow wrapper didn't commit to physical - Assert.IsFalse(MediaFileSystem.FileExists("f1.txt")); + Assert.IsFalse(mediaFileManager.FileSystem.FileExists("f1.txt")); Assert.IsFalse(physMediaFileSystem.FileExists("f1.txt")); } [Test] - public void MediaFileSystem_writes_to_physical_file_system_when_scoped_and_scope_is_completed() + public void MediaFileManager_writes_to_physical_file_system_when_scoped_and_scope_is_completed() { string rootPath = HostingEnvironment.MapPathWebRoot(GlobalSettings.UmbracoMediaPath); string rootUrl = HostingEnvironment.ToAbsolute(GlobalSettings.UmbracoMediaPath); var physMediaFileSystem = new PhysicalFileSystem(IOHelper, HostingEnvironment, GetRequiredService>(), rootPath, rootUrl); - IMediaFileSystem mediaFileSystem = MediaFileSystem; + MediaFileManager mediaFileManager = MediaFileManager; Assert.IsFalse(physMediaFileSystem.FileExists("f1.txt")); @@ -89,20 +88,20 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping { using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) { - mediaFileSystem.AddFile("f1.txt", ms); + mediaFileManager.FileSystem.AddFile("f1.txt", ms); } - Assert.IsTrue(mediaFileSystem.FileExists("f1.txt")); + Assert.IsTrue(mediaFileManager.FileSystem.FileExists("f1.txt")); Assert.IsFalse(physMediaFileSystem.FileExists("f1.txt")); scope.Complete(); - Assert.IsTrue(mediaFileSystem.FileExists("f1.txt")); + Assert.IsTrue(mediaFileManager.FileSystem.FileExists("f1.txt")); Assert.IsFalse(physMediaFileSystem.FileExists("f1.txt")); } // After scope is disposed ensure shadow wrapper writes to physical file system - Assert.IsTrue(MediaFileSystem.FileExists("f1.txt")); + Assert.IsTrue(mediaFileManager.FileSystem.FileExists("f1.txt")); Assert.IsTrue(physMediaFileSystem.FileExists("f1.txt")); } @@ -112,7 +111,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping string rootPath = HostingEnvironment.MapPathWebRoot(GlobalSettings.UmbracoMediaPath); string rootUrl = HostingEnvironment.ToAbsolute(GlobalSettings.UmbracoMediaPath); var physMediaFileSystem = new PhysicalFileSystem(IOHelper, HostingEnvironment, GetRequiredService>(), rootPath, rootUrl); - IMediaFileSystem mediaFileSystem = MediaFileSystem; + MediaFileManager mediaFileManager = MediaFileManager; var taskHelper = new TaskHelper(Mock.Of>()); IScopeProvider scopeProvider = ScopeProvider; @@ -120,23 +119,23 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping { using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) { - mediaFileSystem.AddFile("f1.txt", ms); + mediaFileManager.FileSystem.AddFile("f1.txt", ms); } - Assert.IsTrue(mediaFileSystem.FileExists("f1.txt")); + Assert.IsTrue(mediaFileManager.FileSystem.FileExists("f1.txt")); Assert.IsFalse(physMediaFileSystem.FileExists("f1.txt")); // execute on another disconnected thread (execution context will not flow) Task t = taskHelper.ExecuteBackgroundTask(() => { - Assert.IsFalse(mediaFileSystem.FileExists("f1.txt")); + Assert.IsFalse(mediaFileManager.FileSystem.FileExists("f1.txt")); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) { - mediaFileSystem.AddFile("f2.txt", ms); + mediaFileManager.FileSystem.AddFile("f2.txt", ms); } - Assert.IsTrue(mediaFileSystem.FileExists("f2.txt")); + Assert.IsTrue(mediaFileManager.FileSystem.FileExists("f2.txt")); Assert.IsTrue(physMediaFileSystem.FileExists("f2.txt")); return Task.CompletedTask; @@ -144,7 +143,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping Task.WaitAll(t); - Assert.IsTrue(mediaFileSystem.FileExists("f2.txt")); + Assert.IsTrue(mediaFileManager.FileSystem.FileExists("f2.txt")); Assert.IsTrue(physMediaFileSystem.FileExists("f2.txt")); } } @@ -170,7 +169,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping // not ok to create a 'scoped filesystems' other scope // we will get a "Already shadowing." exception. Assert.Throws(() => - { + { using IScope other = scopeProvider.CreateScope(scopeFileSystems: true); }); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs index e1a65d1541..44eabbac6f 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs @@ -22,6 +22,7 @@ using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Logging; using Umbraco.Cms.Core.Scoping; +using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Migrations.Install; using Umbraco.Cms.Infrastructure.Persistence; using Umbraco.Cms.Infrastructure.Persistence.Mappers; @@ -48,11 +49,12 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Components var connectionStrings = new ConnectionStrings(); var f = new UmbracoDatabaseFactory(loggerFactory.CreateLogger(), loggerFactory, Options.Create(globalSettings), Options.Create(connectionStrings), new Lazy(() => new MapperCollection(Enumerable.Empty())), TestHelper.DbProviderFactoryCreator, new DatabaseSchemaCreatorFactory(loggerFactory.CreateLogger(), loggerFactory, new UmbracoVersion(), Mock.Of())); - var fs = new FileSystems(mock.Object, loggerFactory.CreateLogger(), loggerFactory, IOHelper, Options.Create(globalSettings), Mock.Of()); + var fs = new FileSystems(loggerFactory, IOHelper, Options.Create(globalSettings), Mock.Of()); var coreDebug = new CoreDebugSettings(); - IMediaFileSystem mediaFileSystem = Mock.Of(); + MediaFileManager mediaFileManager = new MediaFileManager(Mock.Of(), + Mock.Of(), Mock.Of>(), Mock.Of()); IEventAggregator eventAggregator = Mock.Of(); - var p = new ScopeProvider(f, fs, Options.Create(coreDebug), mediaFileSystem, loggerFactory.CreateLogger(), loggerFactory, NoAppCache.Instance, eventAggregator); + var p = new ScopeProvider(f, fs, Options.Create(coreDebug), mediaFileManager, loggerFactory.CreateLogger(), loggerFactory, NoAppCache.Instance, eventAggregator); mock.Setup(x => x.GetService(typeof(ILogger))).Returns(logger); mock.Setup(x => x.GetService(typeof(ILogger))).Returns(loggerFactory.CreateLogger); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Scoping/ScopeEventDispatcherTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Scoping/ScopeEventDispatcherTests.cs index 43d14677fa..9820bef8c2 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Scoping/ScopeEventDispatcherTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Scoping/ScopeEventDispatcherTests.cs @@ -13,6 +13,7 @@ using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Persistence; using Umbraco.Cms.Tests.Common.Builders; @@ -73,18 +74,19 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Scoping private ScopeProvider GetScopeProvider(NullLoggerFactory instance) { var fileSystems = new FileSystems( - Mock.Of(), - Mock.Of>(), instance, Mock.Of(), Options.Create(new GlobalSettings()), Mock.Of()); + var mediaFileManager = new MediaFileManager(Mock.Of(), Mock.Of(), + instance.CreateLogger(), Mock.Of()); + return new ScopeProvider( Mock.Of(), fileSystems, Options.Create(new CoreDebugSettings()), - Mock.Of(), + mediaFileManager, Mock.Of>(), instance, Mock.Of(), diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Scoping/ScopeUnitTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Scoping/ScopeUnitTests.cs index 8bfced3b61..6995be88b7 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Scoping/ScopeUnitTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Scoping/ScopeUnitTests.cs @@ -14,6 +14,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Scoping; +using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Persistence; using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; @@ -30,7 +31,10 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Scoping private ScopeProvider GetScopeProvider(out Mock syntaxProviderMock) { var loggerFactory = NullLoggerFactory.Instance; - var fileSystem = new FileSystems(Mock.Of(), loggerFactory.CreateLogger(), loggerFactory, Mock.Of(), Mock.Of>(), Mock.Of()); + var fileSystems = new FileSystems(loggerFactory, + Mock.Of(), Mock.Of>(), Mock.Of()); + var mediaFileManager = new MediaFileManager(Mock.Of(), Mock.Of(), + loggerFactory.CreateLogger(), Mock.Of()); var databaseFactory = new Mock(); var database = new Mock(); var sqlContext = new Mock(); @@ -47,9 +51,9 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Scoping return new ScopeProvider( databaseFactory.Object, - fileSystem, + fileSystems, Options.Create(new CoreDebugSettings()), - Mock.Of(), + mediaFileManager, loggerFactory.CreateLogger(), loggerFactory, Mock.Of(), diff --git a/src/Umbraco.Tests/Models/MediaXmlTest.cs b/src/Umbraco.Tests/Models/MediaXmlTest.cs index bb26b2e70e..fbcfdb91ed 100644 --- a/src/Umbraco.Tests/Models/MediaXmlTest.cs +++ b/src/Umbraco.Tests/Models/MediaXmlTest.cs @@ -3,6 +3,7 @@ using System.Xml.Linq; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; using Moq; using NUnit.Framework; using Umbraco.Cms.Core.Configuration.Models; @@ -35,8 +36,9 @@ namespace Umbraco.Tests.Models var scheme = Mock.Of(); var contentSettings = new ContentSettings(); - var mediaFileSystem = new MediaFileSystem(Mock.Of(), scheme, loggerFactory.CreateLogger(), ShortStringHelper); - var ignored = new FileUploadPropertyEditor(loggerFactory, mediaFileSystem, Microsoft.Extensions.Options.Options.Create(contentSettings), DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, UploadAutoFillProperties, JsonNetSerializer, ContentService); + var mediaFileManager = new MediaFileManager(Mock.Of(), scheme, + loggerFactory.CreateLogger(), ShortStringHelper); + var ignored = new FileUploadPropertyEditor(loggerFactory, mediaFileManager, Microsoft.Extensions.Options.Options.Create(contentSettings), DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, UploadAutoFillProperties, JsonNetSerializer, ContentService); var media = MockedMedia.CreateMediaImage(mediaType, -1); media.WriterId = -1; // else it's zero and that's not a user and it breaks the tests diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs index c285deee65..53b8666889 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs @@ -13,6 +13,7 @@ using Umbraco.Cms.Core.PropertyEditors.ValueConverters; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Core.Templates; using Umbraco.Cms.Core.Web; using Umbraco.Cms.Infrastructure.Serialization; @@ -50,7 +51,9 @@ namespace Umbraco.Tests.PublishedContent var serializer = new ConfigurationEditorJsonSerializer(); var imageSourceParser = new HtmlImageSourceParser(publishedUrlProvider); - var pastedImages = new RichTextEditorPastedImages(umbracoContextAccessor, loggerFactory.CreateLogger(), HostingEnvironment, Mock.Of(), Mock.Of(), Mock.Of(), ShortStringHelper, publishedUrlProvider, serializer); + var mediaFileManager = new MediaFileManager(Mock.Of(), Mock.Of(), + loggerFactory.CreateLogger(), Mock.Of()); + var pastedImages = new RichTextEditorPastedImages(umbracoContextAccessor, loggerFactory.CreateLogger(), HostingEnvironment, Mock.Of(), Mock.Of(), mediaFileManager, ShortStringHelper, publishedUrlProvider, serializer); var localLinkParser = new HtmlLocalLinkParser(umbracoContextAccessor, publishedUrlProvider); var dataTypeService = new TestObjects.TestDataTypeService( new DataType(new RichTextPropertyEditor( diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index 1179887e32..1cb238b0c2 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -51,13 +51,14 @@ namespace Umbraco.Tests.PublishedContent var loggerFactory = NullLoggerFactory.Instance; var mediaService = Mock.Of(); - var mediaFileService = Mock.Of(); var contentTypeBaseServiceProvider = Mock.Of(); var umbracoContextAccessor = Mock.Of(); var backOfficeSecurityAccessor = Mock.Of(); var publishedUrlProvider = Mock.Of(); var imageSourceParser = new HtmlImageSourceParser(publishedUrlProvider); var serializer = new ConfigurationEditorJsonSerializer(); + var mediaFileService = new MediaFileManager(Mock.Of(), Mock.Of(), + loggerFactory.CreateLogger(), Mock.Of()); var pastedImages = new RichTextEditorPastedImages(umbracoContextAccessor, loggerFactory.CreateLogger(), HostingEnvironment, mediaService, contentTypeBaseServiceProvider, mediaFileService, ShortStringHelper, publishedUrlProvider, serializer); var linkParser = new HtmlLocalLinkParser(umbracoContextAccessor, publishedUrlProvider); var localizationService = Mock.Of(); diff --git a/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs b/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs index 7149938b39..d6b7b58c15 100644 --- a/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs @@ -13,6 +13,7 @@ using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.PropertyEditors.ValueConverters; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Core.Web; using Umbraco.Cms.Tests.Common; using Umbraco.Cms.Tests.Common.Testing; @@ -33,13 +34,14 @@ namespace Umbraco.Tests.Routing base.SetUp(); var loggerFactory = NullLoggerFactory.Instance; - var mediaFileSystemMock = Mock.Of(); + var mediaFileManager = new MediaFileManager(Mock.Of(), Mock.Of(), + loggerFactory.CreateLogger(), Mock.Of()); var contentSettings = new ContentSettings(); var dataTypeService = Mock.Of(); var propertyEditors = new MediaUrlGeneratorCollection(new IMediaUrlGenerator[] { - new FileUploadPropertyEditor(loggerFactory, mediaFileSystemMock, Microsoft.Extensions.Options.Options.Create(contentSettings), dataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, UploadAutoFillProperties, JsonNetSerializer, ContentService), - new ImageCropperPropertyEditor(loggerFactory, mediaFileSystemMock, Microsoft.Extensions.Options.Options.Create(contentSettings), dataTypeService, LocalizationService, IOHelper, ShortStringHelper, LocalizedTextService, UploadAutoFillProperties, JsonNetSerializer, ContentService), + new FileUploadPropertyEditor(loggerFactory, mediaFileManager, Microsoft.Extensions.Options.Options.Create(contentSettings), dataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, UploadAutoFillProperties, JsonNetSerializer, ContentService), + new ImageCropperPropertyEditor(loggerFactory, mediaFileManager, Microsoft.Extensions.Options.Options.Create(contentSettings), dataTypeService, LocalizationService, IOHelper, ShortStringHelper, LocalizedTextService, UploadAutoFillProperties, JsonNetSerializer, ContentService), }); _mediaUrlProvider = new DefaultMediaUrlProvider(propertyEditors, UriUtility); } diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs index 94b4f5500b..1f32e73916 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Linq.Expressions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using Moq; using Umbraco.Cms.Core; @@ -18,6 +19,7 @@ using Umbraco.Cms.Core.Web; using Umbraco.Cms.Infrastructure.Persistence; using Umbraco.Cms.Persistence.SqlCe; using Umbraco.Cms.Tests.Common; +using Umbraco.Cms.Tests.Common.TestHelpers; using Umbraco.Extensions; using Umbraco.Web; @@ -142,25 +144,23 @@ namespace Umbraco.Tests.TestHelpers { return new GlobalSettings(); } - public IFileSystems GetFileSystemsMock() + public FileSystems GetFileSystemsMock() { - var fileSystems = Mock.Of(); - - MockFs(fileSystems, x => x.MacroPartialsFileSystem); - MockFs(fileSystems, x => x.MvcViewsFileSystem); - MockFs(fileSystems, x => x.PartialViewsFileSystem); - MockFs(fileSystems, x => x.ScriptsFileSystem); - MockFs(fileSystems, x => x.StylesheetsFileSystem); + var fileSystems = FileSystemsCreator.CreateTestFileSystems( + NullLoggerFactory.Instance, + Mock.Of(), + Mock.Of>(), + Mock.Of(), + Mock.Of(), + Mock.Of(), + Mock.Of(), + Mock.Of(), + Mock.Of() + ); return fileSystems; } - private void MockFs(IFileSystems fileSystems, Expression> fileSystem) - { - var fs = Mock.Of(); - Mock.Get(fileSystems).Setup(fileSystem).Returns(fs); - } - #region Inner classes private class MockDbConnection : DbConnection diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects.cs b/src/Umbraco.Tests/TestHelpers/TestObjects.cs index 524644874c..6f7fcfe2dd 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects.cs @@ -84,11 +84,11 @@ namespace Umbraco.Tests.TestHelpers new DatabaseSchemaCreatorFactory(Mock.Of>(),loggerFactory, new UmbracoVersion(), Mock.Of())); } - fileSystems ??= new FileSystems(Current.Factory, loggerFactory.CreateLogger(), loggerFactory, TestHelper.IOHelper, globalSettings, TestHelper.GetHostingEnvironment()); + fileSystems ??= new FileSystems(loggerFactory, TestHelper.IOHelper, globalSettings, TestHelper.GetHostingEnvironment()); var coreDebug = TestHelper.CoreDebugSettings; - var mediaFileSystem = Mock.Of(); + var mediaFileManager = Mock.Of(); var eventAggregator = Mock.Of(); - return new ScopeProvider(databaseFactory, fileSystems, Options.Create(coreDebugSettings), mediaFileSystem, loggerFactory.CreateLogger(), loggerFactory, NoAppCache.Instance, eventAggregator); + return new ScopeProvider(databaseFactory, fileSystems, Options.Create(coreDebugSettings), mediaFileManager, loggerFactory.CreateLogger(), loggerFactory, NoAppCache.Instance, eventAggregator); } } diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index a33b3f6c10..243e92ad53 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -11,6 +11,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; using Moq; using NUnit.Framework; using Serilog; @@ -462,8 +463,8 @@ namespace Umbraco.Tests.Testing var scheme = Mock.Of(); - var mediaFileSystem = new MediaFileSystem(Mock.Of(), scheme, _loggerFactory.CreateLogger(), TestHelper.ShortStringHelper); - Builder.Services.AddUnique(factory => mediaFileSystem); + var mediaFileManager = new MediaFileManager(Mock.Of(), scheme, Mock.Of>(), Mock.Of()); + Builder.Services.AddUnique(factory => mediaFileManager); // no factory (noop) Builder.Services.AddUnique(); diff --git a/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs b/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs index 710fb5aeb8..822f5a4911 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs @@ -37,7 +37,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public class CodeFileController : BackOfficeNotificationsController { private readonly IHostingEnvironment _hostingEnvironment; - private readonly IFileSystems _fileSystems; + private readonly FileSystems _fileSystems; private readonly IFileService _fileService; private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor; @@ -48,7 +48,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public CodeFileController( IHostingEnvironment hostingEnvironment, - IFileSystems fileSystems, + FileSystems fileSystems, IFileService fileService, IBackOfficeSecurityAccessor backOfficeSecurityAccessor, ILocalizedTextService localizedTextService, diff --git a/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs b/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs index 080f84009a..49bff529bd 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs @@ -39,7 +39,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] public class CurrentUserController : UmbracoAuthorizedJsonController { - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly ContentSettings _contentSettings; private readonly IHostingEnvironment _hostingEnvironment; private readonly IImageUrlGenerator _imageUrlGenerator; @@ -54,7 +54,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers private readonly IPasswordChanger _passwordChanger; public CurrentUserController( - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileManager, IOptions contentSettings, IHostingEnvironment hostingEnvironment, IImageUrlGenerator imageUrlGenerator, @@ -68,7 +68,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers IShortStringHelper shortStringHelper, IPasswordChanger passwordChanger) { - _mediaFileSystem = mediaFileSystem; + _mediaFileManager = mediaFileManager; _contentSettings = contentSettings.Value; _hostingEnvironment = hostingEnvironment; _imageUrlGenerator = imageUrlGenerator; @@ -210,7 +210,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public IActionResult PostSetAvatar(IList file) { //borrow the logic from the user controller - return UsersController.PostSetAvatarInternal(file, _userService, _appCaches.RuntimeCache, _mediaFileSystem, _shortStringHelper, _contentSettings, _hostingEnvironment, _imageUrlGenerator, _backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().ResultOr(0)); + return UsersController.PostSetAvatarInternal(file, _userService, _appCaches.RuntimeCache, _mediaFileManager, _shortStringHelper, _contentSettings, _hostingEnvironment, _imageUrlGenerator, _backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().ResultOr(0)); } /// diff --git a/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs b/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs index 8b9ab5652c..24135bcbe6 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs @@ -16,14 +16,14 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] public class ImagesController : UmbracoAuthorizedApiController { - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly IImageUrlGenerator _imageUrlGenerator; public ImagesController( - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileManager, IImageUrlGenerator imageUrlGenerator) { - _mediaFileSystem = mediaFileSystem; + _mediaFileManager = mediaFileManager; _imageUrlGenerator = imageUrlGenerator; } @@ -63,7 +63,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers DateTimeOffset? imageLastModified = null; try { - imageLastModified = _mediaFileSystem.GetLastModified(imagePath); + imageLastModified = _mediaFileManager.FileSystem.GetLastModified(imagePath); } catch (Exception) { diff --git a/src/Umbraco.Web.BackOffice/Controllers/LogController.cs b/src/Umbraco.Web.BackOffice/Controllers/LogController.cs index 2d8f2fa82b..3c3eaa4eff 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/LogController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/LogController.cs @@ -24,7 +24,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] public class LogController : UmbracoAuthorizedJsonController { - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly IImageUrlGenerator _imageUrlGenerator; private readonly IAuditService _auditService; private readonly IUmbracoMapper _umbracoMapper; @@ -34,7 +34,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers private readonly ISqlContext _sqlContext; public LogController( - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileSystem, IImageUrlGenerator imageUrlGenerator, IAuditService auditService, IUmbracoMapper umbracoMapper, @@ -43,7 +43,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers AppCaches appCaches, ISqlContext sqlContext) { - _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); + _mediaFileManager = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); _imageUrlGenerator = imageUrlGenerator ?? throw new ArgumentNullException(nameof(imageUrlGenerator)); _auditService = auditService ?? throw new ArgumentNullException(nameof(auditService)); _umbracoMapper = umbracoMapper ?? throw new ArgumentNullException(nameof(umbracoMapper)); @@ -111,7 +111,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers { var mappedItems = items.ToList(); var userIds = mappedItems.Select(x => x.UserId).ToArray(); - var userAvatars = Enumerable.ToDictionary(_userService.GetUsersById(userIds), x => x.Id, x => x.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileSystem, _imageUrlGenerator)); + var userAvatars = Enumerable.ToDictionary(_userService.GetUsersById(userIds), x => x.Id, x => x.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileManager, _imageUrlGenerator)); var userNames = Enumerable.ToDictionary(_userService.GetUsersById(userIds), x => x.Id, x => x.Name); foreach (var item in mappedItems) { diff --git a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs index 9d176277ec..88114f673c 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs @@ -89,7 +89,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IRelationService relationService, PropertyEditorCollection propertyEditors, - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileManager, IHostingEnvironment hostingEnvironment, IImageUrlGenerator imageUrlGenerator, IJsonSerializer serializer, @@ -110,7 +110,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider; _relationService = relationService; _propertyEditors = propertyEditors; - _mediaFileSystem = mediaFileSystem; + _mediaFileManager = mediaFileManager; _hostingEnvironment = hostingEnvironment; _logger = loggerFactory.CreateLogger(); _imageUrlGenerator = imageUrlGenerator; @@ -288,7 +288,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers private int[] _userStartNodes; private readonly PropertyEditorCollection _propertyEditors; - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly IHostingEnvironment _hostingEnvironment; @@ -806,7 +806,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers await using (var stream = formFile.OpenReadStream()) { - f.SetValue(_mediaFileSystem,_shortStringHelper, _contentTypeBaseServiceProvider, _serializer, Constants.Conventions.Media.File,fileName, stream); + f.SetValue(_mediaFileManager,_shortStringHelper, _contentTypeBaseServiceProvider, _serializer, Constants.Conventions.Media.File,fileName, stream); } diff --git a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs index 6e7e5f2ed1..2d7418b6bd 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs @@ -52,7 +52,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [IsCurrentUserModelFilter] public class UsersController : UmbracoAuthorizedJsonController { - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly ContentSettings _contentSettings; private readonly IHostingEnvironment _hostingEnvironment; private readonly ISqlContext _sqlContext; @@ -75,7 +75,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers private readonly ILogger _logger; public UsersController( - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileManager, IOptions contentSettings, IHostingEnvironment hostingEnvironment, ISqlContext sqlContext, @@ -96,7 +96,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers UserEditorAuthorizationHelper userEditorAuthorizationHelper, IPasswordChanger passwordChanger) { - _mediaFileSystem = mediaFileSystem; + _mediaFileManager = mediaFileManager; _contentSettings = contentSettings.Value; _hostingEnvironment = hostingEnvironment; _sqlContext = sqlContext; @@ -125,7 +125,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// public ActionResult GetCurrentUserAvatarUrls() { - var urls = _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileSystem, _imageUrlGenerator); + var urls = _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileManager, _imageUrlGenerator); if (urls == null) return new ValidationErrorResult("Could not access Gravatar endpoint"); @@ -136,10 +136,10 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [Authorize(Policy = AuthorizationPolicies.AdminUserEditsRequireAdmin)] public IActionResult PostSetAvatar(int id, IList file) { - return PostSetAvatarInternal(file, _userService, _appCaches.RuntimeCache, _mediaFileSystem, _shortStringHelper, _contentSettings, _hostingEnvironment, _imageUrlGenerator, id); + return PostSetAvatarInternal(file, _userService, _appCaches.RuntimeCache, _mediaFileManager, _shortStringHelper, _contentSettings, _hostingEnvironment, _imageUrlGenerator, id); } - internal static IActionResult PostSetAvatarInternal(IList files, IUserService userService, IAppCache cache, IMediaFileSystem mediaFileSystem, IShortStringHelper shortStringHelper, ContentSettings contentSettings, IHostingEnvironment hostingEnvironment, IImageUrlGenerator imageUrlGenerator, int id) + internal static IActionResult PostSetAvatarInternal(IList files, IUserService userService, IAppCache cache, MediaFileManager mediaFileManager, IShortStringHelper shortStringHelper, ContentSettings contentSettings, IHostingEnvironment hostingEnvironment, IImageUrlGenerator imageUrlGenerator, int id) { if (files is null) { @@ -176,13 +176,13 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers using (var fs = file.OpenReadStream()) { - mediaFileSystem.AddFile(user.Avatar, fs, true); + mediaFileManager.FileSystem.AddFile(user.Avatar, fs, true); } userService.Save(user); } - return new OkObjectResult(user.GetUserAvatarUrls(cache, mediaFileSystem, imageUrlGenerator)); + return new OkObjectResult(user.GetUserAvatarUrls(cache, mediaFileManager, imageUrlGenerator)); } [AppendUserModifiedHeader("id")] @@ -212,11 +212,11 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers if (filePath.IsNullOrWhiteSpace() == false) { - if (_mediaFileSystem.FileExists(filePath)) - _mediaFileSystem.DeleteFile(filePath); + if (_mediaFileManager.FileSystem.FileExists(filePath)) + _mediaFileManager.FileSystem.DeleteFile(filePath); } - return found.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileSystem, _imageUrlGenerator); + return found.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileManager, _imageUrlGenerator); } /// diff --git a/src/Umbraco.Web.BackOffice/Trees/PartialViewMacrosTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/PartialViewMacrosTreeController.cs index 6403ec009d..96d9004635 100644 --- a/src/Umbraco.Web.BackOffice/Trees/PartialViewMacrosTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/PartialViewMacrosTreeController.cs @@ -31,7 +31,7 @@ namespace Umbraco.Cms.Web.BackOffice.Trees ILocalizedTextService localizedTextService, UmbracoApiControllerTypeCollection umbracoApiControllerTypeCollection, IMenuItemCollectionFactory menuItemCollectionFactory, - IFileSystems fileSystems, + FileSystems fileSystems, IEventAggregator eventAggregator) : base(localizedTextService, umbracoApiControllerTypeCollection, menuItemCollectionFactory, fileSystems, eventAggregator) { diff --git a/src/Umbraco.Web.BackOffice/Trees/PartialViewsTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/PartialViewsTreeController.cs index ad8b9f16b4..c79175dba8 100644 --- a/src/Umbraco.Web.BackOffice/Trees/PartialViewsTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/PartialViewsTreeController.cs @@ -31,7 +31,7 @@ namespace Umbraco.Cms.Web.BackOffice.Trees ILocalizedTextService localizedTextService, UmbracoApiControllerTypeCollection umbracoApiControllerTypeCollection, IMenuItemCollectionFactory menuItemCollectionFactory, - IFileSystems fileSystems, + FileSystems fileSystems, IEventAggregator eventAggregator) : base(localizedTextService, umbracoApiControllerTypeCollection, menuItemCollectionFactory, eventAggregator) { diff --git a/src/Umbraco.Web.BackOffice/Trees/ScriptsTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/ScriptsTreeController.cs index 56f1b0ac1d..89e767c124 100644 --- a/src/Umbraco.Web.BackOffice/Trees/ScriptsTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/ScriptsTreeController.cs @@ -23,7 +23,7 @@ namespace Umbraco.Cms.Web.BackOffice.Trees ILocalizedTextService localizedTextService, UmbracoApiControllerTypeCollection umbracoApiControllerTypeCollection, IMenuItemCollectionFactory menuItemCollectionFactory, - IFileSystems fileSystems, + FileSystems fileSystems, IEventAggregator eventAggregator) : base(localizedTextService, umbracoApiControllerTypeCollection, menuItemCollectionFactory, eventAggregator) { diff --git a/src/Umbraco.Web.BackOffice/Trees/StylesheetsTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/StylesheetsTreeController.cs index 18d1be6a67..08dd8f2cde 100644 --- a/src/Umbraco.Web.BackOffice/Trees/StylesheetsTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/StylesheetsTreeController.cs @@ -23,7 +23,7 @@ namespace Umbraco.Cms.Web.BackOffice.Trees ILocalizedTextService localizedTextService, UmbracoApiControllerTypeCollection umbracoApiControllerTypeCollection, IMenuItemCollectionFactory menuItemCollectionFactory, - IFileSystems fileSystems, + FileSystems fileSystems, IEventAggregator eventAggregator) : base(localizedTextService, umbracoApiControllerTypeCollection, menuItemCollectionFactory, eventAggregator) { diff --git a/src/Umbraco.Web/Composing/Current.cs b/src/Umbraco.Web/Composing/Current.cs index 0863d843fa..6240ba27bb 100644 --- a/src/Umbraco.Web/Composing/Current.cs +++ b/src/Umbraco.Web/Composing/Current.cs @@ -144,7 +144,7 @@ namespace Umbraco.Web.Composing // proxy Core for convenience - public static IMediaFileSystem MediaFileSystem => Factory.GetRequiredService(); + public static MediaFileManager MediaFileManager => Factory.GetRequiredService(); public static UmbracoMapper Mapper => Factory.GetRequiredService(); From 720e529474ce679f2b09fb0b1065984923a29f87 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 27 Apr 2021 09:59:51 +0200 Subject: [PATCH 028/182] Remove a bunch of template related, unreferenced methods from file service and template repository (#9876) --- .../Repositories/ITemplateRepository.cs | 20 +--- src/Umbraco.Core/Services/IFileService.cs | 51 +--------- .../Implement/TemplateRepository.cs | 92 ------------------- .../Services/Implement/FileService.cs | 72 --------------- .../Repositories/TemplateRepositoryTest.cs | 7 -- 5 files changed, 2 insertions(+), 240 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/ITemplateRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ITemplateRepository.cs index 24c83ff29d..7e6a81864c 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ITemplateRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ITemplateRepository.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using Umbraco.Cms.Core.Models; @@ -11,17 +11,8 @@ namespace Umbraco.Cms.Core.Persistence.Repositories IEnumerable GetAll(params string[] aliases); IEnumerable GetChildren(int masterTemplateId); - IEnumerable GetChildren(string alias); IEnumerable GetDescendants(int masterTemplateId); - IEnumerable GetDescendants(string alias); - - /// - /// Validates a - /// - /// to validate - /// True if Script is valid, otherwise false - bool ValidateTemplate(ITemplate template); /// /// Gets the content of a template as a stream. @@ -29,14 +20,5 @@ namespace Umbraco.Cms.Core.Persistence.Repositories /// The filesystem path to the template. /// The content of the template. Stream GetFileContentStream(string filepath); - - /// - /// Sets the content of a template. - /// - /// The filesystem path to the template. - /// The content of the template. - void SetFileContent(string filepath, Stream content); - - long GetFileSize(string filepath); } } diff --git a/src/Umbraco.Core/Services/IFileService.cs b/src/Umbraco.Core/Services/IFileService.cs index bcf2a0caee..1f4d33388c 100644 --- a/src/Umbraco.Core/Services/IFileService.cs +++ b/src/Umbraco.Core/Services/IFileService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using Umbraco.Cms.Core.Models; @@ -154,13 +154,6 @@ namespace Umbraco.Cms.Core.Services /// The object matching the identifier, or null. ITemplate GetTemplate(Guid id); - /// - /// Gets the template descendants - /// - /// - /// - IEnumerable GetTemplateDescendants(string alias); - /// /// Gets the template descendants /// @@ -168,20 +161,6 @@ namespace Umbraco.Cms.Core.Services /// IEnumerable GetTemplateDescendants(int masterTemplateId); - /// - /// Gets the template children - /// - /// - /// - IEnumerable GetTemplateChildren(string alias); - - /// - /// Gets the template children - /// - /// - /// - IEnumerable GetTemplateChildren(int masterTemplateId); - /// /// Saves a /// @@ -209,13 +188,6 @@ namespace Umbraco.Cms.Core.Services /// Optional id of the user deleting the template void DeleteTemplate(string alias, int userId = Constants.Security.SuperUserId); - /// - /// Validates a - /// - /// to validate - /// True if template is valid, otherwise false - bool ValidateTemplate(ITemplate template); - /// /// Saves a collection of objects /// @@ -223,27 +195,6 @@ namespace Umbraco.Cms.Core.Services /// Optional id of the user void SaveTemplate(IEnumerable templates, int userId = Constants.Security.SuperUserId); - /// - /// Gets the content of a template as a stream. - /// - /// The filesystem path to the template. - /// The content of the template. - Stream GetTemplateFileContentStream(string filepath); - - /// - /// Sets the content of a template. - /// - /// The filesystem path to the template. - /// The content of the template. - void SetTemplateFileContent(string filepath, Stream content); - - /// - /// Gets the size of a template. - /// - /// The filesystem path to the template. - /// The size of the template. - long GetTemplateFileSize(string filepath); - /// /// Gets the content of a stylesheet as a stream. /// diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs index 3aad92a012..cd42bd5dd5 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs @@ -449,16 +449,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement } } - public void SetFileContent(string filepath, Stream content) - { - GetFileSystem(filepath).AddFile(filepath, content, true); - } - - public long GetFileSize(string filepath) - { - return GetFileSystem(filepath).GetSize(filepath); - } - private IFileSystem GetFileSystem(string filepath) { var ext = Path.GetExtension(filepath); @@ -507,14 +497,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement return children; } - public IEnumerable GetChildren(string alias) - { - //return from base.GetAll, this is all cached - return base.GetMany().Where(x => alias.IsNullOrWhiteSpace() - ? x.MasterTemplateAlias.IsNullOrWhiteSpace() - : x.MasterTemplateAlias.InvariantEquals(alias)); - } - public IEnumerable GetDescendants(int masterTemplateId) { //return from base.GetAll, this is all cached @@ -541,30 +523,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement return descendants; } - public IEnumerable GetDescendants(string alias) - { - var all = base.GetMany().ToArray(); - var descendants = new List(); - if (alias.IsNullOrWhiteSpace() == false) - { - var parent = all.FirstOrDefault(x => x.Alias.InvariantEquals(alias)); - if (parent == null) return Enumerable.Empty(); - //recursively add all children - AddChildren(all, descendants, parent.Alias); - } - else - { - descendants.AddRange(all.Where(x => x.MasterTemplateAlias.IsNullOrWhiteSpace())); - foreach (var parent in descendants) - { - //recursively add all children with a level - AddChildren(all, descendants, parent.Alias); - } - } - //return the list - it will be naturally ordered by level - return descendants; - } - private void AddChildren(ITemplate[] all, List descendants, string masterAlias) { var c = all.Where(x => x.MasterTemplateAlias.InvariantEquals(masterAlias)).ToArray(); @@ -577,56 +535,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement } } - /// - /// Validates a - /// - /// to validate - /// True if Script is valid, otherwise false - public bool ValidateTemplate(ITemplate template) - { - // get path - // TODO: templates should have a real Path somehow - but anyways - // are we using Path for something else?! - var path = template.VirtualPath; - - // get valid paths - var validDirs = new[] { Cms.Core.Constants.SystemDirectories.MvcViews }; - - // get valid extensions - var validExts = new List(); - validExts.Add("cshtml"); - validExts.Add("vbhtml"); - - // validate path and extension - var validFile = _ioHelper.VerifyEditPath(path, validDirs); - var validExtension = _ioHelper.VerifyFileExtension(path, validExts); - return validFile && validExtension; - } - - private static IEnumerable CreateChildren(TemplateNode parent, IEnumerable childTemplates, ITemplate[] allTemplates) - { - var children = new List(); - foreach (var childTemplate in childTemplates) - { - var template = allTemplates.Single(x => x.Id == childTemplate.Id); - var child = new TemplateNode(template) - { - Parent = parent - }; - - //add to our list - children.Add(child); - - //get this node's children - var local = childTemplate; - var kids = allTemplates.Where(x => x.MasterTemplateAlias.InvariantEquals(local.Alias)); - - //recurse - child.Children = CreateChildren(child, kids, allTemplates); - } - return children; - } - #endregion /// diff --git a/src/Umbraco.Infrastructure/Services/Implement/FileService.cs b/src/Umbraco.Infrastructure/Services/Implement/FileService.cs index 68a02fa478..0a01f345dc 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/FileService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/FileService.cs @@ -460,14 +460,6 @@ namespace Umbraco.Cms.Core.Services.Implement } } - public IEnumerable GetTemplateDescendants(string alias) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _templateRepository.GetDescendants(alias); - } - } - /// /// Gets the template descendants /// @@ -481,32 +473,6 @@ namespace Umbraco.Cms.Core.Services.Implement } } - /// - /// Gets the template children - /// - /// - /// - public IEnumerable GetTemplateChildren(string alias) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _templateRepository.GetChildren(alias); - } - } - - /// - /// Gets the template children - /// - /// - /// - public IEnumerable GetTemplateChildren(int masterTemplateId) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _templateRepository.GetChildren(masterTemplateId); - } - } - /// /// Saves a /// @@ -607,44 +573,6 @@ namespace Umbraco.Cms.Core.Services.Implement } } - /// - /// Validates a - /// - /// to validate - /// True if Script is valid, otherwise false - public bool ValidateTemplate(ITemplate template) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _templateRepository.ValidateTemplate(template); - } - } - - public Stream GetTemplateFileContentStream(string filepath) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _templateRepository.GetFileContentStream(filepath); - } - } - - public void SetTemplateFileContent(string filepath, Stream content) - { - using (var scope = ScopeProvider.CreateScope()) - { - _templateRepository.SetFileContent(filepath, content); - scope.Complete(); - } - } - - public long GetTemplateFileSize(string filepath) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _templateRepository.GetFileSize(filepath); - } - } - private string GetViewContent(string fileName) { if (fileName.IsNullOrWhiteSpace()) diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs index 646367c99a..f9587179dd 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs @@ -374,13 +374,10 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.Repos // Act IEnumerable childrenById = repository.GetChildren(created[1].Id); - IEnumerable childrenByAlias = repository.GetChildren(created[1].Alias); // Assert Assert.AreEqual(2, childrenById.Count()); Assert.AreEqual(2, childrenById.DistinctBy(x => x.Id).Count()); - Assert.AreEqual(2, childrenByAlias.Count()); - Assert.AreEqual(2, childrenByAlias.DistinctBy(x => x.Id).Count()); } } @@ -418,14 +415,10 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.Repos // Act IEnumerable descendantsById = repository.GetDescendants(created[1].Id); - IEnumerable descendantsByAlias = repository.GetDescendants(created[1].Alias); // Assert Assert.AreEqual(3, descendantsById.Count()); Assert.AreEqual(3, descendantsById.DistinctBy(x => x.Id).Count()); - - Assert.AreEqual(3, descendantsByAlias.Count()); - Assert.AreEqual(3, descendantsByAlias.DistinctBy(x => x.Id).Count()); } } From 4f0d4d1fcd067dead721dc7109a680f88a14aae9 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 27 Apr 2021 10:36:28 +0200 Subject: [PATCH 029/182] Remove a bunch of script related, unreferenced methods from file service and script repository (#9878) Co-authored-by: Bjarke Berg --- .../Repositories/IScriptRepository.cs | 6 --- src/Umbraco.Core/Services/IFileService.cs | 34 ------------ .../Repositories/Implement/FileRepository.cs | 2 +- .../Implement/ScriptRepository.cs | 54 +------------------ .../Services/Implement/FileService.cs | 43 --------------- .../Repositories/ScriptRepositoryTest.cs | 2 +- 6 files changed, 4 insertions(+), 137 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/IScriptRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IScriptRepository.cs index 6d2dd4bc57..d3835b8e7e 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IScriptRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IScriptRepository.cs @@ -1,15 +1,9 @@ -using System.IO; using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Persistence.Repositories { public interface IScriptRepository : IReadRepository, IWriteRepository { - bool ValidateScript(IScript script); - Stream GetFileContentStream(string filepath); - void SetFileContent(string filepath, Stream content); - long GetFileSize(string filepath); - void AddFolder(string folderPath); void DeleteFolder(string folderPath); } diff --git a/src/Umbraco.Core/Services/IFileService.cs b/src/Umbraco.Core/Services/IFileService.cs index 1f4d33388c..c2d99aceed 100644 --- a/src/Umbraco.Core/Services/IFileService.cs +++ b/src/Umbraco.Core/Services/IFileService.cs @@ -61,12 +61,6 @@ namespace Umbraco.Cms.Core.Services /// True if Stylesheet is valid, otherwise false bool ValidateStylesheet(IStylesheet stylesheet); - /// - /// Gets a list of all objects - /// - /// An enumerable list of objects - IEnumerable GetScripts(params string[] names); - /// /// Gets a object by its name /// @@ -88,13 +82,6 @@ namespace Umbraco.Cms.Core.Services /// Optional id of the user deleting the script void DeleteScript(string path, int userId = Constants.Security.SuperUserId); - /// - /// Validates a - /// - /// to validate - /// True if Script is valid, otherwise false - bool ValidateScript(IScript script); - /// /// Creates a folder for scripts /// @@ -216,27 +203,6 @@ namespace Umbraco.Cms.Core.Services /// The size of the stylesheet. long GetStylesheetFileSize(string filepath); - /// - /// Gets the content of a script file as a stream. - /// - /// The filesystem path to the script. - /// The content of the script file. - Stream GetScriptFileContentStream(string filepath); - - /// - /// Sets the content of a script file. - /// - /// The filesystem path to the script. - /// The content of the script file. - void SetScriptFileContent(string filepath, Stream content); - - /// - /// Gets the size of a script file. - /// - /// The filesystem path to the script file. - /// The size of the script file. - long GetScriptFileSize(string filepath); - /// /// Gets the content of a macro partial view as a stream. /// diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/FileRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/FileRepository.cs index 742f4ab94c..2e7e8af144 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/FileRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/FileRepository.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Text; using Umbraco.Cms.Core.IO; diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ScriptRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ScriptRepository.cs index 77b8ac9e20..3cebe816fb 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ScriptRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ScriptRepository.cs @@ -1,9 +1,5 @@ -using System; -using System.Collections.Generic; -using System.IO; +using System.Collections.Generic; using System.Linq; -using Microsoft.Extensions.Options; -using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; @@ -16,14 +12,9 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement /// internal class ScriptRepository : FileRepository, IScriptRepository { - private readonly IIOHelper _ioHelper; - private readonly GlobalSettings _globalSettings; - - public ScriptRepository(FileSystems fileSystems, IIOHelper ioHelper, IOptions globalSettings) + public ScriptRepository(FileSystems fileSystems) : base(fileSystems.ScriptsFileSystem) { - _ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper)); - _globalSettings = globalSettings.Value; } #region Implementation of IRepository @@ -93,47 +84,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement } } - public bool ValidateScript(IScript script) - { - // get full path - string fullPath; - try - { - // may throw for security reasons - fullPath = FileSystem.GetFullPath(script.Path); - } - catch - { - return false; - } - - // validate path & extension - var validDir = _globalSettings.UmbracoScriptsPath; - var isValidPath = _ioHelper.VerifyEditPath(fullPath, validDir); - var validExts = new[] {"js"}; - var isValidExtension = _ioHelper.VerifyFileExtension(script.Path, validExts); - return isValidPath && isValidExtension; - } - - public Stream GetFileContentStream(string filepath) - { - if (FileSystem.FileExists(filepath) == false) return null; - - try - { - return FileSystem.OpenFile(filepath); - } - catch - { - return null; // deal with race conds - } - } - - public void SetFileContent(string filepath, Stream content) - { - FileSystem.AddFile(filepath, content, true); - } - #endregion } } diff --git a/src/Umbraco.Infrastructure/Services/Implement/FileService.cs b/src/Umbraco.Infrastructure/Services/Implement/FileService.cs index 0a01f345dc..d946863781 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/FileService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/FileService.cs @@ -179,15 +179,6 @@ namespace Umbraco.Cms.Core.Services.Implement #region Scripts - /// - public IEnumerable GetScripts(params string[] names) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _scriptRepository.GetMany(names); - } - } - /// public IScript GetScriptByName(string name) { @@ -246,15 +237,6 @@ namespace Umbraco.Cms.Core.Services.Implement } } - /// - public bool ValidateScript(IScript script) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _scriptRepository.ValidateScript(script); - } - } - public void CreateScriptFolder(string folderPath) { using (var scope = ScopeProvider.CreateScope()) @@ -273,31 +255,6 @@ namespace Umbraco.Cms.Core.Services.Implement } } - public Stream GetScriptFileContentStream(string filepath) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _scriptRepository.GetFileContentStream(filepath); - } - } - - public void SetScriptFileContent(string filepath, Stream content) - { - using (var scope = ScopeProvider.CreateScope()) - { - _scriptRepository.SetFileContent(filepath, content); - scope.Complete(); - } - } - - public long GetScriptFileSize(string filepath) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _scriptRepository.GetFileSize(filepath); - } - } - #endregion #region Templates diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ScriptRepositoryTest.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ScriptRepositoryTest.cs index 001fd89f03..28f9a9eff1 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ScriptRepositoryTest.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ScriptRepositoryTest.cs @@ -57,7 +57,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.Repos private IScriptRepository CreateRepository() { var globalSettings = new GlobalSettings(); - return new ScriptRepository(_fileSystems, IOHelper, Microsoft.Extensions.Options.Options.Create(globalSettings)); + return new ScriptRepository(_fileSystems); } [Test] From 8674244e615733250bfd8643221584c8a1b9f487 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 27 Apr 2021 11:42:06 +0200 Subject: [PATCH 030/182] Remove a bunch of partial views related, unreferenced methods from file service and partial view repositories (#9879) Co-authored-by: Bjarke Berg --- .../Repositories/IPartialViewRepository.cs | 5 -- src/Umbraco.Core/Services/IFileService.cs | 45 ----------- .../Implement/PartialViewMacroRepository.cs | 4 +- .../Implement/PartialViewRepository.cs | 31 +------- .../Services/Implement/FileService.cs | 74 ------------------- .../PartialViewRepositoryTests.cs | 2 +- 6 files changed, 5 insertions(+), 156 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/IPartialViewRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IPartialViewRepository.cs index b0eb7055ec..d1e322dd4c 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IPartialViewRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IPartialViewRepository.cs @@ -1,4 +1,3 @@ -using System.IO; using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Persistence.Repositories @@ -7,9 +6,5 @@ namespace Umbraco.Cms.Core.Persistence.Repositories { void AddFolder(string folderPath); void DeleteFolder(string folderPath); - bool ValidatePartialView(IPartialView partialView); - Stream GetFileContentStream(string filepath); - void SetFileContent(string filepath, Stream content); - long GetFileSize(string filepath); } } diff --git a/src/Umbraco.Core/Services/IFileService.cs b/src/Umbraco.Core/Services/IFileService.cs index c2d99aceed..9fa4d9b8e6 100644 --- a/src/Umbraco.Core/Services/IFileService.cs +++ b/src/Umbraco.Core/Services/IFileService.cs @@ -17,15 +17,12 @@ namespace Umbraco.Cms.Core.Services void DeletePartialViewMacroFolder(string folderPath); IPartialView GetPartialView(string path); IPartialView GetPartialViewMacro(string path); - IEnumerable GetPartialViewMacros(params string[] names); Attempt CreatePartialView(IPartialView partialView, string snippetName = null, int userId = Constants.Security.SuperUserId); Attempt CreatePartialViewMacro(IPartialView partialView, string snippetName = null, int userId = Constants.Security.SuperUserId); bool DeletePartialView(string path, int userId = Constants.Security.SuperUserId); bool DeletePartialViewMacro(string path, int userId = Constants.Security.SuperUserId); Attempt SavePartialView(IPartialView partialView, int userId = Constants.Security.SuperUserId); Attempt SavePartialViewMacro(IPartialView partialView, int userId = Constants.Security.SuperUserId); - bool ValidatePartialView(IPartialView partialView); - bool ValidatePartialViewMacro(IPartialView partialView); /// /// Gets a list of all objects @@ -203,48 +200,6 @@ namespace Umbraco.Cms.Core.Services /// The size of the stylesheet. long GetStylesheetFileSize(string filepath); - /// - /// Gets the content of a macro partial view as a stream. - /// - /// The filesystem path to the macro partial view. - /// The content of the macro partial view. - Stream GetPartialViewMacroFileContentStream(string filepath); - - /// - /// Sets the content of a macro partial view. - /// - /// The filesystem path to the macro partial view. - /// The content of the macro partial view. - void SetPartialViewMacroFileContent(string filepath, Stream content); - - /// - /// Gets the size of a macro partial view. - /// - /// The filesystem path to the macro partial view. - /// The size of the macro partial view. - long GetPartialViewMacroFileSize(string filepath); - - /// - /// Gets the content of a partial view as a stream. - /// - /// The filesystem path to the partial view. - /// The content of the partial view. - Stream GetPartialViewFileContentStream(string filepath); - - /// - /// Sets the content of a partial view. - /// - /// The filesystem path to the partial view. - /// The content of the partial view. - void SetPartialViewFileContent(string filepath, Stream content); - - /// - /// Gets the size of a partial view. - /// - /// The filesystem path to the partial view. - /// The size of the partial view. - long GetPartialViewFileSize(string filepath); - /// /// Gets the content of a macro partial view snippet as a string /// diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewMacroRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewMacroRepository.cs index 47123b790e..b4c8ce4f6c 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewMacroRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewMacroRepository.cs @@ -6,8 +6,8 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement { internal class PartialViewMacroRepository : PartialViewRepository, IPartialViewMacroRepository { - public PartialViewMacroRepository(FileSystems fileSystems, IIOHelper ioHelper) - : base(fileSystems.MacroPartialsFileSystem, ioHelper) + public PartialViewMacroRepository(FileSystems fileSystems) + : base(fileSystems.MacroPartialsFileSystem) { } protected override PartialViewType ViewType => PartialViewType.PartialViewMacro; diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewRepository.cs index a1d2b218c4..751c84ac56 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewRepository.cs @@ -11,18 +11,14 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement { internal class PartialViewRepository : FileRepository, IPartialViewRepository { - private readonly IIOHelper _ioHelper; - - public PartialViewRepository(FileSystems fileSystems, IIOHelper ioHelper) + public PartialViewRepository(FileSystems fileSystems) : base(fileSystems.PartialViewsFileSystem) { - _ioHelper = ioHelper; } - protected PartialViewRepository(IFileSystem fileSystem, IIOHelper ioHelper) + protected PartialViewRepository(IFileSystem fileSystem) : base(fileSystem) { - _ioHelper = ioHelper; } protected virtual PartialViewType ViewType => PartialViewType.PartialView; @@ -93,29 +89,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement } } - private static readonly List ValidExtensions = new List { "cshtml" }; - - public virtual bool ValidatePartialView(IPartialView partialView) - { - // get full path - string fullPath; - try - { - // may throw for security reasons - fullPath = FileSystem.GetFullPath(partialView.Path); - } - catch - { - return false; - } - - // validate path & extension - var validDir = Cms.Core.Constants.SystemDirectories.MvcViews; - var isValidPath = _ioHelper.VerifyEditPath(fullPath, validDir); - var isValidExtension = _ioHelper.VerifyFileExtension(fullPath, ValidExtensions); - return isValidPath && isValidExtension; - } - public Stream GetFileContentStream(string filepath) { if (FileSystem.FileExists(filepath) == false) return null; diff --git a/src/Umbraco.Infrastructure/Services/Implement/FileService.cs b/src/Umbraco.Infrastructure/Services/Implement/FileService.cs index d946863781..b515e3d0a8 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/FileService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/FileService.cs @@ -600,14 +600,6 @@ namespace Umbraco.Cms.Core.Services.Implement } } - public IEnumerable GetPartialViewMacros(params string[] names) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _partialViewMacroRepository.GetMany(names).OrderBy(x => x.Name); - } - } - public Attempt CreatePartialView(IPartialView partialView, string snippetName = null, int userId = Cms.Core.Constants.Security.SuperUserId) { return CreatePartialViewMacro(partialView, PartialViewType.PartialView, snippetName, userId); @@ -762,22 +754,6 @@ namespace Umbraco.Cms.Core.Services.Implement return Attempt.Succeed(partialView); } - public bool ValidatePartialView(IPartialView partialView) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _partialViewRepository.ValidatePartialView(partialView); - } - } - - public bool ValidatePartialViewMacro(IPartialView partialView) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _partialViewMacroRepository.ValidatePartialView(partialView); - } - } - internal string StripPartialViewHeader(string contents) { var headerMatch = new Regex("^@inherits\\s+?.*$", RegexOptions.Multiline); @@ -797,40 +773,6 @@ namespace Umbraco.Cms.Core.Services.Implement : Attempt.Fail(); } - public Stream GetPartialViewMacroFileContentStream(string filepath) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _partialViewMacroRepository.GetFileContentStream(filepath); - } - } - - public void SetPartialViewMacroFileContent(string filepath, Stream content) - { - using (var scope = ScopeProvider.CreateScope()) - { - _partialViewMacroRepository.SetFileContent(filepath, content); - scope.Complete(); - } - } - - public Stream GetPartialViewFileContentStream(string filepath) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _partialViewRepository.GetFileContentStream(filepath); - } - } - - public void SetPartialViewFileContent(string filepath, Stream content) - { - using (var scope = ScopeProvider.CreateScope()) - { - _partialViewRepository.SetFileContent(filepath, content); - scope.Complete(); - } - } - public void CreatePartialViewFolder(string folderPath) { using (var scope = ScopeProvider.CreateScope()) @@ -849,22 +791,6 @@ namespace Umbraco.Cms.Core.Services.Implement } } - public long GetPartialViewMacroFileSize(string filepath) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _partialViewMacroRepository.GetFileSize(filepath); - } - } - - public long GetPartialViewFileSize(string filepath) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _partialViewRepository.GetFileSize(filepath); - } - } - private IPartialViewRepository GetPartialViewRepository(PartialViewType partialViewType) { switch (partialViewType) diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/PartialViewRepositoryTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/PartialViewRepositoryTests.cs index b0769f68ed..02709f7f84 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/PartialViewRepositoryTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/PartialViewRepositoryTests.cs @@ -51,7 +51,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.Repos IScopeProvider provider = ScopeProvider; using (IScope scope = provider.CreateScope()) { - var repository = new PartialViewRepository(fileSystems, IOHelper); + var repository = new PartialViewRepository(fileSystems); var partialView = new PartialView(PartialViewType.PartialView, "test-path-1.cshtml") { Content = "// partialView" }; repository.Save(partialView); From 3781c6b00f4d51d5e88aac74a03322e2278d8723 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 27 Apr 2021 12:30:38 +0200 Subject: [PATCH 031/182] Remove a bunch of template related, unreferenced methods from file service and stylesheet repository (#9875) Co-authored-by: Bjarke Berg --- .../Repositories/IStylesheetRepository.cs | 6 -- src/Umbraco.Core/Services/IFileService.cs | 28 ------ .../Implement/StylesheetRepository.cs | 88 +------------------ .../Services/Implement/FileService.cs | 34 ------- .../Repositories/StylesheetRepositoryTest.cs | 2 +- 5 files changed, 2 insertions(+), 156 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/IStylesheetRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IStylesheetRepository.cs index 445630a535..ce57f2c9ae 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IStylesheetRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IStylesheetRepository.cs @@ -1,15 +1,9 @@ -using System.IO; using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Persistence.Repositories { public interface IStylesheetRepository : IReadRepository, IWriteRepository { - bool ValidateStylesheet(IStylesheet stylesheet); - Stream GetFileContentStream(string filepath); - void SetFileContent(string filepath, Stream content); - long GetFileSize(string filepath); - void AddFolder(string folderPath); void DeleteFolder(string folderPath); } diff --git a/src/Umbraco.Core/Services/IFileService.cs b/src/Umbraco.Core/Services/IFileService.cs index 9fa4d9b8e6..eea1bb6b22 100644 --- a/src/Umbraco.Core/Services/IFileService.cs +++ b/src/Umbraco.Core/Services/IFileService.cs @@ -51,13 +51,6 @@ namespace Umbraco.Cms.Core.Services /// Optional id of the user deleting the stylesheet void DeleteStylesheet(string path, int userId = Constants.Security.SuperUserId); - /// - /// Validates a - /// - /// to validate - /// True if Stylesheet is valid, otherwise false - bool ValidateStylesheet(IStylesheet stylesheet); - /// /// Gets a object by its name /// @@ -179,27 +172,6 @@ namespace Umbraco.Cms.Core.Services /// Optional id of the user void SaveTemplate(IEnumerable templates, int userId = Constants.Security.SuperUserId); - /// - /// Gets the content of a stylesheet as a stream. - /// - /// The filesystem path to the stylesheet. - /// The content of the stylesheet. - Stream GetStylesheetFileContentStream(string filepath); - - /// - /// Sets the content of a stylesheet. - /// - /// The filesystem path to the stylesheet. - /// The content of the stylesheet. - void SetStylesheetFileContent(string filepath, Stream content); - - /// - /// Gets the size of a stylesheet. - /// - /// The filesystem path to the stylesheet. - /// The size of the stylesheet. - long GetStylesheetFileSize(string filepath); - /// /// Gets the content of a macro partial view snippet as a string /// diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/StylesheetRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/StylesheetRepository.cs index ac255890bd..9c0aa6f92a 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/StylesheetRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/StylesheetRepository.cs @@ -1,10 +1,5 @@ -using System; using System.Collections.Generic; -using System.IO; using System.Linq; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; @@ -17,16 +12,9 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement /// internal class StylesheetRepository : FileRepository, IStylesheetRepository { - private readonly ILogger _logger; - private readonly IIOHelper _ioHelper; - private readonly GlobalSettings _globalSettings; - - public StylesheetRepository(ILogger logger, FileSystems fileSystems, IIOHelper ioHelper, IOptions globalSettings) + public StylesheetRepository(FileSystems fileSystems) : base(fileSystems.StylesheetsFileSystem) { - _logger = logger; - _ioHelper = ioHelper; - _globalSettings = globalSettings.Value; } #region Overrides of FileRepository @@ -110,80 +98,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement } } - /// - /// Gets a list of all that exist at the relative path specified. - /// - /// - /// If null or not specified, will return the stylesheets at the root path relative to the IFileSystem - /// - /// - public IEnumerable GetStylesheetsAtPath(string rootPath = null) - { - return FileSystem.GetFiles(rootPath ?? string.Empty, "*.css").Select(Get); - } - - private static readonly List ValidExtensions = new List { "css" }; - - public bool ValidateStylesheet(IStylesheet stylesheet) - { - // get full path - string fullPath; - try - { - // may throw for security reasons - - fullPath = FileSystem.GetFullPath(stylesheet.Path); - } - catch (Exception ex) - { - _logger.LogWarning(ex, "Can't get full path"); - return false; - } - - // validate path and extension - var validDir = _globalSettings.UmbracoCssPath; - _logger.LogWarning("fullPath {fullPath}, validDir: {validDir}",fullPath, validDir); - - var isValidPath = _ioHelper.VerifyEditPath(fullPath, validDir); - var isValidExtension = _ioHelper.VerifyFileExtension(stylesheet.Path, ValidExtensions); - - _logger.LogWarning("isValidPath {isValidPath}, isValidExtension: {isValidExtension}",isValidPath, isValidExtension); - return isValidPath && isValidExtension; - } - - public Stream GetFileContentStream(string filepath) - { - if (FileSystem.FileExists(filepath) == false) return null; - - try - { - return FileSystem.OpenFile(filepath); - } - catch - { - return null; // deal with race conds - } - } - - public void SetFileContent(string filepath, Stream content) - { - FileSystem.AddFile(filepath, content, true); - } - - public new long GetFileSize(string filepath) - { - if (FileSystem.FileExists(filepath) == false) return -1; - - try - { - return FileSystem.GetSize(filepath); - } - catch - { - return -1; // deal with race conds - } - } - #endregion } } diff --git a/src/Umbraco.Infrastructure/Services/Implement/FileService.cs b/src/Umbraco.Infrastructure/Services/Implement/FileService.cs index b515e3d0a8..508afe82b7 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/FileService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/FileService.cs @@ -123,15 +123,6 @@ namespace Umbraco.Cms.Core.Services.Implement } } - /// - public bool ValidateStylesheet(IStylesheet stylesheet) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _stylesheetRepository.ValidateStylesheet(stylesheet); - } - } - public void CreateStyleSheetFolder(string folderPath) { using (var scope = ScopeProvider.CreateScope()) @@ -150,31 +141,6 @@ namespace Umbraco.Cms.Core.Services.Implement } } - public Stream GetStylesheetFileContentStream(string filepath) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _stylesheetRepository.GetFileContentStream(filepath); - } - } - - public void SetStylesheetFileContent(string filepath, Stream content) - { - using (var scope = ScopeProvider.CreateScope()) - { - _stylesheetRepository.SetFileContent(filepath, content); - scope.Complete(); - } - } - - public long GetStylesheetFileSize(string filepath) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _stylesheetRepository.GetFileSize(filepath); - } - } - #endregion #region Scripts diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/StylesheetRepositoryTest.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/StylesheetRepositoryTest.cs index 9b2acd17c1..d9bde0bca2 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/StylesheetRepositoryTest.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/StylesheetRepositoryTest.cs @@ -56,7 +56,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.Repos private IStylesheetRepository CreateRepository() { var globalSettings = new GlobalSettings(); - return new StylesheetRepository(GetRequiredService>(), _fileSystems, IOHelper, Microsoft.Extensions.Options.Options.Create(globalSettings)); + return new StylesheetRepository(_fileSystems); } [Test] From e7a3fd37ce9b00097a45825ed46da692023a94bc Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 27 Apr 2021 14:57:17 +0200 Subject: [PATCH 032/182] Revert "Fixed copy preserving sort order (#10091)" This reverts commit 26d11a83141abc5e4d94c24e733cdca768d1a6f1. --- .../Services/Implement/ContentService.cs | 10 +++---- .../Services/ContentServiceTests.cs | 26 ------------------- 2 files changed, 5 insertions(+), 31 deletions(-) diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index d8e99663ea..e5363d0e2b 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -1415,7 +1415,7 @@ namespace Umbraco.Core.Services.Implement var result = CommitDocumentChangesInternal(scope, d, saveEventArgs, allLangs.Value, d.WriterId); if (result.Success == false) - Logger.Error(null, "Failed to publish document id={DocumentId}, reason={Reason}.", d.Id, result.Result); + Logger.Error(null, "Failed to publish document id={DocumentId}, reason={Reason}.", d.Id, result.Result); results.Add(result); } @@ -2201,7 +2201,7 @@ namespace Umbraco.Core.Services.Implement while (page * pageSize < total) { var descendants = GetPagedDescendants(content.Id, page++, pageSize, out total); - foreach (var descendant in descendants.OrderBy(x => x.Level).ThenBy(y => y.SortOrder)) + foreach (var descendant in descendants) { // if parent has not been copied, skip, else gets its copy id if (idmap.TryGetValue(descendant.ParentId, out parentId) == false) continue; @@ -2420,7 +2420,7 @@ namespace Umbraco.Core.Services.Implement if (report.FixedIssues.Count > 0) { //The event args needs a content item so we'll make a fake one with enough properties to not cause a null ref - var root = new Content("root", -1, new ContentType(-1)) { Id = -1, Key = Guid.Empty }; + var root = new Content("root", -1, new ContentType(-1)) {Id = -1, Key = Guid.Empty}; scope.Events.Dispatch(TreeChanged, this, new TreeChange.EventArgs(new TreeChange(root, TreeChangeTypes.RefreshAll))); } @@ -3169,7 +3169,7 @@ namespace Umbraco.Core.Services.Implement if (rollbackSaveResult.Success == false) { //Log the error/warning - Logger.Error("User '{UserId}' was unable to rollback content '{ContentId}' to version '{VersionId}'", userId, id, versionId); + Logger.Error("User '{UserId}' was unable to rollback content '{ContentId}' to version '{VersionId}'", userId, id, versionId); } else { @@ -3178,7 +3178,7 @@ namespace Umbraco.Core.Services.Implement scope.Events.Dispatch(RolledBack, this, rollbackEventArgs); //Logging & Audit message - Logger.Info("User '{UserId}' rolled back content '{ContentId}' to version '{VersionId}'", userId, id, versionId); + Logger.Info("User '{UserId}' rolled back content '{ContentId}' to version '{VersionId}'", userId, id, versionId); Audit(AuditType.RollBack, userId, id, $"Content '{content.Name}' was rolled back to version '{versionId}'"); } diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 0faa4af316..008c24fcbf 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -2082,32 +2082,6 @@ namespace Umbraco.Tests.Services Assert.AreEqual("world", copiedTags[1].Text); } - [Test] - public void Copy_Recursive_Preserves_Sort_Order() - { - // Arrange - var contentService = ServiceContext.ContentService; - var temp = contentService.GetById(NodeDto.NodeIdSeed + 2); - Assert.AreEqual("Home", temp.Name); - Assert.AreEqual(3, contentService.CountChildren(temp.Id)); - var reversedChildren = contentService.GetPagedChildren(temp.Id, 0, 10, out var total1).Reverse().ToArray(); - contentService.Sort(reversedChildren); - - // Act - var copy = contentService.Copy(temp, temp.ParentId, false, true, Constants.Security.SuperUserId); - var content = contentService.GetById(NodeDto.NodeIdSeed + 2); - - // Assert - Assert.That(copy, Is.Not.Null); - Assert.That(copy.Id, Is.Not.EqualTo(content.Id)); - Assert.AreNotSame(content, copy); - Assert.AreEqual(3, contentService.CountChildren(copy.Id)); - - var copiedChildren = contentService.GetPagedChildren(copy.Id, 0, 10, out var total2).OrderBy(c => c.SortOrder).ToArray(); - Assert.AreEqual(reversedChildren.First().Name, copiedChildren.First().Name); - Assert.AreEqual(reversedChildren.Last().Name, copiedChildren.Last().Name); - } - [Test] public void Can_Rollback_Version_On_Content() { From 5157d6748581cf4ba55ad3ae9970faec245f2009 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 27 Apr 2021 15:01:13 +0200 Subject: [PATCH 033/182] Fix for template --- build/templates/UmbracoProject/.template.config/template.json | 2 +- src/Umbraco.Web.Website/Umbraco.Web.Website.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/templates/UmbracoProject/.template.config/template.json b/build/templates/UmbracoProject/.template.config/template.json index 203c5abd1a..27b945314b 100644 --- a/build/templates/UmbracoProject/.template.config/template.json +++ b/build/templates/UmbracoProject/.template.config/template.json @@ -46,7 +46,7 @@ "defaultValue": "UmbracoProject", "fallbackVariableName": "name" }, - "replaces":"Umbraco.Web.UI.NetCore" + "replaces":"Umbraco.Cms.Web.UI.NetCore" }, "PackageTestSiteName": { "type": "parameter", diff --git a/src/Umbraco.Web.Website/Umbraco.Web.Website.csproj b/src/Umbraco.Web.Website/Umbraco.Web.Website.csproj index 8fa48bd194..043656b23a 100644 --- a/src/Umbraco.Web.Website/Umbraco.Web.Website.csproj +++ b/src/Umbraco.Web.Website/Umbraco.Web.Website.csproj @@ -6,7 +6,7 @@ Umbraco.Cms.Web.Website Umbraco.Cms.Web.Website Umbraco CMS Website - Contains the Website assembly needed to run Umbraco Cms. This package only contains the assembly, and can be used for package development. Use the template in the Umbraco.Templates package to setup Umbraco + Contains the Website assembly needed to run Umbraco Cms (Front office). This package only contains the assembly, and can be used for package development. Use the template in the Umbraco.Templates package to setup Umbraco From ae3fc84369a30dbab394d9c1c0f082eb57eb9b17 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 28 Apr 2021 09:30:05 +0200 Subject: [PATCH 034/182] Fix templates by only allowing generated https ports withing the range 44300-44399 https://docs.microsoft.com/en-us/iis/extensions/using-iis-express/running-iis-express-without-administrative-privileges#summary --- build/templates/UmbracoProject/.template.config/template.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/templates/UmbracoProject/.template.config/template.json b/build/templates/UmbracoProject/.template.config/template.json index 27b945314b..8b332ddcb0 100644 --- a/build/templates/UmbracoProject/.template.config/template.json +++ b/build/templates/UmbracoProject/.template.config/template.json @@ -99,8 +99,8 @@ "generator": "port", "replaces": "HTTPS_PORT_FROM_TEMPLATE", "parameters": { - "high": 65535, - "low": 1024, + "low": 44300, + "high": 44399, "fallback": 5001 } }, From 8a2c8f1c094adbe754226670e8804d973aa0002d Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 28 Apr 2021 10:11:41 +0200 Subject: [PATCH 035/182] Update version to beta002 --- .../.template.config/template.json | 2 +- .../.template.config/template.json | 2 +- src/Directory.Build.props | 2 +- src/Umbraco.Web.UI.Client/package-lock.json | 198 +++++------------- 4 files changed, 52 insertions(+), 152 deletions(-) diff --git a/build/templates/UmbracoPackage/.template.config/template.json b/build/templates/UmbracoPackage/.template.config/template.json index 56989e3e97..d0a50c1f0a 100644 --- a/build/templates/UmbracoPackage/.template.config/template.json +++ b/build/templates/UmbracoPackage/.template.config/template.json @@ -24,7 +24,7 @@ "version": { "type": "parameter", "datatype": "string", - "defaultValue": "9.0.0-beta001", + "defaultValue": "9.0.0-beta002", "description": "The version of Umbraco to load using NuGet", "replaces": "UMBRACO_VERSION_FROM_TEMPLATE" }, diff --git a/build/templates/UmbracoProject/.template.config/template.json b/build/templates/UmbracoProject/.template.config/template.json index 8b332ddcb0..733c6f1fd5 100644 --- a/build/templates/UmbracoProject/.template.config/template.json +++ b/build/templates/UmbracoProject/.template.config/template.json @@ -34,7 +34,7 @@ "version": { "type": "parameter", "datatype": "string", - "defaultValue": "9.0.0-beta001", + "defaultValue": "9.0.0-beta002", "description": "The version of Umbraco to load using NuGet", "replaces": "UMBRACO_VERSION_FROM_TEMPLATE" }, diff --git a/src/Directory.Build.props b/src/Directory.Build.props index b16405633f..ee1f2e9028 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -2,7 +2,7 @@ 9.0.0 9.0.0 - 9.0.0-beta001 + 9.0.0-beta002 9.0.0 9.0 en-US diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 6f2edfa2ae..9bcf82dee7 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -1842,8 +1842,7 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "optional": true + "dev": true }, "base64id": { "version": "1.0.0", @@ -2052,8 +2051,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true, - "optional": true + "dev": true }, "got": { "version": "8.3.2", @@ -2131,7 +2129,6 @@ "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz", "integrity": "sha1-2N0ZeVldLcATnh/ka4tkbLPN8Dg=", "dev": true, - "optional": true, "requires": { "p-finally": "^1.0.0" } @@ -2173,7 +2170,6 @@ "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", "dev": true, - "optional": true, "requires": { "readable-stream": "^2.3.5", "safe-buffer": "^5.1.1" @@ -2183,15 +2179,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true, - "optional": true + "dev": true }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, - "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -2207,7 +2201,6 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, - "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -2348,7 +2341,6 @@ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, - "optional": true, "requires": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -2374,8 +2366,7 @@ "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", - "dev": true, - "optional": true + "dev": true }, "buffer-equal": { "version": "1.0.0", @@ -2572,7 +2563,6 @@ "resolved": "https://registry.npmjs.org/caw/-/caw-2.0.1.tgz", "integrity": "sha1-bDygcfwZRyCIPC3F2psHS/x+npU=", "dev": true, - "optional": true, "requires": { "get-proxy": "^2.0.0", "isurl": "^1.0.0-alpha5", @@ -3096,7 +3086,6 @@ "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", "integrity": "sha1-D96NCRIA616AjK8l/mGMAvSOTvo=", "dev": true, - "optional": true, "requires": { "ini": "^1.3.4", "proto-list": "~1.2.1" @@ -3152,7 +3141,6 @@ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", "integrity": "sha1-4TDK9+cnkIfFYWwgB9BIVpiYT70=", "dev": true, - "optional": true, "requires": { "safe-buffer": "5.1.2" } @@ -3594,7 +3582,6 @@ "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz", "integrity": "sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==", "dev": true, - "optional": true, "requires": { "decompress-tar": "^4.0.0", "decompress-tarbz2": "^4.0.0", @@ -3611,7 +3598,6 @@ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", "integrity": "sha1-ecEDO4BRW9bSTsmTPoYMp17ifww=", "dev": true, - "optional": true, "requires": { "pify": "^3.0.0" }, @@ -3620,8 +3606,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true, - "optional": true + "dev": true } } } @@ -3632,7 +3617,6 @@ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", "dev": true, - "optional": true, "requires": { "mimic-response": "^1.0.0" } @@ -3642,7 +3626,6 @@ "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", "integrity": "sha1-cYy9P8sWIJcW5womuE57pFkuWvE=", "dev": true, - "optional": true, "requires": { "file-type": "^5.2.0", "is-stream": "^1.1.0", @@ -3653,8 +3636,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=", - "dev": true, - "optional": true + "dev": true } } }, @@ -3663,7 +3645,6 @@ "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", "integrity": "sha1-MIKluIDqQEOBY0nzeLVsUWvho5s=", "dev": true, - "optional": true, "requires": { "decompress-tar": "^4.1.0", "file-type": "^6.1.0", @@ -3676,8 +3657,7 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", "integrity": "sha1-5QzXXTVv/tTjBtxPW89Sp5kDqRk=", - "dev": true, - "optional": true + "dev": true } } }, @@ -3686,7 +3666,6 @@ "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", "integrity": "sha1-wJvDXE0R894J8tLaU+neI+fOHu4=", "dev": true, - "optional": true, "requires": { "decompress-tar": "^4.1.1", "file-type": "^5.2.0", @@ -3697,8 +3676,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=", - "dev": true, - "optional": true + "dev": true } } }, @@ -3707,7 +3685,6 @@ "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", "integrity": "sha1-3qrM39FK6vhVePczroIQ+bSEj2k=", "dev": true, - "optional": true, "requires": { "file-type": "^3.8.0", "get-stream": "^2.2.0", @@ -3719,15 +3696,13 @@ "version": "3.9.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=", - "dev": true, - "optional": true + "dev": true }, "get-stream": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=", "dev": true, - "optional": true, "requires": { "object-assign": "^4.0.1", "pinkie-promise": "^2.0.0" @@ -3737,8 +3712,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, - "optional": true + "dev": true } } }, @@ -4026,8 +4000,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true, - "optional": true + "dev": true } } }, @@ -4044,8 +4017,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "dev": true, - "optional": true + "dev": true }, "duplexify": { "version": "3.7.1", @@ -4690,7 +4662,6 @@ "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", "dev": true, - "optional": true, "requires": { "cross-spawn": "^6.0.0", "get-stream": "^4.0.0", @@ -4832,7 +4803,6 @@ "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", "integrity": "sha1-C5jmTtgvWs8PKTG6v2khLvUt3Tc=", "dev": true, - "optional": true, "requires": { "mime-db": "^1.28.0" } @@ -4842,7 +4812,6 @@ "resolved": "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz", "integrity": "sha1-cHgZgdGD7hXROZPIgiBFxQbI8KY=", "dev": true, - "optional": true, "requires": { "ext-list": "^2.0.0", "sort-keys-length": "^1.0.0" @@ -5080,7 +5049,6 @@ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", "dev": true, - "optional": true, "requires": { "pend": "~1.2.0" } @@ -5119,15 +5087,13 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", "integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=", - "dev": true, - "optional": true + "dev": true }, "filenamify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-2.1.0.tgz", "integrity": "sha1-iPr0lfsbR6v9YSMAACoWIoxnfuk=", "dev": true, - "optional": true, "requires": { "filename-reserved-regex": "^2.0.0", "strip-outer": "^1.0.0", @@ -5476,8 +5442,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha1-a+Dem+mYzhavivwkSXue6bfM2a0=", - "dev": true, - "optional": true + "dev": true }, "fs-mkdirp-stream": { "version": "1.0.0", @@ -5524,8 +5489,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -5546,14 +5510,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5568,20 +5530,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -5698,8 +5657,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -5711,7 +5669,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -5726,7 +5683,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5734,14 +5690,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -5760,7 +5714,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -5841,8 +5794,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -5854,7 +5806,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -5940,8 +5891,7 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -5977,7 +5927,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -5997,7 +5946,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6041,14 +5989,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -6075,7 +6021,6 @@ "resolved": "https://registry.npmjs.org/get-proxy/-/get-proxy-2.1.0.tgz", "integrity": "sha1-NJ8rTZHUTE1NTpy6KtkBQ/rF75M=", "dev": true, - "optional": true, "requires": { "npm-conf": "^1.1.0" } @@ -6084,15 +6029,13 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true, - "optional": true + "dev": true }, "get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dev": true, - "optional": true, "requires": { "pump": "^3.0.0" }, @@ -6102,7 +6045,6 @@ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, - "optional": true, "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -6215,8 +6157,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "optional": true + "dev": true }, "pump": { "version": "3.0.0", @@ -7419,8 +7360,7 @@ "version": "1.4.2", "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", "integrity": "sha1-FAn5i8ACR9pF2mfO4KNvKC/yZFU=", - "dev": true, - "optional": true + "dev": true }, "has-symbols": { "version": "1.0.0", @@ -7433,7 +7373,6 @@ "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", "integrity": "sha1-oEWrOD17SyASoAFIqwql8pAETU0=", "dev": true, - "optional": true, "requires": { "has-symbol-support-x": "^1.4.1" } @@ -7639,8 +7578,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "optional": true + "dev": true }, "ignore": { "version": "4.0.6", @@ -7780,8 +7718,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true + "dev": true }, "svgo": { "version": "1.3.2", @@ -7853,7 +7790,6 @@ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", "dev": true, - "optional": true, "requires": { "repeating": "^2.0.0" } @@ -8180,8 +8116,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", - "dev": true, - "optional": true + "dev": true }, "is-fullwidth-code-point": { "version": "1.0.0", @@ -8231,8 +8166,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", "integrity": "sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=", - "dev": true, - "optional": true + "dev": true }, "is-negated-glob": { "version": "1.0.0", @@ -8270,15 +8204,13 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.2.tgz", "integrity": "sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==", - "dev": true, - "optional": true + "dev": true }, "is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true, - "optional": true + "dev": true }, "is-plain-object": { "version": "2.0.4", @@ -8348,15 +8280,13 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", "integrity": "sha1-13hIi9CkZmo76KFIK58rqv7eqLQ=", - "dev": true, - "optional": true + "dev": true }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true, - "optional": true + "dev": true }, "is-svg": { "version": "3.0.0", @@ -8451,7 +8381,6 @@ "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", "integrity": "sha1-sn9PSfPNqj6kSgpbfzRi5u3DnWc=", "dev": true, - "optional": true, "requires": { "has-to-string-tag-x": "^1.2.0", "is-object": "^1.0.1" @@ -9347,8 +9276,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", "integrity": "sha1-b54wtHCE2XGnyCD/FabFFnt0wm8=", - "dev": true, - "optional": true + "dev": true }, "lpad-align": { "version": "1.1.2", @@ -9418,8 +9346,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true, - "optional": true + "dev": true }, "map-visit": { "version": "1.0.0", @@ -9587,8 +9514,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", "integrity": "sha1-SSNTiHju9CBjy4o+OweYeBSHqxs=", - "dev": true, - "optional": true + "dev": true }, "minimatch": { "version": "3.0.4", @@ -12935,7 +12861,6 @@ "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", "integrity": "sha1-JWzEe9DiGMJZxOlVC/QTvCGSr/k=", "dev": true, - "optional": true, "requires": { "config-chain": "^1.1.11", "pify": "^3.0.0" @@ -12945,8 +12870,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true, - "optional": true + "dev": true } } }, @@ -12955,7 +12879,6 @@ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, - "optional": true, "requires": { "path-key": "^2.0.0" } @@ -13324,8 +13247,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true, - "optional": true + "dev": true }, "p-is-promise": { "version": "1.1.0", @@ -13362,7 +13284,6 @@ "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", "dev": true, - "optional": true, "requires": { "p-finally": "^1.0.0" } @@ -13553,8 +13474,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", - "dev": true, - "optional": true + "dev": true }, "performance-now": { "version": "2.1.0", @@ -14061,8 +13981,7 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", - "dev": true, - "optional": true + "dev": true }, "prr": { "version": "1.0.1", @@ -14420,7 +14339,6 @@ "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "dev": true, - "optional": true, "requires": { "is-finite": "^1.0.0" } @@ -14775,7 +14693,6 @@ "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz", "integrity": "sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==", "dev": true, - "optional": true, "requires": { "commander": "^2.8.1" } @@ -15170,7 +15087,6 @@ "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", "dev": true, - "optional": true, "requires": { "is-plain-obj": "^1.0.0" } @@ -15180,7 +15096,6 @@ "resolved": "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz", "integrity": "sha1-nLb09OnkgVWmqgZx7dM2/xR5oYg=", "dev": true, - "optional": true, "requires": { "sort-keys": "^1.0.0" } @@ -15528,7 +15443,6 @@ "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", "integrity": "sha1-SYdzYmT8NEzyD2w0rKnRPR1O1sU=", "dev": true, - "optional": true, "requires": { "is-natural-number": "^4.0.1" } @@ -15537,8 +15451,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true, - "optional": true + "dev": true }, "strip-final-newline": { "version": "2.0.0", @@ -15568,7 +15481,6 @@ "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", "integrity": "sha1-sv0qv2YEudHmATBXGV34Nrip1jE=", "dev": true, - "optional": true, "requires": { "escape-string-regexp": "^1.0.2" } @@ -15694,7 +15606,6 @@ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", "integrity": "sha1-jqVdqzeXIlPZqa+Q/c1VmuQ1xVU=", "dev": true, - "optional": true, "requires": { "bl": "^1.0.0", "buffer-alloc": "^1.2.0", @@ -15709,15 +15620,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true, - "optional": true + "dev": true }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, - "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -15733,7 +15642,6 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, - "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -15744,15 +15652,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", "integrity": "sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=", - "dev": true, - "optional": true + "dev": true }, "tempfile": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-2.0.0.tgz", "integrity": "sha1-awRGhWqbERTRhW/8vlCczLCXcmU=", "dev": true, - "optional": true, "requires": { "temp-dir": "^1.0.0", "uuid": "^3.0.1" @@ -15866,8 +15772,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", - "dev": true, - "optional": true + "dev": true }, "timers-ext": { "version": "0.1.7", @@ -15924,8 +15829,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", "integrity": "sha1-STvUj2LXxD/N7TE6A9ytsuEhOoA=", - "dev": true, - "optional": true + "dev": true }, "to-fast-properties": { "version": "2.0.0", @@ -16027,7 +15931,6 @@ "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", "dev": true, - "optional": true, "requires": { "escape-string-regexp": "^1.0.2" } @@ -16163,7 +16066,6 @@ "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", "dev": true, - "optional": true, "requires": { "buffer": "^5.2.1", "through": "^2.3.8" @@ -16372,8 +16274,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=", - "dev": true, - "optional": true + "dev": true }, "use": { "version": "3.1.1", @@ -16867,7 +16768,6 @@ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", "dev": true, - "optional": true, "requires": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" From b783399c5cd730cfcbff3fdcedf2f7a4ab2eb25c Mon Sep 17 00:00:00 2001 From: Mike Chambers Date: Tue, 27 Apr 2021 13:56:31 +0100 Subject: [PATCH 036/182] Update ModelsBuilderComposer.cs issue #10186 typo in IsExternalModelsBuilderInstalled -> Umbraco.ModelsBuider --- .../Compose/ModelsBuilderComposer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.ModelsBuilder.Embedded/Compose/ModelsBuilderComposer.cs b/src/Umbraco.ModelsBuilder.Embedded/Compose/ModelsBuilderComposer.cs index 01010cca66..fee9b6f62e 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/Compose/ModelsBuilderComposer.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/Compose/ModelsBuilderComposer.cs @@ -47,7 +47,7 @@ namespace Umbraco.ModelsBuilder.Embedded.Compose { var assemblyNames = new[] { - "Umbraco.ModelsBuider", + "Umbraco.ModelsBuilder", "ModelsBuilder.Umbraco" }; From 349c5ac3ab56fe0cc6c0bb3abd0d6ad314fd2bf6 Mon Sep 17 00:00:00 2001 From: Bjarne Fyrstenborg Date: Sun, 21 Mar 2021 15:14:01 +0100 Subject: [PATCH 037/182] Fix issue with active style affected nested block list elements --- .../inlineblock/inlineblock.editor.less | 28 +++++++++++-------- .../umb-block-list-property-editor.html | 1 - .../blocklist/umbblocklistblock.component.js | 4 +-- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklistentryeditors/inlineblock/inlineblock.editor.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklistentryeditors/inlineblock/inlineblock.editor.less index 45a4c08598..12862d8f41 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklistentryeditors/inlineblock/inlineblock.editor.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/blocklistentryeditors/inlineblock/inlineblock.editor.less @@ -45,18 +45,6 @@ } } - .umb-block-list__block.--active & { - border-color: @gray-8; - box-shadow: 0 0 2px 0px rgba(0, 0, 0, 0.05); - - > button { - > .caret { - transform: rotate(0deg); - } - } - } - - ng-form.ng-invalid-val-server-match-content > .umb-block-list__block > .umb-block-list__block--content > div > & { > button { color: @formErrorText; @@ -104,6 +92,22 @@ } } +.umb-block-list__block.--active { + border-color: @gray-8; + box-shadow: 0 0 2px 0px rgba(0, 0, 0, 0.05); + + > .umb-block-list__block--content { + > .umb-block-list__block--view { + > .blockelement-inlineblock-editor { + > button { + > .caret { + transform: rotate(0deg); + } + } + } + } + } +} .blockelement-inlineblock-editor__inner { border-top: 1px solid @gray-8; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umb-block-list-property-editor.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umb-block-list-property-editor.html index 9726daf5e6..38959da6ba 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umb-block-list-property-editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umb-block-list-property-editor.html @@ -61,5 +61,4 @@ model="vm.blockTypePicker"> -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umbblocklistblock.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umbblocklistblock.component.js index 285437b011..1027b82e51 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umbblocklistblock.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umbblocklistblock.component.js @@ -59,12 +59,12 @@ -
    +
    `; $compile(shadowRoot)($scope); } else { - $element.append($compile('
    ')($scope)); + $element.append($compile('
    ')($scope)); } }; From 727be420ff0d7732c19efc395b9ab75ed65ab3b2 Mon Sep 17 00:00:00 2001 From: Bjarne Fyrstenborg Date: Tue, 30 Mar 2021 17:33:33 +0200 Subject: [PATCH 038/182] Use umb-icon component in listview dropdown --- .../src/views/propertyeditors/listview/listview.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html index 1f66d324df..dc226ec307 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html @@ -31,12 +31,12 @@ @@ -53,12 +53,12 @@ @@ -69,7 +69,7 @@
  • -
    +
    - {{currentSection.grid}} -
    @@ -74,7 +73,7 @@ @@ -85,7 +84,7 @@
      -
    • +
    • - + on-change="vm.selectRow(currentSection, row)"> - @@ -153,13 +152,13 @@ button-style="link" label-key="general_close" shortcut="esc" - action="close()"> + action="vm.close()"> + action="vm.submit()"> diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/dialogs/rowconfig.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/dialogs/rowconfig.controller.js index 83a9fd5394..b36352a66b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/dialogs/rowconfig.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/dialogs/rowconfig.controller.js @@ -2,10 +2,26 @@ function RowConfigController($scope, localizationService) { var vm = this; + vm.configureCell = configureCell; + vm.closeArea = closeArea; + vm.deleteArea = deleteArea; + vm.selectEditor = selectEditor; + vm.toggleAllowed = toggleAllowed; + vm.percentage = percentage; + vm.scaleUp = scaleUp; + vm.scaleDown = scaleDown; + vm.close = close; + vm.submit = submit; + vm.labels = {}; function init() { - + + $scope.currentRow = $scope.model.currentRow; + $scope.columns = $scope.model.columns; + $scope.editors = $scope.model.editors; + $scope.nameChanged = false; + var labelKeys = [ "grid_addRowConfiguration", "grid_allowAllEditors" @@ -25,12 +41,8 @@ function RowConfigController($scope, localizationService) { $scope.model.title = value; } } - - $scope.currentRow = $scope.model.currentRow; - $scope.columns = $scope.model.columns; - $scope.editors = $scope.model.editors; - $scope.scaleUp = function(section, max, overflow) { + function scaleUp(section, max, overflow) { var add = 1; if (overflow !== true) { add = (max > 1) ? 1 : max; @@ -39,19 +51,19 @@ function RowConfigController($scope, localizationService) { section.grid = section.grid + add; }; - $scope.scaleDown = function(section) { + function scaleDown(section) { var remove = (section.grid > 1) ? 1 : 0; section.grid = section.grid - remove; - }; + } - $scope.percentage = function(spans) { + function percentage(spans) { return ((spans / $scope.columns) * 100).toFixed(8); - }; + } /**************** area *****************/ - $scope.configureCell = function(cell, row) { + function configureCell(cell, row) { if ($scope.currentCell && $scope.currentCell === cell) { delete $scope.currentCell; } @@ -75,12 +87,13 @@ function RowConfigController($scope, localizationService) { $scope.editors.forEach(function (e) { e.allowed = cell.allowed.indexOf(e.alias) !== -1 }); - $scope.currentCell = cell; - $scope.currentCell.allowAll = cell.allowAll || !cell.allowed || !cell.allowed.length; - } - }; + cell.allowAll = cell.allowAll || !cell.allowed || !cell.allowed.length; - $scope.toggleAllowed = function (cell) { + $scope.currentCell = cell; + } + } + + function toggleAllowed(cell) { cell.allowAll = !cell.allowAll; if (cell.allowed) { @@ -89,21 +102,22 @@ function RowConfigController($scope, localizationService) { else { cell.allowed = []; } - }; + } - $scope.deleteArea = function (cell, row) { + function deleteArea(cell, row) { if ($scope.currentCell === cell) { $scope.currentCell = null; } var index = row.areas.indexOf(cell) row.areas.splice(index, 1); - }; + } - $scope.closeArea = function() { + // This doesn't seem to be used? + function closeArea() { $scope.currentCell = null; - }; + } - $scope.selectEditor = function (cell, editor) { + function selectEditor(cell, editor) { cell.allowed = cell.allowed || []; var index = cell.allowed.indexOf(editor.alias); @@ -115,22 +129,20 @@ function RowConfigController($scope, localizationService) { else { cell.allowed.splice(index, 1); } - }; + } - $scope.close = function () { + function close () { if ($scope.model.close) { $scope.model.close(); } - }; + } - $scope.submit = function () { + function submit() { if ($scope.model.submit) { $scope.model.submit($scope.currentRow); } - }; + } - $scope.nameChanged = false; - var originalName = $scope.currentRow.name; $scope.$watch("currentRow", function(row) { if (row) { @@ -141,6 +153,7 @@ function RowConfigController($scope, localizationService) { $scope.availableRowSpace = $scope.columns - total; + var originalName = $scope.currentRow.name; if (originalName) { if (originalName != row.name) { $scope.nameChanged = true; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/dialogs/rowconfig.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/dialogs/rowconfig.html index 9d105e2629..5cf0676526 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/dialogs/rowconfig.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/dialogs/rowconfig.html @@ -41,10 +41,10 @@
      {{currentCell.grid}} -
      @@ -84,7 +84,7 @@ - @@ -93,7 +93,7 @@ + on-change="vm.selectEditor(currentCell, editor)"> {{editor.name}} ({{editor.alias}}) @@ -132,13 +132,13 @@ button-style="link" label-key="general_close" shortcut="esc" - action="close()"> + action="vm.close()"> + action="vm.submit()"> From 10341a8d62e5f6094620ddee999886f80da98222 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Fri, 7 May 2021 10:34:39 +0200 Subject: [PATCH 072/182] Fix ordering of views so the ordering is the same as v8 --- .../ViewEngines/RenderRazorViewEngineOptionsSetup.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.Website/ViewEngines/RenderRazorViewEngineOptionsSetup.cs b/src/Umbraco.Web.Website/ViewEngines/RenderRazorViewEngineOptionsSetup.cs index 602920dc11..737e93a78a 100644 --- a/src/Umbraco.Web.Website/ViewEngines/RenderRazorViewEngineOptionsSetup.cs +++ b/src/Umbraco.Web.Website/ViewEngines/RenderRazorViewEngineOptionsSetup.cs @@ -31,9 +31,10 @@ namespace Umbraco.Cms.Web.Website.ViewEngines { string[] umbViewLocations = new string[] { + "/Views/{0}.cshtml", + "/Views/Shared/{0}.cshtml", "/Views/Partials/{0}.cshtml", "/Views/MacroPartials/{0}.cshtml", - "/Views/{0}.cshtml" }; viewLocations = umbViewLocations.Concat(viewLocations); From 68fdc80b6a1c3a1fde0d6112e565f7e64f963a1d Mon Sep 17 00:00:00 2001 From: Anders Bjerner Date: Fri, 7 May 2021 10:37:44 +0200 Subject: [PATCH 073/182] Added target="_blank" to "Update available" link --- src/Umbraco.Web.UI.Client/src/main.controller.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/main.controller.js b/src/Umbraco.Web.UI.Client/src/main.controller.js index d21331f106..a5df6904e5 100644 --- a/src/Umbraco.Web.UI.Client/src/main.controller.js +++ b/src/Umbraco.Web.UI.Client/src/main.controller.js @@ -118,7 +118,8 @@ function MainController($scope, $location, appState, treeService, notificationsS message: "Click to download", sticky: true, type: "info", - url: update.url + url: update.url, + target: "_blank" }; notificationsService.add(notification); } From ed62f7d672e8e9f1438a4f31bb9546a4c3fb2dac Mon Sep 17 00:00:00 2001 From: patrickdemooij9 Date: Sun, 9 May 2021 11:42:29 +0200 Subject: [PATCH 074/182] Add "Insert Macro" as language key (#10164) * Add "Insert Macro" as language key * Remove "a" as it wasn't in the original * Formatting * add label in FR lanuage file --- .../grid/editors/macro.controller.js | 104 +++++++++--------- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 1 + .../Umbraco/config/lang/en_us.xml | 3 +- src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml | 1 + 4 files changed, 57 insertions(+), 52 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/macro.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/macro.controller.js index 5c1d729364..5e66684ac5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/macro.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/macro.controller.js @@ -1,57 +1,59 @@ angular.module("umbraco") .controller("Umbraco.PropertyEditors.Grid.MacroController", - function ($scope, $timeout, editorService, macroResource, macroService, $routeParams) { + function ($scope, $timeout, editorService, macroResource, macroService, localizationService, $routeParams) { - $scope.title = "Click to insert macro"; - - $scope.setMacro = function(){ - - var dialogData = { - richTextEditor: true, - macroData: $scope.control.value || { - macroAlias: $scope.control.editor.config && $scope.control.editor.config.macroAlias - ? $scope.control.editor.config.macroAlias : "" - } - }; - - var macroPicker = { - dialogData: dialogData, - submit: function(model) { - var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, dialogData.renderingEngine); - - $scope.control.value = { - macroAlias: macroObject.macroAlias, - macroParamsDictionary: macroObject.macroParamsDictionary - }; - - $scope.setPreview($scope.control.value ); - editorService.close(); - }, - close: function() { - editorService.close(); - } - } - editorService.macroPicker(macroPicker); - }; - - $scope.setPreview = function(macro){ - var contentId = $routeParams.id; - - macroResource.getMacroResultAsHtmlForEditor(macro.macroAlias, contentId, macro.macroParamsDictionary) - .then(function (htmlResult) { - $scope.title = macro.macroAlias; - if(htmlResult.trim().length > 0 && htmlResult.indexOf("Macro:") < 0){ - $scope.preview = htmlResult; - } + localizationService.localize("grid_clickToInsertMacro").then(function(label) { + $scope.title = label; }); - }; + $scope.setMacro = function () { - $timeout(function(){ - if($scope.control.$initializing){ - $scope.setMacro(); - }else if($scope.control.value){ - $scope.setPreview($scope.control.value); - } - }, 200); -}); + var dialogData = { + richTextEditor: true, + macroData: $scope.control.value || { + macroAlias: $scope.control.editor.config && $scope.control.editor.config.macroAlias + ? $scope.control.editor.config.macroAlias : "" + } + }; + + var macroPicker = { + dialogData: dialogData, + submit: function (model) { + var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, dialogData.renderingEngine); + + $scope.control.value = { + macroAlias: macroObject.macroAlias, + macroParamsDictionary: macroObject.macroParamsDictionary + }; + + $scope.setPreview($scope.control.value); + editorService.close(); + }, + close: function () { + editorService.close(); + } + } + editorService.macroPicker(macroPicker); + }; + + $scope.setPreview = function (macro) { + var contentId = $routeParams.id; + + macroResource.getMacroResultAsHtmlForEditor(macro.macroAlias, contentId, macro.macroParamsDictionary) + .then(function (htmlResult) { + $scope.title = macro.macroAlias; + if (htmlResult.trim().length > 0 && htmlResult.indexOf("Macro:") < 0) { + $scope.preview = htmlResult; + } + }); + + }; + + $timeout(function () { + if ($scope.control.$initializing) { + $scope.setMacro(); + } else if ($scope.control.value) { + $scope.setPreview($scope.control.value); + } + }, 200); + }); diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 5865d39a75..4c92c2b293 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -1619,6 +1619,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont This content is allowed here Click to embed Click to insert image + Click to insert macro Image caption... Write here... Grid Layouts diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index 102f04371d..8ca225c48c 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -1637,6 +1637,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont This content is allowed here Click to embed Click to insert image + Click to insert macro Image caption... Write here... Grid Layouts @@ -1663,7 +1664,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Set as default Choose extra Choose default - are added + are added Warning You are deleting the row configuration diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml index 69e2189c67..86684678c9 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml @@ -1576,6 +1576,7 @@ Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à Ce contenu est autorisé ici Cliquez pour intégrer Cliquez pour insérer une image + Cliquez pour insérer une macro Légende de l'image... Ecrivez ici... Mises en pages de la Grid From 8ff88f47de07fee4c270d4afe44f2e2e1d31d532 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 May 2021 17:33:07 +0000 Subject: [PATCH 075/182] Bump lodash from 4.17.19 to 4.17.21 in /src/Umbraco.Web.UI.Client Bumps [lodash](https://github.com/lodash/lodash) from 4.17.19 to 4.17.21. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.19...4.17.21) Signed-off-by: dependabot[bot] --- src/Umbraco.Web.UI.Client/package-lock.json | 10 +++++----- src/Umbraco.Web.UI.Client/package.json | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 1b44664fac..0e8f48084f 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -1,6 +1,6 @@ { - "requires": true, - "lockfileVersion": 1, + "requires": true, + "lockfileVersion": 1, "dependencies": { "@babel/code-frame": { "version": "7.5.5", @@ -8717,9 +8717,9 @@ } }, "lodash": { - "version": "4.17.19", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", - "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, "lodash._basecopy": { diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index dd9ba51138..95f5bcef49 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -82,7 +82,7 @@ "karma-junit-reporter": "2.0.1", "karma-spec-reporter": "0.0.32", "less": "3.10.3", - "lodash": "4.17.19", + "lodash": "4.17.21", "marked": "^0.7.0", "merge-stream": "2.0.0", "run-sequence": "2.2.1" From 96d37a5e64c0aafdef9ede5ca9db367b169e7d02 Mon Sep 17 00:00:00 2001 From: Chad Date: Tue, 11 May 2021 16:32:27 +1200 Subject: [PATCH 076/182] Fix copy button inactive, nodeSelectHandler had too many args passed. (#10158) --- .../src/views/content/content.copy.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.copy.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.copy.controller.js index eac489ab08..01526bb907 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.copy.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.copy.controller.js @@ -143,7 +143,7 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.CopyController", // Mini list view $scope.selectListViewNode = function (node) { node.selected = node.selected === true ? false : true; - nodeSelectHandler({}, { node: node }); + nodeSelectHandler({ node: node }); }; $scope.closeMiniListView = function () { From f5406b9b2f764c6c45f35b1ff91961e9a38d57e9 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 20 Apr 2021 15:55:01 +1000 Subject: [PATCH 077/182] Updates to Examine 1.2.0 and removes ExecutionContext workaround since it's part of Examine now. --- build/NuSpecs/UmbracoCms.Web.nuspec | 2 +- src/Umbraco.Examine/Umbraco.Examine.csproj | 2 +- src/Umbraco.Examine/UmbracoExamineIndex.cs | 19 +++---------------- src/Umbraco.Tests/Umbraco.Tests.csproj | 2 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 2 +- src/Umbraco.Web/Umbraco.Web.csproj | 4 ++-- 6 files changed, 9 insertions(+), 22 deletions(-) diff --git a/build/NuSpecs/UmbracoCms.Web.nuspec b/build/NuSpecs/UmbracoCms.Web.nuspec index d8815bab63..b0758e6ae4 100644 --- a/build/NuSpecs/UmbracoCms.Web.nuspec +++ b/build/NuSpecs/UmbracoCms.Web.nuspec @@ -28,7 +28,7 @@ - + diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index 517edf354c..39fbe927d4 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -49,7 +49,7 @@ - + 1.0.0-beta2-19324-01 runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Umbraco.Examine/UmbracoExamineIndex.cs b/src/Umbraco.Examine/UmbracoExamineIndex.cs index 511d78db92..c9ab2cd446 100644 --- a/src/Umbraco.Examine/UmbracoExamineIndex.cs +++ b/src/Umbraco.Examine/UmbracoExamineIndex.cs @@ -25,8 +25,7 @@ namespace Umbraco.Examine // wrapping all operations that end up calling base.SafelyProcessQueueItems in a safe call // context because they will fork a thread/task/whatever which should *not* capture our // call context (and the database it can contain)! - // TODO: FIX Examine to not flow the ExecutionContext so callers don't need to worry about this! - + /// /// Used to store the path of a content object /// @@ -99,13 +98,7 @@ namespace Umbraco.Examine { if (CanInitialize()) { - // Use SafeCallContext to prevent the current CallContext flow to child - // tasks executed in the base class so we don't leak Scopes. - // TODO: See notes at the top of this class - using (new SafeCallContext()) - { - base.PerformDeleteFromIndex(itemIds, onComplete); - } + base.PerformDeleteFromIndex(itemIds, onComplete); } } @@ -113,13 +106,7 @@ namespace Umbraco.Examine { if (CanInitialize()) { - // Use SafeCallContext to prevent the current CallContext flow to child - // tasks executed in the base class so we don't leak Scopes. - // TODO: See notes at the top of this class - using (new SafeCallContext()) - { - base.PerformIndexItems(values, onComplete); - } + base.PerformIndexItems(values, onComplete); } } diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 9d4bc4294c..242040836b 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -79,7 +79,7 @@ - + 1.8.14 diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 58baadcb35..707c773dc6 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -88,7 +88,7 @@ - + diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index a6cbefa825..ecf0e65b70 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -63,7 +63,7 @@ - + 2.7.0.100 @@ -1306,4 +1306,4 @@ - + \ No newline at end of file From 17e43a6b099eb9a2d893ab96463d893b92d843ab Mon Sep 17 00:00:00 2001 From: Bjarne Fyrstenborg Date: Fri, 2 Apr 2021 16:19:08 +0200 Subject: [PATCH 078/182] Update to noUiSlider v14.6.4 --- src/Umbraco.Web.UI.Client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 95f5bcef49..16d6ff980b 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -41,7 +41,7 @@ "lazyload-js": "1.0.0", "moment": "2.22.2", "ng-file-upload": "12.2.13", - "nouislider": "14.6.3", + "nouislider": "14.6.4", "npm": "^6.14.7", "signalr": "2.4.0", "spectrum-colorpicker2": "2.0.8", From 499d22aa20901d3dafc5bd3c8b096f43bc54df13 Mon Sep 17 00:00:00 2001 From: Patrick de Mooij Date: Mon, 8 Feb 2021 12:58:10 +0100 Subject: [PATCH 079/182] Move the SetWeight logic to WeightedCollectionBuilderBase so other collections can use it too --- .../WeightedCollectionBuilderBase.cs | 16 +++++++++++++ .../Composing/CollectionBuildersTests.cs | 13 +++++++++++ .../Dashboards/DashboardCollectionBuilder.cs | 23 +------------------ 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/Umbraco.Core/Composing/WeightedCollectionBuilderBase.cs b/src/Umbraco.Core/Composing/WeightedCollectionBuilderBase.cs index 88eb61de76..b333fa646d 100644 --- a/src/Umbraco.Core/Composing/WeightedCollectionBuilderBase.cs +++ b/src/Umbraco.Core/Composing/WeightedCollectionBuilderBase.cs @@ -16,6 +16,8 @@ namespace Umbraco.Core.Composing { protected abstract TBuilder This { get; } + private readonly Dictionary _customWeights = new Dictionary(); + /// /// Clears all types in the collection. /// @@ -107,6 +109,18 @@ namespace Umbraco.Core.Composing return This; } + /// + /// Changes the default weight of an item + /// + /// The type of item + /// The new weight + /// + public TBuilder SetWeight(int weight) where T : TItem + { + _customWeights[typeof(T)] = weight; + return This; + } + protected override IEnumerable GetRegisteringTypes(IEnumerable types) { var list = types.ToList(); @@ -118,6 +132,8 @@ namespace Umbraco.Core.Composing protected virtual int GetWeight(Type type) { + if (_customWeights.ContainsKey(type)) + return _customWeights[type]; var attr = type.GetCustomAttributes(typeof(WeightAttribute), false).OfType().SingleOrDefault(); return attr?.Weight ?? DefaultWeight; } diff --git a/src/Umbraco.Tests/Composing/CollectionBuildersTests.cs b/src/Umbraco.Tests/Composing/CollectionBuildersTests.cs index 1d8390e07e..87971360de 100644 --- a/src/Umbraco.Tests/Composing/CollectionBuildersTests.cs +++ b/src/Umbraco.Tests/Composing/CollectionBuildersTests.cs @@ -442,6 +442,19 @@ namespace Umbraco.Tests.Composing AssertCollection(col, typeof(Resolved2), typeof(Resolved1)); } + [Test] + public void WeightedBuilderSetWeight() + { + var builder = _composition.WithCollectionBuilder() + .Add() + .Add(); + builder.SetWeight(10); + + var factory = _composition.CreateFactory(); + var col = builder.CreateCollection(factory); + AssertCollection(col, typeof(Resolved1), typeof(Resolved2)); + } + #region Assertions private static void AssertCollection(IEnumerable col, params Type[] expected) diff --git a/src/Umbraco.Web/Dashboards/DashboardCollectionBuilder.cs b/src/Umbraco.Web/Dashboards/DashboardCollectionBuilder.cs index 1c05da8906..37697d848c 100644 --- a/src/Umbraco.Web/Dashboards/DashboardCollectionBuilder.cs +++ b/src/Umbraco.Web/Dashboards/DashboardCollectionBuilder.cs @@ -10,22 +10,8 @@ namespace Umbraco.Web.Dashboards { public class DashboardCollectionBuilder : WeightedCollectionBuilderBase { - private Dictionary _customWeights = new Dictionary(); - protected override DashboardCollectionBuilder This => this; - /// - /// Changes the default weight of a dashboard - /// - /// The type of dashboard - /// The new dashboard weight - /// - public DashboardCollectionBuilder SetWeight(int weight) where T : IDashboard - { - _customWeights[typeof(T)] = weight; - return this; - } - protected override IEnumerable CreateItems(IFactory factory) { // get the manifest parser just-in-time - injecting it in the ctor would mean that @@ -47,20 +33,13 @@ namespace Umbraco.Web.Dashboards private int GetWeight(IDashboard dashboard) { - var typeOfDashboard = dashboard.GetType(); - if(_customWeights.ContainsKey(typeOfDashboard)) - { - return _customWeights[typeOfDashboard]; - } - switch (dashboard) { case ManifestDashboard manifestDashboardDefinition: return manifestDashboardDefinition.Weight; default: - var weightAttribute = dashboard.GetType().GetCustomAttribute(false); - return weightAttribute?.Weight ?? DefaultWeight; + return GetWeight(dashboard.GetType()); } } } From 2c665e897e7c0170ac41751968ed94c6b2b600ef Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 11 May 2021 08:36:31 +0200 Subject: [PATCH 080/182] https://github.com/umbraco/Umbraco-CMS/issues/10251 Fixed issue with scheduling that was not possible. --- .../Filters/ContentSaveValidationAttribute.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs index beadb10fc7..d4aafe83ff 100644 --- a/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs @@ -6,13 +6,13 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core; using Umbraco.Cms.Core.Actions; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.ContentEditing; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Web.BackOffice.Authorization; using Umbraco.Cms.Web.Common.Authorization; -using Constants = Umbraco.Cms.Core.Constants; namespace Umbraco.Cms.Web.BackOffice.Filters { @@ -228,7 +228,6 @@ namespace Umbraco.Cms.Web.BackOffice.Filters if (!authorizationResult.Succeeded) { - actionContext.Result = new ForbidResult(); return false; } From 3e19824be238c0545280f3bf8d3c4dd253d00790 Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Tue, 11 May 2021 11:29:40 +0200 Subject: [PATCH 081/182] Creates a method available in V8 allowing validation of a user's credentials without actually logging them in. --- .../Security/IUmbracoUserManager.cs | 8 ++ .../Security/MemberUserStore.cs | 15 +- .../Security/UmbracoUserManager.cs | 25 +++- .../Security/UmbracoUserStore.cs | 3 +- .../Security/MemberManagerTests.cs | 130 +++++++++++++++--- .../Security/BackofficeSecurity.cs | 15 +- .../Security/MemberManager.cs | 9 +- 7 files changed, 155 insertions(+), 50 deletions(-) diff --git a/src/Umbraco.Infrastructure/Security/IUmbracoUserManager.cs b/src/Umbraco.Infrastructure/Security/IUmbracoUserManager.cs index 6acf52c3cb..ec364eb850 100644 --- a/src/Umbraco.Infrastructure/Security/IUmbracoUserManager.cs +++ b/src/Umbraco.Infrastructure/Security/IUmbracoUserManager.cs @@ -375,5 +375,13 @@ namespace Umbraco.Cms.Core.Security /// A user can only support a phone number if the BackOfficeUserStore is replaced with another that implements IUserPhoneNumberStore /// Task GetPhoneNumberAsync(TUser user); + + /// + /// Validates that a user's credentials are correct without actually logging them in. + /// + /// The user name. + /// The password. + /// True if the credentials are valid. + Task ValidateCredentialsAsync(string username, string password); } } diff --git a/src/Umbraco.Infrastructure/Security/MemberUserStore.cs b/src/Umbraco.Infrastructure/Security/MemberUserStore.cs index 59f6686f68..fe12a349f8 100644 --- a/src/Umbraco.Infrastructure/Security/MemberUserStore.cs +++ b/src/Umbraco.Infrastructure/Security/MemberUserStore.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Data; using System.Linq; -using System.Security.Claims; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; @@ -17,13 +15,12 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Core.Security { - /// /// A custom user store that uses Umbraco member data /// public class MemberUserStore : UmbracoUserStore, IMemberUserStore { - private const string genericIdentityErrorCode = "IdentityErrorUserStore"; + private const string GenericIdentityErrorCode = "IdentityErrorUserStore"; private readonly IMemberService _memberService; private readonly IUmbracoMapper _mapper; private readonly IScopeProvider _scopeProvider; @@ -103,7 +100,7 @@ namespace Umbraco.Cms.Core.Security } catch (Exception ex) { - return Task.FromResult(IdentityResult.Failed(new IdentityError { Code = genericIdentityErrorCode, Description = ex.Message })); + return Task.FromResult(IdentityResult.Failed(new IdentityError { Code = GenericIdentityErrorCode, Description = ex.Message })); } } @@ -134,7 +131,7 @@ namespace Umbraco.Cms.Core.Security // we have to remember whether Logins property is dirty, since the UpdateMemberProperties will reset it. var isLoginsPropertyDirty = user.IsPropertyDirty(nameof(MemberIdentityUser.Logins)); - var memberChangeType = UpdateMemberProperties(found, user); + MemberDataChangeType memberChangeType = UpdateMemberProperties(found, user); if (memberChangeType == MemberDataChangeType.FullSave) { _memberService.Save(found); @@ -163,7 +160,7 @@ namespace Umbraco.Cms.Core.Security } catch (Exception ex) { - return Task.FromResult(IdentityResult.Failed(new IdentityError { Code = genericIdentityErrorCode, Description = ex.Message })); + return Task.FromResult(IdentityResult.Failed(new IdentityError { Code = GenericIdentityErrorCode, Description = ex.Message })); } } @@ -192,7 +189,7 @@ namespace Umbraco.Cms.Core.Security } catch (Exception ex) { - return Task.FromResult(IdentityResult.Failed(new IdentityError { Code = genericIdentityErrorCode, Description = ex.Message })); + return Task.FromResult(IdentityResult.Failed(new IdentityError { Code = GenericIdentityErrorCode, Description = ex.Message })); } } @@ -505,7 +502,7 @@ namespace Umbraco.Cms.Core.Security private MemberDataChangeType UpdateMemberProperties(IMember member, MemberIdentityUser identityUser) { - var changeType = MemberDataChangeType.None; + MemberDataChangeType changeType = MemberDataChangeType.None; // don't assign anything if nothing has changed as this will trigger the track changes of the model if (identityUser.IsPropertyDirty(nameof(MemberIdentityUser.LastLoginDateUtc)) diff --git a/src/Umbraco.Infrastructure/Security/UmbracoUserManager.cs b/src/Umbraco.Infrastructure/Security/UmbracoUserManager.cs index 7f77b9d8c6..bf198af1c3 100644 --- a/src/Umbraco.Infrastructure/Security/UmbracoUserManager.cs +++ b/src/Umbraco.Infrastructure/Security/UmbracoUserManager.cs @@ -75,11 +75,9 @@ namespace Umbraco.Cms.Core.Security /// True if the session is valid, else false public virtual async Task ValidateSessionIdAsync(string userId, string sessionId) { - var userSessionStore = Store as IUserSessionStore; - // if this is not set, for backwards compat (which would be super rare), we'll just approve it // TODO: This should be removed after members supports this - if (userSessionStore == null) + if (Store is not IUserSessionStore userSessionStore) { return true; } @@ -221,8 +219,7 @@ namespace Umbraco.Cms.Core.Security throw new ArgumentNullException(nameof(user)); } - var lockoutStore = Store as IUserLockoutStore; - if (lockoutStore == null) + if (Store is not IUserLockoutStore lockoutStore) { throw new NotSupportedException("The current user store does not implement " + typeof(IUserLockoutStore<>)); } @@ -241,5 +238,23 @@ namespace Umbraco.Cms.Core.Security return result; } + /// + public async Task ValidateCredentialsAsync(string username, string password) + { + TUser user = await FindByNameAsync(username); + if (user == null) + { + return false; + } + + if (Store is not IUserPasswordStore userPasswordStore) + { + throw new NotSupportedException("The current user store does not implement " + typeof(IUserPasswordStore<>)); + } + + var hash = await userPasswordStore.GetPasswordHashAsync(user, new CancellationToken()); + + return await VerifyPasswordAsync(userPasswordStore, user, password) == PasswordVerificationResult.Success; + } } } diff --git a/src/Umbraco.Infrastructure/Security/UmbracoUserStore.cs b/src/Umbraco.Infrastructure/Security/UmbracoUserStore.cs index 1db823cc78..3884ee31a1 100644 --- a/src/Umbraco.Infrastructure/Security/UmbracoUserStore.cs +++ b/src/Umbraco.Infrastructure/Security/UmbracoUserStore.cs @@ -10,7 +10,8 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Core.Security { - public abstract class UmbracoUserStore : UserStoreBase, IdentityUserRole, IdentityUserLogin, IdentityUserToken, IdentityRoleClaim> + public abstract class UmbracoUserStore + : UserStoreBase, IdentityUserRole, IdentityUserLogin, IdentityUserToken, IdentityRoleClaim> where TUser : UmbracoIdentityUser where TRole : IdentityRole { diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberManagerTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberManagerTests.cs index a4aaa9311c..c8f90050e2 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberManagerTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberManagerTests.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; @@ -9,6 +8,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Moq; using NUnit.Framework; +using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Mapping; using Umbraco.Cms.Core.Models; @@ -35,11 +35,21 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security public MemberManager CreateSut() { - var scopeProvider = new Mock().Object; + IScopeProvider scopeProvider = new Mock().Object; _mockMemberService = new Mock(); + + var mapDefinitions = new List() + { + new IdentityMapDefinition( + Mock.Of(), + Mock.Of(), + Options.Create(new GlobalSettings()), + AppCaches.Disabled), + }; + _fakeMemberStore = new MemberUserStore( _mockMemberService.Object, - new UmbracoMapper(new MapDefinitionCollection(new List()), scopeProvider), + new UmbracoMapper(new MapDefinitionCollection(mapDefinitions), scopeProvider), scopeProvider, new IdentityErrorDescriber(), Mock.Of()); @@ -131,25 +141,11 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security { //arrange MemberManager sut = CreateSut(); - var fakeUser = new MemberIdentityUser(777) - { - UserName = "testUser", - Email = "test@test.com", - Name = "Test", - MemberTypeAlias = "Anything", - PasswordConfig = "testConfig" - }; + MemberIdentityUser fakeUser = CreateValidUser(); - var builder = new MemberTypeBuilder(); - MemberType memberType = builder.BuildSimpleMemberType(); + IMember fakeMember = CreateMember(fakeUser); - IMember fakeMember = new Member(memberType) - { - Id = 777 - }; - - _mockMemberService.Setup(x => x.CreateMember(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(fakeMember); - _mockMemberService.Setup(x => x.Save(fakeMember, false)); + MockMemberServiceForCreateMember(fakeMember); //act IdentityResult identityResult = await sut.CreateAsync(fakeUser); @@ -158,5 +154,99 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security Assert.IsTrue(identityResult.Succeeded); Assert.IsTrue(!identityResult.Errors.Any()); } + + [Test] + public async Task GivenAUserExists_AndTheCorrectCredentialsAreProvided_ThenACheckOfCredentialsShouldSucceed() + { + //arrange + var password = "password"; + MemberManager sut = CreateSut(); + + MemberIdentityUser fakeUser = CreateValidUser(); + + IMember fakeMember = CreateMember(fakeUser); + + MockMemberServiceForCreateMember(fakeMember); + + _mockMemberService.Setup(x => x.GetByUsername(It.Is(y => y == fakeUser.UserName))).Returns(fakeMember); + + _mockPasswordHasher.Setup(x => x.VerifyHashedPassword(It.IsAny(), It.IsAny(), It.IsAny())).Returns(PasswordVerificationResult.Success); + + //act + await sut.CreateAsync(fakeUser); + var result = await sut.ValidateCredentialsAsync(fakeUser.UserName, password); + + //assert + Assert.IsTrue(result); + } + + [Test] + public async Task GivenAUserExists_AndIncorrectCredentialsAreProvided_ThenACheckOfCredentialsShouldFail() + { + //arrange + var password = "password"; + MemberManager sut = CreateSut(); + + MemberIdentityUser fakeUser = CreateValidUser(); + + IMember fakeMember = CreateMember(fakeUser); + + MockMemberServiceForCreateMember(fakeMember); + + _mockMemberService.Setup(x => x.GetByUsername(It.Is(y => y == fakeUser.UserName))).Returns(fakeMember); + + _mockPasswordHasher.Setup(x => x.VerifyHashedPassword(It.IsAny(), It.IsAny(), It.IsAny())).Returns(PasswordVerificationResult.Failed); + + //act + await sut.CreateAsync(fakeUser); + var result = await sut.ValidateCredentialsAsync(fakeUser.UserName, password); + + //assert + Assert.IsFalse(result); + } + + [Test] + public async Task GivenAUserDoesExists_AndCredentialsAreProvided_ThenACheckOfCredentialsShouldFail() + { + //arrange + var password = "password"; + MemberManager sut = CreateSut(); + + _mockMemberService.Setup(x => x.GetByUsername(It.Is(y => y == "testUser"))).Returns((IMember)null); + + //act + var result = await sut.ValidateCredentialsAsync("testUser", password); + + //assert + Assert.IsFalse(result); + } + + private static MemberIdentityUser CreateValidUser() => + new MemberIdentityUser(777) + { + UserName = "testUser", + Email = "test@test.com", + Name = "Test", + MemberTypeAlias = "Anything", + PasswordConfig = "testConfig", + PasswordHash = "hashedPassword" + }; + + private static IMember CreateMember(MemberIdentityUser fakeUser) + { + var builder = new MemberTypeBuilder(); + MemberType memberType = builder.BuildSimpleMemberType(); + return new Member(memberType) + { + Id = 777, + Username = fakeUser.UserName, + }; + } + + private void MockMemberServiceForCreateMember(IMember fakeMember) + { + _mockMemberService.Setup(x => x.CreateMember(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(fakeMember); + _mockMemberService.Setup(x => x.Save(fakeMember, false)); + } } } diff --git a/src/Umbraco.Web.Common/Security/BackofficeSecurity.cs b/src/Umbraco.Web.Common/Security/BackofficeSecurity.cs index ac45c932da..9ea0728ce5 100644 --- a/src/Umbraco.Web.Common/Security/BackofficeSecurity.cs +++ b/src/Umbraco.Web.Common/Security/BackofficeSecurity.cs @@ -1,3 +1,4 @@ +using System.Security.Claims; using Microsoft.AspNetCore.Http; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models.Membership; @@ -23,8 +24,6 @@ namespace Umbraco.Cms.Web.Common.Security _httpContextAccessor = httpContextAccessor; } - - /// public IUser CurrentUser { @@ -39,7 +38,7 @@ namespace Umbraco.Cms.Web.Common.Security //Check again if (_currentUser == null) { - var id = GetUserId(); + Attempt id = GetUserId(); _currentUser = id ? _userService.GetUserById(id.Result) : null; } } @@ -52,22 +51,18 @@ namespace Umbraco.Cms.Web.Common.Security /// public Attempt GetUserId() { - var identity = _httpContextAccessor.HttpContext?.GetCurrentIdentity(); + ClaimsIdentity identity = _httpContextAccessor.HttpContext?.GetCurrentIdentity(); return identity == null ? Attempt.Fail() : Attempt.Succeed(identity.GetId()); } /// public bool IsAuthenticated() { - var httpContext = _httpContextAccessor.HttpContext; + HttpContext httpContext = _httpContextAccessor.HttpContext; return httpContext?.User != null && httpContext.User.Identity.IsAuthenticated && httpContext.GetCurrentIdentity() != null; } /// - public bool UserHasSectionAccess(string section, IUser user) - { - return user.HasSectionAccess(section); - } - + public bool UserHasSectionAccess(string section, IUser user) => user.HasSectionAccess(section); } } diff --git a/src/Umbraco.Web.Common/Security/MemberManager.cs b/src/Umbraco.Web.Common/Security/MemberManager.cs index 9bc8b284c7..f7d0c8e43e 100644 --- a/src/Umbraco.Web.Common/Security/MemberManager.cs +++ b/src/Umbraco.Web.Common/Security/MemberManager.cs @@ -1,18 +1,17 @@ using System; -using System.Linq; using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Umbraco.Extensions; using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.Net; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; -using System.Threading.Tasks; -using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Extensions; namespace Umbraco.Cms.Web.Common.Security { From 9254e7883b72206b889d318b4edcde8e0261486a Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 11 May 2021 12:46:03 +0200 Subject: [PATCH 082/182] Fixed casing issue.. --- .../mediapicker3/umbMediaPicker3PropertyEditor.component.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umbMediaPicker3PropertyEditor.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umbMediaPicker3PropertyEditor.component.js index 675381d46e..3762253d30 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umbMediaPicker3PropertyEditor.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umbMediaPicker3PropertyEditor.component.js @@ -13,7 +13,7 @@ angular .module("umbraco") .component("umbMediaPicker3PropertyEditor", { - templateUrl: "views/propertyeditors/MediaPicker3/umb-media-picker3-property-editor.html", + templateUrl: "views/propertyeditors/mediapicker3/umb-media-picker3-property-editor.html", controller: MediaPicker3Controller, controllerAs: "vm", bindings: { From 2bf86acf3847c93c320b98188a3643a21de1a9a3 Mon Sep 17 00:00:00 2001 From: Mole Date: Tue, 11 May 2021 14:33:49 +0200 Subject: [PATCH 083/182] V9: Place notifications in the same namespace (#10231) * Gather all notifications in Umbraco.Cms.Core.Notifications * Rename notifications to match convention * Move and rename missed notifications * Move the three remaining public notification into Umbraco.Cms.Core.Notifications --- .../Cache/ApplicationCacheRefresher.cs | 1 + src/Umbraco.Core/Cache/CacheRefresherBase.cs | 1 + .../Cache/CacheRefresherNotificationFactory.cs | 1 + src/Umbraco.Core/Cache/ContentCacheRefresher.cs | 1 + .../Cache/ContentTypeCacheRefresher.cs | 1 + src/Umbraco.Core/Cache/DataTypeCacheRefresher.cs | 1 + src/Umbraco.Core/Cache/DictionaryCacheRefresher.cs | 1 + src/Umbraco.Core/Cache/DomainCacheRefresher.cs | 1 + .../Cache/ICacheRefresherNotificationFactory.cs | 3 ++- src/Umbraco.Core/Cache/JsonCacheRefresherBase.cs | 1 + src/Umbraco.Core/Cache/LanguageCacheRefresher.cs | 1 + src/Umbraco.Core/Cache/MacroCacheRefresher.cs | 1 + src/Umbraco.Core/Cache/MediaCacheRefresher.cs | 1 + src/Umbraco.Core/Cache/MemberCacheRefresher.cs | 1 + .../Cache/MemberGroupCacheRefresher.cs | 1 + .../Cache/PayloadCacheRefresherBase.cs | 1 + .../Cache/PublicAccessCacheRefresher.cs | 1 + .../Cache/RelationTypeCacheRefresher.cs | 1 + src/Umbraco.Core/Cache/TemplateCacheRefresher.cs | 1 + src/Umbraco.Core/Cache/UserCacheRefresher.cs | 1 + src/Umbraco.Core/Cache/UserGroupCacheRefresher.cs | 1 + .../DependencyInjection/UmbracoBuilder.Events.cs | 1 + .../DependencyInjection/UmbracoBuilder.cs | 6 +++--- .../Events/EventAggregator.Notifications.cs | 1 + src/Umbraco.Core/Events/EventAggregator.cs | 1 + src/Umbraco.Core/Events/IEventAggregator.cs | 1 + src/Umbraco.Core/Events/INotificationHandler.cs | 1 + .../Events/IScopedNotificationPublisher.cs | 1 + .../Events/RelateOnCopyNotificationHandler.cs | 2 +- .../Events/ScopedNotificationPublisher.cs | 1 + .../Events/UmbracoApplicationStopping.cs | 4 ---- .../Events/UserNotificationsHandler.cs | 2 +- .../Handlers/AuditNotificationsHandler.cs | 2 +- src/Umbraco.Core/Handlers/PublicAccessHandler.cs | 2 +- .../ApplicationCacheRefresherNotification.cs | 2 +- .../AssignedMemberRolesNotification.cs | 2 +- .../AssignedUserGroupPermissionsNotification.cs | 2 +- .../CacheRefresherNotification.cs | 4 +--- .../CancelableEnumerableObjectNotification.cs | 3 ++- .../CancelableNotification.cs | 4 +++- .../CancelableObjectNotification.cs | 4 +++- .../ContentCacheRefresherNotification.cs | 2 +- .../Notifications/ContentCopiedNotification.cs | 2 +- .../Notifications/ContentCopyingNotification.cs | 2 +- .../ContentDeletedBlueprintNotification.cs | 2 +- .../Notifications/ContentDeletedNotification.cs | 2 +- .../ContentDeletedVersionsNotification.cs | 2 +- .../Notifications/ContentDeletingNotification.cs | 2 +- .../ContentDeletingVersionsNotification.cs | 2 +- .../ContentEmptiedRecycleBinNotification.cs | 2 +- .../ContentEmptyingRecycleBinNotification.cs | 2 +- .../Notifications/ContentMovedNotification.cs | 2 +- .../ContentMovedToRecycleBinNotification.cs | 2 +- .../Notifications/ContentMovingNotification.cs | 2 +- .../ContentMovingToRecycleBinNotification.cs | 2 +- .../Notifications/ContentNotificationExtensions.cs | 3 +-- .../Notifications/ContentPublishedNotification.cs | 2 +- .../Notifications/ContentPublishingNotification.cs | 2 +- .../ContentRefreshNotification.cs | 3 ++- .../Notifications/ContentRolledBackNotification.cs | 2 +- .../ContentRollingBackNotification.cs | 2 +- .../ContentSavedBlueprintNotification.cs | 2 +- .../Notifications/ContentSavedNotification.cs | 2 +- .../Notifications/ContentSavingNotification.cs | 2 +- .../ContentSendingToPublishNotification.cs | 2 +- .../ContentSentToPublishNotification.cs | 2 +- .../Notifications/ContentSortedNotification.cs | 2 +- .../Notifications/ContentSortingNotification.cs | 2 +- .../Notifications/ContentTreeChangeNotification.cs | 2 +- .../ContentTypeCacheRefresherNotification.cs | 2 +- .../ContentTypeChangeNotification.cs | 3 ++- .../ContentTypeChangedNotification.cs | 3 ++- .../ContentTypeDeletedNotification.cs | 3 ++- .../ContentTypeDeletingNotification.cs | 3 ++- .../ContentTypeMovedNotification.cs | 3 ++- .../ContentTypeMovingNotification.cs | 3 ++- .../ContentTypeRefreshNotification.cs | 3 ++- .../ContentTypeRefreshedNotification.cs | 3 ++- .../ContentTypeSavedNotification.cs | 3 ++- .../ContentTypeSavingNotification.cs | 3 ++- .../ContentUnpublishedNotification.cs | 2 +- .../ContentUnpublishingNotification.cs | 2 +- .../CopiedNotification.cs | 4 +++- .../CopyingNotification.cs | 4 +++- .../CreatedNotification.cs | 4 +++- .../CreatingNotification.cs | 4 +++- .../CreatingRequestNotification.cs | 3 +-- .../DataTypeCacheRefresherNotification.cs | 2 +- .../DataTypeDeletedNotification.cs | 3 ++- .../DataTypeDeletingNotification.cs | 3 ++- .../DataTypeMovedNotification.cs | 3 ++- .../DataTypeMovingNotification.cs | 3 ++- .../DataTypeSavedNotification.cs | 3 ++- .../DataTypeSavingNotification.cs | 3 ++- .../DeletedNotification.cs | 3 ++- .../Notifications/DeletedVersionsNotification.cs | 2 +- .../DeletedVersionsNotificationBase.cs | 2 +- .../DeletingNotification.cs | 6 ++++-- .../Notifications/DeletingVersionsNotification.cs | 2 +- .../DictionaryCacheRefresherNotification.cs | 2 +- .../DictionaryItemDeletedNotification.cs | 2 +- .../DictionaryItemDeletingNotification.cs | 2 +- .../DictionaryItemSavedNotification.cs | 2 +- .../DictionaryItemSavingNotification.cs | 2 +- .../DomainCacheRefresherNotification.cs | 2 +- .../Notifications/DomainDeletedNotification.cs | 2 +- .../Notifications/DomainDeletingNotification.cs | 2 +- .../Notifications/DomainSavedNotification.cs | 2 +- .../Notifications/DomainSavingNotification.cs | 2 +- .../Notifications/EmptiedRecycleBinNotification.cs | 2 +- .../EmptyingRecycleBinNotification.cs | 2 +- .../EntityContainerDeletedNotification.cs | 3 ++- .../EntityContainerDeletingNotification.cs | 3 ++- .../EntityContainerRenamedNotification.cs | 3 ++- .../EntityContainerRenamingNotification.cs | 3 ++- .../EntityContainerSavedNotification.cs | 3 ++- .../EntityContainerSavingNotification.cs | 3 ++- .../EntityRefreshNotification.cs | 3 ++- .../EnumerableObjectNotification.cs | 3 ++- .../Notifications/ExportedMemberNotification.cs | 3 +-- .../ICancelableNotification.cs | 2 +- .../{Events => Notifications}/INotification.cs | 2 +- .../IStatefulNotification.cs | 2 +- .../ImportedPackageNotification.cs | 2 +- .../ImportingPackageNotification.cs | 2 +- .../LanguageCacheRefresherNotification.cs | 2 +- .../Notifications/LanguageDeletedNotification.cs | 2 +- .../Notifications/LanguageDeletingNotification.cs | 2 +- .../Notifications/LanguageSavedNotification.cs | 2 +- .../Notifications/LanguageSavingNotification.cs | 2 +- .../MacroCacheRefresherNotification.cs | 2 +- .../Notifications/MacroDeletedNotification.cs | 2 +- .../Notifications/MacroDeletingNotification.cs | 2 +- .../Notifications/MacroSavedNotification.cs | 2 +- .../Notifications/MacroSavingNotification.cs | 2 +- .../MediaCacheRefresherNotification.cs | 2 +- .../Notifications/MediaDeletedNotification.cs | 2 +- .../MediaDeletedVersionsNotification.cs | 2 +- .../Notifications/MediaDeletingNotification.cs | 2 +- .../MediaDeletingVersionsNotification.cs | 2 +- .../MediaEmptiedRecycleBinNotification.cs | 2 +- .../MediaEmptyingRecycleBinNotification.cs | 2 +- .../Notifications/MediaMovedNotification.cs | 2 +- .../MediaMovedToRecycleBinNotification.cs | 2 +- .../Notifications/MediaMovingNotification.cs | 2 +- .../MediaMovingToRecycleBinNotification.cs | 2 +- .../MediaRefreshNotification.cs | 3 ++- .../Notifications/MediaSavedNotification.cs | 2 +- .../Notifications/MediaSavingNotification.cs | 2 +- .../Notifications/MediaTreeChangeNotification.cs | 2 +- .../MediaTypeChangedNotification.cs | 3 ++- .../MediaTypeDeletedNotification.cs | 3 ++- .../MediaTypeDeletingNotification.cs | 3 ++- .../MediaTypeMovedNotification.cs | 3 ++- .../MediaTypeMovingNotification.cs | 3 ++- .../MediaTypeRefreshedNotification.cs | 3 ++- .../MediaTypeSavedNotification.cs | 3 ++- .../MediaTypeSavingNotification.cs | 3 ++- .../MemberCacheRefresherNotification.cs | 2 +- .../Notifications/MemberDeletedNotification.cs | 2 +- .../Notifications/MemberDeletingNotification.cs | 2 +- .../MemberGroupCacheRefresherNotification.cs | 2 +- .../MemberGroupDeletedNotification.cs | 2 +- .../MemberGroupDeletingNotification.cs | 2 +- .../Notifications/MemberGroupSavedNotification.cs | 2 +- .../Notifications/MemberGroupSavingNotification.cs | 2 +- .../MemberRefreshNotification.cs | 3 ++- .../Notifications/MemberRolesNotification.cs | 4 +--- .../Notifications/MemberSavedNotification.cs | 2 +- .../Notifications/MemberSavingNotification.cs | 2 +- .../MemberTypeChangedNotification.cs | 3 ++- .../MemberTypeDeletedNotification.cs | 3 ++- .../MemberTypeDeletingNotification.cs | 3 ++- .../MemberTypeMovedNotification.cs | 3 ++- .../MemberTypeMovingNotification.cs | 3 ++- .../MemberTypeRefreshedNotification.cs | 3 ++- .../MemberTypeSavedNotification.cs | 3 ++- .../MemberTypeSavingNotification.cs | 3 ++- .../ModelBindingErrorNotification.cs} | 9 ++++----- .../{Events => Notifications}/MovedNotification.cs | 3 ++- .../Notifications/MovedToRecycleBinNotification.cs | 2 +- .../MovingNotification.cs | 3 ++- .../MovingToRecycleBinNotification.cs | 2 +- .../NotificationExtensions.cs | 2 +- .../ObjectNotification.cs | 4 +++- .../PartialViewCreatedNotification.cs | 2 +- .../PartialViewCreatingNotification.cs | 2 +- .../PartialViewDeletedNotification.cs | 2 +- .../PartialViewDeletingNotification.cs | 2 +- .../Notifications/PartialViewSavedNotification.cs | 2 +- .../Notifications/PartialViewSavingNotification.cs | 2 +- .../PublicAccessCacheRefresherNotification.cs | 2 +- .../PublicAccessEntryDeletedNotification.cs | 2 +- .../PublicAccessEntryDeletingNotification.cs | 2 +- .../PublicAccessEntrySavedNotification.cs | 2 +- .../PublicAccessEntrySavingNotification.cs | 2 +- .../Notifications/RelationDeletedNotification.cs | 2 +- .../Notifications/RelationDeletingNotification.cs | 2 +- .../Notifications/RelationSavedNotification.cs | 2 +- .../Notifications/RelationSavingNotification.cs | 2 +- .../RelationTypeCacheRefresherNotification.cs | 2 +- .../RelationTypeDeletedNotification.cs | 2 +- .../RelationTypeDeletingNotification.cs | 2 +- .../Notifications/RelationTypeSavedNotification.cs | 2 +- .../RelationTypeSavingNotification.cs | 2 +- .../RemovedMemberRolesNotification.cs | 2 +- .../RenamedNotification.cs | 3 ++- .../RenamingNotification.cs | 3 ++- .../RolledBackNotification.cs | 4 +++- .../RollingBackNotification.cs | 4 +++- .../RoutingRequestNotification.cs | 4 ++-- .../{Events => Notifications}/SavedNotification.cs | 3 ++- .../SavingNotification.cs | 3 ++- .../ScopedEntityRemoveNotification.cs | 3 ++- .../Notifications/ScriptDeletedNotification.cs | 2 +- .../Notifications/ScriptDeletingNotification.cs | 2 +- .../Notifications/ScriptSavedNotification.cs | 2 +- .../Notifications/ScriptSavingNotification.cs | 2 +- .../Notifications}/SendingContentNotification.cs | 3 +-- .../SendingDashboardsNotification.cs | 3 +-- .../Notifications}/SendingMediaNotification.cs | 3 +-- .../Notifications}/SendingMemberNotification.cs | 3 +-- .../Notifications}/SendingUserNotification.cs | 3 +-- .../ServerVariablesParsingNotification.cs} | 9 ++++----- .../SortedNotification.cs | 3 ++- .../SortingNotification.cs | 3 ++- .../StatefulNotification.cs | 2 +- .../Notifications/StylesheetDeletedNotification.cs | 2 +- .../StylesheetDeletingNotification.cs | 2 +- .../Notifications/StylesheetSavedNotification.cs | 2 +- .../Notifications/StylesheetSavingNotification.cs | 2 +- .../TemplateCacheRefresherNotification.cs | 2 +- .../Notifications/TemplateDeletedNotification.cs | 2 +- .../Notifications/TemplateDeletingNotification.cs | 2 +- .../Notifications/TemplateSavedNotification.cs | 2 +- .../Notifications/TemplateSavingNotification.cs | 2 +- .../TreeChangeNotification.cs | 3 ++- .../UmbracoApplicationStartingNotification.cs} | 8 ++++---- .../UmbracoApplicationStoppingNotification.cs | 4 ++++ .../UmbracoRequestBeginNotification.cs} | 8 ++++---- .../UmbracoRequestEndNotification.cs} | 8 ++++---- .../UnattendedInstallNotification.cs | 2 +- .../UninstallPackageNotification.cs | 2 +- .../UserCacheRefresherNotification.cs | 2 +- .../Notifications/UserDeletedNotification.cs | 2 +- .../Notifications/UserDeletingNotification.cs | 2 +- .../UserForgotPasswordChangedNotification.cs | 2 +- .../UserForgotPasswordRequestedNotification.cs | 2 +- .../UserGroupCacheRefresherNotification.cs | 2 +- .../Notifications/UserGroupDeletedNotification.cs | 2 +- .../Notifications/UserGroupDeletingNotification.cs | 2 +- .../Notifications/UserGroupSavedNotification.cs | 2 +- .../Notifications/UserGroupSavingNotification.cs | 2 +- .../UserGroupWithUsersSavedNotification.cs | 2 +- .../UserGroupWithUsersSavingNotification.cs | 2 +- .../Notifications}/UserLockedNotification.cs | 2 +- .../Notifications}/UserLoginFailedNotification.cs | 2 +- .../UserLoginRequiresVerificationNotification.cs | 2 +- .../Notifications}/UserLoginSuccessNotification.cs | 2 +- .../UserLogoutSuccessNotification.cs | 2 +- .../Notifications}/UserNotification.cs | 3 +-- .../UserPasswordChangedNotification.cs | 2 +- .../UserPasswordResetNotification.cs | 2 +- .../UserResetAccessFailedCountNotification.cs | 2 +- .../Notifications/UserSavedNotification.cs | 2 +- .../Notifications/UserSavingNotification.cs | 2 +- .../Notifications}/UserUnlockedNotification.cs | 2 +- src/Umbraco.Core/Routing/PublishedRouter.cs | 1 + ...AppPluginsManifestWatcherNotificationHandler.cs | 5 +++-- .../Runtime/EssentialDirectoryCreator.cs | 5 +++-- .../DatabaseServerMessengerNotificationHandler.cs | 7 ++++--- .../Cache/DistributedCacheBinder_Handlers.cs | 2 +- .../Compose/NotificationsComposer.cs | 2 +- .../UmbracoBuilder.DistributedCache.cs | 5 +++-- .../DependencyInjection/UmbracoBuilder.Examine.cs | 3 ++- .../Events/RelateOnTrashNotificationHandler.cs | 2 +- .../Migrations/Install/DatabaseSchemaCreator.cs | 5 +++-- .../DatabaseSchemaCreatedNotification.cs | 3 ++- .../DatabaseSchemaCreatingNotification.cs | 3 ++- .../ModelsBuilder/LiveModelsProvider.cs | 11 ++++++----- .../ModelsBuilder/OutOfDateModelsStatus.cs | 1 + .../Implement/ContentRepositoryBase.cs | 1 + .../Repositories/Implement/DocumentRepository.cs | 1 + .../Repositories/Implement/MediaRepository.cs | 1 + .../Implement/MemberGroupRepository.cs | 4 ++-- .../Repositories/Implement/MemberRepository.cs | 1 + ...plexPropertyEditorContentNotificationHandler.cs | 2 +- .../PropertyEditors/FileUploadPropertyEditor.cs | 2 +- .../PropertyEditors/ImageCropperPropertyEditor.cs | 2 +- .../Routing/RedirectTrackingHandler.cs | 2 +- src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs | 5 +++-- src/Umbraco.Infrastructure/RuntimeState.cs | 1 + .../Search/ExamineNotificationHandler.cs | 5 +++-- .../Services/Implement/ContentService.cs | 2 +- .../Services/Implement/ContentTypeService.cs | 1 + ...entTypeServiceBaseOfTRepositoryTItemTService.cs | 1 + .../Services/Implement/DataTypeService.cs | 3 ++- .../Services/Implement/DomainService.cs | 2 +- .../Services/Implement/FileService.cs | 2 +- .../Services/Implement/LocalizationService.cs | 2 +- .../Services/Implement/MacroService.cs | 2 +- .../Services/Implement/MediaService.cs | 2 +- .../Services/Implement/MediaTypeService.cs | 1 + .../Services/Implement/MemberGroupService.cs | 2 +- .../Services/Implement/MemberService.cs | 2 +- .../Services/Implement/MemberTypeService.cs | 1 + .../Services/Implement/PackagingService.cs | 1 + .../Services/Implement/PublicAccessService.cs | 2 +- .../Services/Implement/RelationService.cs | 2 +- .../Services/Implement/UserService.cs | 2 +- .../WebAssets/ServerVariablesParser.cs | 3 ++- .../Compose/NotificationsComposer.cs | 2 +- .../PublishedSnapshotServiceEventHandler.cs | 2 +- .../Scoping/ScopedRepositoryTests.cs | 2 +- .../Services/ContentEventsTests.cs | 2 +- .../Services/ContentServiceNotificationTests.cs | 2 +- .../Services/ContentServiceTests.cs | 2 +- .../Services/ContentTypeServiceTests.cs | 2 +- .../Services/MediaTypeServiceTests.cs | 2 +- .../Umbraco.Core/Events/EventAggregatorTests.cs | 1 + .../LegacyXmlPublishedCache/XmlStore.cs | 2 +- src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs | 2 +- src/Umbraco.Tests/Scoping/ScopedXmlTests.cs | 2 +- .../UmbracoBuilder.BackOfficeAuth.cs | 1 + .../UmbracoBuilderExtensions.cs | 1 + .../Filters/OutgoingEditorModelEventAttribute.cs | 1 + .../DisableModelsBuilderNotificationHandler.cs | 7 ++++--- .../Security/BackOfficeUserManagerAuditer.cs | 1 + .../SignalR/PreviewHubUpdater.cs | 1 + .../Trees/MenuRenderingNotification.cs | 3 +-- .../Trees/RootNodeRenderingNotification.cs | 3 +-- .../Trees/TreeControllerBase.cs | 1 + .../Trees/TreeNodesRenderingNotification.cs | 3 +-- .../AspNetCore/AspNetCoreRequestAccessor.cs | 5 +++-- .../Cache/HttpContextRequestAppCache.cs | 3 ++- .../UmbracoBuilderExtensions.cs | 5 +++-- .../CreateUnattendedUserNotificationHandler.cs | 1 + .../Middleware/UmbracoRequestMiddleware.cs | 5 +++-- .../ModelBinders/ContentModelBinder.cs | 3 ++- .../UmbracoBuilderDependencyInjectionExtensions.cs | 10 +++++----- .../ModelsBuilderNotificationHandler.cs | 14 +++++++------- .../Profiler/InitializeWebProfiling.cs | 5 +++-- .../Security/BackOfficeUserManager.cs | 1 + 343 files changed, 489 insertions(+), 363 deletions(-) delete mode 100644 src/Umbraco.Core/Events/UmbracoApplicationStopping.cs rename src/Umbraco.Core/{Cache => Notifications}/ApplicationCacheRefresherNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/AssignedMemberRolesNotification.cs (81%) rename src/Umbraco.Core/{Services => }/Notifications/AssignedUserGroupPermissionsNotification.cs (90%) rename src/Umbraco.Core/{Cache => Notifications}/CacheRefresherNotification.cs (86%) rename src/Umbraco.Core/{Events => Notifications}/CancelableEnumerableObjectNotification.cs (87%) rename src/Umbraco.Core/{Events => Notifications}/CancelableNotification.cs (87%) rename src/Umbraco.Core/{Events => Notifications}/CancelableObjectNotification.cs (89%) rename src/Umbraco.Core/{Cache => Notifications}/ContentCacheRefresherNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/ContentCopiedNotification.cs (89%) rename src/Umbraco.Core/{Services => }/Notifications/ContentCopyingNotification.cs (89%) rename src/Umbraco.Core/{Services => }/Notifications/ContentDeletedBlueprintNotification.cs (92%) rename src/Umbraco.Core/{Services => }/Notifications/ContentDeletedNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/ContentDeletedVersionsNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/ContentDeletingNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/ContentDeletingVersionsNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/ContentEmptiedRecycleBinNotification.cs (89%) rename src/Umbraco.Core/{Services => }/Notifications/ContentEmptyingRecycleBinNotification.cs (89%) rename src/Umbraco.Core/{Services => }/Notifications/ContentMovedNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/ContentMovedToRecycleBinNotification.cs (92%) rename src/Umbraco.Core/{Services => }/Notifications/ContentMovingNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/ContentMovingToRecycleBinNotification.cs (92%) rename src/Umbraco.Core/{Services => }/Notifications/ContentNotificationExtensions.cs (97%) rename src/Umbraco.Core/{Services => }/Notifications/ContentPublishedNotification.cs (92%) rename src/Umbraco.Core/{Services => }/Notifications/ContentPublishingNotification.cs (92%) rename src/Umbraco.Core/{Events => Notifications}/ContentRefreshNotification.cs (86%) rename src/Umbraco.Core/{Services => }/Notifications/ContentRolledBackNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/ContentRollingBackNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/ContentSavedBlueprintNotification.cs (89%) rename src/Umbraco.Core/{Services => }/Notifications/ContentSavedNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/ContentSavingNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/ContentSendingToPublishNotification.cs (89%) rename src/Umbraco.Core/{Services => }/Notifications/ContentSentToPublishNotification.cs (88%) rename src/Umbraco.Core/{Services => }/Notifications/ContentSortedNotification.cs (88%) rename src/Umbraco.Core/{Services => }/Notifications/ContentSortingNotification.cs (88%) rename src/Umbraco.Core/{Services => }/Notifications/ContentTreeChangeNotification.cs (95%) rename src/Umbraco.Core/{Cache => Notifications}/ContentTypeCacheRefresherNotification.cs (87%) rename src/Umbraco.Core/{Events => Notifications}/ContentTypeChangeNotification.cs (90%) rename src/Umbraco.Core/{Events => Notifications}/ContentTypeChangedNotification.cs (88%) rename src/Umbraco.Core/{Events => Notifications}/ContentTypeDeletedNotification.cs (86%) rename src/Umbraco.Core/{Events => Notifications}/ContentTypeDeletingNotification.cs (86%) rename src/Umbraco.Core/{Events => Notifications}/ContentTypeMovedNotification.cs (86%) rename src/Umbraco.Core/{Events => Notifications}/ContentTypeMovingNotification.cs (86%) rename src/Umbraco.Core/{Events => Notifications}/ContentTypeRefreshNotification.cs (88%) rename src/Umbraco.Core/{Events => Notifications}/ContentTypeRefreshedNotification.cs (91%) rename src/Umbraco.Core/{Events => Notifications}/ContentTypeSavedNotification.cs (86%) rename src/Umbraco.Core/{Events => Notifications}/ContentTypeSavingNotification.cs (86%) rename src/Umbraco.Core/{Services => }/Notifications/ContentUnpublishedNotification.cs (92%) rename src/Umbraco.Core/{Services => }/Notifications/ContentUnpublishingNotification.cs (92%) rename src/Umbraco.Core/{Events => Notifications}/CopiedNotification.cs (88%) rename src/Umbraco.Core/{Events => Notifications}/CopyingNotification.cs (86%) rename src/Umbraco.Core/{Events => Notifications}/CreatedNotification.cs (81%) rename src/Umbraco.Core/{Events => Notifications}/CreatingNotification.cs (82%) rename src/Umbraco.Core/{Routing => Notifications}/CreatingRequestNotification.cs (89%) rename src/Umbraco.Core/{Cache => Notifications}/DataTypeCacheRefresherNotification.cs (87%) rename src/Umbraco.Core/{Events => Notifications}/DataTypeDeletedNotification.cs (78%) rename src/Umbraco.Core/{Events => Notifications}/DataTypeDeletingNotification.cs (78%) rename src/Umbraco.Core/{Events => Notifications}/DataTypeMovedNotification.cs (78%) rename src/Umbraco.Core/{Events => Notifications}/DataTypeMovingNotification.cs (78%) rename src/Umbraco.Core/{Events => Notifications}/DataTypeSavedNotification.cs (85%) rename src/Umbraco.Core/{Events => Notifications}/DataTypeSavingNotification.cs (85%) rename src/Umbraco.Core/{Events => Notifications}/DeletedNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/DeletedVersionsNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/DeletedVersionsNotificationBase.cs (94%) rename src/Umbraco.Core/{Events => Notifications}/DeletingNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/DeletingVersionsNotification.cs (92%) rename src/Umbraco.Core/{Cache => Notifications}/DictionaryCacheRefresherNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/DictionaryItemDeletedNotification.cs (88%) rename src/Umbraco.Core/{Services => }/Notifications/DictionaryItemDeletingNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/DictionaryItemSavedNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/DictionaryItemSavingNotification.cs (91%) rename src/Umbraco.Core/{Cache => Notifications}/DomainCacheRefresherNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/DomainDeletedNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/DomainDeletingNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/DomainSavedNotification.cs (90%) rename src/Umbraco.Core/{Services => }/Notifications/DomainSavingNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/EmptiedRecycleBinNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/EmptyingRecycleBinNotification.cs (92%) rename src/Umbraco.Core/{Events => Notifications}/EntityContainerDeletedNotification.cs (79%) rename src/Umbraco.Core/{Events => Notifications}/EntityContainerDeletingNotification.cs (79%) rename src/Umbraco.Core/{Events => Notifications}/EntityContainerRenamedNotification.cs (79%) rename src/Umbraco.Core/{Events => Notifications}/EntityContainerRenamingNotification.cs (79%) rename src/Umbraco.Core/{Events => Notifications}/EntityContainerSavedNotification.cs (79%) rename src/Umbraco.Core/{Events => Notifications}/EntityContainerSavingNotification.cs (79%) rename src/Umbraco.Core/{Events => Notifications}/EntityRefreshNotification.cs (80%) rename src/Umbraco.Core/{Events => Notifications}/EnumerableObjectNotification.cs (86%) rename src/Umbraco.Core/{Services => }/Notifications/ExportedMemberNotification.cs (83%) rename src/Umbraco.Core/{Events => Notifications}/ICancelableNotification.cs (80%) rename src/Umbraco.Core/{Events => Notifications}/INotification.cs (83%) rename src/Umbraco.Core/{Events => Notifications}/IStatefulNotification.cs (80%) rename src/Umbraco.Core/{Events => Notifications}/ImportedPackageNotification.cs (92%) rename src/Umbraco.Core/{Events => Notifications}/ImportingPackageNotification.cs (92%) rename src/Umbraco.Core/{Cache => Notifications}/LanguageCacheRefresherNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/LanguageDeletedNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/LanguageDeletingNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/LanguageSavedNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/LanguageSavingNotification.cs (91%) rename src/Umbraco.Core/{Cache => Notifications}/MacroCacheRefresherNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/MacroDeletedNotification.cs (84%) rename src/Umbraco.Core/{Services => }/Notifications/MacroDeletingNotification.cs (90%) rename src/Umbraco.Core/{Services => }/Notifications/MacroSavedNotification.cs (89%) rename src/Umbraco.Core/{Services => }/Notifications/MacroSavingNotification.cs (89%) rename src/Umbraco.Core/{Cache => Notifications}/MediaCacheRefresherNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/MediaDeletedNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/MediaDeletedVersionsNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/MediaDeletingNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/MediaDeletingVersionsNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/MediaEmptiedRecycleBinNotification.cs (89%) rename src/Umbraco.Core/{Services => }/Notifications/MediaEmptyingRecycleBinNotification.cs (89%) rename src/Umbraco.Core/{Services => }/Notifications/MediaMovedNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/MediaMovedToRecycleBinNotification.cs (92%) rename src/Umbraco.Core/{Services => }/Notifications/MediaMovingNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/MediaMovingToRecycleBinNotification.cs (92%) rename src/Umbraco.Core/{Events => Notifications}/MediaRefreshNotification.cs (86%) rename src/Umbraco.Core/{Services => }/Notifications/MediaSavedNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/MediaSavingNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/MediaTreeChangeNotification.cs (95%) rename src/Umbraco.Core/{Events => Notifications}/MediaTypeChangedNotification.cs (88%) rename src/Umbraco.Core/{Events => Notifications}/MediaTypeDeletedNotification.cs (85%) rename src/Umbraco.Core/{Events => Notifications}/MediaTypeDeletingNotification.cs (86%) rename src/Umbraco.Core/{Events => Notifications}/MediaTypeMovedNotification.cs (86%) rename src/Umbraco.Core/{Events => Notifications}/MediaTypeMovingNotification.cs (86%) rename src/Umbraco.Core/{Events => Notifications}/MediaTypeRefreshedNotification.cs (91%) rename src/Umbraco.Core/{Events => Notifications}/MediaTypeSavedNotification.cs (85%) rename src/Umbraco.Core/{Events => Notifications}/MediaTypeSavingNotification.cs (85%) rename src/Umbraco.Core/{Cache => Notifications}/MemberCacheRefresherNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/MemberDeletedNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/MemberDeletingNotification.cs (91%) rename src/Umbraco.Core/{Cache => Notifications}/MemberGroupCacheRefresherNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/MemberGroupDeletedNotification.cs (85%) rename src/Umbraco.Core/{Services => }/Notifications/MemberGroupDeletingNotification.cs (90%) rename src/Umbraco.Core/{Services => }/Notifications/MemberGroupSavedNotification.cs (90%) rename src/Umbraco.Core/{Services => }/Notifications/MemberGroupSavingNotification.cs (90%) rename src/Umbraco.Core/{Events => Notifications}/MemberRefreshNotification.cs (86%) rename src/Umbraco.Core/{Services => }/Notifications/MemberRolesNotification.cs (79%) rename src/Umbraco.Core/{Services => }/Notifications/MemberSavedNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/MemberSavingNotification.cs (91%) rename src/Umbraco.Core/{Events => Notifications}/MemberTypeChangedNotification.cs (88%) rename src/Umbraco.Core/{Events => Notifications}/MemberTypeDeletedNotification.cs (86%) rename src/Umbraco.Core/{Events => Notifications}/MemberTypeDeletingNotification.cs (86%) rename src/Umbraco.Core/{Events => Notifications}/MemberTypeMovedNotification.cs (86%) rename src/Umbraco.Core/{Events => Notifications}/MemberTypeMovingNotification.cs (86%) rename src/Umbraco.Core/{Events => Notifications}/MemberTypeRefreshedNotification.cs (91%) rename src/Umbraco.Core/{Events => Notifications}/MemberTypeSavedNotification.cs (85%) rename src/Umbraco.Core/{Events => Notifications}/MemberTypeSavingNotification.cs (85%) rename src/{Umbraco.Web.Common/ModelBinders/ModelBindingError.cs => Umbraco.Core/Notifications/ModelBindingErrorNotification.cs} (78%) rename src/Umbraco.Core/{Events => Notifications}/MovedNotification.cs (88%) rename src/Umbraco.Core/{Services => }/Notifications/MovedToRecycleBinNotification.cs (92%) rename src/Umbraco.Core/{Events => Notifications}/MovingNotification.cs (88%) rename src/Umbraco.Core/{Services => }/Notifications/MovingToRecycleBinNotification.cs (92%) rename src/Umbraco.Core/{Events => Notifications}/NotificationExtensions.cs (92%) rename src/Umbraco.Core/{Events => Notifications}/ObjectNotification.cs (84%) rename src/Umbraco.Core/{Services => }/Notifications/PartialViewCreatedNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/PartialViewCreatingNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/PartialViewDeletedNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/PartialViewDeletingNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/PartialViewSavedNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/PartialViewSavingNotification.cs (91%) rename src/Umbraco.Core/{Cache => Notifications}/PublicAccessCacheRefresherNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/PublicAccessEntryDeletedNotification.cs (88%) rename src/Umbraco.Core/{Services => }/Notifications/PublicAccessEntryDeletingNotification.cs (92%) rename src/Umbraco.Core/{Services => }/Notifications/PublicAccessEntrySavedNotification.cs (92%) rename src/Umbraco.Core/{Services => }/Notifications/PublicAccessEntrySavingNotification.cs (92%) rename src/Umbraco.Core/{Services => }/Notifications/RelationDeletedNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/RelationDeletingNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/RelationSavedNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/RelationSavingNotification.cs (91%) rename src/Umbraco.Core/{Cache => Notifications}/RelationTypeCacheRefresherNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/RelationTypeDeletedNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/RelationTypeDeletingNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/RelationTypeSavedNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/RelationTypeSavingNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/RemovedMemberRolesNotification.cs (81%) rename src/Umbraco.Core/{Events => Notifications}/RenamedNotification.cs (87%) rename src/Umbraco.Core/{Events => Notifications}/RenamingNotification.cs (87%) rename src/Umbraco.Core/{Events => Notifications}/RolledBackNotification.cs (81%) rename src/Umbraco.Core/{Events => Notifications}/RollingBackNotification.cs (82%) rename src/Umbraco.Core/{Routing => Notifications}/RoutingRequestNotification.cs (89%) rename src/Umbraco.Core/{Events => Notifications}/SavedNotification.cs (87%) rename src/Umbraco.Core/{Events => Notifications}/SavingNotification.cs (87%) rename src/Umbraco.Core/{Events => Notifications}/ScopedEntityRemoveNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/ScriptDeletedNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/ScriptDeletingNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/ScriptSavedNotification.cs (90%) rename src/Umbraco.Core/{Services => }/Notifications/ScriptSavingNotification.cs (91%) rename src/{Umbraco.Web.BackOffice/Filters => Umbraco.Core/Notifications}/SendingContentNotification.cs (85%) rename src/{Umbraco.Web.BackOffice/Filters => Umbraco.Core/Notifications}/SendingDashboardsNotification.cs (88%) rename src/{Umbraco.Web.BackOffice/Filters => Umbraco.Core/Notifications}/SendingMediaNotification.cs (85%) rename src/{Umbraco.Web.BackOffice/Filters => Umbraco.Core/Notifications}/SendingMemberNotification.cs (85%) rename src/{Umbraco.Web.BackOffice/Filters => Umbraco.Core/Notifications}/SendingUserNotification.cs (84%) rename src/{Umbraco.Infrastructure/WebAssets/ServerVariablesParsing.cs => Umbraco.Core/Notifications/ServerVariablesParsingNotification.cs} (60%) rename src/Umbraco.Core/{Events => Notifications}/SortedNotification.cs (84%) rename src/Umbraco.Core/{Events => Notifications}/SortingNotification.cs (84%) rename src/Umbraco.Core/{Events => Notifications}/StatefulNotification.cs (93%) rename src/Umbraco.Core/{Services => }/Notifications/StylesheetDeletedNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/StylesheetDeletingNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/StylesheetSavedNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/StylesheetSavingNotification.cs (91%) rename src/Umbraco.Core/{Cache => Notifications}/TemplateCacheRefresherNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/TemplateDeletedNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/TemplateDeletingNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/TemplateSavedNotification.cs (96%) rename src/Umbraco.Core/{Services => }/Notifications/TemplateSavingNotification.cs (97%) rename src/Umbraco.Core/{Events => Notifications}/TreeChangeNotification.cs (88%) rename src/Umbraco.Core/{Events/UmbracoApplicationStarting.cs => Notifications/UmbracoApplicationStartingNotification.cs} (59%) create mode 100644 src/Umbraco.Core/Notifications/UmbracoApplicationStoppingNotification.cs rename src/Umbraco.Core/{Events/UmbracoRequestBegin.cs => Notifications/UmbracoRequestBeginNotification.cs} (63%) rename src/Umbraco.Core/{Events/UmbracoRequestEnd.cs => Notifications/UmbracoRequestEndNotification.cs} (64%) rename src/Umbraco.Core/{Events => Notifications}/UnattendedInstallNotification.cs (81%) rename src/Umbraco.Core/{Events => Notifications}/UninstallPackageNotification.cs (90%) rename src/Umbraco.Core/{Cache => Notifications}/UserCacheRefresherNotification.cs (86%) rename src/Umbraco.Core/{Services => }/Notifications/UserDeletedNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/UserDeletingNotification.cs (91%) rename src/{Umbraco.Web.Common/Security => Umbraco.Core/Notifications}/UserForgotPasswordChangedNotification.cs (86%) rename src/{Umbraco.Web.Common/Security => Umbraco.Core/Notifications}/UserForgotPasswordRequestedNotification.cs (87%) rename src/Umbraco.Core/{Cache => Notifications}/UserGroupCacheRefresherNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/UserGroupDeletedNotification.cs (88%) rename src/Umbraco.Core/{Services => }/Notifications/UserGroupDeletingNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/UserGroupSavedNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/UserGroupSavingNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/UserGroupWithUsersSavedNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/UserGroupWithUsersSavingNotification.cs (91%) rename src/{Umbraco.Web.Common/Security => Umbraco.Core/Notifications}/UserLockedNotification.cs (85%) rename src/{Umbraco.Web.Common/Security => Umbraco.Core/Notifications}/UserLoginFailedNotification.cs (86%) rename src/{Umbraco.Web.Common/Security => Umbraco.Core/Notifications}/UserLoginRequiresVerificationNotification.cs (87%) rename src/{Umbraco.Web.Common/Security => Umbraco.Core/Notifications}/UserLoginSuccessNotification.cs (86%) rename src/{Umbraco.Web.Common/Security => Umbraco.Core/Notifications}/UserLogoutSuccessNotification.cs (88%) rename src/{Umbraco.Web.Common/Security => Umbraco.Core/Notifications}/UserNotification.cs (93%) rename src/{Umbraco.Web.Common/Security => Umbraco.Core/Notifications}/UserPasswordChangedNotification.cs (86%) rename src/{Umbraco.Web.Common/Security => Umbraco.Core/Notifications}/UserPasswordResetNotification.cs (86%) rename src/{Umbraco.Web.Common/Security => Umbraco.Core/Notifications}/UserResetAccessFailedCountNotification.cs (87%) rename src/Umbraco.Core/{Services => }/Notifications/UserSavedNotification.cs (91%) rename src/Umbraco.Core/{Services => }/Notifications/UserSavingNotification.cs (91%) rename src/{Umbraco.Web.Common/Security => Umbraco.Core/Notifications}/UserUnlockedNotification.cs (85%) rename src/Umbraco.Infrastructure/Migrations/{Events => Notifications}/DatabaseSchemaCreatedNotification.cs (74%) rename src/Umbraco.Infrastructure/Migrations/{Events => Notifications}/DatabaseSchemaCreatingNotification.cs (70%) diff --git a/src/Umbraco.Core/Cache/ApplicationCacheRefresher.cs b/src/Umbraco.Core/Cache/ApplicationCacheRefresher.cs index 8106da11e6..582915fb2e 100644 --- a/src/Umbraco.Core/Cache/ApplicationCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/ApplicationCacheRefresher.cs @@ -1,5 +1,6 @@ using System; using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; namespace Umbraco.Cms.Core.Cache { diff --git a/src/Umbraco.Core/Cache/CacheRefresherBase.cs b/src/Umbraco.Core/Cache/CacheRefresherBase.cs index 323fa6aeca..7b962065c5 100644 --- a/src/Umbraco.Core/Cache/CacheRefresherBase.cs +++ b/src/Umbraco.Core/Cache/CacheRefresherBase.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Entities; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Sync; namespace Umbraco.Cms.Core.Cache diff --git a/src/Umbraco.Core/Cache/CacheRefresherNotificationFactory.cs b/src/Umbraco.Core/Cache/CacheRefresherNotificationFactory.cs index fc9cdefe27..bd41ee9d9b 100644 --- a/src/Umbraco.Core/Cache/CacheRefresherNotificationFactory.cs +++ b/src/Umbraco.Core/Cache/CacheRefresherNotificationFactory.cs @@ -1,4 +1,5 @@ using System; +using Umbraco.Cms.Core.Notifications; using Umbraco.Extensions; using Umbraco.Cms.Core.Sync; diff --git a/src/Umbraco.Core/Cache/ContentCacheRefresher.cs b/src/Umbraco.Core/Cache/ContentCacheRefresher.cs index 7b9b2994a9..7fa77feb94 100644 --- a/src/Umbraco.Core/Cache/ContentCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/ContentCacheRefresher.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Serialization; diff --git a/src/Umbraco.Core/Cache/ContentTypeCacheRefresher.cs b/src/Umbraco.Core/Cache/ContentTypeCacheRefresher.cs index b093df85b8..9a709e9a9f 100644 --- a/src/Umbraco.Core/Cache/ContentTypeCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/ContentTypeCacheRefresher.cs @@ -3,6 +3,7 @@ using System.Linq; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Serialization; diff --git a/src/Umbraco.Core/Cache/DataTypeCacheRefresher.cs b/src/Umbraco.Core/Cache/DataTypeCacheRefresher.cs index dfbf9230e2..24ec91ba75 100644 --- a/src/Umbraco.Core/Cache/DataTypeCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/DataTypeCacheRefresher.cs @@ -2,6 +2,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.PropertyEditors.ValueConverters; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Serialization; diff --git a/src/Umbraco.Core/Cache/DictionaryCacheRefresher.cs b/src/Umbraco.Core/Cache/DictionaryCacheRefresher.cs index c812a4aadc..dbe84b114e 100644 --- a/src/Umbraco.Core/Cache/DictionaryCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/DictionaryCacheRefresher.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; namespace Umbraco.Cms.Core.Cache { diff --git a/src/Umbraco.Core/Cache/DomainCacheRefresher.cs b/src/Umbraco.Core/Cache/DomainCacheRefresher.cs index 228baf4b9a..28e62c854d 100644 --- a/src/Umbraco.Core/Cache/DomainCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/DomainCacheRefresher.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services.Changes; diff --git a/src/Umbraco.Core/Cache/ICacheRefresherNotificationFactory.cs b/src/Umbraco.Core/Cache/ICacheRefresherNotificationFactory.cs index c79f7579a7..04b91e43d8 100644 --- a/src/Umbraco.Core/Cache/ICacheRefresherNotificationFactory.cs +++ b/src/Umbraco.Core/Cache/ICacheRefresherNotificationFactory.cs @@ -1,4 +1,5 @@ -using Umbraco.Cms.Core.Sync; +using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Core.Sync; namespace Umbraco.Cms.Core.Cache { diff --git a/src/Umbraco.Core/Cache/JsonCacheRefresherBase.cs b/src/Umbraco.Core/Cache/JsonCacheRefresherBase.cs index f0946e0e52..92669159f1 100644 --- a/src/Umbraco.Core/Cache/JsonCacheRefresherBase.cs +++ b/src/Umbraco.Core/Cache/JsonCacheRefresherBase.cs @@ -1,4 +1,5 @@ using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Sync; diff --git a/src/Umbraco.Core/Cache/LanguageCacheRefresher.cs b/src/Umbraco.Core/Cache/LanguageCacheRefresher.cs index fb65aaa58d..414c51c186 100644 --- a/src/Umbraco.Core/Cache/LanguageCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/LanguageCacheRefresher.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services.Changes; diff --git a/src/Umbraco.Core/Cache/MacroCacheRefresher.cs b/src/Umbraco.Core/Cache/MacroCacheRefresher.cs index 3c1ad46f2c..77550b81d1 100644 --- a/src/Umbraco.Core/Cache/MacroCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/MacroCacheRefresher.cs @@ -2,6 +2,7 @@ using System; using System.Linq; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Serialization; diff --git a/src/Umbraco.Core/Cache/MediaCacheRefresher.cs b/src/Umbraco.Core/Cache/MediaCacheRefresher.cs index 5d301fc353..c192296375 100644 --- a/src/Umbraco.Core/Cache/MediaCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/MediaCacheRefresher.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Serialization; diff --git a/src/Umbraco.Core/Cache/MemberCacheRefresher.cs b/src/Umbraco.Core/Cache/MemberCacheRefresher.cs index 121dbea738..4f199389d6 100644 --- a/src/Umbraco.Core/Cache/MemberCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/MemberCacheRefresher.cs @@ -3,6 +3,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; diff --git a/src/Umbraco.Core/Cache/MemberGroupCacheRefresher.cs b/src/Umbraco.Core/Cache/MemberGroupCacheRefresher.cs index 1f019e8f30..0866f7b39a 100644 --- a/src/Umbraco.Core/Cache/MemberGroupCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/MemberGroupCacheRefresher.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Serialization; namespace Umbraco.Cms.Core.Cache diff --git a/src/Umbraco.Core/Cache/PayloadCacheRefresherBase.cs b/src/Umbraco.Core/Cache/PayloadCacheRefresherBase.cs index f7867ae3fe..379e04af7c 100644 --- a/src/Umbraco.Core/Cache/PayloadCacheRefresherBase.cs +++ b/src/Umbraco.Core/Cache/PayloadCacheRefresherBase.cs @@ -1,4 +1,5 @@ using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Sync; diff --git a/src/Umbraco.Core/Cache/PublicAccessCacheRefresher.cs b/src/Umbraco.Core/Cache/PublicAccessCacheRefresher.cs index d833a0aea5..5c9eb20b4c 100644 --- a/src/Umbraco.Core/Cache/PublicAccessCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/PublicAccessCacheRefresher.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; namespace Umbraco.Cms.Core.Cache { diff --git a/src/Umbraco.Core/Cache/RelationTypeCacheRefresher.cs b/src/Umbraco.Core/Cache/RelationTypeCacheRefresher.cs index a9694c92f5..aadae52ce6 100644 --- a/src/Umbraco.Core/Cache/RelationTypeCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/RelationTypeCacheRefresher.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; namespace Umbraco.Cms.Core.Cache diff --git a/src/Umbraco.Core/Cache/TemplateCacheRefresher.cs b/src/Umbraco.Core/Cache/TemplateCacheRefresher.cs index c098ccb967..0bc2c6c5ef 100644 --- a/src/Umbraco.Core/Cache/TemplateCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/TemplateCacheRefresher.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Services; diff --git a/src/Umbraco.Core/Cache/UserCacheRefresher.cs b/src/Umbraco.Core/Cache/UserCacheRefresher.cs index 706ed8ae45..b8d271bbb3 100644 --- a/src/Umbraco.Core/Cache/UserCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/UserCacheRefresher.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; namespace Umbraco.Cms.Core.Cache diff --git a/src/Umbraco.Core/Cache/UserGroupCacheRefresher.cs b/src/Umbraco.Core/Cache/UserGroupCacheRefresher.cs index 3a7c8d12b1..3ec88b1ea5 100644 --- a/src/Umbraco.Core/Cache/UserGroupCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/UserGroupCacheRefresher.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; namespace Umbraco.Cms.Core.Cache diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Events.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Events.cs index 6af917078c..441bc836da 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Events.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Events.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; namespace Umbraco.Cms.Core.DependencyInjection { diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs index 366c411ade..d0fe41dfc3 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs @@ -27,12 +27,12 @@ using Umbraco.Cms.Core.Logging; using Umbraco.Cms.Core.Mail; using Umbraco.Cms.Core.Manifest; using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Runtime; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Core.Sync; using Umbraco.Cms.Core.Templates; using Umbraco.Cms.Core.Web; @@ -141,11 +141,11 @@ namespace Umbraco.Cms.Core.DependencyInjection Services.AddUnique(); this.AddAllCoreCollectionBuilders(); - this.AddNotificationHandler(); + this.AddNotificationHandler(); Services.AddSingleton(); Services.AddSingleton(); - this.AddNotificationAsyncHandler(); + this.AddNotificationAsyncHandler(); Services.AddUnique(); diff --git a/src/Umbraco.Core/Events/EventAggregator.Notifications.cs b/src/Umbraco.Core/Events/EventAggregator.Notifications.cs index 859edd4997..58ccb06ed0 100644 --- a/src/Umbraco.Core/Events/EventAggregator.Notifications.cs +++ b/src/Umbraco.Core/Events/EventAggregator.Notifications.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Umbraco.Cms.Core.Notifications; namespace Umbraco.Cms.Core.Events { diff --git a/src/Umbraco.Core/Events/EventAggregator.cs b/src/Umbraco.Core/Events/EventAggregator.cs index f4602039dd..f762d62109 100644 --- a/src/Umbraco.Core/Events/EventAggregator.cs +++ b/src/Umbraco.Core/Events/EventAggregator.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Umbraco.Cms.Core.Notifications; namespace Umbraco.Cms.Core.Events { diff --git a/src/Umbraco.Core/Events/IEventAggregator.cs b/src/Umbraco.Core/Events/IEventAggregator.cs index d59f75b040..c654bb6c86 100644 --- a/src/Umbraco.Core/Events/IEventAggregator.cs +++ b/src/Umbraco.Core/Events/IEventAggregator.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Threading.Tasks; +using Umbraco.Cms.Core.Notifications; namespace Umbraco.Cms.Core.Events { diff --git a/src/Umbraco.Core/Events/INotificationHandler.cs b/src/Umbraco.Core/Events/INotificationHandler.cs index 25aea986c6..d362477733 100644 --- a/src/Umbraco.Core/Events/INotificationHandler.cs +++ b/src/Umbraco.Core/Events/INotificationHandler.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Threading.Tasks; +using Umbraco.Cms.Core.Notifications; namespace Umbraco.Cms.Core.Events { diff --git a/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs b/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs index 96f8e771c0..7df9167ce6 100644 --- a/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs +++ b/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs @@ -2,6 +2,7 @@ // See LICENSE for more details. using System.Threading.Tasks; +using Umbraco.Cms.Core.Notifications; namespace Umbraco.Cms.Core.Events { diff --git a/src/Umbraco.Core/Events/RelateOnCopyNotificationHandler.cs b/src/Umbraco.Core/Events/RelateOnCopyNotificationHandler.cs index caa3777d39..0a6518a1ca 100644 --- a/src/Umbraco.Core/Events/RelateOnCopyNotificationHandler.cs +++ b/src/Umbraco.Core/Events/RelateOnCopyNotificationHandler.cs @@ -2,8 +2,8 @@ // See LICENSE for more details. using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Notifications; namespace Umbraco.Cms.Core.Events { diff --git a/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs b/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs index 206706716c..c9b0080218 100644 --- a/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs +++ b/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Umbraco.Cms.Core.Notifications; namespace Umbraco.Cms.Core.Events { diff --git a/src/Umbraco.Core/Events/UmbracoApplicationStopping.cs b/src/Umbraco.Core/Events/UmbracoApplicationStopping.cs deleted file mode 100644 index 54ce079012..0000000000 --- a/src/Umbraco.Core/Events/UmbracoApplicationStopping.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace Umbraco.Cms.Core.Events -{ - public class UmbracoApplicationStopping : INotification { } -} diff --git a/src/Umbraco.Core/Events/UserNotificationsHandler.cs b/src/Umbraco.Core/Events/UserNotificationsHandler.cs index 1a9a457636..f5172d3e28 100644 --- a/src/Umbraco.Core/Events/UserNotificationsHandler.cs +++ b/src/Umbraco.Core/Events/UserNotificationsHandler.cs @@ -13,9 +13,9 @@ using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Events diff --git a/src/Umbraco.Core/Handlers/AuditNotificationsHandler.cs b/src/Umbraco.Core/Handlers/AuditNotificationsHandler.cs index 2a1322f925..ed7fdb971f 100644 --- a/src/Umbraco.Core/Handlers/AuditNotificationsHandler.cs +++ b/src/Umbraco.Core/Handlers/AuditNotificationsHandler.cs @@ -7,9 +7,9 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Membership; using Umbraco.Cms.Core.Net; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Handlers diff --git a/src/Umbraco.Core/Handlers/PublicAccessHandler.cs b/src/Umbraco.Core/Handlers/PublicAccessHandler.cs index 9645e9a88c..b50375adfe 100644 --- a/src/Umbraco.Core/Handlers/PublicAccessHandler.cs +++ b/src/Umbraco.Core/Handlers/PublicAccessHandler.cs @@ -2,8 +2,8 @@ using System; using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Handlers diff --git a/src/Umbraco.Core/Cache/ApplicationCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/ApplicationCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/ApplicationCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/ApplicationCacheRefresherNotification.cs index 3602a1488f..eb596a3a0b 100644 --- a/src/Umbraco.Core/Cache/ApplicationCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/ApplicationCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class ApplicationCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Services/Notifications/AssignedMemberRolesNotification.cs b/src/Umbraco.Core/Notifications/AssignedMemberRolesNotification.cs similarity index 81% rename from src/Umbraco.Core/Services/Notifications/AssignedMemberRolesNotification.cs rename to src/Umbraco.Core/Notifications/AssignedMemberRolesNotification.cs index edd1738a7d..adcf14d636 100644 --- a/src/Umbraco.Core/Services/Notifications/AssignedMemberRolesNotification.cs +++ b/src/Umbraco.Core/Notifications/AssignedMemberRolesNotification.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class AssignedMemberRolesNotification : MemberRolesNotification { diff --git a/src/Umbraco.Core/Services/Notifications/AssignedUserGroupPermissionsNotification.cs b/src/Umbraco.Core/Notifications/AssignedUserGroupPermissionsNotification.cs similarity index 90% rename from src/Umbraco.Core/Services/Notifications/AssignedUserGroupPermissionsNotification.cs rename to src/Umbraco.Core/Notifications/AssignedUserGroupPermissionsNotification.cs index d11cd01e07..18425f2393 100644 --- a/src/Umbraco.Core/Services/Notifications/AssignedUserGroupPermissionsNotification.cs +++ b/src/Umbraco.Core/Notifications/AssignedUserGroupPermissionsNotification.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class AssignedUserGroupPermissionsNotification : EnumerableObjectNotification { diff --git a/src/Umbraco.Core/Cache/CacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/CacheRefresherNotification.cs similarity index 86% rename from src/Umbraco.Core/Cache/CacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/CacheRefresherNotification.cs index 442fad0147..bd110ad878 100644 --- a/src/Umbraco.Core/Cache/CacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/CacheRefresherNotification.cs @@ -1,9 +1,7 @@ using System; -using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Sync; -using Umbraco.Extensions; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { /// /// Base class for cache refresher notifications diff --git a/src/Umbraco.Core/Events/CancelableEnumerableObjectNotification.cs b/src/Umbraco.Core/Notifications/CancelableEnumerableObjectNotification.cs similarity index 87% rename from src/Umbraco.Core/Events/CancelableEnumerableObjectNotification.cs rename to src/Umbraco.Core/Notifications/CancelableEnumerableObjectNotification.cs index 1db0380af5..ea7476cd3f 100644 --- a/src/Umbraco.Core/Events/CancelableEnumerableObjectNotification.cs +++ b/src/Umbraco.Core/Notifications/CancelableEnumerableObjectNotification.cs @@ -2,8 +2,9 @@ // See LICENSE for more details. using System.Collections.Generic; +using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class CancelableEnumerableObjectNotification : CancelableObjectNotification> { diff --git a/src/Umbraco.Core/Events/CancelableNotification.cs b/src/Umbraco.Core/Notifications/CancelableNotification.cs similarity index 87% rename from src/Umbraco.Core/Events/CancelableNotification.cs rename to src/Umbraco.Core/Notifications/CancelableNotification.cs index 173304017a..13989d50da 100644 --- a/src/Umbraco.Core/Events/CancelableNotification.cs +++ b/src/Umbraco.Core/Notifications/CancelableNotification.cs @@ -1,4 +1,6 @@ -namespace Umbraco.Cms.Core.Events +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Core.Notifications { public class CancelableNotification : StatefulNotification, ICancelableNotification { diff --git a/src/Umbraco.Core/Events/CancelableObjectNotification.cs b/src/Umbraco.Core/Notifications/CancelableObjectNotification.cs similarity index 89% rename from src/Umbraco.Core/Events/CancelableObjectNotification.cs rename to src/Umbraco.Core/Notifications/CancelableObjectNotification.cs index a976ff51d9..25f6a4474f 100644 --- a/src/Umbraco.Core/Events/CancelableObjectNotification.cs +++ b/src/Umbraco.Core/Notifications/CancelableObjectNotification.cs @@ -1,7 +1,9 @@ // Copyright (c) Umbraco. // See LICENSE for more details. -namespace Umbraco.Cms.Core.Events +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Core.Notifications { public abstract class CancelableObjectNotification : ObjectNotification, ICancelableNotification where T : class { diff --git a/src/Umbraco.Core/Cache/ContentCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/ContentCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/ContentCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/ContentCacheRefresherNotification.cs index dd76786393..35b4f472c7 100644 --- a/src/Umbraco.Core/Cache/ContentCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class ContentCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentCopiedNotification.cs b/src/Umbraco.Core/Notifications/ContentCopiedNotification.cs similarity index 89% rename from src/Umbraco.Core/Services/Notifications/ContentCopiedNotification.cs rename to src/Umbraco.Core/Notifications/ContentCopiedNotification.cs index 8589e14778..6399fb714d 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentCopiedNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentCopiedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentCopiedNotification : CopiedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentCopyingNotification.cs b/src/Umbraco.Core/Notifications/ContentCopyingNotification.cs similarity index 89% rename from src/Umbraco.Core/Services/Notifications/ContentCopyingNotification.cs rename to src/Umbraco.Core/Notifications/ContentCopyingNotification.cs index 9c3220e8e2..d30d49efeb 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentCopyingNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentCopyingNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentCopyingNotification : CopyingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentDeletedBlueprintNotification.cs b/src/Umbraco.Core/Notifications/ContentDeletedBlueprintNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/ContentDeletedBlueprintNotification.cs rename to src/Umbraco.Core/Notifications/ContentDeletedBlueprintNotification.cs index e2c49535a7..1c516a295f 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentDeletedBlueprintNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentDeletedBlueprintNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentDeletedBlueprintNotification : EnumerableObjectNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentDeletedNotification.cs b/src/Umbraco.Core/Notifications/ContentDeletedNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/ContentDeletedNotification.cs rename to src/Umbraco.Core/Notifications/ContentDeletedNotification.cs index 77f6e0cc51..6398c4f28e 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentDeletedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentDeletedVersionsNotification.cs b/src/Umbraco.Core/Notifications/ContentDeletedVersionsNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/ContentDeletedVersionsNotification.cs rename to src/Umbraco.Core/Notifications/ContentDeletedVersionsNotification.cs index 34040135ba..30f00b52bf 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentDeletedVersionsNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentDeletedVersionsNotification.cs @@ -5,7 +5,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentDeletedVersionsNotification : DeletedVersionsNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentDeletingNotification.cs b/src/Umbraco.Core/Notifications/ContentDeletingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/ContentDeletingNotification.cs rename to src/Umbraco.Core/Notifications/ContentDeletingNotification.cs index befb932861..ee02c6f339 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentDeletingVersionsNotification.cs b/src/Umbraco.Core/Notifications/ContentDeletingVersionsNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/ContentDeletingVersionsNotification.cs rename to src/Umbraco.Core/Notifications/ContentDeletingVersionsNotification.cs index 4929046ad5..340aaaa559 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentDeletingVersionsNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentDeletingVersionsNotification.cs @@ -5,7 +5,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentDeletingVersionsNotification : DeletingVersionsNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentEmptiedRecycleBinNotification.cs b/src/Umbraco.Core/Notifications/ContentEmptiedRecycleBinNotification.cs similarity index 89% rename from src/Umbraco.Core/Services/Notifications/ContentEmptiedRecycleBinNotification.cs rename to src/Umbraco.Core/Notifications/ContentEmptiedRecycleBinNotification.cs index ad305bd7ab..1453553efa 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentEmptiedRecycleBinNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentEmptiedRecycleBinNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentEmptiedRecycleBinNotification : EmptiedRecycleBinNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentEmptyingRecycleBinNotification.cs b/src/Umbraco.Core/Notifications/ContentEmptyingRecycleBinNotification.cs similarity index 89% rename from src/Umbraco.Core/Services/Notifications/ContentEmptyingRecycleBinNotification.cs rename to src/Umbraco.Core/Notifications/ContentEmptyingRecycleBinNotification.cs index 312d375dd4..f66400a9a8 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentEmptyingRecycleBinNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentEmptyingRecycleBinNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentEmptyingRecycleBinNotification : EmptyingRecycleBinNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentMovedNotification.cs b/src/Umbraco.Core/Notifications/ContentMovedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/ContentMovedNotification.cs rename to src/Umbraco.Core/Notifications/ContentMovedNotification.cs index fb44887d93..607d678049 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentMovedNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentMovedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentMovedNotification : MovedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentMovedToRecycleBinNotification.cs b/src/Umbraco.Core/Notifications/ContentMovedToRecycleBinNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/ContentMovedToRecycleBinNotification.cs rename to src/Umbraco.Core/Notifications/ContentMovedToRecycleBinNotification.cs index c3f2950fef..3b736b1409 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentMovedToRecycleBinNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentMovedToRecycleBinNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentMovedToRecycleBinNotification : MovedToRecycleBinNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentMovingNotification.cs b/src/Umbraco.Core/Notifications/ContentMovingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/ContentMovingNotification.cs rename to src/Umbraco.Core/Notifications/ContentMovingNotification.cs index b066c6aad5..01c04eb226 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentMovingNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentMovingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentMovingNotification : MovingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentMovingToRecycleBinNotification.cs b/src/Umbraco.Core/Notifications/ContentMovingToRecycleBinNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/ContentMovingToRecycleBinNotification.cs rename to src/Umbraco.Core/Notifications/ContentMovingToRecycleBinNotification.cs index 33e2bab8bc..88aa48c7b8 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentMovingToRecycleBinNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentMovingToRecycleBinNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentMovingToRecycleBinNotification : MovingToRecycleBinNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentNotificationExtensions.cs b/src/Umbraco.Core/Notifications/ContentNotificationExtensions.cs similarity index 97% rename from src/Umbraco.Core/Services/Notifications/ContentNotificationExtensions.cs rename to src/Umbraco.Core/Notifications/ContentNotificationExtensions.cs index 27afb52c30..1b9464dcb4 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentNotificationExtensions.cs +++ b/src/Umbraco.Core/Notifications/ContentNotificationExtensions.cs @@ -1,10 +1,9 @@ // Copyright (c) Umbraco. // See LICENSE for more details. -using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public static class ContentNotificationExtensions { diff --git a/src/Umbraco.Core/Services/Notifications/ContentPublishedNotification.cs b/src/Umbraco.Core/Notifications/ContentPublishedNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/ContentPublishedNotification.cs rename to src/Umbraco.Core/Notifications/ContentPublishedNotification.cs index f6c25cb800..69d1751e58 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentPublishedNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentPublishedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentPublishedNotification : EnumerableObjectNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentPublishingNotification.cs b/src/Umbraco.Core/Notifications/ContentPublishingNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/ContentPublishingNotification.cs rename to src/Umbraco.Core/Notifications/ContentPublishingNotification.cs index d0937019d9..65a8efdadf 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentPublishingNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentPublishingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentPublishingNotification : CancelableEnumerableObjectNotification { diff --git a/src/Umbraco.Core/Events/ContentRefreshNotification.cs b/src/Umbraco.Core/Notifications/ContentRefreshNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/ContentRefreshNotification.cs rename to src/Umbraco.Core/Notifications/ContentRefreshNotification.cs index 0132f4de8c..6957da7f70 100644 --- a/src/Umbraco.Core/Events/ContentRefreshNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentRefreshNotification.cs @@ -1,8 +1,9 @@ using System; using System.ComponentModel; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { [Obsolete("This is only used for the internal cache and will change, use saved notifications instead")] [EditorBrowsable(EditorBrowsableState.Never)] diff --git a/src/Umbraco.Core/Services/Notifications/ContentRolledBackNotification.cs b/src/Umbraco.Core/Notifications/ContentRolledBackNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/ContentRolledBackNotification.cs rename to src/Umbraco.Core/Notifications/ContentRolledBackNotification.cs index 0bb7a10cf1..a1f370bd94 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentRolledBackNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentRolledBackNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentRolledBackNotification : RolledBackNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentRollingBackNotification.cs b/src/Umbraco.Core/Notifications/ContentRollingBackNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/ContentRollingBackNotification.cs rename to src/Umbraco.Core/Notifications/ContentRollingBackNotification.cs index 9163e4b61c..e12bfa1631 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentRollingBackNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentRollingBackNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentRollingBackNotification : RollingBackNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentSavedBlueprintNotification.cs b/src/Umbraco.Core/Notifications/ContentSavedBlueprintNotification.cs similarity index 89% rename from src/Umbraco.Core/Services/Notifications/ContentSavedBlueprintNotification.cs rename to src/Umbraco.Core/Notifications/ContentSavedBlueprintNotification.cs index bbf85fca17..6addde88c1 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentSavedBlueprintNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentSavedBlueprintNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentSavedBlueprintNotification : ObjectNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentSavedNotification.cs b/src/Umbraco.Core/Notifications/ContentSavedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/ContentSavedNotification.cs rename to src/Umbraco.Core/Notifications/ContentSavedNotification.cs index c0ad60aa33..b58a366368 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentSavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentSavingNotification.cs b/src/Umbraco.Core/Notifications/ContentSavingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/ContentSavingNotification.cs rename to src/Umbraco.Core/Notifications/ContentSavingNotification.cs index 61a9b14861..afe21bf870 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentSavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentSendingToPublishNotification.cs b/src/Umbraco.Core/Notifications/ContentSendingToPublishNotification.cs similarity index 89% rename from src/Umbraco.Core/Services/Notifications/ContentSendingToPublishNotification.cs rename to src/Umbraco.Core/Notifications/ContentSendingToPublishNotification.cs index 6aa367dd70..0a5c018883 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentSendingToPublishNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentSendingToPublishNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentSendingToPublishNotification : CancelableObjectNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentSentToPublishNotification.cs b/src/Umbraco.Core/Notifications/ContentSentToPublishNotification.cs similarity index 88% rename from src/Umbraco.Core/Services/Notifications/ContentSentToPublishNotification.cs rename to src/Umbraco.Core/Notifications/ContentSentToPublishNotification.cs index 47088e01b9..c5e2e5dc3b 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentSentToPublishNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentSentToPublishNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentSentToPublishNotification : ObjectNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentSortedNotification.cs b/src/Umbraco.Core/Notifications/ContentSortedNotification.cs similarity index 88% rename from src/Umbraco.Core/Services/Notifications/ContentSortedNotification.cs rename to src/Umbraco.Core/Notifications/ContentSortedNotification.cs index 1646b5cf55..0a299e3c0a 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentSortedNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentSortedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentSortedNotification : SortedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentSortingNotification.cs b/src/Umbraco.Core/Notifications/ContentSortingNotification.cs similarity index 88% rename from src/Umbraco.Core/Services/Notifications/ContentSortingNotification.cs rename to src/Umbraco.Core/Notifications/ContentSortingNotification.cs index 375bbceead..1d6cd31c5a 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentSortingNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentSortingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentSortingNotification : SortingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentTreeChangeNotification.cs b/src/Umbraco.Core/Notifications/ContentTreeChangeNotification.cs similarity index 95% rename from src/Umbraco.Core/Services/Notifications/ContentTreeChangeNotification.cs rename to src/Umbraco.Core/Notifications/ContentTreeChangeNotification.cs index 2161b4782a..b5b100038b 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentTreeChangeNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentTreeChangeNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services.Changes; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class ContentTreeChangeNotification : TreeChangeNotification { diff --git a/src/Umbraco.Core/Cache/ContentTypeCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/ContentTypeCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/ContentTypeCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/ContentTypeCacheRefresherNotification.cs index 17bd955d03..8bd06a4c46 100644 --- a/src/Umbraco.Core/Cache/ContentTypeCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentTypeCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class ContentTypeCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Events/ContentTypeChangeNotification.cs b/src/Umbraco.Core/Notifications/ContentTypeChangeNotification.cs similarity index 90% rename from src/Umbraco.Core/Events/ContentTypeChangeNotification.cs rename to src/Umbraco.Core/Notifications/ContentTypeChangeNotification.cs index 80a0f333a3..e03f381318 100644 --- a/src/Umbraco.Core/Events/ContentTypeChangeNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentTypeChangeNotification.cs @@ -1,8 +1,9 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services.Changes; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class ContentTypeChangeNotification : EnumerableObjectNotification> where T : class, IContentTypeComposition { diff --git a/src/Umbraco.Core/Events/ContentTypeChangedNotification.cs b/src/Umbraco.Core/Notifications/ContentTypeChangedNotification.cs similarity index 88% rename from src/Umbraco.Core/Events/ContentTypeChangedNotification.cs rename to src/Umbraco.Core/Notifications/ContentTypeChangedNotification.cs index 37e79c576d..e0aca73cd2 100644 --- a/src/Umbraco.Core/Events/ContentTypeChangedNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentTypeChangedNotification.cs @@ -1,8 +1,9 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services.Changes; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class ContentTypeChangedNotification : ContentTypeChangeNotification { diff --git a/src/Umbraco.Core/Events/ContentTypeDeletedNotification.cs b/src/Umbraco.Core/Notifications/ContentTypeDeletedNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/ContentTypeDeletedNotification.cs rename to src/Umbraco.Core/Notifications/ContentTypeDeletedNotification.cs index c8a704ceb7..d5b2b3e28e 100644 --- a/src/Umbraco.Core/Events/ContentTypeDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentTypeDeletedNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class ContentTypeDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Events/ContentTypeDeletingNotification.cs b/src/Umbraco.Core/Notifications/ContentTypeDeletingNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/ContentTypeDeletingNotification.cs rename to src/Umbraco.Core/Notifications/ContentTypeDeletingNotification.cs index 031dd9805f..56863b93fb 100644 --- a/src/Umbraco.Core/Events/ContentTypeDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentTypeDeletingNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class ContentTypeDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Events/ContentTypeMovedNotification.cs b/src/Umbraco.Core/Notifications/ContentTypeMovedNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/ContentTypeMovedNotification.cs rename to src/Umbraco.Core/Notifications/ContentTypeMovedNotification.cs index c7d9334098..d4794329cf 100644 --- a/src/Umbraco.Core/Events/ContentTypeMovedNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentTypeMovedNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class ContentTypeMovedNotification : MovedNotification { diff --git a/src/Umbraco.Core/Events/ContentTypeMovingNotification.cs b/src/Umbraco.Core/Notifications/ContentTypeMovingNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/ContentTypeMovingNotification.cs rename to src/Umbraco.Core/Notifications/ContentTypeMovingNotification.cs index ec1ecd530c..a888150097 100644 --- a/src/Umbraco.Core/Events/ContentTypeMovingNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentTypeMovingNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class ContentTypeMovingNotification : MovingNotification { diff --git a/src/Umbraco.Core/Events/ContentTypeRefreshNotification.cs b/src/Umbraco.Core/Notifications/ContentTypeRefreshNotification.cs similarity index 88% rename from src/Umbraco.Core/Events/ContentTypeRefreshNotification.cs rename to src/Umbraco.Core/Notifications/ContentTypeRefreshNotification.cs index 47a74c4a45..717225db2d 100644 --- a/src/Umbraco.Core/Events/ContentTypeRefreshNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentTypeRefreshNotification.cs @@ -1,8 +1,9 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services.Changes; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class ContentTypeRefreshNotification : ContentTypeChangeNotification where T: class, IContentTypeComposition { diff --git a/src/Umbraco.Core/Events/ContentTypeRefreshedNotification.cs b/src/Umbraco.Core/Notifications/ContentTypeRefreshedNotification.cs similarity index 91% rename from src/Umbraco.Core/Events/ContentTypeRefreshedNotification.cs rename to src/Umbraco.Core/Notifications/ContentTypeRefreshedNotification.cs index bd5c3ee95f..72d111bb67 100644 --- a/src/Umbraco.Core/Events/ContentTypeRefreshedNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentTypeRefreshedNotification.cs @@ -1,10 +1,11 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services.Changes; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { [Obsolete("This is only used for the internal cache and will change, use saved notifications instead")] [EditorBrowsable(EditorBrowsableState.Never)] diff --git a/src/Umbraco.Core/Events/ContentTypeSavedNotification.cs b/src/Umbraco.Core/Notifications/ContentTypeSavedNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/ContentTypeSavedNotification.cs rename to src/Umbraco.Core/Notifications/ContentTypeSavedNotification.cs index c92666a7ae..5b9a231d60 100644 --- a/src/Umbraco.Core/Events/ContentTypeSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentTypeSavedNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class ContentTypeSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Events/ContentTypeSavingNotification.cs b/src/Umbraco.Core/Notifications/ContentTypeSavingNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/ContentTypeSavingNotification.cs rename to src/Umbraco.Core/Notifications/ContentTypeSavingNotification.cs index 28ba53d3c0..85deb91418 100644 --- a/src/Umbraco.Core/Events/ContentTypeSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentTypeSavingNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class ContentTypeSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentUnpublishedNotification.cs b/src/Umbraco.Core/Notifications/ContentUnpublishedNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/ContentUnpublishedNotification.cs rename to src/Umbraco.Core/Notifications/ContentUnpublishedNotification.cs index 03bedec921..c08d79ac59 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentUnpublishedNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentUnpublishedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentUnpublishedNotification : EnumerableObjectNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentUnpublishingNotification.cs b/src/Umbraco.Core/Notifications/ContentUnpublishingNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/ContentUnpublishingNotification.cs rename to src/Umbraco.Core/Notifications/ContentUnpublishingNotification.cs index bb7b499ab7..5fb5034515 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentUnpublishingNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentUnpublishingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentUnpublishingNotification : CancelableEnumerableObjectNotification { diff --git a/src/Umbraco.Core/Events/CopiedNotification.cs b/src/Umbraco.Core/Notifications/CopiedNotification.cs similarity index 88% rename from src/Umbraco.Core/Events/CopiedNotification.cs rename to src/Umbraco.Core/Notifications/CopiedNotification.cs index 807ffa1282..c7d6c88bcd 100644 --- a/src/Umbraco.Core/Events/CopiedNotification.cs +++ b/src/Umbraco.Core/Notifications/CopiedNotification.cs @@ -1,7 +1,9 @@ // Copyright (c) Umbraco. // See LICENSE for more details. -namespace Umbraco.Cms.Core.Events +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Core.Notifications { public abstract class CopiedNotification : ObjectNotification where T : class { diff --git a/src/Umbraco.Core/Events/CopyingNotification.cs b/src/Umbraco.Core/Notifications/CopyingNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/CopyingNotification.cs rename to src/Umbraco.Core/Notifications/CopyingNotification.cs index c89146b494..99f46f8b43 100644 --- a/src/Umbraco.Core/Events/CopyingNotification.cs +++ b/src/Umbraco.Core/Notifications/CopyingNotification.cs @@ -1,7 +1,9 @@ // Copyright (c) Umbraco. // See LICENSE for more details. -namespace Umbraco.Cms.Core.Events +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Core.Notifications { public abstract class CopyingNotification : CancelableObjectNotification where T : class { diff --git a/src/Umbraco.Core/Events/CreatedNotification.cs b/src/Umbraco.Core/Notifications/CreatedNotification.cs similarity index 81% rename from src/Umbraco.Core/Events/CreatedNotification.cs rename to src/Umbraco.Core/Notifications/CreatedNotification.cs index 3334fb8227..2108b5fb5c 100644 --- a/src/Umbraco.Core/Events/CreatedNotification.cs +++ b/src/Umbraco.Core/Notifications/CreatedNotification.cs @@ -1,7 +1,9 @@ // Copyright (c) Umbraco. // See LICENSE for more details. -namespace Umbraco.Cms.Core.Events +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Core.Notifications { public abstract class CreatedNotification : ObjectNotification where T : class { diff --git a/src/Umbraco.Core/Events/CreatingNotification.cs b/src/Umbraco.Core/Notifications/CreatingNotification.cs similarity index 82% rename from src/Umbraco.Core/Events/CreatingNotification.cs rename to src/Umbraco.Core/Notifications/CreatingNotification.cs index 2cace11cd5..da4fbfe742 100644 --- a/src/Umbraco.Core/Events/CreatingNotification.cs +++ b/src/Umbraco.Core/Notifications/CreatingNotification.cs @@ -1,7 +1,9 @@ // Copyright (c) Umbraco. // See LICENSE for more details. -namespace Umbraco.Cms.Core.Events +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Core.Notifications { public abstract class CreatingNotification : CancelableObjectNotification where T : class { diff --git a/src/Umbraco.Core/Routing/CreatingRequestNotification.cs b/src/Umbraco.Core/Notifications/CreatingRequestNotification.cs similarity index 89% rename from src/Umbraco.Core/Routing/CreatingRequestNotification.cs rename to src/Umbraco.Core/Notifications/CreatingRequestNotification.cs index 7b7122b332..aacca17afb 100644 --- a/src/Umbraco.Core/Routing/CreatingRequestNotification.cs +++ b/src/Umbraco.Core/Notifications/CreatingRequestNotification.cs @@ -1,7 +1,6 @@ using System; -using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Routing +namespace Umbraco.Cms.Core.Notifications { /// /// Used for notifying when an Umbraco request is being created diff --git a/src/Umbraco.Core/Cache/DataTypeCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/DataTypeCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/DataTypeCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/DataTypeCacheRefresherNotification.cs index d64dd53431..f59de3ebd0 100644 --- a/src/Umbraco.Core/Cache/DataTypeCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/DataTypeCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class DataTypeCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Events/DataTypeDeletedNotification.cs b/src/Umbraco.Core/Notifications/DataTypeDeletedNotification.cs similarity index 78% rename from src/Umbraco.Core/Events/DataTypeDeletedNotification.cs rename to src/Umbraco.Core/Notifications/DataTypeDeletedNotification.cs index b3461e27f1..405af74c1c 100644 --- a/src/Umbraco.Core/Events/DataTypeDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/DataTypeDeletedNotification.cs @@ -1,6 +1,7 @@ +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class DataTypeDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Events/DataTypeDeletingNotification.cs b/src/Umbraco.Core/Notifications/DataTypeDeletingNotification.cs similarity index 78% rename from src/Umbraco.Core/Events/DataTypeDeletingNotification.cs rename to src/Umbraco.Core/Notifications/DataTypeDeletingNotification.cs index 16b2ee68ac..ab997a0def 100644 --- a/src/Umbraco.Core/Events/DataTypeDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/DataTypeDeletingNotification.cs @@ -1,6 +1,7 @@ +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class DataTypeDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Events/DataTypeMovedNotification.cs b/src/Umbraco.Core/Notifications/DataTypeMovedNotification.cs similarity index 78% rename from src/Umbraco.Core/Events/DataTypeMovedNotification.cs rename to src/Umbraco.Core/Notifications/DataTypeMovedNotification.cs index 10e42c7a82..150582547b 100644 --- a/src/Umbraco.Core/Events/DataTypeMovedNotification.cs +++ b/src/Umbraco.Core/Notifications/DataTypeMovedNotification.cs @@ -1,6 +1,7 @@ +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class DataTypeMovedNotification : MovedNotification { diff --git a/src/Umbraco.Core/Events/DataTypeMovingNotification.cs b/src/Umbraco.Core/Notifications/DataTypeMovingNotification.cs similarity index 78% rename from src/Umbraco.Core/Events/DataTypeMovingNotification.cs rename to src/Umbraco.Core/Notifications/DataTypeMovingNotification.cs index 4b48f51a00..ae8fb4be6e 100644 --- a/src/Umbraco.Core/Events/DataTypeMovingNotification.cs +++ b/src/Umbraco.Core/Notifications/DataTypeMovingNotification.cs @@ -1,6 +1,7 @@ +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class DataTypeMovingNotification : MovingNotification { diff --git a/src/Umbraco.Core/Events/DataTypeSavedNotification.cs b/src/Umbraco.Core/Notifications/DataTypeSavedNotification.cs similarity index 85% rename from src/Umbraco.Core/Events/DataTypeSavedNotification.cs rename to src/Umbraco.Core/Notifications/DataTypeSavedNotification.cs index 09c42d32a9..6c1a806069 100644 --- a/src/Umbraco.Core/Events/DataTypeSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/DataTypeSavedNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class DataTypeSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Events/DataTypeSavingNotification.cs b/src/Umbraco.Core/Notifications/DataTypeSavingNotification.cs similarity index 85% rename from src/Umbraco.Core/Events/DataTypeSavingNotification.cs rename to src/Umbraco.Core/Notifications/DataTypeSavingNotification.cs index dd110df5a3..3538950b12 100644 --- a/src/Umbraco.Core/Events/DataTypeSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/DataTypeSavingNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class DataTypeSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Events/DeletedNotification.cs b/src/Umbraco.Core/Notifications/DeletedNotification.cs similarity index 87% rename from src/Umbraco.Core/Events/DeletedNotification.cs rename to src/Umbraco.Core/Notifications/DeletedNotification.cs index 3036b53746..3b2a370388 100644 --- a/src/Umbraco.Core/Events/DeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/DeletedNotification.cs @@ -2,8 +2,9 @@ // See LICENSE for more details. using System.Collections.Generic; +using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class DeletedNotification : EnumerableObjectNotification { diff --git a/src/Umbraco.Core/Services/Notifications/DeletedVersionsNotification.cs b/src/Umbraco.Core/Notifications/DeletedVersionsNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/DeletedVersionsNotification.cs rename to src/Umbraco.Core/Notifications/DeletedVersionsNotification.cs index 836a9832a5..420323afaf 100644 --- a/src/Umbraco.Core/Services/Notifications/DeletedVersionsNotification.cs +++ b/src/Umbraco.Core/Notifications/DeletedVersionsNotification.cs @@ -4,7 +4,7 @@ using System; using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public abstract class DeletedVersionsNotification : DeletedVersionsNotificationBase where T : class { diff --git a/src/Umbraco.Core/Services/Notifications/DeletedVersionsNotificationBase.cs b/src/Umbraco.Core/Notifications/DeletedVersionsNotificationBase.cs similarity index 94% rename from src/Umbraco.Core/Services/Notifications/DeletedVersionsNotificationBase.cs rename to src/Umbraco.Core/Notifications/DeletedVersionsNotificationBase.cs index 2a3c78ee21..352eee8cae 100644 --- a/src/Umbraco.Core/Services/Notifications/DeletedVersionsNotificationBase.cs +++ b/src/Umbraco.Core/Notifications/DeletedVersionsNotificationBase.cs @@ -4,7 +4,7 @@ using System; using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public abstract class DeletedVersionsNotificationBase : StatefulNotification where T : class { diff --git a/src/Umbraco.Core/Events/DeletingNotification.cs b/src/Umbraco.Core/Notifications/DeletingNotification.cs similarity index 87% rename from src/Umbraco.Core/Events/DeletingNotification.cs rename to src/Umbraco.Core/Notifications/DeletingNotification.cs index d88345b427..b4090a5b83 100644 --- a/src/Umbraco.Core/Events/DeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/DeletingNotification.cs @@ -1,8 +1,10 @@ // Copyright (c) Umbraco. // See LICENSE for more details. -using System.Collections.Generic; -namespace Umbraco.Cms.Core.Events +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Core.Notifications { public abstract class DeletingNotification : CancelableEnumerableObjectNotification { diff --git a/src/Umbraco.Core/Services/Notifications/DeletingVersionsNotification.cs b/src/Umbraco.Core/Notifications/DeletingVersionsNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/DeletingVersionsNotification.cs rename to src/Umbraco.Core/Notifications/DeletingVersionsNotification.cs index cc8b06d839..ca8f1f2514 100644 --- a/src/Umbraco.Core/Services/Notifications/DeletingVersionsNotification.cs +++ b/src/Umbraco.Core/Notifications/DeletingVersionsNotification.cs @@ -4,7 +4,7 @@ using System; using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public abstract class DeletingVersionsNotification : DeletedVersionsNotificationBase, ICancelableNotification where T : class { diff --git a/src/Umbraco.Core/Cache/DictionaryCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/DictionaryCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/DictionaryCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/DictionaryCacheRefresherNotification.cs index 57474466d0..b36939800e 100644 --- a/src/Umbraco.Core/Cache/DictionaryCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/DictionaryCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class DictionaryCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Services/Notifications/DictionaryItemDeletedNotification.cs b/src/Umbraco.Core/Notifications/DictionaryItemDeletedNotification.cs similarity index 88% rename from src/Umbraco.Core/Services/Notifications/DictionaryItemDeletedNotification.cs rename to src/Umbraco.Core/Notifications/DictionaryItemDeletedNotification.cs index 7c2b702888..c151e7ec60 100644 --- a/src/Umbraco.Core/Services/Notifications/DictionaryItemDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/DictionaryItemDeletedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class DictionaryItemDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/DictionaryItemDeletingNotification.cs b/src/Umbraco.Core/Notifications/DictionaryItemDeletingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/DictionaryItemDeletingNotification.cs rename to src/Umbraco.Core/Notifications/DictionaryItemDeletingNotification.cs index b468e4ca1d..5be95c478b 100644 --- a/src/Umbraco.Core/Services/Notifications/DictionaryItemDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/DictionaryItemDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class DictionaryItemDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/DictionaryItemSavedNotification.cs b/src/Umbraco.Core/Notifications/DictionaryItemSavedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/DictionaryItemSavedNotification.cs rename to src/Umbraco.Core/Notifications/DictionaryItemSavedNotification.cs index 596bb20a65..dc5194b847 100644 --- a/src/Umbraco.Core/Services/Notifications/DictionaryItemSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/DictionaryItemSavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class DictionaryItemSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/DictionaryItemSavingNotification.cs b/src/Umbraco.Core/Notifications/DictionaryItemSavingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/DictionaryItemSavingNotification.cs rename to src/Umbraco.Core/Notifications/DictionaryItemSavingNotification.cs index 71c5cf8cac..79fef15aef 100644 --- a/src/Umbraco.Core/Services/Notifications/DictionaryItemSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/DictionaryItemSavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class DictionaryItemSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Cache/DomainCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/DomainCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/DomainCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/DomainCacheRefresherNotification.cs index 53495ec6cc..326a7d2b64 100644 --- a/src/Umbraco.Core/Cache/DomainCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/DomainCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class DomainCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Services/Notifications/DomainDeletedNotification.cs b/src/Umbraco.Core/Notifications/DomainDeletedNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/DomainDeletedNotification.cs rename to src/Umbraco.Core/Notifications/DomainDeletedNotification.cs index 150850e3d2..b1b3a80ba1 100644 --- a/src/Umbraco.Core/Services/Notifications/DomainDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/DomainDeletedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class DomainDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/DomainDeletingNotification.cs b/src/Umbraco.Core/Notifications/DomainDeletingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/DomainDeletingNotification.cs rename to src/Umbraco.Core/Notifications/DomainDeletingNotification.cs index 822d45cd5b..cd678d3689 100644 --- a/src/Umbraco.Core/Services/Notifications/DomainDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/DomainDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class DomainDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/DomainSavedNotification.cs b/src/Umbraco.Core/Notifications/DomainSavedNotification.cs similarity index 90% rename from src/Umbraco.Core/Services/Notifications/DomainSavedNotification.cs rename to src/Umbraco.Core/Notifications/DomainSavedNotification.cs index 4854c41f23..61bd8ba3a4 100644 --- a/src/Umbraco.Core/Services/Notifications/DomainSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/DomainSavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class DomainSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/DomainSavingNotification.cs b/src/Umbraco.Core/Notifications/DomainSavingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/DomainSavingNotification.cs rename to src/Umbraco.Core/Notifications/DomainSavingNotification.cs index 3cc8567d3d..32a2d71a73 100644 --- a/src/Umbraco.Core/Services/Notifications/DomainSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/DomainSavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class DomainSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/EmptiedRecycleBinNotification.cs b/src/Umbraco.Core/Notifications/EmptiedRecycleBinNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/EmptiedRecycleBinNotification.cs rename to src/Umbraco.Core/Notifications/EmptiedRecycleBinNotification.cs index d42619b2c3..2773f3140f 100644 --- a/src/Umbraco.Core/Services/Notifications/EmptiedRecycleBinNotification.cs +++ b/src/Umbraco.Core/Notifications/EmptiedRecycleBinNotification.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public abstract class EmptiedRecycleBinNotification : StatefulNotification where T : class { diff --git a/src/Umbraco.Core/Services/Notifications/EmptyingRecycleBinNotification.cs b/src/Umbraco.Core/Notifications/EmptyingRecycleBinNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/EmptyingRecycleBinNotification.cs rename to src/Umbraco.Core/Notifications/EmptyingRecycleBinNotification.cs index 620be5ab15..3d1732acfa 100644 --- a/src/Umbraco.Core/Services/Notifications/EmptyingRecycleBinNotification.cs +++ b/src/Umbraco.Core/Notifications/EmptyingRecycleBinNotification.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public abstract class EmptyingRecycleBinNotification : StatefulNotification, ICancelableNotification where T : class { diff --git a/src/Umbraco.Core/Events/EntityContainerDeletedNotification.cs b/src/Umbraco.Core/Notifications/EntityContainerDeletedNotification.cs similarity index 79% rename from src/Umbraco.Core/Events/EntityContainerDeletedNotification.cs rename to src/Umbraco.Core/Notifications/EntityContainerDeletedNotification.cs index eca96962be..66c55e94ad 100644 --- a/src/Umbraco.Core/Events/EntityContainerDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/EntityContainerDeletedNotification.cs @@ -1,6 +1,7 @@ +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class EntityContainerDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Events/EntityContainerDeletingNotification.cs b/src/Umbraco.Core/Notifications/EntityContainerDeletingNotification.cs similarity index 79% rename from src/Umbraco.Core/Events/EntityContainerDeletingNotification.cs rename to src/Umbraco.Core/Notifications/EntityContainerDeletingNotification.cs index 58d0830cf9..45a7a5b6c8 100644 --- a/src/Umbraco.Core/Events/EntityContainerDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/EntityContainerDeletingNotification.cs @@ -1,6 +1,7 @@ +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class EntityContainerDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Events/EntityContainerRenamedNotification.cs b/src/Umbraco.Core/Notifications/EntityContainerRenamedNotification.cs similarity index 79% rename from src/Umbraco.Core/Events/EntityContainerRenamedNotification.cs rename to src/Umbraco.Core/Notifications/EntityContainerRenamedNotification.cs index dc1b858bd9..e6046c9a58 100644 --- a/src/Umbraco.Core/Events/EntityContainerRenamedNotification.cs +++ b/src/Umbraco.Core/Notifications/EntityContainerRenamedNotification.cs @@ -1,6 +1,7 @@ +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class EntityContainerRenamedNotification : RenamedNotification { diff --git a/src/Umbraco.Core/Events/EntityContainerRenamingNotification.cs b/src/Umbraco.Core/Notifications/EntityContainerRenamingNotification.cs similarity index 79% rename from src/Umbraco.Core/Events/EntityContainerRenamingNotification.cs rename to src/Umbraco.Core/Notifications/EntityContainerRenamingNotification.cs index f2408a1faf..c03d5f5ee3 100644 --- a/src/Umbraco.Core/Events/EntityContainerRenamingNotification.cs +++ b/src/Umbraco.Core/Notifications/EntityContainerRenamingNotification.cs @@ -1,6 +1,7 @@ +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class EntityContainerRenamingNotification : RenamingNotification { diff --git a/src/Umbraco.Core/Events/EntityContainerSavedNotification.cs b/src/Umbraco.Core/Notifications/EntityContainerSavedNotification.cs similarity index 79% rename from src/Umbraco.Core/Events/EntityContainerSavedNotification.cs rename to src/Umbraco.Core/Notifications/EntityContainerSavedNotification.cs index 108d6649df..33cac9effd 100644 --- a/src/Umbraco.Core/Events/EntityContainerSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/EntityContainerSavedNotification.cs @@ -1,6 +1,7 @@ +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class EntityContainerSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Events/EntityContainerSavingNotification.cs b/src/Umbraco.Core/Notifications/EntityContainerSavingNotification.cs similarity index 79% rename from src/Umbraco.Core/Events/EntityContainerSavingNotification.cs rename to src/Umbraco.Core/Notifications/EntityContainerSavingNotification.cs index 98a8a34926..25cbfc9311 100644 --- a/src/Umbraco.Core/Events/EntityContainerSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/EntityContainerSavingNotification.cs @@ -1,6 +1,7 @@ +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class EntityContainerSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Events/EntityRefreshNotification.cs b/src/Umbraco.Core/Notifications/EntityRefreshNotification.cs similarity index 80% rename from src/Umbraco.Core/Events/EntityRefreshNotification.cs rename to src/Umbraco.Core/Notifications/EntityRefreshNotification.cs index b69900d92b..1afc1fa078 100644 --- a/src/Umbraco.Core/Events/EntityRefreshNotification.cs +++ b/src/Umbraco.Core/Notifications/EntityRefreshNotification.cs @@ -1,6 +1,7 @@ +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class EntityRefreshNotification : ObjectNotification where T : class, IContentBase { diff --git a/src/Umbraco.Core/Events/EnumerableObjectNotification.cs b/src/Umbraco.Core/Notifications/EnumerableObjectNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/EnumerableObjectNotification.cs rename to src/Umbraco.Core/Notifications/EnumerableObjectNotification.cs index a80b8bf7ee..fde93f0139 100644 --- a/src/Umbraco.Core/Events/EnumerableObjectNotification.cs +++ b/src/Umbraco.Core/Notifications/EnumerableObjectNotification.cs @@ -2,8 +2,9 @@ // See LICENSE for more details. using System.Collections.Generic; +using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class EnumerableObjectNotification : ObjectNotification> { diff --git a/src/Umbraco.Core/Services/Notifications/ExportedMemberNotification.cs b/src/Umbraco.Core/Notifications/ExportedMemberNotification.cs similarity index 83% rename from src/Umbraco.Core/Services/Notifications/ExportedMemberNotification.cs rename to src/Umbraco.Core/Notifications/ExportedMemberNotification.cs index 0218710010..9cf69032e6 100644 --- a/src/Umbraco.Core/Services/Notifications/ExportedMemberNotification.cs +++ b/src/Umbraco.Core/Notifications/ExportedMemberNotification.cs @@ -1,8 +1,7 @@ -using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Membership; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class ExportedMemberNotification : INotification { diff --git a/src/Umbraco.Core/Events/ICancelableNotification.cs b/src/Umbraco.Core/Notifications/ICancelableNotification.cs similarity index 80% rename from src/Umbraco.Core/Events/ICancelableNotification.cs rename to src/Umbraco.Core/Notifications/ICancelableNotification.cs index df1abc672e..c30e6613fe 100644 --- a/src/Umbraco.Core/Events/ICancelableNotification.cs +++ b/src/Umbraco.Core/Notifications/ICancelableNotification.cs @@ -1,7 +1,7 @@ // Copyright (c) Umbraco. // See LICENSE for more details. -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public interface ICancelableNotification : INotification { diff --git a/src/Umbraco.Core/Events/INotification.cs b/src/Umbraco.Core/Notifications/INotification.cs similarity index 83% rename from src/Umbraco.Core/Events/INotification.cs rename to src/Umbraco.Core/Notifications/INotification.cs index 734e9343cf..2427da1454 100644 --- a/src/Umbraco.Core/Events/INotification.cs +++ b/src/Umbraco.Core/Notifications/INotification.cs @@ -1,7 +1,7 @@ // Copyright (c) Umbraco. // See LICENSE for more details. -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { /// /// A marker interface to represent a notification. diff --git a/src/Umbraco.Core/Events/IStatefulNotification.cs b/src/Umbraco.Core/Notifications/IStatefulNotification.cs similarity index 80% rename from src/Umbraco.Core/Events/IStatefulNotification.cs rename to src/Umbraco.Core/Notifications/IStatefulNotification.cs index dafebe6173..7fa2382038 100644 --- a/src/Umbraco.Core/Events/IStatefulNotification.cs +++ b/src/Umbraco.Core/Notifications/IStatefulNotification.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public interface IStatefulNotification : INotification { diff --git a/src/Umbraco.Core/Events/ImportedPackageNotification.cs b/src/Umbraco.Core/Notifications/ImportedPackageNotification.cs similarity index 92% rename from src/Umbraco.Core/Events/ImportedPackageNotification.cs rename to src/Umbraco.Core/Notifications/ImportedPackageNotification.cs index f4f0bac1b4..ada7a0aef9 100644 --- a/src/Umbraco.Core/Events/ImportedPackageNotification.cs +++ b/src/Umbraco.Core/Notifications/ImportedPackageNotification.cs @@ -1,7 +1,7 @@ using Umbraco.Cms.Core.Models.Packaging; using Umbraco.Cms.Core.Packaging; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class ImportedPackageNotification : StatefulNotification { diff --git a/src/Umbraco.Core/Events/ImportingPackageNotification.cs b/src/Umbraco.Core/Notifications/ImportingPackageNotification.cs similarity index 92% rename from src/Umbraco.Core/Events/ImportingPackageNotification.cs rename to src/Umbraco.Core/Notifications/ImportingPackageNotification.cs index 143fad3ff4..d9a8763e3a 100644 --- a/src/Umbraco.Core/Events/ImportingPackageNotification.cs +++ b/src/Umbraco.Core/Notifications/ImportingPackageNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Models.Packaging; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class ImportingPackageNotification : StatefulNotification, ICancelableNotification { diff --git a/src/Umbraco.Core/Cache/LanguageCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/LanguageCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/LanguageCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/LanguageCacheRefresherNotification.cs index fde0090c28..436d8a4fbf 100644 --- a/src/Umbraco.Core/Cache/LanguageCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/LanguageCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class LanguageCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Services/Notifications/LanguageDeletedNotification.cs b/src/Umbraco.Core/Notifications/LanguageDeletedNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/LanguageDeletedNotification.cs rename to src/Umbraco.Core/Notifications/LanguageDeletedNotification.cs index a8242f4ced..ccc17c8a90 100644 --- a/src/Umbraco.Core/Services/Notifications/LanguageDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/LanguageDeletedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class LanguageDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/LanguageDeletingNotification.cs b/src/Umbraco.Core/Notifications/LanguageDeletingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/LanguageDeletingNotification.cs rename to src/Umbraco.Core/Notifications/LanguageDeletingNotification.cs index 52897714c3..c4e4682500 100644 --- a/src/Umbraco.Core/Services/Notifications/LanguageDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/LanguageDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class LanguageDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/LanguageSavedNotification.cs b/src/Umbraco.Core/Notifications/LanguageSavedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/LanguageSavedNotification.cs rename to src/Umbraco.Core/Notifications/LanguageSavedNotification.cs index 39b3008eaa..29265c86ca 100644 --- a/src/Umbraco.Core/Services/Notifications/LanguageSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/LanguageSavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class LanguageSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/LanguageSavingNotification.cs b/src/Umbraco.Core/Notifications/LanguageSavingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/LanguageSavingNotification.cs rename to src/Umbraco.Core/Notifications/LanguageSavingNotification.cs index 49620634df..5fcb892e25 100644 --- a/src/Umbraco.Core/Services/Notifications/LanguageSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/LanguageSavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class LanguageSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Cache/MacroCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/MacroCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/MacroCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/MacroCacheRefresherNotification.cs index f5ca7985c8..5fb5554b1b 100644 --- a/src/Umbraco.Core/Cache/MacroCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/MacroCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class MacroCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MacroDeletedNotification.cs b/src/Umbraco.Core/Notifications/MacroDeletedNotification.cs similarity index 84% rename from src/Umbraco.Core/Services/Notifications/MacroDeletedNotification.cs rename to src/Umbraco.Core/Notifications/MacroDeletedNotification.cs index e5c56c8bcf..237cce38fe 100644 --- a/src/Umbraco.Core/Services/Notifications/MacroDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/MacroDeletedNotification.cs @@ -1,7 +1,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class MacroDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MacroDeletingNotification.cs b/src/Umbraco.Core/Notifications/MacroDeletingNotification.cs similarity index 90% rename from src/Umbraco.Core/Services/Notifications/MacroDeletingNotification.cs rename to src/Umbraco.Core/Notifications/MacroDeletingNotification.cs index bd64c33ff6..d36a9896bc 100644 --- a/src/Umbraco.Core/Services/Notifications/MacroDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/MacroDeletingNotification.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class MacroDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MacroSavedNotification.cs b/src/Umbraco.Core/Notifications/MacroSavedNotification.cs similarity index 89% rename from src/Umbraco.Core/Services/Notifications/MacroSavedNotification.cs rename to src/Umbraco.Core/Notifications/MacroSavedNotification.cs index d7b85246be..8aa776dcc6 100644 --- a/src/Umbraco.Core/Services/Notifications/MacroSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/MacroSavedNotification.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class MacroSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MacroSavingNotification.cs b/src/Umbraco.Core/Notifications/MacroSavingNotification.cs similarity index 89% rename from src/Umbraco.Core/Services/Notifications/MacroSavingNotification.cs rename to src/Umbraco.Core/Notifications/MacroSavingNotification.cs index c310951d23..965ee6b22e 100644 --- a/src/Umbraco.Core/Services/Notifications/MacroSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/MacroSavingNotification.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class MacroSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Cache/MediaCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/MediaCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/MediaCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/MediaCacheRefresherNotification.cs index afdd60bb4a..079475232d 100644 --- a/src/Umbraco.Core/Cache/MediaCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class MediaCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MediaDeletedNotification.cs b/src/Umbraco.Core/Notifications/MediaDeletedNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/MediaDeletedNotification.cs rename to src/Umbraco.Core/Notifications/MediaDeletedNotification.cs index 9ad1e75a41..b8cce7e747 100644 --- a/src/Umbraco.Core/Services/Notifications/MediaDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaDeletedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MediaDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MediaDeletedVersionsNotification.cs b/src/Umbraco.Core/Notifications/MediaDeletedVersionsNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/MediaDeletedVersionsNotification.cs rename to src/Umbraco.Core/Notifications/MediaDeletedVersionsNotification.cs index 7efa10f317..6bbdb3c098 100644 --- a/src/Umbraco.Core/Services/Notifications/MediaDeletedVersionsNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaDeletedVersionsNotification.cs @@ -5,7 +5,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MediaDeletedVersionsNotification : DeletedVersionsNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MediaDeletingNotification.cs b/src/Umbraco.Core/Notifications/MediaDeletingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/MediaDeletingNotification.cs rename to src/Umbraco.Core/Notifications/MediaDeletingNotification.cs index 027e0f7b50..358a553b28 100644 --- a/src/Umbraco.Core/Services/Notifications/MediaDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MediaDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MediaDeletingVersionsNotification.cs b/src/Umbraco.Core/Notifications/MediaDeletingVersionsNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/MediaDeletingVersionsNotification.cs rename to src/Umbraco.Core/Notifications/MediaDeletingVersionsNotification.cs index 361739cee3..fa7b3ba8e0 100644 --- a/src/Umbraco.Core/Services/Notifications/MediaDeletingVersionsNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaDeletingVersionsNotification.cs @@ -5,7 +5,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MediaDeletingVersionsNotification : DeletingVersionsNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MediaEmptiedRecycleBinNotification.cs b/src/Umbraco.Core/Notifications/MediaEmptiedRecycleBinNotification.cs similarity index 89% rename from src/Umbraco.Core/Services/Notifications/MediaEmptiedRecycleBinNotification.cs rename to src/Umbraco.Core/Notifications/MediaEmptiedRecycleBinNotification.cs index 9e0ef67d7d..0862296925 100644 --- a/src/Umbraco.Core/Services/Notifications/MediaEmptiedRecycleBinNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaEmptiedRecycleBinNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MediaEmptiedRecycleBinNotification : EmptiedRecycleBinNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MediaEmptyingRecycleBinNotification.cs b/src/Umbraco.Core/Notifications/MediaEmptyingRecycleBinNotification.cs similarity index 89% rename from src/Umbraco.Core/Services/Notifications/MediaEmptyingRecycleBinNotification.cs rename to src/Umbraco.Core/Notifications/MediaEmptyingRecycleBinNotification.cs index a7aebd79fe..4e257cfb38 100644 --- a/src/Umbraco.Core/Services/Notifications/MediaEmptyingRecycleBinNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaEmptyingRecycleBinNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MediaEmptyingRecycleBinNotification : EmptyingRecycleBinNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MediaMovedNotification.cs b/src/Umbraco.Core/Notifications/MediaMovedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/MediaMovedNotification.cs rename to src/Umbraco.Core/Notifications/MediaMovedNotification.cs index fed8da869f..2012f16f4b 100644 --- a/src/Umbraco.Core/Services/Notifications/MediaMovedNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaMovedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MediaMovedNotification : MovedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MediaMovedToRecycleBinNotification.cs b/src/Umbraco.Core/Notifications/MediaMovedToRecycleBinNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/MediaMovedToRecycleBinNotification.cs rename to src/Umbraco.Core/Notifications/MediaMovedToRecycleBinNotification.cs index 599ea97ce4..44120674bd 100644 --- a/src/Umbraco.Core/Services/Notifications/MediaMovedToRecycleBinNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaMovedToRecycleBinNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MediaMovedToRecycleBinNotification : MovedToRecycleBinNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MediaMovingNotification.cs b/src/Umbraco.Core/Notifications/MediaMovingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/MediaMovingNotification.cs rename to src/Umbraco.Core/Notifications/MediaMovingNotification.cs index 30cac1fd87..fcfb50787b 100644 --- a/src/Umbraco.Core/Services/Notifications/MediaMovingNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaMovingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MediaMovingNotification : MovingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MediaMovingToRecycleBinNotification.cs b/src/Umbraco.Core/Notifications/MediaMovingToRecycleBinNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/MediaMovingToRecycleBinNotification.cs rename to src/Umbraco.Core/Notifications/MediaMovingToRecycleBinNotification.cs index 9895c4296b..856b66c0c4 100644 --- a/src/Umbraco.Core/Services/Notifications/MediaMovingToRecycleBinNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaMovingToRecycleBinNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MediaMovingToRecycleBinNotification : MovingToRecycleBinNotification { diff --git a/src/Umbraco.Core/Events/MediaRefreshNotification.cs b/src/Umbraco.Core/Notifications/MediaRefreshNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/MediaRefreshNotification.cs rename to src/Umbraco.Core/Notifications/MediaRefreshNotification.cs index b05eaeebb5..1c8b8b9bea 100644 --- a/src/Umbraco.Core/Events/MediaRefreshNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaRefreshNotification.cs @@ -1,8 +1,9 @@ using System; using System.ComponentModel; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { [Obsolete("This is only used for the internal cache and will change, use tree change notifications instead")] [EditorBrowsable(EditorBrowsableState.Never)] diff --git a/src/Umbraco.Core/Services/Notifications/MediaSavedNotification.cs b/src/Umbraco.Core/Notifications/MediaSavedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/MediaSavedNotification.cs rename to src/Umbraco.Core/Notifications/MediaSavedNotification.cs index e23e95c27d..addeda617e 100644 --- a/src/Umbraco.Core/Services/Notifications/MediaSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaSavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MediaSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MediaSavingNotification.cs b/src/Umbraco.Core/Notifications/MediaSavingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/MediaSavingNotification.cs rename to src/Umbraco.Core/Notifications/MediaSavingNotification.cs index 50deaffaed..638d27c968 100644 --- a/src/Umbraco.Core/Services/Notifications/MediaSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaSavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MediaSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MediaTreeChangeNotification.cs b/src/Umbraco.Core/Notifications/MediaTreeChangeNotification.cs similarity index 95% rename from src/Umbraco.Core/Services/Notifications/MediaTreeChangeNotification.cs rename to src/Umbraco.Core/Notifications/MediaTreeChangeNotification.cs index 264bdb6bb2..00e0e6b42c 100644 --- a/src/Umbraco.Core/Services/Notifications/MediaTreeChangeNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaTreeChangeNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services.Changes; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class MediaTreeChangeNotification : TreeChangeNotification { diff --git a/src/Umbraco.Core/Events/MediaTypeChangedNotification.cs b/src/Umbraco.Core/Notifications/MediaTypeChangedNotification.cs similarity index 88% rename from src/Umbraco.Core/Events/MediaTypeChangedNotification.cs rename to src/Umbraco.Core/Notifications/MediaTypeChangedNotification.cs index 9018ab3186..322a6bb1ab 100644 --- a/src/Umbraco.Core/Events/MediaTypeChangedNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaTypeChangedNotification.cs @@ -1,8 +1,9 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services.Changes; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class MediaTypeChangedNotification : ContentTypeChangeNotification { diff --git a/src/Umbraco.Core/Events/MediaTypeDeletedNotification.cs b/src/Umbraco.Core/Notifications/MediaTypeDeletedNotification.cs similarity index 85% rename from src/Umbraco.Core/Events/MediaTypeDeletedNotification.cs rename to src/Umbraco.Core/Notifications/MediaTypeDeletedNotification.cs index 77ff9ce720..59c7114ca0 100644 --- a/src/Umbraco.Core/Events/MediaTypeDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaTypeDeletedNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class MediaTypeDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Events/MediaTypeDeletingNotification.cs b/src/Umbraco.Core/Notifications/MediaTypeDeletingNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/MediaTypeDeletingNotification.cs rename to src/Umbraco.Core/Notifications/MediaTypeDeletingNotification.cs index 4aa45b0dee..1cb4f7c99d 100644 --- a/src/Umbraco.Core/Events/MediaTypeDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaTypeDeletingNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class MediaTypeDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Events/MediaTypeMovedNotification.cs b/src/Umbraco.Core/Notifications/MediaTypeMovedNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/MediaTypeMovedNotification.cs rename to src/Umbraco.Core/Notifications/MediaTypeMovedNotification.cs index ab53cc6cce..c17aa222de 100644 --- a/src/Umbraco.Core/Events/MediaTypeMovedNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaTypeMovedNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class MediaTypeMovedNotification : MovedNotification { diff --git a/src/Umbraco.Core/Events/MediaTypeMovingNotification.cs b/src/Umbraco.Core/Notifications/MediaTypeMovingNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/MediaTypeMovingNotification.cs rename to src/Umbraco.Core/Notifications/MediaTypeMovingNotification.cs index f9bdd78ab1..43499430b0 100644 --- a/src/Umbraco.Core/Events/MediaTypeMovingNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaTypeMovingNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class MediaTypeMovingNotification : MovingNotification { diff --git a/src/Umbraco.Core/Events/MediaTypeRefreshedNotification.cs b/src/Umbraco.Core/Notifications/MediaTypeRefreshedNotification.cs similarity index 91% rename from src/Umbraco.Core/Events/MediaTypeRefreshedNotification.cs rename to src/Umbraco.Core/Notifications/MediaTypeRefreshedNotification.cs index 91d04811bb..6b59e3220e 100644 --- a/src/Umbraco.Core/Events/MediaTypeRefreshedNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaTypeRefreshedNotification.cs @@ -1,10 +1,11 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services.Changes; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { [Obsolete("This is only used for the internal cache and will change, use tree change notifications instead")] [EditorBrowsable(EditorBrowsableState.Never)] diff --git a/src/Umbraco.Core/Events/MediaTypeSavedNotification.cs b/src/Umbraco.Core/Notifications/MediaTypeSavedNotification.cs similarity index 85% rename from src/Umbraco.Core/Events/MediaTypeSavedNotification.cs rename to src/Umbraco.Core/Notifications/MediaTypeSavedNotification.cs index 212b8e0933..b4b2372b7f 100644 --- a/src/Umbraco.Core/Events/MediaTypeSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaTypeSavedNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class MediaTypeSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Events/MediaTypeSavingNotification.cs b/src/Umbraco.Core/Notifications/MediaTypeSavingNotification.cs similarity index 85% rename from src/Umbraco.Core/Events/MediaTypeSavingNotification.cs rename to src/Umbraco.Core/Notifications/MediaTypeSavingNotification.cs index 8868b19991..0a93f08671 100644 --- a/src/Umbraco.Core/Events/MediaTypeSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaTypeSavingNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class MediaTypeSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Cache/MemberCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/MemberCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/MemberCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/MemberCacheRefresherNotification.cs index 6154505947..c2d920843d 100644 --- a/src/Umbraco.Core/Cache/MemberCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class MemberCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MemberDeletedNotification.cs b/src/Umbraco.Core/Notifications/MemberDeletedNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/MemberDeletedNotification.cs rename to src/Umbraco.Core/Notifications/MemberDeletedNotification.cs index 09e27e3c43..7539d6b133 100644 --- a/src/Umbraco.Core/Services/Notifications/MemberDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberDeletedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MemberDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MemberDeletingNotification.cs b/src/Umbraco.Core/Notifications/MemberDeletingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/MemberDeletingNotification.cs rename to src/Umbraco.Core/Notifications/MemberDeletingNotification.cs index dd0fbcb70c..9d09d40e15 100644 --- a/src/Umbraco.Core/Services/Notifications/MemberDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MemberDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Cache/MemberGroupCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/MemberGroupCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/MemberGroupCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/MemberGroupCacheRefresherNotification.cs index 643e9bd51e..f882b61167 100644 --- a/src/Umbraco.Core/Cache/MemberGroupCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberGroupCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class MemberGroupCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MemberGroupDeletedNotification.cs b/src/Umbraco.Core/Notifications/MemberGroupDeletedNotification.cs similarity index 85% rename from src/Umbraco.Core/Services/Notifications/MemberGroupDeletedNotification.cs rename to src/Umbraco.Core/Notifications/MemberGroupDeletedNotification.cs index 59a827ee20..8665cc5f71 100644 --- a/src/Umbraco.Core/Services/Notifications/MemberGroupDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberGroupDeletedNotification.cs @@ -1,7 +1,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class MemberGroupDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MemberGroupDeletingNotification.cs b/src/Umbraco.Core/Notifications/MemberGroupDeletingNotification.cs similarity index 90% rename from src/Umbraco.Core/Services/Notifications/MemberGroupDeletingNotification.cs rename to src/Umbraco.Core/Notifications/MemberGroupDeletingNotification.cs index 4f3d06dbc7..2b0f94af64 100644 --- a/src/Umbraco.Core/Services/Notifications/MemberGroupDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberGroupDeletingNotification.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class MemberGroupDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MemberGroupSavedNotification.cs b/src/Umbraco.Core/Notifications/MemberGroupSavedNotification.cs similarity index 90% rename from src/Umbraco.Core/Services/Notifications/MemberGroupSavedNotification.cs rename to src/Umbraco.Core/Notifications/MemberGroupSavedNotification.cs index 745660d8c7..e5beffe76b 100644 --- a/src/Umbraco.Core/Services/Notifications/MemberGroupSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberGroupSavedNotification.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class MemberGroupSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MemberGroupSavingNotification.cs b/src/Umbraco.Core/Notifications/MemberGroupSavingNotification.cs similarity index 90% rename from src/Umbraco.Core/Services/Notifications/MemberGroupSavingNotification.cs rename to src/Umbraco.Core/Notifications/MemberGroupSavingNotification.cs index 49d88ea92a..a0341ab2ef 100644 --- a/src/Umbraco.Core/Services/Notifications/MemberGroupSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberGroupSavingNotification.cs @@ -2,7 +2,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class MemberGroupSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Events/MemberRefreshNotification.cs b/src/Umbraco.Core/Notifications/MemberRefreshNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/MemberRefreshNotification.cs rename to src/Umbraco.Core/Notifications/MemberRefreshNotification.cs index a5ea0911bf..a22c48348f 100644 --- a/src/Umbraco.Core/Events/MemberRefreshNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberRefreshNotification.cs @@ -1,8 +1,9 @@ using System; using System.ComponentModel; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { [Obsolete("This is only used for the internal cache and will change, use tree change notifications instead")] [EditorBrowsable(EditorBrowsableState.Never)] diff --git a/src/Umbraco.Core/Services/Notifications/MemberRolesNotification.cs b/src/Umbraco.Core/Notifications/MemberRolesNotification.cs similarity index 79% rename from src/Umbraco.Core/Services/Notifications/MemberRolesNotification.cs rename to src/Umbraco.Core/Notifications/MemberRolesNotification.cs index bfc576fbb0..9ea6548833 100644 --- a/src/Umbraco.Core/Services/Notifications/MemberRolesNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberRolesNotification.cs @@ -1,6 +1,4 @@ -using Umbraco.Cms.Core.Events; - -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public abstract class MemberRolesNotification : INotification { diff --git a/src/Umbraco.Core/Services/Notifications/MemberSavedNotification.cs b/src/Umbraco.Core/Notifications/MemberSavedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/MemberSavedNotification.cs rename to src/Umbraco.Core/Notifications/MemberSavedNotification.cs index 9f245502bd..2c4f4755eb 100644 --- a/src/Umbraco.Core/Services/Notifications/MemberSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberSavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MemberSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MemberSavingNotification.cs b/src/Umbraco.Core/Notifications/MemberSavingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/MemberSavingNotification.cs rename to src/Umbraco.Core/Notifications/MemberSavingNotification.cs index 37c90a9127..fc8198c6f9 100644 --- a/src/Umbraco.Core/Services/Notifications/MemberSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberSavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MemberSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Events/MemberTypeChangedNotification.cs b/src/Umbraco.Core/Notifications/MemberTypeChangedNotification.cs similarity index 88% rename from src/Umbraco.Core/Events/MemberTypeChangedNotification.cs rename to src/Umbraco.Core/Notifications/MemberTypeChangedNotification.cs index 576f52cf09..c22908c108 100644 --- a/src/Umbraco.Core/Events/MemberTypeChangedNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberTypeChangedNotification.cs @@ -1,8 +1,9 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services.Changes; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class MemberTypeChangedNotification : ContentTypeChangeNotification { diff --git a/src/Umbraco.Core/Events/MemberTypeDeletedNotification.cs b/src/Umbraco.Core/Notifications/MemberTypeDeletedNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/MemberTypeDeletedNotification.cs rename to src/Umbraco.Core/Notifications/MemberTypeDeletedNotification.cs index bdd6108dd0..490db24cf3 100644 --- a/src/Umbraco.Core/Events/MemberTypeDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberTypeDeletedNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class MemberTypeDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Events/MemberTypeDeletingNotification.cs b/src/Umbraco.Core/Notifications/MemberTypeDeletingNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/MemberTypeDeletingNotification.cs rename to src/Umbraco.Core/Notifications/MemberTypeDeletingNotification.cs index 8c17f31a26..04821eb0c2 100644 --- a/src/Umbraco.Core/Events/MemberTypeDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberTypeDeletingNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class MemberTypeDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Events/MemberTypeMovedNotification.cs b/src/Umbraco.Core/Notifications/MemberTypeMovedNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/MemberTypeMovedNotification.cs rename to src/Umbraco.Core/Notifications/MemberTypeMovedNotification.cs index 325c765451..8e74076119 100644 --- a/src/Umbraco.Core/Events/MemberTypeMovedNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberTypeMovedNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class MemberTypeMovedNotification : MovedNotification { diff --git a/src/Umbraco.Core/Events/MemberTypeMovingNotification.cs b/src/Umbraco.Core/Notifications/MemberTypeMovingNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/MemberTypeMovingNotification.cs rename to src/Umbraco.Core/Notifications/MemberTypeMovingNotification.cs index eff8f8b9ef..b4627aaf30 100644 --- a/src/Umbraco.Core/Events/MemberTypeMovingNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberTypeMovingNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class MemberTypeMovingNotification : MovingNotification { diff --git a/src/Umbraco.Core/Events/MemberTypeRefreshedNotification.cs b/src/Umbraco.Core/Notifications/MemberTypeRefreshedNotification.cs similarity index 91% rename from src/Umbraco.Core/Events/MemberTypeRefreshedNotification.cs rename to src/Umbraco.Core/Notifications/MemberTypeRefreshedNotification.cs index 5cefd64946..89147a523f 100644 --- a/src/Umbraco.Core/Events/MemberTypeRefreshedNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberTypeRefreshedNotification.cs @@ -1,10 +1,11 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services.Changes; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { [Obsolete("This is only used for the internal cache and will change, use tree change notifications instead")] [EditorBrowsable(EditorBrowsableState.Never)] diff --git a/src/Umbraco.Core/Events/MemberTypeSavedNotification.cs b/src/Umbraco.Core/Notifications/MemberTypeSavedNotification.cs similarity index 85% rename from src/Umbraco.Core/Events/MemberTypeSavedNotification.cs rename to src/Umbraco.Core/Notifications/MemberTypeSavedNotification.cs index 5d877dea3f..768f9e8bb0 100644 --- a/src/Umbraco.Core/Events/MemberTypeSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberTypeSavedNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class MemberTypeSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Events/MemberTypeSavingNotification.cs b/src/Umbraco.Core/Notifications/MemberTypeSavingNotification.cs similarity index 85% rename from src/Umbraco.Core/Events/MemberTypeSavingNotification.cs rename to src/Umbraco.Core/Notifications/MemberTypeSavingNotification.cs index de06be6c8d..598aadffa4 100644 --- a/src/Umbraco.Core/Events/MemberTypeSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberTypeSavingNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class MemberTypeSavingNotification : SavingNotification { diff --git a/src/Umbraco.Web.Common/ModelBinders/ModelBindingError.cs b/src/Umbraco.Core/Notifications/ModelBindingErrorNotification.cs similarity index 78% rename from src/Umbraco.Web.Common/ModelBinders/ModelBindingError.cs rename to src/Umbraco.Core/Notifications/ModelBindingErrorNotification.cs index f32d1273a4..e4adadcd52 100644 --- a/src/Umbraco.Web.Common/ModelBinders/ModelBindingError.cs +++ b/src/Umbraco.Core/Notifications/ModelBindingErrorNotification.cs @@ -1,18 +1,17 @@ using System; using System.Text; -using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Web.Common.ModelBinders +namespace Umbraco.Cms.Core.Notifications { /// /// Contains event data for the event. /// - public class ModelBindingError : INotification + public class ModelBindingErrorNotification : INotification { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public ModelBindingError(Type sourceType, Type modelType, StringBuilder message) + public ModelBindingErrorNotification(Type sourceType, Type modelType, StringBuilder message) { SourceType = sourceType; ModelType = modelType; diff --git a/src/Umbraco.Core/Events/MovedNotification.cs b/src/Umbraco.Core/Notifications/MovedNotification.cs similarity index 88% rename from src/Umbraco.Core/Events/MovedNotification.cs rename to src/Umbraco.Core/Notifications/MovedNotification.cs index af96e70feb..4573d5e45a 100644 --- a/src/Umbraco.Core/Events/MovedNotification.cs +++ b/src/Umbraco.Core/Notifications/MovedNotification.cs @@ -2,8 +2,9 @@ // See LICENSE for more details. using System.Collections.Generic; +using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class MovedNotification : ObjectNotification>> { diff --git a/src/Umbraco.Core/Services/Notifications/MovedToRecycleBinNotification.cs b/src/Umbraco.Core/Notifications/MovedToRecycleBinNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/MovedToRecycleBinNotification.cs rename to src/Umbraco.Core/Notifications/MovedToRecycleBinNotification.cs index 8470edd311..1e02d30eb7 100644 --- a/src/Umbraco.Core/Services/Notifications/MovedToRecycleBinNotification.cs +++ b/src/Umbraco.Core/Notifications/MovedToRecycleBinNotification.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public abstract class MovedToRecycleBinNotification : ObjectNotification>> { diff --git a/src/Umbraco.Core/Events/MovingNotification.cs b/src/Umbraco.Core/Notifications/MovingNotification.cs similarity index 88% rename from src/Umbraco.Core/Events/MovingNotification.cs rename to src/Umbraco.Core/Notifications/MovingNotification.cs index 917cc283aa..6bf493fc1b 100644 --- a/src/Umbraco.Core/Events/MovingNotification.cs +++ b/src/Umbraco.Core/Notifications/MovingNotification.cs @@ -2,8 +2,9 @@ // See LICENSE for more details. using System.Collections.Generic; +using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class MovingNotification : CancelableObjectNotification>> { diff --git a/src/Umbraco.Core/Services/Notifications/MovingToRecycleBinNotification.cs b/src/Umbraco.Core/Notifications/MovingToRecycleBinNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/MovingToRecycleBinNotification.cs rename to src/Umbraco.Core/Notifications/MovingToRecycleBinNotification.cs index b62e6cfa94..ef8c36ce6f 100644 --- a/src/Umbraco.Core/Services/Notifications/MovingToRecycleBinNotification.cs +++ b/src/Umbraco.Core/Notifications/MovingToRecycleBinNotification.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public abstract class MovingToRecycleBinNotification : CancelableObjectNotification>> { diff --git a/src/Umbraco.Core/Events/NotificationExtensions.cs b/src/Umbraco.Core/Notifications/NotificationExtensions.cs similarity index 92% rename from src/Umbraco.Core/Events/NotificationExtensions.cs rename to src/Umbraco.Core/Notifications/NotificationExtensions.cs index de51f016e4..53677c05a1 100644 --- a/src/Umbraco.Core/Events/NotificationExtensions.cs +++ b/src/Umbraco.Core/Notifications/NotificationExtensions.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public static class NotificationExtensions { diff --git a/src/Umbraco.Core/Events/ObjectNotification.cs b/src/Umbraco.Core/Notifications/ObjectNotification.cs similarity index 84% rename from src/Umbraco.Core/Events/ObjectNotification.cs rename to src/Umbraco.Core/Notifications/ObjectNotification.cs index f1d957d42b..a550754d32 100644 --- a/src/Umbraco.Core/Events/ObjectNotification.cs +++ b/src/Umbraco.Core/Notifications/ObjectNotification.cs @@ -1,7 +1,9 @@ // Copyright (c) Umbraco. // See LICENSE for more details. -namespace Umbraco.Cms.Core.Events +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Core.Notifications { public abstract class ObjectNotification : StatefulNotification where T : class { diff --git a/src/Umbraco.Core/Services/Notifications/PartialViewCreatedNotification.cs b/src/Umbraco.Core/Notifications/PartialViewCreatedNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/PartialViewCreatedNotification.cs rename to src/Umbraco.Core/Notifications/PartialViewCreatedNotification.cs index 92f5738e71..3f34c4b1c6 100644 --- a/src/Umbraco.Core/Services/Notifications/PartialViewCreatedNotification.cs +++ b/src/Umbraco.Core/Notifications/PartialViewCreatedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class PartialViewCreatedNotification : CreatedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/PartialViewCreatingNotification.cs b/src/Umbraco.Core/Notifications/PartialViewCreatingNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/PartialViewCreatingNotification.cs rename to src/Umbraco.Core/Notifications/PartialViewCreatingNotification.cs index 1439db3af1..425879fb06 100644 --- a/src/Umbraco.Core/Services/Notifications/PartialViewCreatingNotification.cs +++ b/src/Umbraco.Core/Notifications/PartialViewCreatingNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class PartialViewCreatingNotification : CreatingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/PartialViewDeletedNotification.cs b/src/Umbraco.Core/Notifications/PartialViewDeletedNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/PartialViewDeletedNotification.cs rename to src/Umbraco.Core/Notifications/PartialViewDeletedNotification.cs index 60d6d1aae4..4ef4058b5c 100644 --- a/src/Umbraco.Core/Services/Notifications/PartialViewDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/PartialViewDeletedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class PartialViewDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/PartialViewDeletingNotification.cs b/src/Umbraco.Core/Notifications/PartialViewDeletingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/PartialViewDeletingNotification.cs rename to src/Umbraco.Core/Notifications/PartialViewDeletingNotification.cs index 67ee8314c7..6473713408 100644 --- a/src/Umbraco.Core/Services/Notifications/PartialViewDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/PartialViewDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class PartialViewDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/PartialViewSavedNotification.cs b/src/Umbraco.Core/Notifications/PartialViewSavedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/PartialViewSavedNotification.cs rename to src/Umbraco.Core/Notifications/PartialViewSavedNotification.cs index 9ecde239a6..d50ed08faf 100644 --- a/src/Umbraco.Core/Services/Notifications/PartialViewSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/PartialViewSavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class PartialViewSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/PartialViewSavingNotification.cs b/src/Umbraco.Core/Notifications/PartialViewSavingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/PartialViewSavingNotification.cs rename to src/Umbraco.Core/Notifications/PartialViewSavingNotification.cs index 98140b1c8a..fd2e0ee34a 100644 --- a/src/Umbraco.Core/Services/Notifications/PartialViewSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/PartialViewSavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class PartialViewSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Cache/PublicAccessCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/PublicAccessCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/PublicAccessCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/PublicAccessCacheRefresherNotification.cs index d3334571b3..1e753217ab 100644 --- a/src/Umbraco.Core/Cache/PublicAccessCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/PublicAccessCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class PublicAccessCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Services/Notifications/PublicAccessEntryDeletedNotification.cs b/src/Umbraco.Core/Notifications/PublicAccessEntryDeletedNotification.cs similarity index 88% rename from src/Umbraco.Core/Services/Notifications/PublicAccessEntryDeletedNotification.cs rename to src/Umbraco.Core/Notifications/PublicAccessEntryDeletedNotification.cs index 1dd8fb6bca..f6aa16500a 100644 --- a/src/Umbraco.Core/Services/Notifications/PublicAccessEntryDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/PublicAccessEntryDeletedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class PublicAccessEntryDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/PublicAccessEntryDeletingNotification.cs b/src/Umbraco.Core/Notifications/PublicAccessEntryDeletingNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/PublicAccessEntryDeletingNotification.cs rename to src/Umbraco.Core/Notifications/PublicAccessEntryDeletingNotification.cs index a02388c7b4..42c4c1bdb9 100644 --- a/src/Umbraco.Core/Services/Notifications/PublicAccessEntryDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/PublicAccessEntryDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class PublicAccessEntryDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/PublicAccessEntrySavedNotification.cs b/src/Umbraco.Core/Notifications/PublicAccessEntrySavedNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/PublicAccessEntrySavedNotification.cs rename to src/Umbraco.Core/Notifications/PublicAccessEntrySavedNotification.cs index 6f72bc6ed3..8c0d253500 100644 --- a/src/Umbraco.Core/Services/Notifications/PublicAccessEntrySavedNotification.cs +++ b/src/Umbraco.Core/Notifications/PublicAccessEntrySavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class PublicAccessEntrySavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/PublicAccessEntrySavingNotification.cs b/src/Umbraco.Core/Notifications/PublicAccessEntrySavingNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/PublicAccessEntrySavingNotification.cs rename to src/Umbraco.Core/Notifications/PublicAccessEntrySavingNotification.cs index 57a6709b13..3fbd666b8d 100644 --- a/src/Umbraco.Core/Services/Notifications/PublicAccessEntrySavingNotification.cs +++ b/src/Umbraco.Core/Notifications/PublicAccessEntrySavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class PublicAccessEntrySavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/RelationDeletedNotification.cs b/src/Umbraco.Core/Notifications/RelationDeletedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/RelationDeletedNotification.cs rename to src/Umbraco.Core/Notifications/RelationDeletedNotification.cs index ca613c59c9..f7af0e9b29 100644 --- a/src/Umbraco.Core/Services/Notifications/RelationDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/RelationDeletedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class RelationDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/RelationDeletingNotification.cs b/src/Umbraco.Core/Notifications/RelationDeletingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/RelationDeletingNotification.cs rename to src/Umbraco.Core/Notifications/RelationDeletingNotification.cs index 7684d24c3d..8873d95226 100644 --- a/src/Umbraco.Core/Services/Notifications/RelationDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/RelationDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class RelationDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/RelationSavedNotification.cs b/src/Umbraco.Core/Notifications/RelationSavedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/RelationSavedNotification.cs rename to src/Umbraco.Core/Notifications/RelationSavedNotification.cs index c24e71f4bb..8b0313f87c 100644 --- a/src/Umbraco.Core/Services/Notifications/RelationSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/RelationSavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class RelationSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/RelationSavingNotification.cs b/src/Umbraco.Core/Notifications/RelationSavingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/RelationSavingNotification.cs rename to src/Umbraco.Core/Notifications/RelationSavingNotification.cs index f88d42c10a..5afe71da53 100644 --- a/src/Umbraco.Core/Services/Notifications/RelationSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/RelationSavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class RelationSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Cache/RelationTypeCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/RelationTypeCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/RelationTypeCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/RelationTypeCacheRefresherNotification.cs index 851eba915d..ff8cf52891 100644 --- a/src/Umbraco.Core/Cache/RelationTypeCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/RelationTypeCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class RelationTypeCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Services/Notifications/RelationTypeDeletedNotification.cs b/src/Umbraco.Core/Notifications/RelationTypeDeletedNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/RelationTypeDeletedNotification.cs rename to src/Umbraco.Core/Notifications/RelationTypeDeletedNotification.cs index eee42da028..8534edcb49 100644 --- a/src/Umbraco.Core/Services/Notifications/RelationTypeDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/RelationTypeDeletedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class RelationTypeDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/RelationTypeDeletingNotification.cs b/src/Umbraco.Core/Notifications/RelationTypeDeletingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/RelationTypeDeletingNotification.cs rename to src/Umbraco.Core/Notifications/RelationTypeDeletingNotification.cs index 1749817677..904a82c08b 100644 --- a/src/Umbraco.Core/Services/Notifications/RelationTypeDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/RelationTypeDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class RelationTypeDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/RelationTypeSavedNotification.cs b/src/Umbraco.Core/Notifications/RelationTypeSavedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/RelationTypeSavedNotification.cs rename to src/Umbraco.Core/Notifications/RelationTypeSavedNotification.cs index 489f53dc65..e2e69475d7 100644 --- a/src/Umbraco.Core/Services/Notifications/RelationTypeSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/RelationTypeSavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class RelationTypeSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/RelationTypeSavingNotification.cs b/src/Umbraco.Core/Notifications/RelationTypeSavingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/RelationTypeSavingNotification.cs rename to src/Umbraco.Core/Notifications/RelationTypeSavingNotification.cs index 402f6c6eeb..2fdebe97e7 100644 --- a/src/Umbraco.Core/Services/Notifications/RelationTypeSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/RelationTypeSavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class RelationTypeSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/RemovedMemberRolesNotification.cs b/src/Umbraco.Core/Notifications/RemovedMemberRolesNotification.cs similarity index 81% rename from src/Umbraco.Core/Services/Notifications/RemovedMemberRolesNotification.cs rename to src/Umbraco.Core/Notifications/RemovedMemberRolesNotification.cs index 1e7d149d45..ed76cfbf69 100644 --- a/src/Umbraco.Core/Services/Notifications/RemovedMemberRolesNotification.cs +++ b/src/Umbraco.Core/Notifications/RemovedMemberRolesNotification.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class RemovedMemberRolesNotification : MemberRolesNotification { diff --git a/src/Umbraco.Core/Events/RenamedNotification.cs b/src/Umbraco.Core/Notifications/RenamedNotification.cs similarity index 87% rename from src/Umbraco.Core/Events/RenamedNotification.cs rename to src/Umbraco.Core/Notifications/RenamedNotification.cs index 81a568b6df..724069aba7 100644 --- a/src/Umbraco.Core/Events/RenamedNotification.cs +++ b/src/Umbraco.Core/Notifications/RenamedNotification.cs @@ -2,8 +2,9 @@ // See LICENSE for more details. using System.Collections.Generic; +using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class RenamedNotification : EnumerableObjectNotification { diff --git a/src/Umbraco.Core/Events/RenamingNotification.cs b/src/Umbraco.Core/Notifications/RenamingNotification.cs similarity index 87% rename from src/Umbraco.Core/Events/RenamingNotification.cs rename to src/Umbraco.Core/Notifications/RenamingNotification.cs index f215a8ea54..1e4184bc3d 100644 --- a/src/Umbraco.Core/Events/RenamingNotification.cs +++ b/src/Umbraco.Core/Notifications/RenamingNotification.cs @@ -2,8 +2,9 @@ // See LICENSE for more details. using System.Collections.Generic; +using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class RenamingNotification : CancelableEnumerableObjectNotification { diff --git a/src/Umbraco.Core/Events/RolledBackNotification.cs b/src/Umbraco.Core/Notifications/RolledBackNotification.cs similarity index 81% rename from src/Umbraco.Core/Events/RolledBackNotification.cs rename to src/Umbraco.Core/Notifications/RolledBackNotification.cs index 596cb29520..fded45c6b1 100644 --- a/src/Umbraco.Core/Events/RolledBackNotification.cs +++ b/src/Umbraco.Core/Notifications/RolledBackNotification.cs @@ -1,7 +1,9 @@ // Copyright (c) Umbraco. // See LICENSE for more details. -namespace Umbraco.Cms.Core.Events +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Core.Notifications { public abstract class RolledBackNotification : ObjectNotification where T : class { diff --git a/src/Umbraco.Core/Events/RollingBackNotification.cs b/src/Umbraco.Core/Notifications/RollingBackNotification.cs similarity index 82% rename from src/Umbraco.Core/Events/RollingBackNotification.cs rename to src/Umbraco.Core/Notifications/RollingBackNotification.cs index 265669bcdf..1064a7897c 100644 --- a/src/Umbraco.Core/Events/RollingBackNotification.cs +++ b/src/Umbraco.Core/Notifications/RollingBackNotification.cs @@ -1,7 +1,9 @@ // Copyright (c) Umbraco. // See LICENSE for more details. -namespace Umbraco.Cms.Core.Events +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Core.Notifications { public abstract class RollingBackNotification : CancelableObjectNotification where T : class { diff --git a/src/Umbraco.Core/Routing/RoutingRequestNotification.cs b/src/Umbraco.Core/Notifications/RoutingRequestNotification.cs similarity index 89% rename from src/Umbraco.Core/Routing/RoutingRequestNotification.cs rename to src/Umbraco.Core/Notifications/RoutingRequestNotification.cs index 7104274e39..c8b2d8e0d6 100644 --- a/src/Umbraco.Core/Routing/RoutingRequestNotification.cs +++ b/src/Umbraco.Core/Notifications/RoutingRequestNotification.cs @@ -1,6 +1,6 @@ -using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Routing; -namespace Umbraco.Cms.Core.Routing +namespace Umbraco.Cms.Core.Notifications { /// /// Used for notifying when an Umbraco request is being built diff --git a/src/Umbraco.Core/Events/SavedNotification.cs b/src/Umbraco.Core/Notifications/SavedNotification.cs similarity index 87% rename from src/Umbraco.Core/Events/SavedNotification.cs rename to src/Umbraco.Core/Notifications/SavedNotification.cs index 9ed7afd933..0a9af8c1ff 100644 --- a/src/Umbraco.Core/Events/SavedNotification.cs +++ b/src/Umbraco.Core/Notifications/SavedNotification.cs @@ -2,8 +2,9 @@ // See LICENSE for more details. using System.Collections.Generic; +using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class SavedNotification : EnumerableObjectNotification { diff --git a/src/Umbraco.Core/Events/SavingNotification.cs b/src/Umbraco.Core/Notifications/SavingNotification.cs similarity index 87% rename from src/Umbraco.Core/Events/SavingNotification.cs rename to src/Umbraco.Core/Notifications/SavingNotification.cs index d58607be10..34962f5396 100644 --- a/src/Umbraco.Core/Events/SavingNotification.cs +++ b/src/Umbraco.Core/Notifications/SavingNotification.cs @@ -2,8 +2,9 @@ // See LICENSE for more details. using System.Collections.Generic; +using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class SavingNotification : CancelableEnumerableObjectNotification { diff --git a/src/Umbraco.Core/Events/ScopedEntityRemoveNotification.cs b/src/Umbraco.Core/Notifications/ScopedEntityRemoveNotification.cs similarity index 87% rename from src/Umbraco.Core/Events/ScopedEntityRemoveNotification.cs rename to src/Umbraco.Core/Notifications/ScopedEntityRemoveNotification.cs index c2c7b9d3f2..307ae2103c 100644 --- a/src/Umbraco.Core/Events/ScopedEntityRemoveNotification.cs +++ b/src/Umbraco.Core/Notifications/ScopedEntityRemoveNotification.cs @@ -1,8 +1,9 @@ using System; using System.ComponentModel; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { [Obsolete("This is only used for the internal cache and will change, use tree change notifications instead")] [EditorBrowsable(EditorBrowsableState.Never)] diff --git a/src/Umbraco.Core/Services/Notifications/ScriptDeletedNotification.cs b/src/Umbraco.Core/Notifications/ScriptDeletedNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/ScriptDeletedNotification.cs rename to src/Umbraco.Core/Notifications/ScriptDeletedNotification.cs index 27ea080204..650f2d0564 100644 --- a/src/Umbraco.Core/Services/Notifications/ScriptDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/ScriptDeletedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class ScriptDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ScriptDeletingNotification.cs b/src/Umbraco.Core/Notifications/ScriptDeletingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/ScriptDeletingNotification.cs rename to src/Umbraco.Core/Notifications/ScriptDeletingNotification.cs index 8ee6db4406..085c98d600 100644 --- a/src/Umbraco.Core/Services/Notifications/ScriptDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/ScriptDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class ScriptDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ScriptSavedNotification.cs b/src/Umbraco.Core/Notifications/ScriptSavedNotification.cs similarity index 90% rename from src/Umbraco.Core/Services/Notifications/ScriptSavedNotification.cs rename to src/Umbraco.Core/Notifications/ScriptSavedNotification.cs index 86cb55426c..6ccb9f1446 100644 --- a/src/Umbraco.Core/Services/Notifications/ScriptSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/ScriptSavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class ScriptSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ScriptSavingNotification.cs b/src/Umbraco.Core/Notifications/ScriptSavingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/ScriptSavingNotification.cs rename to src/Umbraco.Core/Notifications/ScriptSavingNotification.cs index dc8c5a8990..92ad0ded4e 100644 --- a/src/Umbraco.Core/Services/Notifications/ScriptSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/ScriptSavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class ScriptSavingNotification : SavingNotification { diff --git a/src/Umbraco.Web.BackOffice/Filters/SendingContentNotification.cs b/src/Umbraco.Core/Notifications/SendingContentNotification.cs similarity index 85% rename from src/Umbraco.Web.BackOffice/Filters/SendingContentNotification.cs rename to src/Umbraco.Core/Notifications/SendingContentNotification.cs index 3955c95e54..4d8d93ce75 100644 --- a/src/Umbraco.Web.BackOffice/Filters/SendingContentNotification.cs +++ b/src/Umbraco.Core/Notifications/SendingContentNotification.cs @@ -1,8 +1,7 @@ -using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.ContentEditing; using Umbraco.Cms.Core.Web; -namespace Umbraco.Cms.Web.BackOffice.Filters +namespace Umbraco.Cms.Core.Notifications { public class SendingContentNotification : INotification { diff --git a/src/Umbraco.Web.BackOffice/Filters/SendingDashboardsNotification.cs b/src/Umbraco.Core/Notifications/SendingDashboardsNotification.cs similarity index 88% rename from src/Umbraco.Web.BackOffice/Filters/SendingDashboardsNotification.cs rename to src/Umbraco.Core/Notifications/SendingDashboardsNotification.cs index cfaa91fc2b..b81339fcbf 100644 --- a/src/Umbraco.Web.BackOffice/Filters/SendingDashboardsNotification.cs +++ b/src/Umbraco.Core/Notifications/SendingDashboardsNotification.cs @@ -1,10 +1,9 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Dashboards; -using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.ContentEditing; using Umbraco.Cms.Core.Web; -namespace Umbraco.Cms.Web.BackOffice.Filters +namespace Umbraco.Cms.Core.Notifications { public class SendingDashboardsNotification : INotification { diff --git a/src/Umbraco.Web.BackOffice/Filters/SendingMediaNotification.cs b/src/Umbraco.Core/Notifications/SendingMediaNotification.cs similarity index 85% rename from src/Umbraco.Web.BackOffice/Filters/SendingMediaNotification.cs rename to src/Umbraco.Core/Notifications/SendingMediaNotification.cs index cc5d7e7620..2fd8f65a4d 100644 --- a/src/Umbraco.Web.BackOffice/Filters/SendingMediaNotification.cs +++ b/src/Umbraco.Core/Notifications/SendingMediaNotification.cs @@ -1,8 +1,7 @@ -using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.ContentEditing; using Umbraco.Cms.Core.Web; -namespace Umbraco.Cms.Web.BackOffice.Filters +namespace Umbraco.Cms.Core.Notifications { public class SendingMediaNotification : INotification { diff --git a/src/Umbraco.Web.BackOffice/Filters/SendingMemberNotification.cs b/src/Umbraco.Core/Notifications/SendingMemberNotification.cs similarity index 85% rename from src/Umbraco.Web.BackOffice/Filters/SendingMemberNotification.cs rename to src/Umbraco.Core/Notifications/SendingMemberNotification.cs index ea2a1d8927..cc868836f9 100644 --- a/src/Umbraco.Web.BackOffice/Filters/SendingMemberNotification.cs +++ b/src/Umbraco.Core/Notifications/SendingMemberNotification.cs @@ -1,8 +1,7 @@ -using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.ContentEditing; using Umbraco.Cms.Core.Web; -namespace Umbraco.Cms.Web.BackOffice.Filters +namespace Umbraco.Cms.Core.Notifications { public class SendingMemberNotification : INotification { diff --git a/src/Umbraco.Web.BackOffice/Filters/SendingUserNotification.cs b/src/Umbraco.Core/Notifications/SendingUserNotification.cs similarity index 84% rename from src/Umbraco.Web.BackOffice/Filters/SendingUserNotification.cs rename to src/Umbraco.Core/Notifications/SendingUserNotification.cs index 91bbe2f0db..9e3422f1d9 100644 --- a/src/Umbraco.Web.BackOffice/Filters/SendingUserNotification.cs +++ b/src/Umbraco.Core/Notifications/SendingUserNotification.cs @@ -1,8 +1,7 @@ -using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.ContentEditing; using Umbraco.Cms.Core.Web; -namespace Umbraco.Cms.Web.BackOffice.Filters +namespace Umbraco.Cms.Core.Notifications { public class SendingUserNotification : INotification { diff --git a/src/Umbraco.Infrastructure/WebAssets/ServerVariablesParsing.cs b/src/Umbraco.Core/Notifications/ServerVariablesParsingNotification.cs similarity index 60% rename from src/Umbraco.Infrastructure/WebAssets/ServerVariablesParsing.cs rename to src/Umbraco.Core/Notifications/ServerVariablesParsingNotification.cs index ffa8713add..7fa83a5a6d 100644 --- a/src/Umbraco.Infrastructure/WebAssets/ServerVariablesParsing.cs +++ b/src/Umbraco.Core/Notifications/ServerVariablesParsingNotification.cs @@ -1,17 +1,16 @@ using System.Collections.Generic; -using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Infrastructure.WebAssets +namespace Umbraco.Cms.Core.Notifications { /// /// A notification for when server variables are parsing /// - public class ServerVariablesParsing : INotification + public class ServerVariablesParsingNotification : INotification { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public ServerVariablesParsing(IDictionary serverVariables) => ServerVariables = serverVariables; + public ServerVariablesParsingNotification(IDictionary serverVariables) => ServerVariables = serverVariables; /// /// Gets a mutable dictionary of server variables diff --git a/src/Umbraco.Core/Events/SortedNotification.cs b/src/Umbraco.Core/Notifications/SortedNotification.cs similarity index 84% rename from src/Umbraco.Core/Events/SortedNotification.cs rename to src/Umbraco.Core/Notifications/SortedNotification.cs index 11c81f001b..ffc50d6bc9 100644 --- a/src/Umbraco.Core/Events/SortedNotification.cs +++ b/src/Umbraco.Core/Notifications/SortedNotification.cs @@ -2,8 +2,9 @@ // See LICENSE for more details. using System.Collections.Generic; +using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class SortedNotification : EnumerableObjectNotification { diff --git a/src/Umbraco.Core/Events/SortingNotification.cs b/src/Umbraco.Core/Notifications/SortingNotification.cs similarity index 84% rename from src/Umbraco.Core/Events/SortingNotification.cs rename to src/Umbraco.Core/Notifications/SortingNotification.cs index 78a301dfb5..1801bfa656 100644 --- a/src/Umbraco.Core/Events/SortingNotification.cs +++ b/src/Umbraco.Core/Notifications/SortingNotification.cs @@ -2,8 +2,9 @@ // See LICENSE for more details. using System.Collections.Generic; +using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class SortingNotification : CancelableEnumerableObjectNotification { diff --git a/src/Umbraco.Core/Events/StatefulNotification.cs b/src/Umbraco.Core/Notifications/StatefulNotification.cs similarity index 93% rename from src/Umbraco.Core/Events/StatefulNotification.cs rename to src/Umbraco.Core/Notifications/StatefulNotification.cs index 4d86262322..cf01b86e58 100644 --- a/src/Umbraco.Core/Events/StatefulNotification.cs +++ b/src/Umbraco.Core/Notifications/StatefulNotification.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class StatefulNotification : IStatefulNotification { diff --git a/src/Umbraco.Core/Services/Notifications/StylesheetDeletedNotification.cs b/src/Umbraco.Core/Notifications/StylesheetDeletedNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/StylesheetDeletedNotification.cs rename to src/Umbraco.Core/Notifications/StylesheetDeletedNotification.cs index 12b84283bc..743cadab63 100644 --- a/src/Umbraco.Core/Services/Notifications/StylesheetDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/StylesheetDeletedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class StylesheetDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/StylesheetDeletingNotification.cs b/src/Umbraco.Core/Notifications/StylesheetDeletingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/StylesheetDeletingNotification.cs rename to src/Umbraco.Core/Notifications/StylesheetDeletingNotification.cs index eccf10d4c6..8a0c411b13 100644 --- a/src/Umbraco.Core/Services/Notifications/StylesheetDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/StylesheetDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class StylesheetDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/StylesheetSavedNotification.cs b/src/Umbraco.Core/Notifications/StylesheetSavedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/StylesheetSavedNotification.cs rename to src/Umbraco.Core/Notifications/StylesheetSavedNotification.cs index 1e0690ba88..0ceeb209e0 100644 --- a/src/Umbraco.Core/Services/Notifications/StylesheetSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/StylesheetSavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class StylesheetSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/StylesheetSavingNotification.cs b/src/Umbraco.Core/Notifications/StylesheetSavingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/StylesheetSavingNotification.cs rename to src/Umbraco.Core/Notifications/StylesheetSavingNotification.cs index 6bd2f2e4a7..d08bdebac4 100644 --- a/src/Umbraco.Core/Services/Notifications/StylesheetSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/StylesheetSavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class StylesheetSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Cache/TemplateCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/TemplateCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/TemplateCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/TemplateCacheRefresherNotification.cs index 88ff2284cb..689d2a52ff 100644 --- a/src/Umbraco.Core/Cache/TemplateCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/TemplateCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class TemplateCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Services/Notifications/TemplateDeletedNotification.cs b/src/Umbraco.Core/Notifications/TemplateDeletedNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/TemplateDeletedNotification.cs rename to src/Umbraco.Core/Notifications/TemplateDeletedNotification.cs index 2b51f6adc1..01d6dc7e6d 100644 --- a/src/Umbraco.Core/Services/Notifications/TemplateDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/TemplateDeletedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class TemplateDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/TemplateDeletingNotification.cs b/src/Umbraco.Core/Notifications/TemplateDeletingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/TemplateDeletingNotification.cs rename to src/Umbraco.Core/Notifications/TemplateDeletingNotification.cs index 9384aa9af5..6434c47c46 100644 --- a/src/Umbraco.Core/Services/Notifications/TemplateDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/TemplateDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class TemplateDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/TemplateSavedNotification.cs b/src/Umbraco.Core/Notifications/TemplateSavedNotification.cs similarity index 96% rename from src/Umbraco.Core/Services/Notifications/TemplateSavedNotification.cs rename to src/Umbraco.Core/Notifications/TemplateSavedNotification.cs index 81940620c5..7cfbd8a7bb 100644 --- a/src/Umbraco.Core/Services/Notifications/TemplateSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/TemplateSavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class TemplateSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/TemplateSavingNotification.cs b/src/Umbraco.Core/Notifications/TemplateSavingNotification.cs similarity index 97% rename from src/Umbraco.Core/Services/Notifications/TemplateSavingNotification.cs rename to src/Umbraco.Core/Notifications/TemplateSavingNotification.cs index 36c70e72dd..95023441d7 100644 --- a/src/Umbraco.Core/Services/Notifications/TemplateSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/TemplateSavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class TemplateSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Events/TreeChangeNotification.cs b/src/Umbraco.Core/Notifications/TreeChangeNotification.cs similarity index 88% rename from src/Umbraco.Core/Events/TreeChangeNotification.cs rename to src/Umbraco.Core/Notifications/TreeChangeNotification.cs index 83b28383a5..bdbd0fc044 100644 --- a/src/Umbraco.Core/Events/TreeChangeNotification.cs +++ b/src/Umbraco.Core/Notifications/TreeChangeNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Services.Changes; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class TreeChangeNotification : EnumerableObjectNotification> { diff --git a/src/Umbraco.Core/Events/UmbracoApplicationStarting.cs b/src/Umbraco.Core/Notifications/UmbracoApplicationStartingNotification.cs similarity index 59% rename from src/Umbraco.Core/Events/UmbracoApplicationStarting.cs rename to src/Umbraco.Core/Notifications/UmbracoApplicationStartingNotification.cs index 2e7d8c768c..9d324a4ca5 100644 --- a/src/Umbraco.Core/Events/UmbracoApplicationStarting.cs +++ b/src/Umbraco.Core/Notifications/UmbracoApplicationStartingNotification.cs @@ -1,16 +1,16 @@ // Copyright (c) Umbraco. // See LICENSE for more details. -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { - public class UmbracoApplicationStarting : INotification + public class UmbracoApplicationStartingNotification : INotification { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The runtime level - public UmbracoApplicationStarting(RuntimeLevel runtimeLevel) => RuntimeLevel = runtimeLevel; + public UmbracoApplicationStartingNotification(RuntimeLevel runtimeLevel) => RuntimeLevel = runtimeLevel; /// /// Gets the runtime level of execution. diff --git a/src/Umbraco.Core/Notifications/UmbracoApplicationStoppingNotification.cs b/src/Umbraco.Core/Notifications/UmbracoApplicationStoppingNotification.cs new file mode 100644 index 0000000000..db86a1e614 --- /dev/null +++ b/src/Umbraco.Core/Notifications/UmbracoApplicationStoppingNotification.cs @@ -0,0 +1,4 @@ +namespace Umbraco.Cms.Core.Notifications +{ + public class UmbracoApplicationStoppingNotification : INotification { } +} diff --git a/src/Umbraco.Core/Events/UmbracoRequestBegin.cs b/src/Umbraco.Core/Notifications/UmbracoRequestBeginNotification.cs similarity index 63% rename from src/Umbraco.Core/Events/UmbracoRequestBegin.cs rename to src/Umbraco.Core/Notifications/UmbracoRequestBeginNotification.cs index 00eb41df96..76683f8d65 100644 --- a/src/Umbraco.Core/Events/UmbracoRequestBegin.cs +++ b/src/Umbraco.Core/Notifications/UmbracoRequestBeginNotification.cs @@ -3,17 +3,17 @@ using Umbraco.Cms.Core.Web; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { /// /// Notification raised on each request begin. /// - public class UmbracoRequestBegin : INotification + public class UmbracoRequestBeginNotification : INotification { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public UmbracoRequestBegin(IUmbracoContext umbracoContext) => UmbracoContext = umbracoContext; + public UmbracoRequestBeginNotification(IUmbracoContext umbracoContext) => UmbracoContext = umbracoContext; /// /// Gets the diff --git a/src/Umbraco.Core/Events/UmbracoRequestEnd.cs b/src/Umbraco.Core/Notifications/UmbracoRequestEndNotification.cs similarity index 64% rename from src/Umbraco.Core/Events/UmbracoRequestEnd.cs rename to src/Umbraco.Core/Notifications/UmbracoRequestEndNotification.cs index b4e0f26b81..84aad8a3b3 100644 --- a/src/Umbraco.Core/Events/UmbracoRequestEnd.cs +++ b/src/Umbraco.Core/Notifications/UmbracoRequestEndNotification.cs @@ -3,17 +3,17 @@ using Umbraco.Cms.Core.Web; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { /// /// Notification raised on each request end. /// - public class UmbracoRequestEnd : INotification + public class UmbracoRequestEndNotification : INotification { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public UmbracoRequestEnd(IUmbracoContext umbracoContext) => UmbracoContext = umbracoContext; + public UmbracoRequestEndNotification(IUmbracoContext umbracoContext) => UmbracoContext = umbracoContext; /// /// Gets the diff --git a/src/Umbraco.Core/Events/UnattendedInstallNotification.cs b/src/Umbraco.Core/Notifications/UnattendedInstallNotification.cs similarity index 81% rename from src/Umbraco.Core/Events/UnattendedInstallNotification.cs rename to src/Umbraco.Core/Notifications/UnattendedInstallNotification.cs index 20373ad15b..7f9b239ce2 100644 --- a/src/Umbraco.Core/Events/UnattendedInstallNotification.cs +++ b/src/Umbraco.Core/Notifications/UnattendedInstallNotification.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { /// /// Used to notify that an Unattended install has completed diff --git a/src/Umbraco.Core/Events/UninstallPackageNotification.cs b/src/Umbraco.Core/Notifications/UninstallPackageNotification.cs similarity index 90% rename from src/Umbraco.Core/Events/UninstallPackageNotification.cs rename to src/Umbraco.Core/Notifications/UninstallPackageNotification.cs index 2ce76fd5fc..de6afc4eb6 100644 --- a/src/Umbraco.Core/Events/UninstallPackageNotification.cs +++ b/src/Umbraco.Core/Notifications/UninstallPackageNotification.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Packaging; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class UninstallPackageNotification : INotification { diff --git a/src/Umbraco.Core/Cache/UserCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/UserCacheRefresherNotification.cs similarity index 86% rename from src/Umbraco.Core/Cache/UserCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/UserCacheRefresherNotification.cs index b91a7b93c8..4181d74dd7 100644 --- a/src/Umbraco.Core/Cache/UserCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/UserCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class UserCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Services/Notifications/UserDeletedNotification.cs b/src/Umbraco.Core/Notifications/UserDeletedNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/UserDeletedNotification.cs rename to src/Umbraco.Core/Notifications/UserDeletedNotification.cs index 4347211847..c272e51b22 100644 --- a/src/Umbraco.Core/Services/Notifications/UserDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/UserDeletedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class UserDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/UserDeletingNotification.cs b/src/Umbraco.Core/Notifications/UserDeletingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/UserDeletingNotification.cs rename to src/Umbraco.Core/Notifications/UserDeletingNotification.cs index 5e5c094b65..febfa27d94 100644 --- a/src/Umbraco.Core/Services/Notifications/UserDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/UserDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class UserDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Web.Common/Security/UserForgotPasswordChangedNotification.cs b/src/Umbraco.Core/Notifications/UserForgotPasswordChangedNotification.cs similarity index 86% rename from src/Umbraco.Web.Common/Security/UserForgotPasswordChangedNotification.cs rename to src/Umbraco.Core/Notifications/UserForgotPasswordChangedNotification.cs index 04fb74a1ca..b4e93f8b67 100644 --- a/src/Umbraco.Web.Common/Security/UserForgotPasswordChangedNotification.cs +++ b/src/Umbraco.Core/Notifications/UserForgotPasswordChangedNotification.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Web.Common.Security +namespace Umbraco.Cms.Core.Notifications { public class UserForgotPasswordChangedNotification : UserNotification { diff --git a/src/Umbraco.Web.Common/Security/UserForgotPasswordRequestedNotification.cs b/src/Umbraco.Core/Notifications/UserForgotPasswordRequestedNotification.cs similarity index 87% rename from src/Umbraco.Web.Common/Security/UserForgotPasswordRequestedNotification.cs rename to src/Umbraco.Core/Notifications/UserForgotPasswordRequestedNotification.cs index 766e1c61ca..608e5c0f63 100644 --- a/src/Umbraco.Web.Common/Security/UserForgotPasswordRequestedNotification.cs +++ b/src/Umbraco.Core/Notifications/UserForgotPasswordRequestedNotification.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Web.Common.Security +namespace Umbraco.Cms.Core.Notifications { public class UserForgotPasswordRequestedNotification : UserNotification { diff --git a/src/Umbraco.Core/Cache/UserGroupCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/UserGroupCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/UserGroupCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/UserGroupCacheRefresherNotification.cs index a0adf915d8..7aca0d5edb 100644 --- a/src/Umbraco.Core/Cache/UserGroupCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/UserGroupCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class UserGroupCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Services/Notifications/UserGroupDeletedNotification.cs b/src/Umbraco.Core/Notifications/UserGroupDeletedNotification.cs similarity index 88% rename from src/Umbraco.Core/Services/Notifications/UserGroupDeletedNotification.cs rename to src/Umbraco.Core/Notifications/UserGroupDeletedNotification.cs index 934f607226..9877d95441 100644 --- a/src/Umbraco.Core/Services/Notifications/UserGroupDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/UserGroupDeletedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class UserGroupDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/UserGroupDeletingNotification.cs b/src/Umbraco.Core/Notifications/UserGroupDeletingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/UserGroupDeletingNotification.cs rename to src/Umbraco.Core/Notifications/UserGroupDeletingNotification.cs index 17ee3a5f83..af0e8d76d6 100644 --- a/src/Umbraco.Core/Services/Notifications/UserGroupDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/UserGroupDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class UserGroupDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/UserGroupSavedNotification.cs b/src/Umbraco.Core/Notifications/UserGroupSavedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/UserGroupSavedNotification.cs rename to src/Umbraco.Core/Notifications/UserGroupSavedNotification.cs index 459c265eaa..fee23c06ea 100644 --- a/src/Umbraco.Core/Services/Notifications/UserGroupSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/UserGroupSavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class UserGroupSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/UserGroupSavingNotification.cs b/src/Umbraco.Core/Notifications/UserGroupSavingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/UserGroupSavingNotification.cs rename to src/Umbraco.Core/Notifications/UserGroupSavingNotification.cs index d479e2d15d..0dc074bfdc 100644 --- a/src/Umbraco.Core/Services/Notifications/UserGroupSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/UserGroupSavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class UserGroupSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/UserGroupWithUsersSavedNotification.cs b/src/Umbraco.Core/Notifications/UserGroupWithUsersSavedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/UserGroupWithUsersSavedNotification.cs rename to src/Umbraco.Core/Notifications/UserGroupWithUsersSavedNotification.cs index d1a3ad2651..5e239660aa 100644 --- a/src/Umbraco.Core/Services/Notifications/UserGroupWithUsersSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/UserGroupWithUsersSavedNotification.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class UserGroupWithUsersSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/UserGroupWithUsersSavingNotification.cs b/src/Umbraco.Core/Notifications/UserGroupWithUsersSavingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/UserGroupWithUsersSavingNotification.cs rename to src/Umbraco.Core/Notifications/UserGroupWithUsersSavingNotification.cs index a2705e7971..f3dd362c20 100644 --- a/src/Umbraco.Core/Services/Notifications/UserGroupWithUsersSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/UserGroupWithUsersSavingNotification.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class UserGroupWithUsersSavingNotification : SavingNotification { diff --git a/src/Umbraco.Web.Common/Security/UserLockedNotification.cs b/src/Umbraco.Core/Notifications/UserLockedNotification.cs similarity index 85% rename from src/Umbraco.Web.Common/Security/UserLockedNotification.cs rename to src/Umbraco.Core/Notifications/UserLockedNotification.cs index 6c4737fb49..492296b7a2 100644 --- a/src/Umbraco.Web.Common/Security/UserLockedNotification.cs +++ b/src/Umbraco.Core/Notifications/UserLockedNotification.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Web.Common.Security +namespace Umbraco.Cms.Core.Notifications { public class UserLockedNotification : UserNotification { diff --git a/src/Umbraco.Web.Common/Security/UserLoginFailedNotification.cs b/src/Umbraco.Core/Notifications/UserLoginFailedNotification.cs similarity index 86% rename from src/Umbraco.Web.Common/Security/UserLoginFailedNotification.cs rename to src/Umbraco.Core/Notifications/UserLoginFailedNotification.cs index de8578015e..ff07b57832 100644 --- a/src/Umbraco.Web.Common/Security/UserLoginFailedNotification.cs +++ b/src/Umbraco.Core/Notifications/UserLoginFailedNotification.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Web.Common.Security +namespace Umbraco.Cms.Core.Notifications { public class UserLoginFailedNotification : UserNotification { diff --git a/src/Umbraco.Web.Common/Security/UserLoginRequiresVerificationNotification.cs b/src/Umbraco.Core/Notifications/UserLoginRequiresVerificationNotification.cs similarity index 87% rename from src/Umbraco.Web.Common/Security/UserLoginRequiresVerificationNotification.cs rename to src/Umbraco.Core/Notifications/UserLoginRequiresVerificationNotification.cs index cff2d9f45e..b99bf58b7e 100644 --- a/src/Umbraco.Web.Common/Security/UserLoginRequiresVerificationNotification.cs +++ b/src/Umbraco.Core/Notifications/UserLoginRequiresVerificationNotification.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Web.Common.Security +namespace Umbraco.Cms.Core.Notifications { public class UserLoginRequiresVerificationNotification : UserNotification { diff --git a/src/Umbraco.Web.Common/Security/UserLoginSuccessNotification.cs b/src/Umbraco.Core/Notifications/UserLoginSuccessNotification.cs similarity index 86% rename from src/Umbraco.Web.Common/Security/UserLoginSuccessNotification.cs rename to src/Umbraco.Core/Notifications/UserLoginSuccessNotification.cs index bb9f3ccecb..e9b79c68fe 100644 --- a/src/Umbraco.Web.Common/Security/UserLoginSuccessNotification.cs +++ b/src/Umbraco.Core/Notifications/UserLoginSuccessNotification.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Web.Common.Security +namespace Umbraco.Cms.Core.Notifications { public class UserLoginSuccessNotification : UserNotification { diff --git a/src/Umbraco.Web.Common/Security/UserLogoutSuccessNotification.cs b/src/Umbraco.Core/Notifications/UserLogoutSuccessNotification.cs similarity index 88% rename from src/Umbraco.Web.Common/Security/UserLogoutSuccessNotification.cs rename to src/Umbraco.Core/Notifications/UserLogoutSuccessNotification.cs index 9ef9d5d0e7..cc6a2f819b 100644 --- a/src/Umbraco.Web.Common/Security/UserLogoutSuccessNotification.cs +++ b/src/Umbraco.Core/Notifications/UserLogoutSuccessNotification.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Web.Common.Security +namespace Umbraco.Cms.Core.Notifications { public class UserLogoutSuccessNotification : UserNotification { diff --git a/src/Umbraco.Web.Common/Security/UserNotification.cs b/src/Umbraco.Core/Notifications/UserNotification.cs similarity index 93% rename from src/Umbraco.Web.Common/Security/UserNotification.cs rename to src/Umbraco.Core/Notifications/UserNotification.cs index 40d37ec82a..aa3223e980 100644 --- a/src/Umbraco.Web.Common/Security/UserNotification.cs +++ b/src/Umbraco.Core/Notifications/UserNotification.cs @@ -1,7 +1,6 @@ using System; -using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Web.Common.Security +namespace Umbraco.Cms.Core.Notifications { public abstract class UserNotification : INotification { diff --git a/src/Umbraco.Web.Common/Security/UserPasswordChangedNotification.cs b/src/Umbraco.Core/Notifications/UserPasswordChangedNotification.cs similarity index 86% rename from src/Umbraco.Web.Common/Security/UserPasswordChangedNotification.cs rename to src/Umbraco.Core/Notifications/UserPasswordChangedNotification.cs index 68115d411c..098be36867 100644 --- a/src/Umbraco.Web.Common/Security/UserPasswordChangedNotification.cs +++ b/src/Umbraco.Core/Notifications/UserPasswordChangedNotification.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Web.Common.Security +namespace Umbraco.Cms.Core.Notifications { public class UserPasswordChangedNotification : UserNotification { diff --git a/src/Umbraco.Web.Common/Security/UserPasswordResetNotification.cs b/src/Umbraco.Core/Notifications/UserPasswordResetNotification.cs similarity index 86% rename from src/Umbraco.Web.Common/Security/UserPasswordResetNotification.cs rename to src/Umbraco.Core/Notifications/UserPasswordResetNotification.cs index 618fa04f4c..fc60eef61e 100644 --- a/src/Umbraco.Web.Common/Security/UserPasswordResetNotification.cs +++ b/src/Umbraco.Core/Notifications/UserPasswordResetNotification.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Web.Common.Security +namespace Umbraco.Cms.Core.Notifications { public class UserPasswordResetNotification : UserNotification { diff --git a/src/Umbraco.Web.Common/Security/UserResetAccessFailedCountNotification.cs b/src/Umbraco.Core/Notifications/UserResetAccessFailedCountNotification.cs similarity index 87% rename from src/Umbraco.Web.Common/Security/UserResetAccessFailedCountNotification.cs rename to src/Umbraco.Core/Notifications/UserResetAccessFailedCountNotification.cs index 9335bd76a0..5cd03cc140 100644 --- a/src/Umbraco.Web.Common/Security/UserResetAccessFailedCountNotification.cs +++ b/src/Umbraco.Core/Notifications/UserResetAccessFailedCountNotification.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Web.Common.Security +namespace Umbraco.Cms.Core.Notifications { public class UserResetAccessFailedCountNotification : UserNotification { diff --git a/src/Umbraco.Core/Services/Notifications/UserSavedNotification.cs b/src/Umbraco.Core/Notifications/UserSavedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/UserSavedNotification.cs rename to src/Umbraco.Core/Notifications/UserSavedNotification.cs index b173709a2b..892218af82 100644 --- a/src/Umbraco.Core/Services/Notifications/UserSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/UserSavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class UserSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/UserSavingNotification.cs b/src/Umbraco.Core/Notifications/UserSavingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/UserSavingNotification.cs rename to src/Umbraco.Core/Notifications/UserSavingNotification.cs index ddb10dd181..57c0d867fa 100644 --- a/src/Umbraco.Core/Services/Notifications/UserSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/UserSavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class UserSavingNotification : SavingNotification { diff --git a/src/Umbraco.Web.Common/Security/UserUnlockedNotification.cs b/src/Umbraco.Core/Notifications/UserUnlockedNotification.cs similarity index 85% rename from src/Umbraco.Web.Common/Security/UserUnlockedNotification.cs rename to src/Umbraco.Core/Notifications/UserUnlockedNotification.cs index 0ecba4d597..0c6cc7b9fd 100644 --- a/src/Umbraco.Web.Common/Security/UserUnlockedNotification.cs +++ b/src/Umbraco.Core/Notifications/UserUnlockedNotification.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Web.Common.Security +namespace Umbraco.Cms.Core.Notifications { public class UserUnlockedNotification : UserNotification { diff --git a/src/Umbraco.Core/Routing/PublishedRouter.cs b/src/Umbraco.Core/Routing/PublishedRouter.cs index f8697e640a..502868fb69 100644 --- a/src/Umbraco.Core/Routing/PublishedRouter.cs +++ b/src/Umbraco.Core/Routing/PublishedRouter.cs @@ -10,6 +10,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Logging; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Web; diff --git a/src/Umbraco.Core/Runtime/AppPluginsManifestWatcherNotificationHandler.cs b/src/Umbraco.Core/Runtime/AppPluginsManifestWatcherNotificationHandler.cs index 0b46dc0afd..a416452966 100644 --- a/src/Umbraco.Core/Runtime/AppPluginsManifestWatcherNotificationHandler.cs +++ b/src/Umbraco.Core/Runtime/AppPluginsManifestWatcherNotificationHandler.cs @@ -5,13 +5,14 @@ using System.Threading.Tasks; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Manifest; +using Umbraco.Cms.Core.Notifications; namespace Umbraco.Cms.Core.Runtime { /// /// Starts monitoring AppPlugins directory during debug runs, to restart site when a plugin manifest changes. /// - public sealed class AppPluginsManifestWatcherNotificationHandler : INotificationAsyncHandler + public sealed class AppPluginsManifestWatcherNotificationHandler : INotificationAsyncHandler { private readonly ManifestWatcher _manifestWatcher; private readonly IHostingEnvironment _hostingEnvironment; @@ -22,7 +23,7 @@ namespace Umbraco.Cms.Core.Runtime _hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment)); } - public Task HandleAsync(UmbracoApplicationStarting notification, CancellationToken cancellationToken) + public Task HandleAsync(UmbracoApplicationStartingNotification notification, CancellationToken cancellationToken) { if (!_hostingEnvironment.IsDebugMode) { diff --git a/src/Umbraco.Core/Runtime/EssentialDirectoryCreator.cs b/src/Umbraco.Core/Runtime/EssentialDirectoryCreator.cs index c1ef8f98b4..a9564712c3 100644 --- a/src/Umbraco.Core/Runtime/EssentialDirectoryCreator.cs +++ b/src/Umbraco.Core/Runtime/EssentialDirectoryCreator.cs @@ -3,10 +3,11 @@ using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; +using Umbraco.Cms.Core.Notifications; namespace Umbraco.Cms.Core.Runtime { - public class EssentialDirectoryCreator : INotificationHandler + public class EssentialDirectoryCreator : INotificationHandler { private readonly IIOHelper _ioHelper; private readonly IHostingEnvironment _hostingEnvironment; @@ -19,7 +20,7 @@ namespace Umbraco.Cms.Core.Runtime _globalSettings = globalSettings.Value; } - public void Handle(UmbracoApplicationStarting notification) + public void Handle(UmbracoApplicationStartingNotification notification) { // ensure we have some essential directories // every other component can then initialize safely diff --git a/src/Umbraco.Infrastructure/Cache/DatabaseServerMessengerNotificationHandler.cs b/src/Umbraco.Infrastructure/Cache/DatabaseServerMessengerNotificationHandler.cs index 72fb312d08..e627284f8b 100644 --- a/src/Umbraco.Infrastructure/Cache/DatabaseServerMessengerNotificationHandler.cs +++ b/src/Umbraco.Infrastructure/Cache/DatabaseServerMessengerNotificationHandler.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Sync; using Umbraco.Cms.Infrastructure.Persistence; @@ -12,7 +13,7 @@ namespace Umbraco.Cms.Core.Cache /// /// Ensures that distributed cache events are setup and the is initialized /// - public sealed class DatabaseServerMessengerNotificationHandler : INotificationHandler, INotificationHandler + public sealed class DatabaseServerMessengerNotificationHandler : INotificationHandler, INotificationHandler { private readonly IServerMessenger _messenger; private readonly IUmbracoDatabaseFactory _databaseFactory; @@ -35,7 +36,7 @@ namespace Umbraco.Cms.Core.Cache } /// - public void Handle(UmbracoApplicationStarting notification) + public void Handle(UmbracoApplicationStartingNotification notification) { if (_databaseFactory.CanConnect == false) { @@ -55,6 +56,6 @@ namespace Umbraco.Cms.Core.Cache /// /// Clear the batch on end request /// - public void Handle(UmbracoRequestEnd notification) => _messenger?.SendMessages(); + public void Handle(UmbracoRequestEndNotification notification) => _messenger?.SendMessages(); } } diff --git a/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs b/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs index b37ccabb61..35845f3cd0 100644 --- a/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs +++ b/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs @@ -5,8 +5,8 @@ using System.Linq; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Cache diff --git a/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs b/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs index a95ac9b07c..f14e0d6a0b 100644 --- a/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs +++ b/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs @@ -6,9 +6,9 @@ using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Handlers; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Routing; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Compose diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.DistributedCache.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.DistributedCache.cs index ed44e170fb..0757f2c725 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.DistributedCache.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.DistributedCache.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Services.Changes; using Umbraco.Cms.Core.Sync; @@ -28,8 +29,8 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection { builder.SetDatabaseServerMessengerCallbacks(GetCallbacks); builder.SetServerMessenger(); - builder.AddNotificationHandler(); - builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + builder.AddNotificationHandler(); return builder; } diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Examine.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Examine.cs index 033ab76298..103d7a198d 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Examine.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Examine.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services; @@ -50,7 +51,7 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection builder.Services.AddUnique, MemberValueSetBuilder>(); builder.Services.AddUnique(); - builder.AddNotificationHandler(); + builder.AddNotificationHandler(); builder.AddNotificationHandler(); builder.AddNotificationHandler(); builder.AddNotificationHandler(); diff --git a/src/Umbraco.Infrastructure/Events/RelateOnTrashNotificationHandler.cs b/src/Umbraco.Infrastructure/Events/RelateOnTrashNotificationHandler.cs index fc338fd3fe..67c1dbda06 100644 --- a/src/Umbraco.Infrastructure/Events/RelateOnTrashNotificationHandler.cs +++ b/src/Umbraco.Infrastructure/Events/RelateOnTrashNotificationHandler.cs @@ -3,9 +3,9 @@ using System.Linq; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Events diff --git a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseSchemaCreator.cs b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseSchemaCreator.cs index 61c32ea1d0..4052a8ae56 100644 --- a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseSchemaCreator.cs +++ b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseSchemaCreator.cs @@ -5,7 +5,8 @@ using Microsoft.Extensions.Logging; using NPoco; using Umbraco.Cms.Core.Configuration; using Umbraco.Cms.Core.Events; -using Umbraco.Cms.Infrastructure.Migrations.Events; +using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Infrastructure.Migrations.Notifications; using Umbraco.Cms.Infrastructure.Persistence; using Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions; using Umbraco.Cms.Infrastructure.Persistence.Dtos; @@ -319,7 +320,7 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install #region Notifications /// - /// Publishes the notification. + /// Publishes the notification. /// /// Cancelable notification marking the creation having begun. internal virtual void FireBeforeCreation(DatabaseSchemaCreatingNotification notification) => diff --git a/src/Umbraco.Infrastructure/Migrations/Events/DatabaseSchemaCreatedNotification.cs b/src/Umbraco.Infrastructure/Migrations/Notifications/DatabaseSchemaCreatedNotification.cs similarity index 74% rename from src/Umbraco.Infrastructure/Migrations/Events/DatabaseSchemaCreatedNotification.cs rename to src/Umbraco.Infrastructure/Migrations/Notifications/DatabaseSchemaCreatedNotification.cs index 4da6b5fabb..2c2888c19d 100644 --- a/src/Umbraco.Infrastructure/Migrations/Events/DatabaseSchemaCreatedNotification.cs +++ b/src/Umbraco.Infrastructure/Migrations/Notifications/DatabaseSchemaCreatedNotification.cs @@ -1,6 +1,7 @@ using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; -namespace Umbraco.Cms.Infrastructure.Migrations.Events +namespace Umbraco.Cms.Infrastructure.Migrations.Notifications { internal class DatabaseSchemaCreatedNotification : StatefulNotification { diff --git a/src/Umbraco.Infrastructure/Migrations/Events/DatabaseSchemaCreatingNotification.cs b/src/Umbraco.Infrastructure/Migrations/Notifications/DatabaseSchemaCreatingNotification.cs similarity index 70% rename from src/Umbraco.Infrastructure/Migrations/Events/DatabaseSchemaCreatingNotification.cs rename to src/Umbraco.Infrastructure/Migrations/Notifications/DatabaseSchemaCreatingNotification.cs index 9ca079acbe..1be96c9a9a 100644 --- a/src/Umbraco.Infrastructure/Migrations/Events/DatabaseSchemaCreatingNotification.cs +++ b/src/Umbraco.Infrastructure/Migrations/Notifications/DatabaseSchemaCreatingNotification.cs @@ -1,6 +1,7 @@ using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; -namespace Umbraco.Cms.Infrastructure.Migrations.Events +namespace Umbraco.Cms.Infrastructure.Migrations.Notifications { internal class DatabaseSchemaCreatingNotification : CancelableNotification { diff --git a/src/Umbraco.Infrastructure/ModelsBuilder/LiveModelsProvider.cs b/src/Umbraco.Infrastructure/ModelsBuilder/LiveModelsProvider.cs index 9ff03e4d45..000f4ab1b2 100644 --- a/src/Umbraco.Infrastructure/ModelsBuilder/LiveModelsProvider.cs +++ b/src/Umbraco.Infrastructure/ModelsBuilder/LiveModelsProvider.cs @@ -5,6 +5,7 @@ using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Runtime; using Umbraco.Cms.Infrastructure.ModelsBuilder.Building; using Umbraco.Extensions; @@ -12,8 +13,8 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.ModelsBuilder { // supports LiveAppData - but not PureLive - public sealed class LiveModelsProvider : INotificationHandler, - INotificationHandler, + public sealed class LiveModelsProvider : INotificationHandler, + INotificationHandler, INotificationHandler, INotificationHandler { @@ -45,9 +46,9 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder internal bool IsEnabled => _config.ModelsMode.IsLiveNotPure(); /// - /// Handles the notification + /// Handles the notification /// - public void Handle(UmbracoApplicationStarting notification) => Install(); + public void Handle(UmbracoApplicationStartingNotification notification) => Install(); private void Install() { @@ -112,7 +113,7 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder } } - public void Handle(UmbracoRequestEnd notification) + public void Handle(UmbracoRequestEndNotification notification) { if (IsEnabled && _mainDom.IsMainDom) { diff --git a/src/Umbraco.Infrastructure/ModelsBuilder/OutOfDateModelsStatus.cs b/src/Umbraco.Infrastructure/ModelsBuilder/OutOfDateModelsStatus.cs index 8b14a6030b..3816db52bf 100644 --- a/src/Umbraco.Infrastructure/ModelsBuilder/OutOfDateModelsStatus.cs +++ b/src/Umbraco.Infrastructure/ModelsBuilder/OutOfDateModelsStatus.cs @@ -4,6 +4,7 @@ using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.ModelsBuilder diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentRepositoryBase.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentRepositoryBase.cs index 0aca22950d..790f82e0a2 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentRepositoryBase.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentRepositoryBase.cs @@ -9,6 +9,7 @@ using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Editors; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs index dd30f85872..b246839440 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs @@ -8,6 +8,7 @@ using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaRepository.cs index 80804004b8..b69907d71b 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaRepository.cs @@ -7,6 +7,7 @@ using NPoco; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberGroupRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberGroupRepository.cs index b372c3b479..5ad27b7506 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberGroupRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberGroupRepository.cs @@ -6,10 +6,10 @@ using NPoco; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Infrastructure.Persistence.Dtos; using Umbraco.Cms.Infrastructure.Persistence.Factories; using Umbraco.Cms.Infrastructure.Persistence.Querying; @@ -203,7 +203,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement .Select(x => MemberGroupFactory.BuildEntity(x)); } - + public void ReplaceRoles(int[] memberIds, string[] roleNames) => AssignRolesInternal(memberIds, roleNames, true); diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberRepository.cs index 45c904eff0..698d0fffa7 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberRepository.cs @@ -9,6 +9,7 @@ using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.PropertyEditors; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentNotificationHandler.cs b/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentNotificationHandler.cs index 73748c4a48..e4039b6cee 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentNotificationHandler.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentNotificationHandler.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Services.Notifications; +using Umbraco.Cms.Core.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.PropertyEditors diff --git a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs index db895444c4..7395c0bf52 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs @@ -11,9 +11,9 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Media; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Core.Strings; using Umbraco.Extensions; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs index fe66a31e13..40538df1f7 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs @@ -13,9 +13,9 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Media; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Core.Strings; using Umbraco.Extensions; diff --git a/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs b/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs index d71b5570d2..7b3b3f86ed 100644 --- a/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs +++ b/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs @@ -9,9 +9,9 @@ using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Routing diff --git a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs index ef1d808b9a..e004313ac3 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs @@ -9,6 +9,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Exceptions; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Logging; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Runtime; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.Migrations.Install; @@ -118,7 +119,7 @@ namespace Umbraco.Cms.Infrastructure.Runtime } - await _eventAggregator.PublishAsync(new UmbracoApplicationStarting(State.Level), cancellationToken); + await _eventAggregator.PublishAsync(new UmbracoApplicationStartingNotification(State.Level), cancellationToken); // create & initialize the components _components.Initialize(); @@ -144,7 +145,7 @@ namespace Umbraco.Cms.Infrastructure.Runtime public async Task StopAsync(CancellationToken cancellationToken) { _components.Terminate(); - await _eventAggregator.PublishAsync(new UmbracoApplicationStopping(), cancellationToken); + await _eventAggregator.PublishAsync(new UmbracoApplicationStoppingNotification(), cancellationToken); StaticApplicationLogging.Initialize(null); } diff --git a/src/Umbraco.Infrastructure/RuntimeState.cs b/src/Umbraco.Infrastructure/RuntimeState.cs index 76700e2001..894536e4e7 100644 --- a/src/Umbraco.Infrastructure/RuntimeState.cs +++ b/src/Umbraco.Infrastructure/RuntimeState.cs @@ -6,6 +6,7 @@ using Umbraco.Cms.Core.Configuration; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Exceptions; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Semver; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.Migrations.Install; diff --git a/src/Umbraco.Infrastructure/Search/ExamineNotificationHandler.cs b/src/Umbraco.Infrastructure/Search/ExamineNotificationHandler.cs index d22acb87e2..d0541cfd97 100644 --- a/src/Umbraco.Infrastructure/Search/ExamineNotificationHandler.cs +++ b/src/Umbraco.Infrastructure/Search/ExamineNotificationHandler.cs @@ -10,6 +10,7 @@ using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Logging; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Runtime; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services; @@ -21,7 +22,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Search { public sealed class ExamineNotificationHandler : - INotificationHandler, + INotificationHandler, INotificationHandler, INotificationHandler, INotificationHandler, @@ -79,7 +80,7 @@ namespace Umbraco.Cms.Infrastructure.Search _logger = logger; _indexCreator = indexCreator; } - public void Handle(UmbracoApplicationStarting notification) + public void Handle(UmbracoApplicationStartingNotification notification) { //let's deal with shutting down Examine with MainDom var examineShutdownRegistered = _mainDom.Register(release: () => diff --git a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs index 63edbb6ed8..85bc2c7f4d 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs @@ -7,11 +7,11 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Exceptions; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services.Changes; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Persistence.Querying; using Umbraco.Extensions; diff --git a/src/Umbraco.Infrastructure/Services/Implement/ContentTypeService.cs b/src/Umbraco.Infrastructure/Services/Implement/ContentTypeService.cs index 7a947a420c..3d8dcf33bf 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ContentTypeService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ContentTypeService.cs @@ -4,6 +4,7 @@ using System.Linq; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services.Changes; diff --git a/src/Umbraco.Infrastructure/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs b/src/Umbraco.Infrastructure/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs index f04793fe8e..942c9c1e5e 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs @@ -6,6 +6,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Exceptions; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services.Changes; diff --git a/src/Umbraco.Infrastructure/Services/Implement/DataTypeService.cs b/src/Umbraco.Infrastructure/Services/Implement/DataTypeService.cs index 4bb1e46c29..9ae439caf2 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/DataTypeService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/DataTypeService.cs @@ -6,6 +6,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Exceptions; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Scoping; @@ -371,7 +372,7 @@ namespace Umbraco.Cms.Core.Services.Implement moveInfo.AddRange(_dataTypeRepository.Move(toMove, container)); scope.Notifications.Publish(new DataTypeMovedNotification(moveEventInfo, evtMsgs).WithStateFrom(movingDataTypeNotification)); - + scope.Complete(); } catch (DataOperationException ex) diff --git a/src/Umbraco.Infrastructure/Services/Implement/DomainService.cs b/src/Umbraco.Infrastructure/Services/Implement/DomainService.cs index d3937b5bb0..fc5d26e4df 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/DomainService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/DomainService.cs @@ -2,9 +2,9 @@ using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Services.Notifications; namespace Umbraco.Cms.Core.Services.Implement { diff --git a/src/Umbraco.Infrastructure/Services/Implement/FileService.cs b/src/Umbraco.Infrastructure/Services/Implement/FileService.cs index 508afe82b7..174adde65b 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/FileService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/FileService.cs @@ -9,9 +9,9 @@ using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Core.Strings; using Umbraco.Extensions; diff --git a/src/Umbraco.Infrastructure/Services/Implement/LocalizationService.cs b/src/Umbraco.Infrastructure/Services/Implement/LocalizationService.cs index 0e6b89693d..b024b8935e 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/LocalizationService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/LocalizationService.cs @@ -4,9 +4,9 @@ using System.Linq; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Services.Implement diff --git a/src/Umbraco.Infrastructure/Services/Implement/MacroService.cs b/src/Umbraco.Infrastructure/Services/Implement/MacroService.cs index 6d053eb1d1..a79d9fddce 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MacroService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MacroService.cs @@ -4,9 +4,9 @@ using System.Linq; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Services.Notifications; namespace Umbraco.Cms.Core.Services.Implement { diff --git a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs index 3f12d94cf1..34d1c2a5ce 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs @@ -7,11 +7,11 @@ using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services.Changes; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Persistence.Querying; using Umbraco.Extensions; diff --git a/src/Umbraco.Infrastructure/Services/Implement/MediaTypeService.cs b/src/Umbraco.Infrastructure/Services/Implement/MediaTypeService.cs index ede72f77a5..205e5b176e 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MediaTypeService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MediaTypeService.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services.Changes; diff --git a/src/Umbraco.Infrastructure/Services/Implement/MemberGroupService.cs b/src/Umbraco.Infrastructure/Services/Implement/MemberGroupService.cs index a41a19b541..096ff164a0 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MemberGroupService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MemberGroupService.cs @@ -4,9 +4,9 @@ using System.Linq; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Services.Notifications; namespace Umbraco.Cms.Core.Services.Implement { diff --git a/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs b/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs index 78f6d410a0..94476ff1e1 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs @@ -5,10 +5,10 @@ using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Infrastructure.Persistence.Querying; using Umbraco.Extensions; diff --git a/src/Umbraco.Infrastructure/Services/Implement/MemberTypeService.cs b/src/Umbraco.Infrastructure/Services/Implement/MemberTypeService.cs index 7f9e6e96e3..ad0498d0b2 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MemberTypeService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MemberTypeService.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services.Changes; diff --git a/src/Umbraco.Infrastructure/Services/Implement/PackagingService.cs b/src/Umbraco.Infrastructure/Services/Implement/PackagingService.cs index f3b199751e..3df297b47a 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/PackagingService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/PackagingService.cs @@ -8,6 +8,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Packaging; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Packaging; using Umbraco.Cms.Core.Semver; using Umbraco.Extensions; diff --git a/src/Umbraco.Infrastructure/Services/Implement/PublicAccessService.cs b/src/Umbraco.Infrastructure/Services/Implement/PublicAccessService.cs index dadee3b24a..419881fe1c 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/PublicAccessService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/PublicAccessService.cs @@ -4,9 +4,9 @@ using System.Linq; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Services.Implement diff --git a/src/Umbraco.Infrastructure/Services/Implement/RelationService.cs b/src/Umbraco.Infrastructure/Services/Implement/RelationService.cs index 668544af02..7a5d10c222 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/RelationService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/RelationService.cs @@ -5,10 +5,10 @@ using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Services.Implement diff --git a/src/Umbraco.Infrastructure/Services/Implement/UserService.cs b/src/Umbraco.Infrastructure/Services/Implement/UserService.cs index 4bc7141fae..743b4816da 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/UserService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/UserService.cs @@ -9,10 +9,10 @@ using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Infrastructure.Persistence.Querying; using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement; using Umbraco.Extensions; diff --git a/src/Umbraco.Infrastructure/WebAssets/ServerVariablesParser.cs b/src/Umbraco.Infrastructure/WebAssets/ServerVariablesParser.cs index c5f4eb128d..a7863bdbb5 100644 --- a/src/Umbraco.Infrastructure/WebAssets/ServerVariablesParser.cs +++ b/src/Umbraco.Infrastructure/WebAssets/ServerVariablesParser.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Newtonsoft.Json.Linq; using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; namespace Umbraco.Cms.Infrastructure.WebAssets { @@ -26,7 +27,7 @@ namespace Umbraco.Cms.Infrastructure.WebAssets var vars = Resources.ServerVariables; // Raise event for developers to add custom variables - await _eventAggregator.PublishAsync(new ServerVariablesParsing(items)); + await _eventAggregator.PublishAsync(new ServerVariablesParsingNotification(items)); var json = JObject.FromObject(items); return vars.Replace(Token, json.ToString()); diff --git a/src/Umbraco.PublishedCache.NuCache/Compose/NotificationsComposer.cs b/src/Umbraco.PublishedCache.NuCache/Compose/NotificationsComposer.cs index 9942761040..9cb8936431 100644 --- a/src/Umbraco.PublishedCache.NuCache/Compose/NotificationsComposer.cs +++ b/src/Umbraco.PublishedCache.NuCache/Compose/NotificationsComposer.cs @@ -1,7 +1,7 @@ using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Events; -using Umbraco.Cms.Core.Services.Notifications; +using Umbraco.Cms.Core.Notifications; namespace Umbraco.Cms.Infrastructure.PublishedCache.Compose { diff --git a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotServiceEventHandler.cs b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotServiceEventHandler.cs index f5043b0e53..898f22e989 100644 --- a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotServiceEventHandler.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotServiceEventHandler.cs @@ -4,10 +4,10 @@ using System.Linq; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.Changes; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Infrastructure.PublishedCache.Persistence; using Umbraco.Extensions; diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/ScopedRepositoryTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/ScopedRepositoryTests.cs index 0d27ecac5c..4da6e8ec98 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/ScopedRepositoryTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/ScopedRepositoryTests.cs @@ -9,10 +9,10 @@ using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.Implement; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Core.Sync; using Umbraco.Cms.Infrastructure.PublishedCache; using Umbraco.Cms.Infrastructure.Sync; diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentEventsTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentEventsTests.cs index fc9cae55a2..30eae39929 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentEventsTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentEventsTests.cs @@ -13,8 +13,8 @@ using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Core.Sync; using Umbraco.Cms.Core.Web; using Umbraco.Cms.Infrastructure.Sync; diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceNotificationTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceNotificationTests.cs index b18228718b..56b2dc9169 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceNotificationTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceNotificationTests.cs @@ -8,9 +8,9 @@ using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.Implement; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement; using Umbraco.Cms.Tests.Common.Builders; using Umbraco.Cms.Tests.Common.Testing; diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceTests.cs index ea6d50e116..45fbf7582e 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceTests.cs @@ -13,13 +13,13 @@ using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.Implement; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Infrastructure.Persistence; using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement; using Umbraco.Cms.Tests.Common.Builders; diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceTests.cs index f0721d3825..88e8e1b939 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceTests.cs @@ -10,9 +10,9 @@ using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Exceptions; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.Implement; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Tests.Common.Builders; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Cms.Tests.Integration.Testing; diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MediaTypeServiceTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MediaTypeServiceTests.cs index d9f0c596f0..1825598332 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MediaTypeServiceTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MediaTypeServiceTests.cs @@ -9,9 +9,9 @@ using NUnit.Framework; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.Implement; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Tests.Common.Builders; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Cms.Tests.Integration.Testing; diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Events/EventAggregatorTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Events/EventAggregatorTests.cs index bd43daf975..b747594fd1 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Events/EventAggregatorTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Events/EventAggregatorTests.cs @@ -9,6 +9,7 @@ using Moq; using NUnit.Framework; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Tests.UnitTests.TestHelpers; namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Events diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs index ab066cdb8b..af0848010d 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs @@ -15,6 +15,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Runtime; @@ -22,7 +23,6 @@ using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.Changes; using Umbraco.Cms.Core.Services.Implement; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Core.Xml; using Umbraco.Cms.Infrastructure.Persistence; diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index 3a212e085c..b1131d7787 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -13,11 +13,11 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Core.Sync; using Umbraco.Cms.Core.Web; diff --git a/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs b/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs index c5041b0ece..53ef2e0246 100644 --- a/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs @@ -9,8 +9,8 @@ using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Core.Sync; using Umbraco.Cms.Infrastructure.Sync; using Umbraco.Cms.Tests.Common.Testing; diff --git a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.BackOfficeAuth.cs b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.BackOfficeAuth.cs index d348ccdfc0..a652136a0f 100644 --- a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.BackOfficeAuth.cs +++ b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.BackOfficeAuth.cs @@ -9,6 +9,7 @@ using Umbraco.Cms.Web.BackOffice.Security; using Umbraco.Cms.Web.Common.Authorization; using Umbraco.Cms.Web.Common.Security; using Umbraco.Cms.Core.Actions; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Web.BackOffice.Authorization; diff --git a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs index 54956184ac..92f8c51d82 100644 --- a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs @@ -7,6 +7,7 @@ using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.DependencyInjection; using Umbraco.Cms.Infrastructure.WebAssets; diff --git a/src/Umbraco.Web.BackOffice/Filters/OutgoingEditorModelEventAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/OutgoingEditorModelEventAttribute.cs index 28f3dfbe60..5bda5eb936 100644 --- a/src/Umbraco.Web.BackOffice/Filters/OutgoingEditorModelEventAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/OutgoingEditorModelEventAttribute.cs @@ -6,6 +6,7 @@ using Umbraco.Cms.Core.Dashboards; using Umbraco.Cms.Core.Editors; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.ContentEditing; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Web; using Umbraco.Extensions; diff --git a/src/Umbraco.Web.BackOffice/ModelsBuilder/DisableModelsBuilderNotificationHandler.cs b/src/Umbraco.Web.BackOffice/ModelsBuilder/DisableModelsBuilderNotificationHandler.cs index dad8e54960..e41b6d3d83 100644 --- a/src/Umbraco.Web.BackOffice/ModelsBuilder/DisableModelsBuilderNotificationHandler.cs +++ b/src/Umbraco.Web.BackOffice/ModelsBuilder/DisableModelsBuilderNotificationHandler.cs @@ -1,21 +1,22 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Features; +using Umbraco.Cms.Core.Notifications; namespace Umbraco.Cms.Web.BackOffice.ModelsBuilder { /// /// Used in conjunction with /// - internal class DisableModelsBuilderNotificationHandler : INotificationHandler + internal class DisableModelsBuilderNotificationHandler : INotificationHandler { private readonly UmbracoFeatures _features; public DisableModelsBuilderNotificationHandler(UmbracoFeatures features) => _features = features; /// - /// Handles the notification to disable MB controller features + /// Handles the notification to disable MB controller features /// - public void Handle(UmbracoApplicationStarting notification) => + public void Handle(UmbracoApplicationStartingNotification notification) => // disable the embedded dashboard controller _features.Disabled.Controllers.Add(); } diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeUserManagerAuditer.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeUserManagerAuditer.cs index c74d445a89..a47987667f 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeUserManagerAuditer.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeUserManagerAuditer.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Web.Common.Security; using Umbraco.Extensions; diff --git a/src/Umbraco.Web.BackOffice/SignalR/PreviewHubUpdater.cs b/src/Umbraco.Web.BackOffice/SignalR/PreviewHubUpdater.cs index a71b5439d4..ddab717aa6 100644 --- a/src/Umbraco.Web.BackOffice/SignalR/PreviewHubUpdater.cs +++ b/src/Umbraco.Web.BackOffice/SignalR/PreviewHubUpdater.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.SignalR; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Sync; namespace Umbraco.Cms.Web.BackOffice.SignalR diff --git a/src/Umbraco.Web.BackOffice/Trees/MenuRenderingNotification.cs b/src/Umbraco.Web.BackOffice/Trees/MenuRenderingNotification.cs index cd30f2225e..71987360f4 100644 --- a/src/Umbraco.Web.BackOffice/Trees/MenuRenderingNotification.cs +++ b/src/Umbraco.Web.BackOffice/Trees/MenuRenderingNotification.cs @@ -1,8 +1,7 @@ using Microsoft.AspNetCore.Http; -using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Trees; -namespace Umbraco.Cms.Web.BackOffice.Trees +namespace Umbraco.Cms.Core.Notifications { /// /// A notification that allows developers to modify the menu that is being rendered diff --git a/src/Umbraco.Web.BackOffice/Trees/RootNodeRenderingNotification.cs b/src/Umbraco.Web.BackOffice/Trees/RootNodeRenderingNotification.cs index 28a0d90326..86d03c0d86 100644 --- a/src/Umbraco.Web.BackOffice/Trees/RootNodeRenderingNotification.cs +++ b/src/Umbraco.Web.BackOffice/Trees/RootNodeRenderingNotification.cs @@ -1,8 +1,7 @@ using Microsoft.AspNetCore.Http; -using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Trees; -namespace Umbraco.Cms.Web.BackOffice.Trees +namespace Umbraco.Cms.Core.Notifications { /// /// A notification that allows developer to modify the root tree node that is being rendered diff --git a/src/Umbraco.Web.BackOffice/Trees/TreeControllerBase.cs b/src/Umbraco.Web.BackOffice/Trees/TreeControllerBase.cs index 87e58de496..b6f2948965 100644 --- a/src/Umbraco.Web.BackOffice/Trees/TreeControllerBase.cs +++ b/src/Umbraco.Web.BackOffice/Trees/TreeControllerBase.cs @@ -7,6 +7,7 @@ using Umbraco.Cms.Core; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Trees; using Umbraco.Cms.Web.BackOffice.Controllers; using Umbraco.Cms.Web.Common.Filters; diff --git a/src/Umbraco.Web.BackOffice/Trees/TreeNodesRenderingNotification.cs b/src/Umbraco.Web.BackOffice/Trees/TreeNodesRenderingNotification.cs index 3b1b7816d6..f022b23a20 100644 --- a/src/Umbraco.Web.BackOffice/Trees/TreeNodesRenderingNotification.cs +++ b/src/Umbraco.Web.BackOffice/Trees/TreeNodesRenderingNotification.cs @@ -1,8 +1,7 @@ using Microsoft.AspNetCore.Http; -using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Trees; -namespace Umbraco.Cms.Web.BackOffice.Trees +namespace Umbraco.Cms.Core.Notifications { /// /// A notification that allows developers to modify the tree node collection that is being rendered diff --git a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreRequestAccessor.cs b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreRequestAccessor.cs index 273213ecbb..80c1a4ddab 100644 --- a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreRequestAccessor.cs +++ b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreRequestAccessor.cs @@ -6,12 +6,13 @@ using Microsoft.AspNetCore.Http.Extensions; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Web; using Umbraco.Extensions; namespace Umbraco.Cms.Web.Common.AspNetCore { - public class AspNetCoreRequestAccessor : IRequestAccessor, INotificationHandler + public class AspNetCoreRequestAccessor : IRequestAccessor, INotificationHandler { private readonly IHttpContextAccessor _httpContextAccessor; private readonly WebRoutingSettings _webRoutingSettings; @@ -89,7 +90,7 @@ namespace Umbraco.Cms.Web.Common.AspNetCore /// TODO: This doesn't belong here, the GetApplicationUrl doesn't belong to IRequestAccessor /// this should be part of middleware not a lazy init based on an INotification /// - public void Handle(UmbracoRequestBegin notification) + public void Handle(UmbracoRequestBeginNotification notification) => LazyInitializer.EnsureInitialized(ref _hasAppUrl, ref _isInit, ref _initLocker, () => { GetApplicationUrl(); diff --git a/src/Umbraco.Web.Common/Cache/HttpContextRequestAppCache.cs b/src/Umbraco.Web.Common/Cache/HttpContextRequestAppCache.cs index f43f9a9a24..03acc9abb1 100644 --- a/src/Umbraco.Web.Common/Cache/HttpContextRequestAppCache.cs +++ b/src/Umbraco.Web.Common/Cache/HttpContextRequestAppCache.cs @@ -6,6 +6,7 @@ using System.Threading; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Cache @@ -15,7 +16,7 @@ namespace Umbraco.Cms.Core.Cache /// /// /// The HttpContext is not thread safe and no part of it is which means we need to include our own thread - /// safety mechanisms. This relies on notifications: and + /// safety mechanisms. This relies on notifications: and /// in order to facilitate the correct locking and releasing allocations. /// /// diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs index fc3c806b22..6771a683f1 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs @@ -30,6 +30,7 @@ using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Logging; using Umbraco.Cms.Core.Macros; using Umbraco.Cms.Core.Net; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Templates; @@ -196,7 +197,7 @@ namespace Umbraco.Extensions options.IgnoredPaths.Remove("/content/"); }); - builder.AddNotificationHandler(); + builder.AddNotificationHandler(); return builder; } @@ -269,7 +270,7 @@ namespace Umbraco.Extensions // AspNetCore specific services builder.Services.AddUnique(); - builder.AddNotificationHandler(); + builder.AddNotificationHandler(); // Password hasher builder.Services.AddUnique(); diff --git a/src/Umbraco.Web.Common/Install/CreateUnattendedUserNotificationHandler.cs b/src/Umbraco.Web.Common/Install/CreateUnattendedUserNotificationHandler.cs index 73b1fc3c36..df25dda78a 100644 --- a/src/Umbraco.Web.Common/Install/CreateUnattendedUserNotificationHandler.cs +++ b/src/Umbraco.Web.Common/Install/CreateUnattendedUserNotificationHandler.cs @@ -7,6 +7,7 @@ using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; using Umbraco.Extensions; diff --git a/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs b/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs index 78b8ede69e..c8a7abf727 100644 --- a/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs +++ b/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs @@ -13,6 +13,7 @@ using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Logging; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Security; @@ -121,7 +122,7 @@ namespace Umbraco.Cms.Web.Common.Middleware try { LazyInitializeBackOfficeServices(context.Request.Path); - await _eventAggregator.PublishAsync(new UmbracoRequestBegin(umbracoContextReference.UmbracoContext)); + await _eventAggregator.PublishAsync(new UmbracoRequestBeginNotification(umbracoContextReference.UmbracoContext)); } catch (Exception ex) { @@ -137,7 +138,7 @@ namespace Umbraco.Cms.Web.Common.Middleware } finally { - await _eventAggregator.PublishAsync(new UmbracoRequestEnd(umbracoContextReference.UmbracoContext)); + await _eventAggregator.PublishAsync(new UmbracoRequestEndNotification(umbracoContextReference.UmbracoContext)); } } } diff --git a/src/Umbraco.Web.Common/ModelBinders/ContentModelBinder.cs b/src/Umbraco.Web.Common/ModelBinders/ContentModelBinder.cs index 564e0992b5..21a6af1723 100644 --- a/src/Umbraco.Web.Common/ModelBinders/ContentModelBinder.cs +++ b/src/Umbraco.Web.Common/ModelBinders/ContentModelBinder.cs @@ -6,6 +6,7 @@ using Umbraco.Cms.Core; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Web.Common.Routing; using Umbraco.Extensions; @@ -161,7 +162,7 @@ namespace Umbraco.Cms.Web.Common.ModelBinders // raise event, to give model factories a chance at reporting // the error with more details, and optionally request that // the application restarts. - var args = new ModelBindingError(sourceType, modelType, msg); + var args = new ModelBindingErrorNotification(sourceType, modelType, msg); _eventAggregator.Publish(args); throw new ModelBindingException(msg.ToString()); diff --git a/src/Umbraco.Web.Common/ModelsBuilder/DependencyInjection/UmbracoBuilderDependencyInjectionExtensions.cs b/src/Umbraco.Web.Common/ModelsBuilder/DependencyInjection/UmbracoBuilderDependencyInjectionExtensions.cs index 3c057785e5..93534f6da4 100644 --- a/src/Umbraco.Web.Common/ModelsBuilder/DependencyInjection/UmbracoBuilderDependencyInjectionExtensions.cs +++ b/src/Umbraco.Web.Common/ModelsBuilder/DependencyInjection/UmbracoBuilderDependencyInjectionExtensions.cs @@ -9,7 +9,7 @@ using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.Services.Notifications; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Infrastructure.ModelsBuilder; using Umbraco.Cms.Infrastructure.ModelsBuilder.Building; using Umbraco.Cms.Infrastructure.WebAssets; @@ -100,10 +100,10 @@ namespace Umbraco.Extensions // TODO: I feel like we could just do builder.AddNotificationHandler() and it // would automatically just register for all implemented INotificationHandler{T}? builder.AddNotificationHandler(); - builder.AddNotificationHandler(); - builder.AddNotificationHandler(); - builder.AddNotificationHandler(); - builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + builder.AddNotificationHandler(); builder.AddNotificationHandler(); builder.AddNotificationHandler(); diff --git a/src/Umbraco.Web.Common/ModelsBuilder/ModelsBuilderNotificationHandler.cs b/src/Umbraco.Web.Common/ModelsBuilder/ModelsBuilderNotificationHandler.cs index c57ae8b7b9..100dff7352 100644 --- a/src/Umbraco.Web.Common/ModelsBuilder/ModelsBuilderNotificationHandler.cs +++ b/src/Umbraco.Web.Common/ModelsBuilder/ModelsBuilderNotificationHandler.cs @@ -7,7 +7,7 @@ using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Services.Notifications; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.ModelsBuilder; using Umbraco.Cms.Infrastructure.WebAssets; @@ -16,11 +16,11 @@ using Umbraco.Cms.Web.Common.ModelBinders; namespace Umbraco.Cms.Web.Common.ModelsBuilder { /// - /// Handles and notifications to initialize MB + /// Handles and notifications to initialize MB /// internal class ModelsBuilderNotificationHandler : - INotificationHandler, - INotificationHandler, + INotificationHandler, + INotificationHandler, INotificationHandler { private readonly ModelsBuilderSettings _config; @@ -38,9 +38,9 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder } /// - /// Handles the notification to add custom urls and MB mode + /// Handles the notification to add custom urls and MB mode /// - public void Handle(ServerVariablesParsing notification) + public void Handle(ServerVariablesParsingNotification notification) { IDictionary serverVars = notification.ServerVariables; @@ -137,7 +137,7 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder /// /// Handles when a model binding error occurs /// - public void Handle(ModelBindingError notification) + public void Handle(ModelBindingErrorNotification notification) { ModelsBuilderAssemblyAttribute sourceAttr = notification.SourceType.Assembly.GetCustomAttribute(); ModelsBuilderAssemblyAttribute modelAttr = notification.ModelType.Assembly.GetCustomAttribute(); diff --git a/src/Umbraco.Web.Common/Profiler/InitializeWebProfiling.cs b/src/Umbraco.Web.Common/Profiler/InitializeWebProfiling.cs index 257fd948a1..755282bcf4 100644 --- a/src/Umbraco.Web.Common/Profiler/InitializeWebProfiling.cs +++ b/src/Umbraco.Web.Common/Profiler/InitializeWebProfiling.cs @@ -4,13 +4,14 @@ using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Logging; +using Umbraco.Cms.Core.Notifications; namespace Umbraco.Cms.Web.Common.Profiler { /// /// Initialized the web profiling. Ensures the boot process profiling is stopped. /// - public class InitializeWebProfiling : INotificationHandler + public class InitializeWebProfiling : INotificationHandler { private readonly bool _profile; private readonly WebProfiler _profiler; @@ -41,7 +42,7 @@ namespace Umbraco.Cms.Web.Common.Profiler } /// - public void Handle(UmbracoApplicationStarting notification) + public void Handle(UmbracoApplicationStartingNotification notification) { if (_profile) { diff --git a/src/Umbraco.Web.Common/Security/BackOfficeUserManager.cs b/src/Umbraco.Web.Common/Security/BackOfficeUserManager.cs index 0b15378289..27b8638aa1 100644 --- a/src/Umbraco.Web.Common/Security/BackOfficeUserManager.cs +++ b/src/Umbraco.Web.Common/Security/BackOfficeUserManager.cs @@ -10,6 +10,7 @@ using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Net; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Infrastructure.Security; using Umbraco.Extensions; From 980d9ad5cfbfb60abd475473548ad46063ffea8e Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Wed, 12 May 2021 12:36:13 +1000 Subject: [PATCH 084/182] #10193 The listview search within a dialog (eg. minilistview) shows the loading indicator forever for zero results (#10202) * The listview search within a dialog (eg. minilistview) shows the loading indicator forever for zero results #10193 * Updated comment. * Another updated comment. Co-authored-by: Adam Nelson --- .../directives/components/umbminilistview.directive.js | 3 +++ .../src/views/components/umb-mini-list-view.html | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbminilistview.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbminilistview.directive.js index 783cd7f90a..f7b634a710 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbminilistview.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbminilistview.directive.js @@ -58,6 +58,9 @@ entityResource.getPagedChildren(miniListView.node.id, scope.entityType, miniListView.pagination) .then(function (data) { + if (!data.items) { + data.items = []; + } if (scope.onItemsLoaded) { scope.onItemsLoaded({items: data.items}); } diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-list-view.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-list-view.html index 6ec8b08da6..e2319f099d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-list-view.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-list-view.html @@ -74,14 +74,14 @@
      {{ child.name }}
      - -
      + +
      No items have been added Sorry, we can not find what you are looking for.
      - -
      + +
      From a0fd6ad293eb7477923c352878679a49354004a3 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 12 May 2021 09:41:40 +0200 Subject: [PATCH 085/182] Fix issue with build number as part of the version --- src/Umbraco.Core/Extensions/SemVersionExtensions.cs | 7 +++++++ .../Controllers/BackOfficeServerVariables.cs | 5 +++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Extensions/SemVersionExtensions.cs b/src/Umbraco.Core/Extensions/SemVersionExtensions.cs index 85e4892a09..e8b2a2534b 100644 --- a/src/Umbraco.Core/Extensions/SemVersionExtensions.cs +++ b/src/Umbraco.Core/Extensions/SemVersionExtensions.cs @@ -11,5 +11,12 @@ namespace Umbraco.Extensions { return semVersion.ToString().Replace("--", "-").Replace("-+", "+"); } + + public static string ToSemanticStringWithoutBuild(this SemVersion semVersion) + { + var version = semVersion.ToSemanticString(); + var indexOfBuild = version.IndexOf('+'); + return indexOfBuild >= 0 ? version.Substring(0, indexOfBuild) : version; + } } } diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs index 6ec2abc60e..29ba133334 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs @@ -506,18 +506,19 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// private Dictionary GetApplicationState() { + var version = _runtimeState.SemanticVersion.ToSemanticStringWithoutBuild(); var app = new Dictionary { // add versions - see UmbracoVersion for details & differences // the complete application version (eg "8.1.2-alpha.25") - { "version", _umbracoVersion.SemanticVersion.ToSemanticString() }, + { "version", version }, // the assembly version (eg "8.0.0") { "assemblyVersion", _umbracoVersion.AssemblyVersion.ToString() } }; - var version = _runtimeState.SemanticVersion.ToSemanticString(); + //the value is the hash of the version, cdf version and the configured state app.Add("cacheBuster", $"{version}.{_runtimeState.Level}.{_runtimeMinifier.CacheBuster}".GenerateHash()); From de0929762c60039b3a41dd34c48227ad85ae377f Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 12 May 2021 09:49:41 +0200 Subject: [PATCH 086/182] Fix issue with build number as part of the version --- src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs | 2 +- src/Umbraco.Web.BackOffice/Controllers/DashboardController.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs b/src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs index fe540b73dd..b1411270c3 100644 --- a/src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs +++ b/src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs @@ -64,7 +64,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices using (var request = new HttpRequestMessage(HttpMethod.Post, "installs/")) { - var postData = new TelemetryReportData { Id = telemetrySiteIdentifier, Version = _umbracoVersion.SemanticVersion.ToSemanticString() }; + var postData = new TelemetryReportData { Id = telemetrySiteIdentifier, Version = _umbracoVersion.SemanticVersion.ToSemanticStringWithoutBuild() }; request.Content = new StringContent(JsonConvert.SerializeObject(postData), Encoding.UTF8, "application/json"); //CONTENT-TYPE header // Set a low timeout - no need to use a larger default timeout for this POST request diff --git a/src/Umbraco.Web.BackOffice/Controllers/DashboardController.cs b/src/Umbraco.Web.BackOffice/Controllers/DashboardController.cs index b3de544184..32ad18e909 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/DashboardController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/DashboardController.cs @@ -70,7 +70,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers var user = _backOfficeSecurityAccessor.BackOfficeSecurity.CurrentUser; var allowedSections = string.Join(",", user.AllowedSections); var language = user.Language; - var version = _umbracoVersion.SemanticVersion.ToSemanticString(); + var version = _umbracoVersion.SemanticVersion.ToSemanticStringWithoutBuild(); var isAdmin = user.IsAdmin(); var url = string.Format(baseUrl + "{0}?section={0}&allowed={1}&lang={2}&version={3}&admin={4}", section, allowedSections, language, version, isAdmin); From fd9faf5a995cb55d86d4f98184b56df77979e188 Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Wed, 12 May 2021 11:58:36 +0200 Subject: [PATCH 087/182] Re-implemented various file service methods from V8 required in Deploy. --- .../Repositories/IFileRepository.cs | 13 + .../IFileWithFoldersRepository.cs | 9 + .../Repositories/IPartialViewRepository.cs | 4 +- .../Repositories/IScriptRepository.cs | 5 +- .../Repositories/IStylesheetRepository.cs | 4 +- .../Repositories/ITemplateRepository.cs | 10 +- src/Umbraco.Core/Services/IFileService.cs | 119 +++++++- .../Repositories/Implement/FileRepository.cs | 86 +++--- .../Implement/ScriptRepository.cs | 3 +- .../Implement/TemplateRepository.cs | 189 +++++++----- .../Services/Implement/FileService.cs | 280 +++++++++++++----- 11 files changed, 508 insertions(+), 214 deletions(-) create mode 100644 src/Umbraco.Core/Persistence/Repositories/IFileRepository.cs create mode 100644 src/Umbraco.Core/Persistence/Repositories/IFileWithFoldersRepository.cs diff --git a/src/Umbraco.Core/Persistence/Repositories/IFileRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IFileRepository.cs new file mode 100644 index 0000000000..ce76086ed2 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Repositories/IFileRepository.cs @@ -0,0 +1,13 @@ +using System.IO; + +namespace Umbraco.Cms.Core.Persistence.Repositories +{ + public interface IFileRepository + { + Stream GetFileContentStream(string filepath); + + void SetFileContent(string filepath, Stream content); + + long GetFileSize(string filepath); + } +} diff --git a/src/Umbraco.Core/Persistence/Repositories/IFileWithFoldersRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IFileWithFoldersRepository.cs new file mode 100644 index 0000000000..77c2f9d40b --- /dev/null +++ b/src/Umbraco.Core/Persistence/Repositories/IFileWithFoldersRepository.cs @@ -0,0 +1,9 @@ +namespace Umbraco.Cms.Core.Persistence.Repositories +{ + public interface IFileWithFoldersRepository + { + void AddFolder(string folderPath); + + void DeleteFolder(string folderPath); + } +} diff --git a/src/Umbraco.Core/Persistence/Repositories/IPartialViewRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IPartialViewRepository.cs index d1e322dd4c..a8a84079fa 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IPartialViewRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IPartialViewRepository.cs @@ -2,9 +2,7 @@ using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Persistence.Repositories { - public interface IPartialViewRepository : IReadRepository, IWriteRepository + public interface IPartialViewRepository : IReadRepository, IWriteRepository, IFileRepository, IFileWithFoldersRepository { - void AddFolder(string folderPath); - void DeleteFolder(string folderPath); } } diff --git a/src/Umbraco.Core/Persistence/Repositories/IScriptRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IScriptRepository.cs index d3835b8e7e..604e1da8d2 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IScriptRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IScriptRepository.cs @@ -1,10 +1,9 @@ +using System.IO; using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Persistence.Repositories { - public interface IScriptRepository : IReadRepository, IWriteRepository + public interface IScriptRepository : IReadRepository, IWriteRepository, IFileRepository, IFileWithFoldersRepository { - void AddFolder(string folderPath); - void DeleteFolder(string folderPath); } } diff --git a/src/Umbraco.Core/Persistence/Repositories/IStylesheetRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IStylesheetRepository.cs index ce57f2c9ae..dcdb5debe7 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IStylesheetRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IStylesheetRepository.cs @@ -2,9 +2,7 @@ using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Persistence.Repositories { - public interface IStylesheetRepository : IReadRepository, IWriteRepository + public interface IStylesheetRepository : IReadRepository, IWriteRepository, IFileRepository, IFileWithFoldersRepository { - void AddFolder(string folderPath); - void DeleteFolder(string folderPath); } } diff --git a/src/Umbraco.Core/Persistence/Repositories/ITemplateRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ITemplateRepository.cs index 7e6a81864c..3c9174e818 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ITemplateRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ITemplateRepository.cs @@ -1,10 +1,9 @@ using System.Collections.Generic; -using System.IO; using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Persistence.Repositories { - public interface ITemplateRepository : IReadWriteQueryRepository + public interface ITemplateRepository : IReadWriteQueryRepository, IFileRepository { ITemplate Get(string alias); @@ -13,12 +12,5 @@ namespace Umbraco.Cms.Core.Persistence.Repositories IEnumerable GetChildren(int masterTemplateId); IEnumerable GetDescendants(int masterTemplateId); - - /// - /// Gets the content of a template as a stream. - /// - /// The filesystem path to the template. - /// The content of the template. - Stream GetFileContentStream(string filepath); } } diff --git a/src/Umbraco.Core/Services/IFileService.cs b/src/Umbraco.Core/Services/IFileService.cs index eea1bb6b22..5df5602bc6 100644 --- a/src/Umbraco.Core/Services/IFileService.cs +++ b/src/Umbraco.Core/Services/IFileService.cs @@ -24,6 +24,48 @@ namespace Umbraco.Cms.Core.Services Attempt SavePartialView(IPartialView partialView, int userId = Constants.Security.SuperUserId); Attempt SavePartialViewMacro(IPartialView partialView, int userId = Constants.Security.SuperUserId); + /// + /// Gets the content of a partial view as a stream. + /// + /// The filesystem path to the partial view. + /// The content of the partial view. + Stream GetPartialViewFileContentStream(string filepath); + + /// + /// Sets the content of a partial view. + /// + /// The filesystem path to the partial view. + /// The content of the partial view. + void SetPartialViewFileContent(string filepath, Stream content); + + /// + /// Gets the size of a partial view. + /// + /// The filesystem path to the partial view. + /// The size of the partial view. + long GetPartialViewFileSize(string filepath); + + /// + /// Gets the content of a macro partial view as a stream. + /// + /// The filesystem path to the macro partial view. + /// The content of the macro partial view. + Stream GetPartialViewMacroFileContentStream(string filepath); + + /// + /// Sets the content of a macro partial view. + /// + /// The filesystem path to the macro partial view. + /// The content of the macro partial view. + void SetPartialViewMacroFileContent(string filepath, Stream content); + + /// + /// Gets the size of a macro partial view. + /// + /// The filesystem path to the macro partial view. + /// The size of the macro partial view. + long GetPartialViewMacroFileSize(string filepath); + /// /// Gets a list of all objects /// @@ -51,6 +93,40 @@ namespace Umbraco.Cms.Core.Services /// Optional id of the user deleting the stylesheet void DeleteStylesheet(string path, int userId = Constants.Security.SuperUserId); + /// + /// Creates a folder for style sheets + /// + /// + /// + void CreateStyleSheetFolder(string folderPath); + + /// + /// Deletes a folder for style sheets + /// + /// + void DeleteStyleSheetFolder(string folderPath); + + /// + /// Gets the content of a stylesheet as a stream. + /// + /// The filesystem path to the stylesheet. + /// The content of the stylesheet. + Stream GetStylesheetFileContentStream(string filepath); + + /// + /// Sets the content of a stylesheet. + /// + /// The filesystem path to the stylesheet. + /// The content of the stylesheet. + void SetStylesheetFileContent(string filepath, Stream content); + + /// + /// Gets the size of a stylesheet. + /// + /// The filesystem path to the stylesheet. + /// The size of the stylesheet. + long GetStylesheetFileSize(string filepath); + /// /// Gets a object by its name /// @@ -86,17 +162,25 @@ namespace Umbraco.Cms.Core.Services void DeleteScriptFolder(string folderPath); /// - /// Creates a folder for style sheets + /// Gets the content of a script file as a stream. /// - /// - /// - void CreateStyleSheetFolder(string folderPath); + /// The filesystem path to the script. + /// The content of the script file. + Stream GetScriptFileContentStream(string filepath); /// - /// Deletes a folder for style sheets + /// Sets the content of a script file. /// - /// - void DeleteStyleSheetFolder(string folderPath); + /// The filesystem path to the script. + /// The content of the script file. + void SetScriptFileContent(string filepath, Stream content); + + /// + /// Gets the size of a script file. + /// + /// The filesystem path to the script file. + /// The size of the script file. + long GetScriptFileSize(string filepath); /// /// Gets a list of all objects @@ -172,6 +256,27 @@ namespace Umbraco.Cms.Core.Services /// Optional id of the user void SaveTemplate(IEnumerable templates, int userId = Constants.Security.SuperUserId); + /// + /// Gets the content of a template as a stream. + /// + /// The filesystem path to the template. + /// The content of the template. + Stream GetTemplateFileContentStream(string filepath); + + /// + /// Sets the content of a template. + /// + /// The filesystem path to the template. + /// The content of the template. + void SetTemplateFileContent(string filepath, Stream content); + + /// + /// Gets the size of a template. + /// + /// The filesystem path to the template. + /// The size of the template. + long GetTemplateFileSize(string filepath); + /// /// Gets the content of a macro partial view snippet as a string /// diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/FileRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/FileRepository.cs index 2e7e8af144..23365939e0 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/FileRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/FileRepository.cs @@ -12,46 +12,35 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement internal abstract class FileRepository : IReadRepository, IWriteRepository where TEntity : IFile { - protected FileRepository(IFileSystem fileSystem) - { - FileSystem = fileSystem; - } + protected FileRepository(IFileSystem fileSystem) => FileSystem = fileSystem; protected IFileSystem FileSystem { get; } - public virtual void AddFolder(string folderPath) - { - PersistNewItem(new Folder(folderPath)); - } + public virtual void AddFolder(string folderPath) => PersistNewItem(new Folder(folderPath)); - public virtual void DeleteFolder(string folderPath) - { - PersistDeletedItem(new Folder(folderPath)); - } + public virtual void DeleteFolder(string folderPath) => PersistDeletedItem(new Folder(folderPath)); #region Implementation of IRepository public virtual void Save(TEntity entity) { if (FileSystem.FileExists(entity.OriginalPath) == false) + { PersistNewItem(entity); + } else + { PersistUpdatedItem(entity); + } } - public virtual void Delete(TEntity entity) - { - PersistDeletedItem(entity); - } + public virtual void Delete(TEntity entity) => PersistDeletedItem(entity); public abstract TEntity Get(TId id); public abstract IEnumerable GetMany(params TId[] ids); - public virtual bool Exists(TId id) - { - return FileSystem.FileExists(id.ToString()); - } + public virtual bool Exists(TId id) => FileSystem.FileExists(id.ToString()); #endregion @@ -60,8 +49,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement public void PersistNewItem(IEntity entity) { //special case for folder - var folder = entity as Folder; - if (folder != null) + if (entity is Folder folder) { PersistNewFolder(folder); } @@ -71,16 +59,12 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement } } - public void PersistUpdatedItem(IEntity entity) - { - PersistUpdatedItem((TEntity)entity); - } + public void PersistUpdatedItem(IEntity entity) => PersistUpdatedItem((TEntity)entity); public void PersistDeletedItem(IEntity entity) { //special case for folder - var folder = entity as Folder; - if (folder != null) + if (entity is Folder folder) { PersistDeletedFolder(folder); } @@ -92,21 +76,15 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement #endregion - internal virtual void PersistNewFolder(Folder entity) - { - FileSystem.CreateFolder(entity.Path); - } + internal virtual void PersistNewFolder(Folder entity) => FileSystem.CreateFolder(entity.Path); - internal virtual void PersistDeletedFolder(Folder entity) - { - FileSystem.DeleteDirectory(entity.Path); - } + internal virtual void PersistDeletedFolder(Folder entity) => FileSystem.DeleteDirectory(entity.Path); #region Abstract IUnitOfWorkRepository Methods protected virtual void PersistNewItem(TEntity entity) { - using (var stream = GetContentStream(entity.Content)) + using (Stream stream = GetContentStream(entity.Content)) { FileSystem.AddFile(entity.Path, stream, true); entity.CreateDate = FileSystem.GetCreated(entity.Path).UtcDateTime; @@ -120,7 +98,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement protected virtual void PersistUpdatedItem(TEntity entity) { - using (var stream = GetContentStream(entity.Content)) + using (Stream stream = GetContentStream(entity.Content)) { FileSystem.AddFile(entity.Path, stream, true); entity.CreateDate = FileSystem.GetCreated(entity.Path).UtcDateTime; @@ -156,10 +134,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement /// /// /// - protected virtual Stream GetContentStream(string content) - { - return new MemoryStream(Encoding.UTF8.GetBytes(content)); - } + protected virtual Stream GetContentStream(string content) => new MemoryStream(Encoding.UTF8.GetBytes(content)); /// /// Returns all files in the file system @@ -179,7 +154,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement var list = new List(); list.AddRange(FileSystem.GetFiles(path, filter)); - var directories = FileSystem.GetDirectories(path); + IEnumerable directories = FileSystem.GetDirectories(path); foreach (var directory in directories) { list.AddRange(FindAllFiles(directory, filter)); @@ -191,11 +166,13 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement protected string GetFileContent(string filename) { if (FileSystem.FileExists(filename) == false) + { return null; + } try { - using (var stream = FileSystem.OpenFile(filename)) + using (Stream stream = FileSystem.OpenFile(filename)) using (var reader = new StreamReader(stream, Encoding.UTF8, true)) { return reader.ReadToEnd(); @@ -207,10 +184,31 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement } } + public Stream GetFileContentStream(string filepath) + { + if (FileSystem.FileExists(filepath) == false) + { + return null; + } + + try + { + return FileSystem.OpenFile(filepath); + } + catch + { + return null; // deal with race conds + } + } + + public void SetFileContent(string filepath, Stream content) => FileSystem.AddFile(filepath, content, true); + public long GetFileSize(string filename) { if (FileSystem.FileExists(filename) == false) + { return -1; + } try { diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ScriptRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ScriptRepository.cs index 3cebe816fb..711a43f716 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ScriptRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ScriptRepository.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System.Collections.Generic; +using System.IO; using System.Linq; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs index cd42bd5dd5..37eb9fab79 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs @@ -39,39 +39,38 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement _viewHelper = new ViewHelper(_viewsFileSystem); } - protected override IRepositoryCachePolicy CreateCachePolicy() - { - return new FullDataSetRepositoryCachePolicy(GlobalIsolatedCache, ScopeAccessor, GetEntityId, /*expires:*/ false); - } + protected override IRepositoryCachePolicy CreateCachePolicy() => + new FullDataSetRepositoryCachePolicy(GlobalIsolatedCache, ScopeAccessor, GetEntityId, /*expires:*/ false); #region Overrides of RepositoryBase - protected override ITemplate PerformGet(int id) - { + protected override ITemplate PerformGet(int id) => //use the underlying GetAll which will force cache all templates - return base.GetMany().FirstOrDefault(x => x.Id == id); - } + base.GetMany().FirstOrDefault(x => x.Id == id); protected override IEnumerable PerformGetAll(params int[] ids) { - var sql = GetBaseQuery(false); + Sql sql = GetBaseQuery(false); if (ids.Any()) { - sql.Where("umbracoNode.id in (@ids)", new { ids = ids }); + sql.Where("umbracoNode.id in (@ids)", new { ids }); } else { sql.Where(x => x.NodeObjectType == NodeObjectTypeId); } - var dtos = Database.Fetch(sql); + List dtos = Database.Fetch(sql); - if (dtos.Count == 0) return Enumerable.Empty(); + if (dtos.Count == 0) + { + return Enumerable.Empty(); + } //look up the simple template definitions that have a master template assigned, this is used // later to populate the template item's properties - var childIds = (ids.Any() + IUmbracoEntity[] childIds = (ids.Any() ? GetAxisDefinitions(dtos.ToArray()) : dtos.Select(x => new EntitySlim { @@ -85,17 +84,20 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement protected override IEnumerable PerformGetByQuery(IQuery query) { - var sqlClause = GetBaseQuery(false); + Sql sqlClause = GetBaseQuery(false); var translator = new SqlTranslator(sqlClause, query); - var sql = translator.Translate(); + Sql sql = translator.Translate(); - var dtos = Database.Fetch(sql); + List dtos = Database.Fetch(sql); - if (dtos.Count == 0) return Enumerable.Empty(); + if (dtos.Count == 0) + { + return Enumerable.Empty(); + } //look up the simple template definitions that have a master template assigned, this is used // later to populate the template item's properties - var childIds = GetAxisDefinitions(dtos.ToArray()).ToArray(); + IUmbracoEntity[] childIds = GetAxisDefinitions(dtos.ToArray()).ToArray(); return dtos.Select(d => MapFromDto(d, childIds)); } @@ -106,7 +108,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement protected override Sql GetBaseQuery(bool isCount) { - var sql = SqlContext.Sql(); + Sql sql = SqlContext.Sql(); sql = isCount ? sql.SelectCount() @@ -121,10 +123,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement return sql; } - protected override string GetBaseWhereClause() - { - return Cms.Core.Constants.DatabaseSchema.Tables.Node + ".id = @id"; - } + protected override string GetBaseWhereClause() => Cms.Core.Constants.DatabaseSchema.Tables.Node + ".id = @id"; protected override IEnumerable GetDeleteClauses() { @@ -150,15 +149,15 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement var template = (Template)entity; template.AddingEntity(); - var dto = TemplateFactory.BuildDto(template, NodeObjectTypeId, template.Id); + TemplateDto dto = TemplateFactory.BuildDto(template, NodeObjectTypeId, template.Id); //Create the (base) node data - umbracoNode - var nodeDto = dto.NodeDto; + NodeDto nodeDto = dto.NodeDto; nodeDto.Path = "-1," + dto.NodeDto.NodeId; - var o = Database.IsNew(nodeDto) ? Convert.ToInt32(Database.Insert(nodeDto)) : Database.Update(nodeDto); + int o = Database.IsNew(nodeDto) ? Convert.ToInt32(Database.Insert(nodeDto)) : Database.Update(nodeDto); //Update with new correct path - var parent = Get(template.MasterTemplateId.Value); + ITemplate parent = Get(template.MasterTemplateId.Value); if (parent != null) { nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId); @@ -184,7 +183,9 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement // ensure that from now on, content is lazy-loaded if (template.GetFileContent == null) + { template.GetFileContent = file => GetFileContent((Template) file, false); + } } protected override void PersistUpdatedItem(ITemplate entity) @@ -192,11 +193,11 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement EnsureValidAlias(entity); //store the changed alias if there is one for use with updating files later - var originalAlias = entity.Alias; + string originalAlias = entity.Alias; if (entity.IsPropertyDirty("Alias")) { //we need to check what it currently is before saving and remove that file - var current = Get(entity.Id); + ITemplate current = Get(entity.Id); originalAlias = current.Alias; } @@ -204,7 +205,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement if (entity.IsPropertyDirty("MasterTemplateId")) { - var parent = Get(template.MasterTemplateId.Value); + ITemplate parent = Get(template.MasterTemplateId.Value); if (parent != null) { entity.Path = string.Concat(parent.Path, ",", entity.Id); @@ -218,17 +219,16 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement } //Get TemplateDto from db to get the Primary key of the entity - var templateDto = Database.SingleOrDefault("WHERE nodeId = @Id", new { Id = entity.Id }); + TemplateDto templateDto = Database.SingleOrDefault("WHERE nodeId = @Id", new { entity.Id }); + //Save updated entity to db - template.UpdateDate = DateTime.Now; - var dto = TemplateFactory.BuildDto(template, NodeObjectTypeId, templateDto.PrimaryKey); - + TemplateDto dto = TemplateFactory.BuildDto(template, NodeObjectTypeId, templateDto.PrimaryKey); Database.Update(dto.NodeDto); Database.Update(dto); //re-update if this is a master template, since it could have changed! - var axisDefs = GetAxisDefinitions(dto); + IEnumerable axisDefs = GetAxisDefinitions(dto); template.IsMasterTemplate = axisDefs.Any(x => x.ParentId == dto.NodeId); //now do the file work @@ -238,15 +238,16 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement // ensure that from now on, content is lazy-loaded if (template.GetFileContent == null) + { template.GetFileContent = file => GetFileContent((Template) file, false); + } } private void SaveFile(Template template, string originalAlias = null) { string content; - var templateOnDisk = template as TemplateOnDisk; - if (templateOnDisk != null && templateOnDisk.IsOnDisk) + if (template is TemplateOnDisk templateOnDisk && templateOnDisk.IsOnDisk) { // if "template on disk" load content from disk content = _viewHelper.GetFileContents(template); @@ -266,7 +267,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement protected override void PersistDeletedItem(ITemplate entity) { - var deletes = GetDeleteClauses().ToArray(); + string[] deletes = GetDeleteClauses().ToArray(); var descendants = GetDescendants(entity.Id).ToList(); @@ -274,21 +275,21 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement descendants.Reverse(); //delete the hierarchy - foreach (var descendant in descendants) + foreach (ITemplate descendant in descendants) { - foreach (var delete in deletes) + foreach (string delete in deletes) { Database.Execute(delete, new { id = GetEntityId(descendant) }); } } //now we can delete this one - foreach (var delete in deletes) + foreach (string delete in deletes) { Database.Execute(delete, new { id = GetEntityId(entity) }); } - var viewName = string.Concat(entity.Alias, ".cshtml"); + string viewName = string.Concat(entity.Alias, ".cshtml"); _viewsFileSystem.DeleteFile(viewName); entity.DeleteDate = DateTime.Now; @@ -300,7 +301,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement { //look up the simple template definitions that have a master template assigned, this is used // later to populate the template item's properties - var childIdsSql = SqlContext.Sql() + Sql childIdsSql = SqlContext.Sql() .Select("nodeId,alias,parentID") .From() .InnerJoin() @@ -309,7 +310,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement .Where("umbracoNode." + SqlContext.SqlSyntax.GetQuotedColumnName("id") + " IN (@parentIds) OR umbracoNode.parentID IN (@childIds)", new {parentIds = templates.Select(x => x.NodeDto.ParentId), childIds = templates.Select(x => x.NodeId)}); - var childIds = Database.Fetch(childIdsSql) + IEnumerable childIds = Database.Fetch(childIdsSql) .Select(x => new EntitySlim { Id = x.nodeId, @@ -330,11 +331,11 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement private ITemplate MapFromDto(TemplateDto dto, IUmbracoEntity[] axisDefinitions) { - var template = TemplateFactory.BuildEntity(_shortStringHelper, dto, axisDefinitions, file => GetFileContent((Template) file, false)); + Template template = TemplateFactory.BuildEntity(_shortStringHelper, dto, axisDefinitions, file => GetFileContent((Template) file, false)); if (dto.NodeDto.ParentId > 0) { - var masterTemplate = axisDefinitions.FirstOrDefault(x => x.Id == dto.NodeDto.ParentId); + IUmbracoEntity masterTemplate = axisDefinitions.FirstOrDefault(x => x.Id == dto.NodeDto.ParentId); if (masterTemplate != null) { template.MasterTemplateAlias = masterTemplate.Name; @@ -354,7 +355,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement private void SetVirtualPath(ITemplate template) { - var path = template.OriginalPath; + string path = template.OriginalPath; if (string.IsNullOrWhiteSpace(path)) { // we need to discover the path @@ -382,16 +383,21 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement private string GetFileContent(ITemplate template, bool init) { - var path = template.OriginalPath; + string path = template.OriginalPath; if (string.IsNullOrWhiteSpace(path)) { // we need to discover the path path = string.Concat(template.Alias, ".cshtml"); if (_viewsFileSystem.FileExists(path)) + { return GetFileContent(template, _viewsFileSystem, path, init); + } + path = string.Concat(template.Alias, ".vbhtml"); if (_viewsFileSystem.FileExists(path)) + { return GetFileContent(template, _viewsFileSystem, path, init); + } } else { @@ -427,7 +433,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement private string GetFileContent(IFileSystem fs, string filename) { - using (var stream = fs.OpenFile(filename)) + using (Stream stream = fs.OpenFile(filename)) using (var reader = new StreamReader(stream, Encoding.UTF8, true)) { return reader.ReadToEnd(); @@ -436,8 +442,11 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement public Stream GetFileContentStream(string filepath) { - var fs = GetFileSystem(filepath); - if (fs.FileExists(filepath) == false) return null; + IFileSystem fs = GetFileSystem(filepath); + if (fs.FileExists(filepath) == false) + { + return null; + } try { @@ -449,9 +458,28 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement } } + public void SetFileContent(string filepath, Stream content) => GetFileSystem(filepath).AddFile(filepath, content, true); + + public long GetFileSize(string filename) + { + if (GetFileSystem(filename).FileExists(filename) == false) + { + return -1; + } + + try + { + return GetFileSystem(filename).GetSize(filename); + } + catch + { + return -1; // deal with race conds + } + } + private IFileSystem GetFileSystem(string filepath) { - var ext = Path.GetExtension(filepath); + string ext = Path.GetExtension(filepath); IFileSystem fs; switch (ext) { @@ -467,17 +495,17 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement #region Implementation of ITemplateRepository - public ITemplate Get(string alias) - { - return GetAll(alias).FirstOrDefault(); - } + public ITemplate Get(string alias) => GetAll(alias).FirstOrDefault(); public IEnumerable GetAll(params string[] aliases) { //We must call the base (normal) GetAll method // which is cached. This is a specialized method and unfortunately with the params[] it // overlaps with the normal GetAll method. - if (aliases.Any() == false) return base.GetMany(); + if (aliases.Any() == false) + { + return base.GetMany(); + } //return from base.GetAll, this is all cached return base.GetMany().Where(x => aliases.InvariantContains(x.Alias)); @@ -486,33 +514,43 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement public IEnumerable GetChildren(int masterTemplateId) { //return from base.GetAll, this is all cached - var all = base.GetMany().ToArray(); + ITemplate[] all = base.GetMany().ToArray(); - if (masterTemplateId <= 0) return all.Where(x => x.MasterTemplateAlias.IsNullOrWhiteSpace()); + if (masterTemplateId <= 0) + { + return all.Where(x => x.MasterTemplateAlias.IsNullOrWhiteSpace()); + } - var parent = all.FirstOrDefault(x => x.Id == masterTemplateId); - if (parent == null) return Enumerable.Empty(); + ITemplate parent = all.FirstOrDefault(x => x.Id == masterTemplateId); + if (parent == null) + { + return Enumerable.Empty(); + } - var children = all.Where(x => x.MasterTemplateAlias.InvariantEquals(parent.Alias)); + IEnumerable children = all.Where(x => x.MasterTemplateAlias.InvariantEquals(parent.Alias)); return children; } public IEnumerable GetDescendants(int masterTemplateId) { //return from base.GetAll, this is all cached - var all = base.GetMany().ToArray(); + ITemplate[] all = base.GetMany().ToArray(); var descendants = new List(); if (masterTemplateId > 0) { - var parent = all.FirstOrDefault(x => x.Id == masterTemplateId); - if (parent == null) return Enumerable.Empty(); + ITemplate parent = all.FirstOrDefault(x => x.Id == masterTemplateId); + if (parent == null) + { + return Enumerable.Empty(); + } + //recursively add all children with a level AddChildren(all, descendants, parent.Alias); } else { descendants.AddRange(all.Where(x => x.MasterTemplateAlias.IsNullOrWhiteSpace())); - foreach (var parent in descendants) + foreach (ITemplate parent in descendants) { //recursively add all children with a level AddChildren(all, descendants, parent.Alias); @@ -525,11 +563,15 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement private void AddChildren(ITemplate[] all, List descendants, string masterAlias) { - var c = all.Where(x => x.MasterTemplateAlias.InvariantEquals(masterAlias)).ToArray(); + ITemplate[] c = all.Where(x => x.MasterTemplateAlias.InvariantEquals(masterAlias)).ToArray(); descendants.AddRange(c); - if (c.Any() == false) return; + if (c.Any() == false) + { + return; + } + //recurse through all children - foreach (var child in c) + foreach (ITemplate child in c) { AddChildren(all, descendants, child.Alias); } @@ -547,7 +589,9 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement template.Alias = template.Alias.ToCleanString(_shortStringHelper, CleanStringType.UnderscoreAlias); if (template.Alias.Length > 100) + { template.Alias = template.Alias.Substring(0, 95); + } if (AliasAlreadExists(template)) { @@ -557,8 +601,8 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement private bool AliasAlreadExists(ITemplate template) { - var sql = GetBaseQuery(true).Where(x => x.Alias.InvariantEquals(template.Alias) && x.NodeId != template.Id); - var count = Database.ExecuteScalar(sql); + Sql sql = GetBaseQuery(true).Where(x => x.Alias.InvariantEquals(template.Alias) && x.NodeId != template.Id); + int count = Database.ExecuteScalar(sql); return count > 0; } @@ -566,7 +610,10 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement { // TODO: This is ported from the old data layer... pretty crap way of doing this but it works for now. if (AliasAlreadExists(template)) + { return template.Alias + attempts; + } + attempts++; return EnsureUniqueAlias(template, attempts); } diff --git a/src/Umbraco.Infrastructure/Services/Implement/FileService.cs b/src/Umbraco.Infrastructure/Services/Implement/FileService.cs index 174adde65b..6d7d5b4490 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/FileService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/FileService.cs @@ -10,6 +10,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Strings; @@ -57,7 +58,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// public IEnumerable GetStylesheets(params string[] names) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { return _stylesheetRepository.GetMany(names); } @@ -66,14 +67,14 @@ namespace Umbraco.Cms.Core.Services.Implement /// public IStylesheet GetStylesheetByName(string name) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { return _stylesheetRepository.Get(name); } } /// - public void SaveStylesheet(IStylesheet stylesheet, int userId = Cms.Core.Constants.Security.SuperUserId) + public void SaveStylesheet(IStylesheet stylesheet, int userId = Constants.Security.SuperUserId) { using (IScope scope = ScopeProvider.CreateScope()) { @@ -95,7 +96,7 @@ namespace Umbraco.Cms.Core.Services.Implement } /// - public void DeleteStylesheet(string path, int userId = Cms.Core.Constants.Security.SuperUserId) + public void DeleteStylesheet(string path, int userId = Constants.Security.SuperUserId) { using (IScope scope = ScopeProvider.CreateScope()) { @@ -123,24 +124,54 @@ namespace Umbraco.Cms.Core.Services.Implement } } + /// public void CreateStyleSheetFolder(string folderPath) { - using (var scope = ScopeProvider.CreateScope()) + using (IScope scope = ScopeProvider.CreateScope()) { _stylesheetRepository.AddFolder(folderPath); scope.Complete(); } } + /// public void DeleteStyleSheetFolder(string folderPath) { - using (var scope = ScopeProvider.CreateScope()) + using (IScope scope = ScopeProvider.CreateScope()) { _stylesheetRepository.DeleteFolder(folderPath); scope.Complete(); } } + /// + public Stream GetStylesheetFileContentStream(string filepath) + { + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) + { + return _stylesheetRepository.GetFileContentStream(filepath); + } + } + + /// + public void SetStylesheetFileContent(string filepath, Stream content) + { + using (IScope scope = ScopeProvider.CreateScope()) + { + _stylesheetRepository.SetFileContent(filepath, content); + scope.Complete(); + } + } + + /// + public long GetStylesheetFileSize(string filepath) + { + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) + { + return _stylesheetRepository.GetFileSize(filepath); + } + } + #endregion #region Scripts @@ -148,14 +179,14 @@ namespace Umbraco.Cms.Core.Services.Implement /// public IScript GetScriptByName(string name) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { return _scriptRepository.Get(name); } } /// - public void SaveScript(IScript script, int userId = Cms.Core.Constants.Security.SuperUserId) + public void SaveScript(IScript script, int userId = Constants.Security.SuperUserId) { using (IScope scope = ScopeProvider.CreateScope()) { @@ -176,7 +207,7 @@ namespace Umbraco.Cms.Core.Services.Implement } /// - public void DeleteScript(string path, int userId = Cms.Core.Constants.Security.SuperUserId) + public void DeleteScript(string path, int userId = Constants.Security.SuperUserId) { using (IScope scope = ScopeProvider.CreateScope()) { @@ -203,24 +234,54 @@ namespace Umbraco.Cms.Core.Services.Implement } } + /// public void CreateScriptFolder(string folderPath) { - using (var scope = ScopeProvider.CreateScope()) + using (IScope scope = ScopeProvider.CreateScope()) { _scriptRepository.AddFolder(folderPath); scope.Complete(); } } + /// public void DeleteScriptFolder(string folderPath) { - using (var scope = ScopeProvider.CreateScope()) + using (IScope scope = ScopeProvider.CreateScope()) { _scriptRepository.DeleteFolder(folderPath); scope.Complete(); } } + /// + public Stream GetScriptFileContentStream(string filepath) + { + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) + { + return _scriptRepository.GetFileContentStream(filepath); + } + } + + /// + public void SetScriptFileContent(string filepath, Stream content) + { + using (IScope scope = ScopeProvider.CreateScope()) + { + _scriptRepository.SetFileContent(filepath, content); + scope.Complete(); + } + } + + /// + public long GetScriptFileSize(string filepath) + { + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) + { + return _scriptRepository.GetFileSize(filepath); + } + } + #endregion #region Templates @@ -234,7 +295,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// The template created /// - public Attempt> CreateTemplateForContentType(string contentTypeAlias, string contentTypeName, int userId = Cms.Core.Constants.Security.SuperUserId) + public Attempt> CreateTemplateForContentType(string contentTypeAlias, string contentTypeName, int userId = Constants.Security.SuperUserId) { var template = new Template(_shortStringHelper, contentTypeName, //NOTE: We are NOT passing in the content type alias here, we want to use it's name since we don't @@ -286,7 +347,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// /// - public ITemplate CreateTemplateWithIdentity(string name, string alias, string content, ITemplate masterTemplate = null, int userId = Cms.Core.Constants.Security.SuperUserId) + public ITemplate CreateTemplateWithIdentity(string name, string alias, string content, ITemplate masterTemplate = null, int userId = Constants.Security.SuperUserId) { if (name == null) { @@ -325,7 +386,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// An enumerable list of objects public IEnumerable GetTemplates(params string[] aliases) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { return _templateRepository.GetAll(aliases).OrderBy(x => x.Name); } @@ -337,7 +398,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// An enumerable list of objects public IEnumerable GetTemplates(int masterTemplateId) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { return _templateRepository.GetChildren(masterTemplateId).OrderBy(x => x.Name); } @@ -350,7 +411,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// The object matching the alias, or null. public ITemplate GetTemplate(string alias) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { return _templateRepository.Get(alias); } @@ -363,7 +424,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// The object matching the identifier, or null. public ITemplate GetTemplate(int id) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { return _templateRepository.Get(id); } @@ -376,9 +437,9 @@ namespace Umbraco.Cms.Core.Services.Implement /// The object matching the identifier, or null. public ITemplate GetTemplate(Guid id) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { - var query = Query().Where(x => x.Key == id); + IQuery query = Query().Where(x => x.Key == id); return _templateRepository.Get(query).SingleOrDefault(); } } @@ -390,7 +451,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// public IEnumerable GetTemplateDescendants(int masterTemplateId) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { return _templateRepository.GetDescendants(masterTemplateId); } @@ -401,7 +462,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// to save /// - public void SaveTemplate(ITemplate template, int userId = Cms.Core.Constants.Security.SuperUserId) + public void SaveTemplate(ITemplate template, int userId = Constants.Security.SuperUserId) { if (template == null) { @@ -438,7 +499,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// List of to save /// Optional id of the user - public void SaveTemplate(IEnumerable templates, int userId = Cms.Core.Constants.Security.SuperUserId) + public void SaveTemplate(IEnumerable templates, int userId = Constants.Security.SuperUserId) { ITemplate[] templatesA = templates.ToArray(); using (IScope scope = ScopeProvider.CreateScope()) @@ -468,7 +529,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// Alias of the to delete /// - public void DeleteTemplate(string alias, int userId = Cms.Core.Constants.Security.SuperUserId) + public void DeleteTemplate(string alias, int userId = Constants.Security.SuperUserId) { using (IScope scope = ScopeProvider.CreateScope()) { @@ -499,22 +560,58 @@ namespace Umbraco.Cms.Core.Services.Implement private string GetViewContent(string fileName) { if (fileName.IsNullOrWhiteSpace()) + { throw new ArgumentNullException(nameof(fileName)); + } if (!fileName.EndsWith(".cshtml")) + { fileName = $"{fileName}.cshtml"; + } + + Stream fs = _templateRepository.GetFileContentStream(fileName); + if (fs == null) + { + return null; + } - var fs = _templateRepository.GetFileContentStream(fileName); - if (fs == null) return null; using (var view = new StreamReader(fs)) { return view.ReadToEnd().Trim(); } } -#endregion + /// + public Stream GetTemplateFileContentStream(string filepath) + { + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) + { + return _templateRepository.GetFileContentStream(filepath); + } + } -#region Partial Views + /// + public void SetTemplateFileContent(string filepath, Stream content) + { + using (IScope scope = ScopeProvider.CreateScope()) + { + _templateRepository.SetFileContent(filepath, content); + scope.Complete(); + } + } + + /// + public long GetTemplateFileSize(string filepath) + { + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) + { + return _templateRepository.GetFileSize(filepath); + } + } + + #endregion + + #region Partial Views public IEnumerable GetPartialViewSnippetNames(params string[] filterNames) { @@ -534,7 +631,7 @@ namespace Umbraco.Cms.Core.Services.Implement public void DeletePartialViewFolder(string folderPath) { - using (var scope = ScopeProvider.CreateScope()) + using (IScope scope = ScopeProvider.CreateScope()) { _partialViewRepository.DeleteFolder(folderPath); scope.Complete(); @@ -543,7 +640,7 @@ namespace Umbraco.Cms.Core.Services.Implement public void DeletePartialViewMacroFolder(string folderPath) { - using (var scope = ScopeProvider.CreateScope()) + using (IScope scope = ScopeProvider.CreateScope()) { _partialViewMacroRepository.DeleteFolder(folderPath); scope.Complete(); @@ -552,7 +649,7 @@ namespace Umbraco.Cms.Core.Services.Implement public IPartialView GetPartialView(string path) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { return _partialViewRepository.Get(path); } @@ -560,23 +657,19 @@ namespace Umbraco.Cms.Core.Services.Implement public IPartialView GetPartialViewMacro(string path) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { return _partialViewMacroRepository.Get(path); } } - public Attempt CreatePartialView(IPartialView partialView, string snippetName = null, int userId = Cms.Core.Constants.Security.SuperUserId) - { - return CreatePartialViewMacro(partialView, PartialViewType.PartialView, snippetName, userId); - } + public Attempt CreatePartialView(IPartialView partialView, string snippetName = null, int userId = Constants.Security.SuperUserId) => + CreatePartialViewMacro(partialView, PartialViewType.PartialView, snippetName, userId); - public Attempt CreatePartialViewMacro(IPartialView partialView, string snippetName = null, int userId = Cms.Core.Constants.Security.SuperUserId) - { - return CreatePartialViewMacro(partialView, PartialViewType.PartialViewMacro, snippetName, userId); - } + public Attempt CreatePartialViewMacro(IPartialView partialView, string snippetName = null, int userId = Constants.Security.SuperUserId) => + CreatePartialViewMacro(partialView, PartialViewType.PartialViewMacro, snippetName, userId); - private Attempt CreatePartialViewMacro(IPartialView partialView, PartialViewType partialViewType, string snippetName = null, int userId = Cms.Core.Constants.Security.SuperUserId) + private Attempt CreatePartialViewMacro(IPartialView partialView, PartialViewType partialViewType, string snippetName = null, int userId = Constants.Security.SuperUserId) { string partialViewHeader; switch (partialViewType) @@ -646,17 +739,13 @@ namespace Umbraco.Cms.Core.Services.Implement return Attempt.Succeed(partialView); } - public bool DeletePartialView(string path, int userId = Cms.Core.Constants.Security.SuperUserId) - { - return DeletePartialViewMacro(path, PartialViewType.PartialView, userId); - } + public bool DeletePartialView(string path, int userId = Constants.Security.SuperUserId) => + DeletePartialViewMacro(path, PartialViewType.PartialView, userId); - public bool DeletePartialViewMacro(string path, int userId = Cms.Core.Constants.Security.SuperUserId) - { - return DeletePartialViewMacro(path, PartialViewType.PartialViewMacro, userId); - } + public bool DeletePartialViewMacro(string path, int userId = Constants.Security.SuperUserId) => + DeletePartialViewMacro(path, PartialViewType.PartialViewMacro, userId); - private bool DeletePartialViewMacro(string path, PartialViewType partialViewType, int userId = Cms.Core.Constants.Security.SuperUserId) + private bool DeletePartialViewMacro(string path, PartialViewType partialViewType, int userId = Constants.Security.SuperUserId) { using (IScope scope = ScopeProvider.CreateScope()) { @@ -686,17 +775,13 @@ namespace Umbraco.Cms.Core.Services.Implement return true; } - public Attempt SavePartialView(IPartialView partialView, int userId = Cms.Core.Constants.Security.SuperUserId) - { - return SavePartialView(partialView, PartialViewType.PartialView, userId); - } + public Attempt SavePartialView(IPartialView partialView, int userId = Constants.Security.SuperUserId) => + SavePartialView(partialView, PartialViewType.PartialView, userId); - public Attempt SavePartialViewMacro(IPartialView partialView, int userId = Cms.Core.Constants.Security.SuperUserId) - { - return SavePartialView(partialView, PartialViewType.PartialViewMacro, userId); - } + public Attempt SavePartialViewMacro(IPartialView partialView, int userId = Constants.Security.SuperUserId) => + SavePartialView(partialView, PartialViewType.PartialViewMacro, userId); - private Attempt SavePartialView(IPartialView partialView, PartialViewType partialViewType, int userId = Cms.Core.Constants.Security.SuperUserId) + private Attempt SavePartialView(IPartialView partialView, PartialViewType partialViewType, int userId = Constants.Security.SuperUserId) { using (IScope scope = ScopeProvider.CreateScope()) { @@ -741,7 +826,7 @@ namespace Umbraco.Cms.Core.Services.Implement public void CreatePartialViewFolder(string folderPath) { - using (var scope = ScopeProvider.CreateScope()) + using (IScope scope = ScopeProvider.CreateScope()) { _partialViewRepository.AddFolder(folderPath); scope.Complete(); @@ -750,7 +835,7 @@ namespace Umbraco.Cms.Core.Services.Implement public void CreatePartialViewMacroFolder(string folderPath) { - using (var scope = ScopeProvider.CreateScope()) + using (IScope scope = ScopeProvider.CreateScope()) { _partialViewMacroRepository.AddFolder(folderPath); scope.Complete(); @@ -770,24 +855,76 @@ namespace Umbraco.Cms.Core.Services.Implement } } + /// + public Stream GetPartialViewFileContentStream(string filepath) + { + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) + { + return _partialViewRepository.GetFileContentStream(filepath); + } + } + + /// + public void SetPartialViewFileContent(string filepath, Stream content) + { + using (IScope scope = ScopeProvider.CreateScope()) + { + _partialViewRepository.SetFileContent(filepath, content); + scope.Complete(); + } + } + + /// + public long GetPartialViewFileSize(string filepath) + { + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) + { + return _partialViewRepository.GetFileSize(filepath); + } + } + + /// + public Stream GetPartialViewMacroFileContentStream(string filepath) + { + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) + { + return _partialViewMacroRepository.GetFileContentStream(filepath); + } + } + + /// + public void SetPartialViewMacroFileContent(string filepath, Stream content) + { + using (IScope scope = ScopeProvider.CreateScope()) + { + _partialViewMacroRepository.SetFileContent(filepath, content); + scope.Complete(); + } + } + + /// + public long GetPartialViewMacroFileSize(string filepath) + { + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) + { + return _partialViewMacroRepository.GetFileSize(filepath); + } + } + #endregion #region Snippets - public string GetPartialViewSnippetContent(string snippetName) - { - return GetPartialViewMacroSnippetContent(snippetName, PartialViewType.PartialView); - } + public string GetPartialViewSnippetContent(string snippetName) => GetPartialViewMacroSnippetContent(snippetName, PartialViewType.PartialView); - public string GetPartialViewMacroSnippetContent(string snippetName) - { - return GetPartialViewMacroSnippetContent(snippetName, PartialViewType.PartialViewMacro); - } + public string GetPartialViewMacroSnippetContent(string snippetName) => GetPartialViewMacroSnippetContent(snippetName, PartialViewType.PartialViewMacro); private string GetPartialViewMacroSnippetContent(string snippetName, PartialViewType partialViewType) { if (snippetName.IsNullOrWhiteSpace()) + { throw new ArgumentNullException(nameof(snippetName)); + } string partialViewHeader; switch (partialViewType) @@ -803,7 +940,7 @@ namespace Umbraco.Cms.Core.Services.Implement } // Try and get the snippet path - var snippetPathAttempt = TryGetSnippetPath(snippetName); + Attempt snippetPathAttempt = TryGetSnippetPath(snippetName); if (snippetPathAttempt.Success == false) { throw new InvalidOperationException("Could not load snippet with name " + snippetName); @@ -831,10 +968,7 @@ namespace Umbraco.Cms.Core.Services.Implement #endregion - private void Audit(AuditType type, int userId, int objectId, string entityType) - { - _auditRepository.Save(new AuditItem(objectId, type, userId, entityType)); - } + private void Audit(AuditType type, int userId, int objectId, string entityType) => _auditRepository.Save(new AuditItem(objectId, type, userId, entityType)); // TODO: Method to change name and/or alias of view template } From 9507ba84f216fad33299ff75a0a8b8222dc77a36 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 12 May 2021 14:35:06 +0200 Subject: [PATCH 088/182] Updated version to beta003 --- build/templates/UmbracoPackage/.template.config/template.json | 2 +- build/templates/UmbracoProject/.template.config/template.json | 2 +- src/Directory.Build.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/templates/UmbracoPackage/.template.config/template.json b/build/templates/UmbracoPackage/.template.config/template.json index d0a50c1f0a..0a4c8f2f2b 100644 --- a/build/templates/UmbracoPackage/.template.config/template.json +++ b/build/templates/UmbracoPackage/.template.config/template.json @@ -24,7 +24,7 @@ "version": { "type": "parameter", "datatype": "string", - "defaultValue": "9.0.0-beta002", + "defaultValue": "9.0.0-beta003", "description": "The version of Umbraco to load using NuGet", "replaces": "UMBRACO_VERSION_FROM_TEMPLATE" }, diff --git a/build/templates/UmbracoProject/.template.config/template.json b/build/templates/UmbracoProject/.template.config/template.json index 733c6f1fd5..ff4cdfd54c 100644 --- a/build/templates/UmbracoProject/.template.config/template.json +++ b/build/templates/UmbracoProject/.template.config/template.json @@ -34,7 +34,7 @@ "version": { "type": "parameter", "datatype": "string", - "defaultValue": "9.0.0-beta002", + "defaultValue": "9.0.0-beta003", "description": "The version of Umbraco to load using NuGet", "replaces": "UMBRACO_VERSION_FROM_TEMPLATE" }, diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 759d3027d8..66b7cb4bb5 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -2,7 +2,7 @@ 9.0.0 9.0.0 - 9.0.0-beta002 + 9.0.0-beta003 9.0.0 9.0 en-US From 01643e494a5eb0de1d3c3da1ea1bfd641069b109 Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Wed, 12 May 2021 14:53:43 +0200 Subject: [PATCH 089/182] Removed unecessary second calls to retrieve the file system in TemplateRepository. --- .../Repositories/Implement/TemplateRepository.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs index 37eb9fab79..4e4f52c93a 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs @@ -442,15 +442,15 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement public Stream GetFileContentStream(string filepath) { - IFileSystem fs = GetFileSystem(filepath); - if (fs.FileExists(filepath) == false) + IFileSystem fileSystem = GetFileSystem(filepath); + if (fileSystem.FileExists(filepath) == false) { return null; } try { - return GetFileSystem(filepath).OpenFile(filepath); + return fileSystem.OpenFile(filepath); } catch { @@ -462,14 +462,15 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement public long GetFileSize(string filename) { - if (GetFileSystem(filename).FileExists(filename) == false) + IFileSystem fileSystem = GetFileSystem(filename); + if (fileSystem.FileExists(filename) == false) { return -1; } try { - return GetFileSystem(filename).GetSize(filename); + return fileSystem.GetSize(filename); } catch { From 474a76fd2b954fbc385a9080e1c316259297ff36 Mon Sep 17 00:00:00 2001 From: Chad Currie Date: Sat, 15 May 2021 20:23:24 +1200 Subject: [PATCH 090/182] Use ContainsKey for Dictionaries. O(1) --- src/Umbraco.Core/Models/Entities/BeingDirtyBase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Models/Entities/BeingDirtyBase.cs b/src/Umbraco.Core/Models/Entities/BeingDirtyBase.cs index a1f4bad9a1..65aafafbcd 100644 --- a/src/Umbraco.Core/Models/Entities/BeingDirtyBase.cs +++ b/src/Umbraco.Core/Models/Entities/BeingDirtyBase.cs @@ -29,7 +29,7 @@ namespace Umbraco.Core.Models.Entities /// public virtual bool IsPropertyDirty(string propertyName) { - return _currentChanges != null && _currentChanges.Any(x => x.Key == propertyName); + return _currentChanges != null && _currentChanges.ContainsKey(propertyName); } /// @@ -61,7 +61,7 @@ namespace Umbraco.Core.Models.Entities /// public virtual bool WasPropertyDirty(string propertyName) { - return _savedChanges != null && _savedChanges.Any(x => x.Key == propertyName); + return _savedChanges != null && _savedChanges.ContainsKey(propertyName); } /// From aaf5cd5faeed798f5b508d2c999405160e7fc6a5 Mon Sep 17 00:00:00 2001 From: Paul Seal Date: Fri, 14 May 2021 10:02:46 +0100 Subject: [PATCH 091/182] Added a link to 404 documentation I think it will be useful to include a link to the documentation on how to create a custom 404 --- src/Umbraco.Web/Routing/PublishedContentNotFoundHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Routing/PublishedContentNotFoundHandler.cs b/src/Umbraco.Web/Routing/PublishedContentNotFoundHandler.cs index 0045cf33dc..a6e4f4b450 100644 --- a/src/Umbraco.Web/Routing/PublishedContentNotFoundHandler.cs +++ b/src/Umbraco.Web/Routing/PublishedContentNotFoundHandler.cs @@ -42,7 +42,7 @@ namespace Umbraco.Web.Routing response.Write(""); if (string.IsNullOrWhiteSpace(_message) == false) response.Write("

      " + _message + "

      "); - response.Write("

      This page can be replaced with a custom 404. Check the documentation for \"custom 404\".

      "); + response.Write("

      This page can be replaced with a custom 404. Check the documentation for Custom 404 Error Pages.

      "); response.Write("

      This page is intentionally left ugly ;-)

      "); response.Write(""); } From 69a507e5db91fc9835eefeeda1e1536f4ef0584b Mon Sep 17 00:00:00 2001 From: Mario Lopez Date: Mon, 17 May 2021 17:50:58 +1000 Subject: [PATCH 092/182] V9 - Include models folder in the project (#10250) * fixes #10213. When models folder is included in project the models have a Build Action of C# Compiler * models directory comment indicates correct models location * added PureLive files to App_Data/Temp * changed PureLive directory * include generated models in project * https://github.com/umbraco/Umbraco-CMS/pull/10250/ - Cleanup in csproj file and fix the generated template too Co-authored-by: Mario Lopez Co-authored-by: Bjarke Berg --- .../UmbracoProject/UmbracoProject.csproj | 4 -- .../Models/ModelsBuilderSettings.cs | 5 +- .../ModelsBuilder/PureLiveModelFactory.cs | 52 +++++++++++-------- .../Umbraco.Web.UI.NetCore.csproj | 7 +-- 4 files changed, 36 insertions(+), 32 deletions(-) diff --git a/build/templates/UmbracoProject/UmbracoProject.csproj b/build/templates/UmbracoProject/UmbracoProject.csproj index 08643bb150..3f229fde99 100644 --- a/build/templates/UmbracoProject/UmbracoProject.csproj +++ b/build/templates/UmbracoProject/UmbracoProject.csproj @@ -18,16 +18,13 @@ - - - @@ -44,7 +41,6 @@ - diff --git a/src/Umbraco.Core/Configuration/Models/ModelsBuilderSettings.cs b/src/Umbraco.Core/Configuration/Models/ModelsBuilderSettings.cs index 192c3966f6..349d9f01da 100644 --- a/src/Umbraco.Core/Configuration/Models/ModelsBuilderSettings.cs +++ b/src/Umbraco.Core/Configuration/Models/ModelsBuilderSettings.cs @@ -30,7 +30,7 @@ namespace Umbraco.Cms.Core.Configuration.Models /// /// /// Models become out-of-date when data types or content types are updated. When this - /// setting is activated the ~/App_Data/Models/ood.txt file is then created. When models are + /// setting is activated the ~/umbraco/models/PureLive/ood.txt file is then created. When models are /// generated through the dashboard, the files is cleared. Default value is false. /// public bool FlagOutOfDateModels @@ -51,9 +51,10 @@ namespace Umbraco.Cms.Core.Configuration.Models /// /// Gets or sets a value for the models directory. /// - /// Default is ~/App_Data/Models but that can be changed. + /// Default is ~/umbraco/models but that can be changed. public string ModelsDirectory { get; set; } = DefaultModelsDirectory; + /// /// Gets or sets a value indicating whether to accept an unsafe value for ModelsDirectory. /// diff --git a/src/Umbraco.Web.Common/ModelsBuilder/PureLiveModelFactory.cs b/src/Umbraco.Web.Common/ModelsBuilder/PureLiveModelFactory.cs index ef8f54c1c1..568ef73362 100644 --- a/src/Umbraco.Web.Common/ModelsBuilder/PureLiveModelFactory.cs +++ b/src/Umbraco.Web.Common/ModelsBuilder/PureLiveModelFactory.cs @@ -50,6 +50,9 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder private readonly ApplicationPartManager _applicationPartManager; private static readonly Regex s_usingRegex = new Regex("^using(.*);", RegexOptions.Compiled | RegexOptions.Multiline); private static readonly Regex s_aattrRegex = new Regex("^\\[assembly:(.*)\\]", RegexOptions.Compiled | RegexOptions.Multiline); + private readonly Lazy _pureLiveDirectory; + + public PureLiveModelFactory( Lazy umbracoServices, @@ -77,17 +80,18 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder return; } - var modelsDirectory = _config.ModelsDirectoryAbsolute(_hostingEnvironment); - if (!Directory.Exists(modelsDirectory)) + _pureLiveDirectory = new Lazy(PureLiveDirectoryAbsolute); + + if (!Directory.Exists(_pureLiveDirectory.Value)) { - Directory.CreateDirectory(modelsDirectory); + Directory.CreateDirectory(_pureLiveDirectory.Value); } // BEWARE! if the watcher is not properly released then for some reason the // BuildManager will start confusing types - using a 'registered object' here // though we should probably plug into Umbraco's MainDom - which is internal _hostingLifetime.RegisterObject(this); - _watcher = new FileSystemWatcher(modelsDirectory); + _watcher = new FileSystemWatcher(_pureLiveDirectory.Value); _watcher.Changed += WatcherOnChanged; _watcher.EnableRaisingEvents = true; @@ -218,15 +222,14 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder _hasModels = false; _pendingRebuild = true; - var modelsDirectory = _config.ModelsDirectoryAbsolute(_hostingEnvironment); - if (!Directory.Exists(modelsDirectory)) + if (!Directory.Exists(_pureLiveDirectory.Value)) { - Directory.CreateDirectory(modelsDirectory); + Directory.CreateDirectory(_pureLiveDirectory.Value); } // clear stuff - var modelsHashFile = Path.Combine(modelsDirectory, "models.hash"); - var dllPathFile = Path.Combine(modelsDirectory, "all.dll.path"); + var modelsHashFile = Path.Combine(_pureLiveDirectory.Value, "models.hash"); + var dllPathFile = Path.Combine(_pureLiveDirectory.Value, "all.dll.path"); if (File.Exists(dllPathFile)) { @@ -340,6 +343,10 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder } } + + public string PureLiveDirectoryAbsolute() => _hostingEnvironment.MapPathContentRoot("~/umbraco/Data/TEMP/PureLive"); + + // This is NOT thread safe but it is only called from within a lock private Assembly ReloadAssembly(string pathToAssembly) { @@ -389,18 +396,18 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder // This is NOT thread safe but it is only called from within a lock private Assembly GetModelsAssembly(bool forceRebuild) { - var modelsDirectory = _config.ModelsDirectoryAbsolute(_hostingEnvironment); - if (!Directory.Exists(modelsDirectory)) + + if (!Directory.Exists(_pureLiveDirectory.Value)) { - Directory.CreateDirectory(modelsDirectory); + Directory.CreateDirectory(_pureLiveDirectory.Value); } IList typeModels = UmbracoServices.GetAllTypes(); var currentHash = TypeModelHasher.Hash(typeModels); - var modelsHashFile = Path.Combine(modelsDirectory, "models.hash"); - var modelsSrcFile = Path.Combine(modelsDirectory, "models.generated.cs"); - var projFile = Path.Combine(modelsDirectory, "all.generated.cs"); - var dllPathFile = Path.Combine(modelsDirectory, "all.dll.path"); + var modelsHashFile = Path.Combine(_pureLiveDirectory.Value, "models.hash"); + var modelsSrcFile = Path.Combine(_pureLiveDirectory.Value, "models.generated.cs"); + var projFile = Path.Combine(_pureLiveDirectory.Value, "all.generated.cs"); + var dllPathFile = Path.Combine(_pureLiveDirectory.Value, "all.dll.path"); // caching the generated models speeds up booting // currentHash hashes both the types & the user's partials @@ -569,7 +576,7 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder private string GetOutputAssemblyPath(string currentHash) { - var dirInfo = new DirectoryInfo(Path.Combine(_config.ModelsDirectoryAbsolute(_hostingEnvironment), "Compiled")); + var dirInfo = new DirectoryInfo(Path.Combine(_pureLiveDirectory.Value, "Compiled")); if (!dirInfo.Exists) { Directory.CreateDirectory(dirInfo.FullName); @@ -578,6 +585,7 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder return Path.Combine(dirInfo.FullName, $"generated.cs{currentHash}.dll"); } + private void ClearOnFailingToCompile(string dllPathFile, string modelsHashFile, string projFile) { _logger.LogDebug("Failed to compile."); @@ -666,13 +674,13 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder private string GenerateModelsCode(IList typeModels) { - var modelsDirectory = _config.ModelsDirectoryAbsolute(_hostingEnvironment); - if (!Directory.Exists(modelsDirectory)) + + if (!Directory.Exists(_pureLiveDirectory.Value)) { - Directory.CreateDirectory(modelsDirectory); + Directory.CreateDirectory(_pureLiveDirectory.Value); } - foreach (var file in Directory.GetFiles(modelsDirectory, "*.generated.cs")) + foreach (var file in Directory.GetFiles(_pureLiveDirectory.Value, "*.generated.cs")) { File.Delete(file); } @@ -686,6 +694,8 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder return code; } + + private static string GenerateModelsProj(IDictionary files) { // ideally we would generate a CSPROJ file but then we'd need a BuildProvider for csproj 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 62ed7d1bbd..e19bd9f54b 100644 --- a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj +++ b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj @@ -1,4 +1,4 @@ - + net5.0 @@ -28,7 +28,6 @@ - @@ -38,7 +37,6 @@ - @@ -46,7 +44,6 @@ - @@ -64,9 +61,9 @@ - + From cce3c49e78db32ecca916a0824783af6f4bca859 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 May 2021 20:34:33 +0000 Subject: [PATCH 093/182] Bump underscore from 1.9.1 to 1.12.1 in /src/Umbraco.Web.UI.Client Bumps [underscore](https://github.com/jashkenas/underscore) from 1.9.1 to 1.12.1. - [Release notes](https://github.com/jashkenas/underscore/releases) - [Commits](https://github.com/jashkenas/underscore/compare/1.9.1...1.12.1) Signed-off-by: dependabot[bot] --- src/Umbraco.Web.UI.Client/package-lock.json | 6 +++--- src/Umbraco.Web.UI.Client/package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 0e8f48084f..3f53638fc6 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -15755,9 +15755,9 @@ "dev": true }, "underscore": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", - "integrity": "sha1-BtzjSg5op7q8KbNluOdLiSUgOWE=" + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", + "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==" }, "undertaker": { "version": "1.2.1", diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 16d6ff980b..6514f2f217 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -47,7 +47,7 @@ "spectrum-colorpicker2": "2.0.8", "tinymce": "4.9.11", "typeahead.js": "0.11.1", - "underscore": "1.9.1", + "underscore": "1.12.1", "wicg-inert": "^3.0.2" }, "devDependencies": { From df8b2d5580bf8589119b629ff5870122728cc96c Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Mon, 17 May 2021 10:50:25 +0200 Subject: [PATCH 094/182] Bump version to 8.14.0-rc --- src/SolutionInfo.cs | 4 ++-- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index 2a7386cb45..91918498f9 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -18,5 +18,5 @@ using System.Resources; [assembly: AssemblyVersion("8.0.0")] // these are FYI and changed automatically -[assembly: AssemblyFileVersion("8.13.0")] -[assembly: AssemblyInformationalVersion("8.13.0-rc")] +[assembly: AssemblyFileVersion("8.14.0")] +[assembly: AssemblyInformationalVersion("8.14.0-rc")] diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 707c773dc6..a887159256 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -348,9 +348,9 @@ False True - 8130 + 8140 / - http://localhost:8130 + http://localhost:8140 False False From 63670bbd3465bb78b26b9f90347ca702e0f7c4fb Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Mon, 17 May 2021 12:09:36 +0200 Subject: [PATCH 095/182] Removed the [DataContract] attribute added on ArtifactBase in the migration from V8 to V9, which has the effect of making all properties on derived artifacts opt-in. Handled the omission of the checksum value from the serialized string without taking a Json.Net dependency. --- src/Umbraco.Core/Deploy/ArtifactBase.cs | 31 +++++--------- .../Umbraco.Core/Deploy/ArtifactBaseTests.cs | 41 +++++++++++++++++++ 2 files changed, 51 insertions(+), 21 deletions(-) create mode 100644 src/Umbraco.Tests.UnitTests/Umbraco.Core/Deploy/ArtifactBaseTests.cs diff --git a/src/Umbraco.Core/Deploy/ArtifactBase.cs b/src/Umbraco.Core/Deploy/ArtifactBase.cs index 7c6f4c1814..25457adcdf 100644 --- a/src/Umbraco.Core/Deploy/ArtifactBase.cs +++ b/src/Umbraco.Core/Deploy/ArtifactBase.cs @@ -1,22 +1,18 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; -using System.Runtime.Serialization; namespace Umbraco.Cms.Core.Deploy { /// /// Provides a base class to all artifacts. /// - [DataContract] public abstract class ArtifactBase : IArtifact where TUdi : Udi { protected ArtifactBase(TUdi udi, IEnumerable dependencies = null) { - if (udi == null) - throw new ArgumentNullException("udi"); - Udi = udi; + Udi = udi ?? throw new ArgumentNullException("udi"); Name = Udi.ToString(); Dependencies = dependencies ?? Enumerable.Empty(); @@ -24,38 +20,31 @@ namespace Umbraco.Cms.Core.Deploy } private readonly Lazy _checksum; + private IEnumerable _dependencies; protected abstract string GetChecksum(); #region Abstract implementation of IArtifactSignature - Udi IArtifactSignature.Udi - { - get { return Udi; } - } + Udi IArtifactSignature.Udi => Udi; - [DataMember] public TUdi Udi { get; set; } - [IgnoreDataMember] - public string Checksum - { - get { return _checksum.Value; } - } + public string Checksum => _checksum.Value; + + public bool ShouldSerializeChecksum() => false; - [DataMember] public IEnumerable Dependencies { - get { return _dependencies; } - set { _dependencies = value.OrderBy(x => x.Udi); } + get => _dependencies; + set => _dependencies = value.OrderBy(x => x.Udi); } #endregion - [DataMember] public string Name { get; set; } - [DataMember] + public string Alias { get; set; } } } diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Deploy/ArtifactBaseTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Deploy/ArtifactBaseTests.cs new file mode 100644 index 0000000000..1a2ffa8011 --- /dev/null +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Deploy/ArtifactBaseTests.cs @@ -0,0 +1,41 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using NUnit.Framework; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Deploy; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Deploy +{ + [TestFixture] + public class ArtifactBaseTests + { + [Test] + public void CanSerialize() + { + var udi = new GuidUdi("test", Guid.Parse("3382d5433b5749d08919bc9961422a1f")); + var artifact = new TestArtifact(udi, new List()) + { + Name = "Test Name", + Alias = "testAlias", + }; + + var serialized = JsonConvert.SerializeObject(artifact); + + var expected = "{\"Udi\":\"umb://test/3382d5433b5749d08919bc9961422a1f\",\"Dependencies\":[],\"Name\":\"Test Name\",\"Alias\":\"testAlias\"}"; + Assert.AreEqual(expected, serialized); + } + + private class TestArtifact : ArtifactBase + { + public TestArtifact(GuidUdi udi, IEnumerable dependencies = null) : base(udi, dependencies) + { + } + + protected override string GetChecksum() => "test checksum value"; + } + } +} From 130be8b303619e8c3f46ef0de1f91a9a31b62f4a Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Mon, 17 May 2021 12:49:08 +0200 Subject: [PATCH 096/182] Added comment explaining use of ShouldSerialize... method in ArtifactBase. --- src/Umbraco.Core/Deploy/ArtifactBase.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Umbraco.Core/Deploy/ArtifactBase.cs b/src/Umbraco.Core/Deploy/ArtifactBase.cs index 25457adcdf..61bf7b2abf 100644 --- a/src/Umbraco.Core/Deploy/ArtifactBase.cs +++ b/src/Umbraco.Core/Deploy/ArtifactBase.cs @@ -33,6 +33,14 @@ namespace Umbraco.Cms.Core.Deploy public string Checksum => _checksum.Value; + /// + /// Prevents the property from being serialized. + /// + /// + /// Note that we can't use here as that works only on fields, not properties. And we want to avoid using [JsonIgnore] + /// as that would require an external dependency in Umbraco.Cms.Core. + /// So using this method of excluding properties from serialized data, documented here: https://www.newtonsoft.com/json/help/html/ConditionalProperties.htm + /// public bool ShouldSerializeChecksum() => false; public IEnumerable Dependencies From eba6373a1239d89770fa02287665b7ebfa3bd5db Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Tue, 18 May 2021 18:31:38 +1000 Subject: [PATCH 097/182] Examine 2.0 integration (#10241) * Init commit for examine 2.0 work, most old umb examine tests working, probably a lot that doesn't * Gets Umbraco Examine tests passing and makes some sense out of them, fixes some underlying issues. * Large refactor, remove TaskHelper, rename Notifications to be consistent, Gets all examine/lucene indexes building and startup ordered in the correct way, removes old files, creates new IUmbracoIndexingHandler for abstracting out all index operations for umbraco data, abstracts out IIndexRebuilder, Fixes Stack overflow with LiveModelsProvider and loading assemblies, ports some changes from v8 for startup handling with cold boots, refactors out LastSyncedFileManager * fix up issues with rebuilding and management dashboard. * removes old files, removes NetworkHelper, fixes LastSyncedFileManager implementation to ensure the machine name is used, fix up logging with cold boot state. * Makes MainDom safer to use and makes PublishedSnapshotService lazily register with MainDom * lazily acquire application id (fix unit tests) * Fixes resource casing and missing test file * Ensures caches when requiring internal services for PublishedSnapshotService, UseNuCache is a separate call, shouldn't be buried in AddWebComponents, was also causing issues in integration tests since nucache was being used for the Id2Key service. * For UmbracoTestServerTestBase enable nucache services * Fixing tests * Fix another test * Fixes tests, use TestHostingEnvironment, make Tests.Common use net5, remove old Lucene.Net.Contrib ref. * Fixes up some review notes * Fixes issue with doubly registering PublishedSnapshotService meanig there could be 2x instances of it * Checks for parseexception when executing the query * Use application root instead of duplicating functionality. * Added Examine project to netcore only solution file * Fixed casing issue with LazyLoad, that is not lowercase. * uses cancellationToken instead of bool flag, fixes always reading lastId from the LastSyncedFileManager, fixes RecurringHostedServiceBase so that there isn't an overlapping thread for the same task type * Fix tests * remove legacy test project from solution file * Fix test Co-authored-by: Bjarke Berg --- NuGet.Config | 1 - src/Umbraco.Core/Composing/TypeLoader.cs | 2 +- src/Umbraco.Core/Constants-Indexes.cs | 12 +- .../DependencyInjection/UmbracoBuilder.cs | 2 + src/Umbraco.Core/EnvironmentHelper.cs | 17 + .../Extensions/StringExtensions.cs | 27 +- .../Hosting/IHostingEnvironment.cs | 15 + src/Umbraco.Core/NetworkHelper.cs | 48 - .../UmbracoApplicationStartingNotification.cs | 5 +- .../UmbracoRequestEndNotification.cs | 2 +- src/Umbraco.Core/Runtime/MainDom.cs | 22 +- ...ructionServiceProcessInstructionsResult.cs | 26 - .../Services/ICacheInstructionService.cs | 10 +- .../Services/ProcessInstructionsResult.cs | 26 + .../Sync/DatabaseServerMessengerCallbacks.cs | 20 - src/Umbraco.Core/Sync/IServerAddress.cs | 2 +- .../Sync/ISyncBootStateAccessor.cs | 14 + .../Sync/NonRuntimeLevelBootStateAccessor.cs | 10 + src/Umbraco.Core/Sync/SyncBootState.cs | 20 + .../BackOfficeExamineSearcher.cs | 23 +- .../ConfigurationEnabledDirectoryFactory.cs | 83 ++ .../ConfigureIndexOptions.cs | 46 + .../UmbracoBuilderExtensions.cs | 40 + .../ExamineLuceneComponent.cs | 53 -- .../ExamineLuceneComposer.cs | 29 - .../ExamineLuceneFinalComponent.cs | 37 - .../ExamineLuceneFinalComposer.cs | 14 - .../Extensions/ExamineExtensions.cs | 100 +-- .../ILuceneDirectoryFactory.cs | 10 - .../LuceneFileSystemDirectoryFactory.cs | 73 -- .../LuceneIndexCreator.cs | 32 - .../LuceneIndexDiagnostics.cs | 42 +- .../LuceneIndexDiagnosticsFactory.cs | 8 +- .../LuceneRAMDirectoryFactory.cs | 15 +- .../NoPrefixSimpleFsLockFactory.cs | 1 + .../Umbraco.Examine.Lucene.csproj | 96 +- .../UmbracoApplicationRoot.cs | 23 + .../UmbracoContentIndex.cs | 132 ++- .../UmbracoExamineIndex.cs | 135 +-- .../UmbracoIndexesCreator.cs | 126 --- .../UmbracoLockFactory.cs | 15 + .../UmbracoMemberIndex.cs | 33 +- .../UmbracoBuilder.CoreServices.cs | 3 - .../UmbracoBuilder.DistributedCache.cs | 52 +- .../UmbracoBuilder.Examine.cs | 18 +- .../Examine/ContentIndexPopulator.cs | 35 +- .../Examine/ContentValueSetValidator.cs | 6 +- .../{Search => Examine}/ExamineIndexModel.cs | 4 +- .../Examine/ExamineIndexRebuilder.cs | 207 +++++ .../ExamineSearcherModel.cs | 4 +- .../Examine/ExamineUmbracoIndexingHandler.cs | 394 ++++++++ .../Examine/GenericIndexDiagnostics.cs | 19 +- .../Examine/IIndexCreator.cs | 13 - .../Examine/IIndexDiagnostics.cs | 16 +- .../Examine/IIndexRebuilder.cs | 14 + .../Examine/IUmbracoIndex.cs | 10 +- .../Examine/IUmbracoIndexesCreator.cs | 10 - .../Examine/IndexDiagnosticsFactory.cs | 7 +- .../Examine/IndexPopulator.cs | 2 +- .../Examine/IndexRebuilder.cs | 81 -- .../Examine/IndexRebuildingEventArgs.cs | 19 - .../Examine/MediaIndexPopulator.cs | 17 +- .../Examine/NoopUmbracoIndexesCreator.cs | 14 - .../Examine/PublishedContentIndexPopulator.cs | 7 +- .../Examine/RebuildOnStartupHandler.cs | 60 ++ .../Examine/UmbracoExamineExtensions.cs | 21 +- .../HostedServices/QueuedHostedService.cs | 5 +- .../RecurringHostedServiceBase.cs | 25 +- .../PublishedContentQuery.cs | 9 +- .../Runtime/CoreRuntime.cs | 4 +- .../Runtime/SqlMainDomLock.cs | 2 +- .../Search/BackgroundIndexRebuilder.cs | 83 -- .../Search/ExamineNotificationHandler.cs | 838 ------------------ .../Search/ExamineUserComponent.cs | 37 - .../Search/IUmbracoIndexingHandler.cs | 37 + .../IndexingNotificationHandler.Content.cs | 137 +++ ...IndexingNotificationHandler.ContentType.cs | 180 ++++ .../IndexingNotificationHandler.Language.cs | 50 ++ .../IndexingNotificationHandler.Media.cs | 90 ++ .../IndexingNotificationHandler.Member.cs | 90 ++ .../Implement/CacheInstructionService.cs | 96 +- .../Implement/ServerRegistrationService.cs | 2 +- src/Umbraco.Infrastructure/Suspendable.cs | 3 +- .../Sync/BatchedDatabaseServerMessenger.cs | 7 +- .../Sync/DatabaseServerMessenger.cs | 187 ++-- .../Sync/LastSyncedFileManager.cs | 89 ++ .../Sync/SyncBootStateAccessor.cs | 84 ++ .../Umbraco.Infrastructure.csproj | 2 +- .../UmbracoBuilderExtensions.cs | 3 - .../PublishedSnapshot.cs | 16 +- .../PublishedSnapshotService.cs | 110 ++- .../PublishedSnapshotStatus.cs | 10 +- .../TaskHelper.cs | 6 +- .../Testing}/TestHostingEnvironment.cs | 15 +- .../Umbraco.Tests.Common.csproj | 5 +- .../ComponentRuntimeTests.cs | 7 +- .../UmbracoBuilderExtensions.cs | 24 +- .../Implementations/TestHelper.cs | 3 + .../UmbracoTestServerTestBase.cs | 1 + .../Testing/IntegrationTestComponent.cs | 4 +- .../Testing/UmbracoIntegrationTest.cs | 3 +- .../UmbracoExamine/ExamineBaseTest.cs | 135 +++ .../ExamineDemoDataContentService.cs | 4 +- .../ExamineDemoDataMediaService.cs | 4 +- .../UmbracoExamine/ExamineExtensions.cs | 4 +- .../UmbracoExamine/IndexInitializer.cs | 147 +-- .../UmbracoExamine/IndexTest.cs | 218 ++--- .../PublishedContentQueryTests.cs | 46 +- .../UmbracoExamine/RandomIdRAMDirectory.cs | 11 + .../UmbracoExamine/SearchTests.cs | 42 +- .../UmbracoExamine/TestFiles.Designer.cs | 6 +- .../UmbracoExamine/TestFiles.resx | 10 +- .../UmbracoExamine/TestFiles/media.xml | 0 .../TestFiles/umbraco-sort.config | 0 .../UmbracoExamine/TestFiles/umbraco.config | 0 .../Scoping/ScopeFileSystemsTests.cs | 2 +- .../Scoping/ScopeTests.cs | 2 +- .../Scoping/ScopedRepositoryTests.cs | 7 +- .../Services/CacheInstructionServiceTests.cs | 35 +- .../Services/ContentEventsTests.cs | 1 + .../ContentTypeServiceVariantsTests.cs | 1 + .../Services/TrackRelationsTests.cs | 8 + .../Umbraco.Tests.Integration.csproj | 21 +- .../TestHelpers/TestHelper.cs | 4 +- .../Models/GlobalSettingsTests.cs | 7 +- .../Extensions/UriExtensionsTests.cs | 23 - .../Routing/UmbracoRequestPathsTests.cs | 12 +- .../Umbraco.Core/TaskHelperTests.cs | 2 +- .../Umbraco.Tests.UnitTests.csproj | 1 - .../PublishedMediaCache.cs | 6 +- .../PublishedContent/PublishedMediaTests.cs | 1 - .../TestHelpers/RandomIdRamDirectory.cs | 22 - src/Umbraco.Tests/Umbraco.Tests.csproj | 28 - .../UmbracoExamine/EventsTest.cs | 45 - .../UmbracoExamine/ExamineBaseTest.cs | 38 - .../UmbracoExamine/RandomIdRamDirectory.cs | 17 - .../ExamineManagementController.cs | 68 +- .../UmbracoBuilderExtensions.cs | 5 +- .../AspNetCoreHostingEnvironment.cs | 31 +- .../UmbracoBuilderExtensions.cs | 16 +- .../Extensions/PublishedContentExtensions.cs | 8 +- .../ModelsBuilder/PureLiveModelFactory.cs | 4 +- .../Umbraco.Web.Common.csproj | 1 + .../UmbracoBackOffice/AuthorizeUpgrade.cshtml | 2 +- src/umbraco-netcore-only.sln | 7 + src/umbraco.sln | 7 - 146 files changed, 2899 insertions(+), 2904 deletions(-) create mode 100644 src/Umbraco.Core/EnvironmentHelper.cs delete mode 100644 src/Umbraco.Core/NetworkHelper.cs delete mode 100644 src/Umbraco.Core/Services/CacheInstructionServiceProcessInstructionsResult.cs create mode 100644 src/Umbraco.Core/Services/ProcessInstructionsResult.cs delete mode 100644 src/Umbraco.Core/Sync/DatabaseServerMessengerCallbacks.cs create mode 100644 src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs create mode 100644 src/Umbraco.Core/Sync/NonRuntimeLevelBootStateAccessor.cs create mode 100644 src/Umbraco.Core/Sync/SyncBootState.cs create mode 100644 src/Umbraco.Examine.Lucene/ConfigurationEnabledDirectoryFactory.cs create mode 100644 src/Umbraco.Examine.Lucene/DependencyInjection/ConfigureIndexOptions.cs create mode 100644 src/Umbraco.Examine.Lucene/DependencyInjection/UmbracoBuilderExtensions.cs delete mode 100644 src/Umbraco.Examine.Lucene/ExamineLuceneComponent.cs delete mode 100644 src/Umbraco.Examine.Lucene/ExamineLuceneComposer.cs delete mode 100644 src/Umbraco.Examine.Lucene/ExamineLuceneFinalComponent.cs delete mode 100644 src/Umbraco.Examine.Lucene/ExamineLuceneFinalComposer.cs delete mode 100644 src/Umbraco.Examine.Lucene/ILuceneDirectoryFactory.cs delete mode 100644 src/Umbraco.Examine.Lucene/LuceneFileSystemDirectoryFactory.cs delete mode 100644 src/Umbraco.Examine.Lucene/LuceneIndexCreator.cs create mode 100644 src/Umbraco.Examine.Lucene/UmbracoApplicationRoot.cs delete mode 100644 src/Umbraco.Examine.Lucene/UmbracoIndexesCreator.cs create mode 100644 src/Umbraco.Examine.Lucene/UmbracoLockFactory.cs rename src/Umbraco.Infrastructure/{Search => Examine}/ExamineIndexModel.cs (88%) create mode 100644 src/Umbraco.Infrastructure/Examine/ExamineIndexRebuilder.cs rename src/Umbraco.Infrastructure/{Search => Examine}/ExamineSearcherModel.cs (74%) create mode 100644 src/Umbraco.Infrastructure/Examine/ExamineUmbracoIndexingHandler.cs delete mode 100644 src/Umbraco.Infrastructure/Examine/IIndexCreator.cs create mode 100644 src/Umbraco.Infrastructure/Examine/IIndexRebuilder.cs delete mode 100644 src/Umbraco.Infrastructure/Examine/IUmbracoIndexesCreator.cs delete mode 100644 src/Umbraco.Infrastructure/Examine/IndexRebuilder.cs delete mode 100644 src/Umbraco.Infrastructure/Examine/IndexRebuildingEventArgs.cs delete mode 100644 src/Umbraco.Infrastructure/Examine/NoopUmbracoIndexesCreator.cs create mode 100644 src/Umbraco.Infrastructure/Examine/RebuildOnStartupHandler.cs delete mode 100644 src/Umbraco.Infrastructure/Search/BackgroundIndexRebuilder.cs delete mode 100644 src/Umbraco.Infrastructure/Search/ExamineNotificationHandler.cs delete mode 100644 src/Umbraco.Infrastructure/Search/ExamineUserComponent.cs create mode 100644 src/Umbraco.Infrastructure/Search/IUmbracoIndexingHandler.cs create mode 100644 src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.Content.cs create mode 100644 src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.ContentType.cs create mode 100644 src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.Language.cs create mode 100644 src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.Media.cs create mode 100644 src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.Member.cs create mode 100644 src/Umbraco.Infrastructure/Sync/LastSyncedFileManager.cs create mode 100644 src/Umbraco.Infrastructure/Sync/SyncBootStateAccessor.cs rename src/{Umbraco.Core => Umbraco.Tests.Common}/TaskHelper.cs (95%) rename src/{Umbraco.Tests.Integration/Implementations => Umbraco.Tests.Common/Testing}/TestHostingEnvironment.cs (62%) create mode 100644 src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineBaseTest.cs rename src/{Umbraco.Tests => Umbraco.Tests.Integration/Umbraco.Examine.Lucene}/UmbracoExamine/ExamineDemoDataContentService.cs (94%) rename src/{Umbraco.Tests => Umbraco.Tests.Integration/Umbraco.Examine.Lucene}/UmbracoExamine/ExamineDemoDataMediaService.cs (88%) rename src/{Umbraco.Tests => Umbraco.Tests.Integration/Umbraco.Examine.Lucene}/UmbracoExamine/ExamineExtensions.cs (98%) rename src/{Umbraco.Tests => Umbraco.Tests.Integration/Umbraco.Examine.Lucene}/UmbracoExamine/IndexInitializer.cs (53%) rename src/{Umbraco.Tests => Umbraco.Tests.Integration/Umbraco.Examine.Lucene}/UmbracoExamine/IndexTest.cs (54%) rename src/{Umbraco.Tests/Web => Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine}/PublishedContentQueryTests.cs (62%) create mode 100644 src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/RandomIdRAMDirectory.cs rename src/{Umbraco.Tests => Umbraco.Tests.Integration/Umbraco.Examine.Lucene}/UmbracoExamine/SearchTests.cs (69%) rename src/{Umbraco.Tests => Umbraco.Tests.Integration/Umbraco.Examine.Lucene}/UmbracoExamine/TestFiles.Designer.cs (93%) rename src/{Umbraco.Tests => Umbraco.Tests.Integration/Umbraco.Examine.Lucene}/UmbracoExamine/TestFiles.resx (96%) rename src/{Umbraco.Tests => Umbraco.Tests.Integration/Umbraco.Examine.Lucene}/UmbracoExamine/TestFiles/media.xml (100%) rename src/{Umbraco.Tests => Umbraco.Tests.Integration/Umbraco.Examine.Lucene}/UmbracoExamine/TestFiles/umbraco-sort.config (100%) rename src/{Umbraco.Tests => Umbraco.Tests.Integration/Umbraco.Examine.Lucene}/UmbracoExamine/TestFiles/umbraco.config (100%) delete mode 100644 src/Umbraco.Tests/TestHelpers/RandomIdRamDirectory.cs delete mode 100644 src/Umbraco.Tests/UmbracoExamine/EventsTest.cs delete mode 100644 src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs delete mode 100644 src/Umbraco.Tests/UmbracoExamine/RandomIdRamDirectory.cs diff --git a/NuGet.Config b/NuGet.Config index 92eaf83792..f8dc68d26a 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -9,7 +9,6 @@ - diff --git a/src/Umbraco.Core/Composing/TypeLoader.cs b/src/Umbraco.Core/Composing/TypeLoader.cs index c53e6ee4d2..d248be19b7 100644 --- a/src/Umbraco.Core/Composing/TypeLoader.cs +++ b/src/Umbraco.Core/Composing/TypeLoader.cs @@ -316,7 +316,7 @@ namespace Umbraco.Cms.Core.Composing /// private string GetFileBasePath() { - var fileBasePath = Path.Combine(_localTempPath.FullName, "TypesCache", "umbraco-types." + NetworkHelper.FileSafeMachineName); + var fileBasePath = Path.Combine(_localTempPath.FullName, "TypesCache", "umbraco-types." + EnvironmentHelper.FileSafeMachineName); // ensure that the folder exists var directory = Path.GetDirectoryName(fileBasePath); diff --git a/src/Umbraco.Core/Constants-Indexes.cs b/src/Umbraco.Core/Constants-Indexes.cs index 8384faa08d..fcf2e7ed14 100644 --- a/src/Umbraco.Core/Constants-Indexes.cs +++ b/src/Umbraco.Core/Constants-Indexes.cs @@ -1,16 +1,12 @@ -namespace Umbraco.Cms.Core +namespace Umbraco.Cms.Core { public static partial class Constants { public static class UmbracoIndexes { - public const string InternalIndexName = InternalIndexPath + "Index"; - public const string ExternalIndexName = ExternalIndexPath + "Index"; - public const string MembersIndexName = MembersIndexPath + "Index"; - - public const string InternalIndexPath = "Internal"; - public const string ExternalIndexPath = "External"; - public const string MembersIndexPath = "Members"; + public const string InternalIndexName = "InternalIndex"; + public const string ExternalIndexName = "ExternalIndex"; + public const string MembersIndexName = "MembersIndex"; } } } diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs index d0fe41dfc3..316dba75c1 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs @@ -225,6 +225,8 @@ namespace Umbraco.Cms.Core.DependencyInjection Services .AddNotificationHandler() .AddNotificationHandler(); + + Services.AddSingleton(); } } } diff --git a/src/Umbraco.Core/EnvironmentHelper.cs b/src/Umbraco.Core/EnvironmentHelper.cs new file mode 100644 index 0000000000..097ffc9629 --- /dev/null +++ b/src/Umbraco.Core/EnvironmentHelper.cs @@ -0,0 +1,17 @@ +using System; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Core +{ + /// + /// Currently just used to get the machine name for use with file names + /// + internal class EnvironmentHelper + { + /// + /// Returns the machine name that is safe to use in file paths. + /// + public static string FileSafeMachineName => Environment.MachineName.ReplaceNonAlphanumericChars('-'); + + } +} diff --git a/src/Umbraco.Core/Extensions/StringExtensions.cs b/src/Umbraco.Core/Extensions/StringExtensions.cs index 3b42426cd6..e815f219ca 100644 --- a/src/Umbraco.Core/Extensions/StringExtensions.cs +++ b/src/Umbraco.Core/Extensions/StringExtensions.cs @@ -616,12 +616,7 @@ namespace Umbraco.Extensions /// /// Refers to itself /// The hashed string - public static string GenerateHash(this string str) - { - return CryptoConfig.AllowOnlyFipsAlgorithms - ? str.ToSHA1() - : str.ToMd5(); - } + public static string GenerateHash(this string str) => str.ToSHA1(); /// /// Generate a hash of a string based on the specified hash algorithm. @@ -632,30 +627,14 @@ namespace Umbraco.Extensions /// The hashed string. /// public static string GenerateHash(this string str) - where T : HashAlgorithm - { - return str.GenerateHash(typeof(T).FullName); - } - - /// - /// Converts the string to MD5 - /// - /// Refers to itself - /// The MD5 hashed string - public static string ToMd5(this string stringToConvert) - { - return stringToConvert.GenerateHash("MD5"); - } + where T : HashAlgorithm => str.GenerateHash(typeof(T).FullName); /// /// Converts the string to SHA1 /// /// refers to itself /// The SHA1 hashed string - public static string ToSHA1(this string stringToConvert) - { - return stringToConvert.GenerateHash("SHA1"); - } + public static string ToSHA1(this string stringToConvert) => stringToConvert.GenerateHash("SHA1"); /// Generate a hash of a string based on the hashType passed in /// diff --git a/src/Umbraco.Core/Hosting/IHostingEnvironment.cs b/src/Umbraco.Core/Hosting/IHostingEnvironment.cs index 311d7559d0..dedf809230 100644 --- a/src/Umbraco.Core/Hosting/IHostingEnvironment.cs +++ b/src/Umbraco.Core/Hosting/IHostingEnvironment.cs @@ -6,6 +6,21 @@ namespace Umbraco.Cms.Core.Hosting { string SiteName { get; } + /// + /// The unique application ID for this Umbraco website. + /// + /// + /// + /// The returned value will be the same consistent value for an Umbraco website on a specific server and will the same + /// between restarts of that Umbraco website/application on that specific server. + /// + /// + /// The value of this does not necesarily distinguish between unique workers/servers for this Umbraco application. + /// Usage of this must take into account that the same may be returned for the same + /// Umbraco website hosted on different servers. Similarly the usage of this must take into account that a different + /// may be returned for the same Umbraco website hosted on different servers. + /// + /// string ApplicationId { get; } /// diff --git a/src/Umbraco.Core/NetworkHelper.cs b/src/Umbraco.Core/NetworkHelper.cs deleted file mode 100644 index 8e1bfaea92..0000000000 --- a/src/Umbraco.Core/NetworkHelper.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Core -{ - /// - /// Currently just used to get the machine name in med trust and to format a machine name for use with file names - /// - public class NetworkHelper - { - /// - /// Returns the machine name that is safe to use in file paths. - /// - public static string FileSafeMachineName - { - get { return MachineName.ReplaceNonAlphanumericChars('-'); } - } - - /// - /// Returns the current machine name - /// - /// - /// Tries to resolve the machine name, if it cannot it uses the config section. - /// - public static string MachineName - { - get - { - try - { - return Environment.MachineName; - } - catch - { - try - { - return System.Net.Dns.GetHostName(); - } - catch - { - //if we get here it means we cannot access the machine name - throw new ApplicationException("Cannot resolve the current machine name either by Environment.MachineName or by Dns.GetHostname()"); - } - } - } - } - } -} diff --git a/src/Umbraco.Core/Notifications/UmbracoApplicationStartingNotification.cs b/src/Umbraco.Core/Notifications/UmbracoApplicationStartingNotification.cs index 9d324a4ca5..4cbf0a55c6 100644 --- a/src/Umbraco.Core/Notifications/UmbracoApplicationStartingNotification.cs +++ b/src/Umbraco.Core/Notifications/UmbracoApplicationStartingNotification.cs @@ -3,7 +3,10 @@ namespace Umbraco.Cms.Core.Notifications { - + /// + /// Notification that occurs at the very end of the Umbraco boot + /// process and after all initialize. + /// public class UmbracoApplicationStartingNotification : INotification { /// diff --git a/src/Umbraco.Core/Notifications/UmbracoRequestEndNotification.cs b/src/Umbraco.Core/Notifications/UmbracoRequestEndNotification.cs index 84aad8a3b3..27fb6ff09d 100644 --- a/src/Umbraco.Core/Notifications/UmbracoRequestEndNotification.cs +++ b/src/Umbraco.Core/Notifications/UmbracoRequestEndNotification.cs @@ -1,4 +1,4 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. using Umbraco.Cms.Core.Web; diff --git a/src/Umbraco.Core/Runtime/MainDom.cs b/src/Umbraco.Core/Runtime/MainDom.cs index adab97ed6d..ec4e56df1b 100644 --- a/src/Umbraco.Core/Runtime/MainDom.cs +++ b/src/Umbraco.Core/Runtime/MainDom.cs @@ -31,7 +31,7 @@ namespace Umbraco.Cms.Core.Runtime private bool _isInitialized; // indicates whether... - private bool _isMainDom; // we are the main domain + private bool? _isMainDom; // we are the main domain private volatile bool _signaled; // we have been signaled // actions to run before releasing the main domain @@ -64,7 +64,7 @@ namespace Umbraco.Cms.Core.Runtime { hostingEnvironment.RegisterObject(this); return Acquire(); - }); + }).Value; } /// @@ -85,7 +85,11 @@ namespace Umbraco.Cms.Core.Runtime return false; } - if (_isMainDom == false) + if (_isMainDom.HasValue == false) + { + throw new InvalidOperationException("Register called when MainDom has not been acquired"); + } + else if (_isMainDom == false) { _logger.LogWarning("Register called when MainDom has not been acquired"); return false; @@ -215,7 +219,17 @@ namespace Umbraco.Cms.Core.Runtime /// /// Acquire must be called first else this will always return false /// - public bool IsMainDom => _isMainDom; + public bool IsMainDom + { + get + { + if (!_isMainDom.HasValue) + { + throw new InvalidOperationException("MainDom has not been acquired yet"); + } + return _isMainDom.Value; + } + } // IRegisteredObject void IRegisteredObject.Stop(bool immediate) diff --git a/src/Umbraco.Core/Services/CacheInstructionServiceProcessInstructionsResult.cs b/src/Umbraco.Core/Services/CacheInstructionServiceProcessInstructionsResult.cs deleted file mode 100644 index 84116584a2..0000000000 --- a/src/Umbraco.Core/Services/CacheInstructionServiceProcessInstructionsResult.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; - -namespace Umbraco.Cms.Core.Services -{ - /// - /// Defines a result object for the operation. - /// - public class CacheInstructionServiceProcessInstructionsResult - { - private CacheInstructionServiceProcessInstructionsResult() - { - } - - public int NumberOfInstructionsProcessed { get; private set; } - - public int LastId { get; private set; } - - public bool InstructionsWerePruned { get; private set; } - - public static CacheInstructionServiceProcessInstructionsResult AsCompleted(int numberOfInstructionsProcessed, int lastId) => - new CacheInstructionServiceProcessInstructionsResult { NumberOfInstructionsProcessed = numberOfInstructionsProcessed, LastId = lastId }; - - public static CacheInstructionServiceProcessInstructionsResult AsCompletedAndPruned(int numberOfInstructionsProcessed, int lastId) => - new CacheInstructionServiceProcessInstructionsResult { NumberOfInstructionsProcessed = numberOfInstructionsProcessed, LastId = lastId, InstructionsWerePruned = true }; - }; -} diff --git a/src/Umbraco.Core/Services/ICacheInstructionService.cs b/src/Umbraco.Core/Services/ICacheInstructionService.cs index faf05f2237..c884b8bed8 100644 --- a/src/Umbraco.Core/Services/ICacheInstructionService.cs +++ b/src/Umbraco.Core/Services/ICacheInstructionService.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Threading; +using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Sync; namespace Umbraco.Cms.Core.Services @@ -40,6 +42,12 @@ namespace Umbraco.Cms.Core.Services /// Local identity of the executing AppDomain. /// Date of last prune operation. /// Id of the latest processed instruction - CacheInstructionServiceProcessInstructionsResult ProcessInstructions(bool released, string localIdentity, DateTime lastPruned, int lastId); + ProcessInstructionsResult ProcessInstructions( + CacheRefresherCollection cacheRefreshers, + ServerRole serverRole, + CancellationToken cancellationToken, + string localIdentity, + DateTime lastPruned, + int lastId); } } diff --git a/src/Umbraco.Core/Services/ProcessInstructionsResult.cs b/src/Umbraco.Core/Services/ProcessInstructionsResult.cs new file mode 100644 index 0000000000..9a368dab7e --- /dev/null +++ b/src/Umbraco.Core/Services/ProcessInstructionsResult.cs @@ -0,0 +1,26 @@ +using System; + +namespace Umbraco.Cms.Core.Services +{ + /// + /// Defines a result object for the operation. + /// + public class ProcessInstructionsResult + { + private ProcessInstructionsResult() + { + } + + public int NumberOfInstructionsProcessed { get; private set; } + + public int LastId { get; private set; } + + public bool InstructionsWerePruned { get; private set; } + + public static ProcessInstructionsResult AsCompleted(int numberOfInstructionsProcessed, int lastId) => + new ProcessInstructionsResult { NumberOfInstructionsProcessed = numberOfInstructionsProcessed, LastId = lastId }; + + public static ProcessInstructionsResult AsCompletedAndPruned(int numberOfInstructionsProcessed, int lastId) => + new ProcessInstructionsResult { NumberOfInstructionsProcessed = numberOfInstructionsProcessed, LastId = lastId, InstructionsWerePruned = true }; + }; +} diff --git a/src/Umbraco.Core/Sync/DatabaseServerMessengerCallbacks.cs b/src/Umbraco.Core/Sync/DatabaseServerMessengerCallbacks.cs deleted file mode 100644 index 2107bbde20..0000000000 --- a/src/Umbraco.Core/Sync/DatabaseServerMessengerCallbacks.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Umbraco.Cms.Core.Sync -{ - /// - /// Holds a list of callbacks associated with implementations of . - /// - public class DatabaseServerMessengerCallbacks - { - /// - /// A list of callbacks that will be invoked if the lastsynced.txt file does not exist. - /// - /// - /// These callbacks will typically be for e.g. rebuilding the xml cache file, or examine indexes, based on - /// the data in the database to get this particular server node up to date. - /// - public IEnumerable InitializingCallbacks { get; set; } - } -} diff --git a/src/Umbraco.Core/Sync/IServerAddress.cs b/src/Umbraco.Core/Sync/IServerAddress.cs index c9333f33b8..a177454886 100644 --- a/src/Umbraco.Core/Sync/IServerAddress.cs +++ b/src/Umbraco.Core/Sync/IServerAddress.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Core.Sync +namespace Umbraco.Cms.Core.Sync { /// /// Provides the address of a server. diff --git a/src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs b/src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs new file mode 100644 index 0000000000..4ced4acf83 --- /dev/null +++ b/src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs @@ -0,0 +1,14 @@ +namespace Umbraco.Cms.Core.Sync +{ + /// + /// Retrieve the for the application during startup + /// + public interface ISyncBootStateAccessor + { + /// + /// Get the + /// + /// + SyncBootState GetSyncBootState(); + } +} diff --git a/src/Umbraco.Core/Sync/NonRuntimeLevelBootStateAccessor.cs b/src/Umbraco.Core/Sync/NonRuntimeLevelBootStateAccessor.cs new file mode 100644 index 0000000000..0dcfa471db --- /dev/null +++ b/src/Umbraco.Core/Sync/NonRuntimeLevelBootStateAccessor.cs @@ -0,0 +1,10 @@ +namespace Umbraco.Cms.Core.Sync +{ + /// + /// Boot state implementation for when umbraco is not in the run state + /// + public sealed class NonRuntimeLevelBootStateAccessor : ISyncBootStateAccessor + { + public SyncBootState GetSyncBootState() => SyncBootState.Unknown; + } +} diff --git a/src/Umbraco.Core/Sync/SyncBootState.cs b/src/Umbraco.Core/Sync/SyncBootState.cs new file mode 100644 index 0000000000..e07898486f --- /dev/null +++ b/src/Umbraco.Core/Sync/SyncBootState.cs @@ -0,0 +1,20 @@ +namespace Umbraco.Cms.Core.Sync +{ + public enum SyncBootState + { + /// + /// Unknown state. Treat as WarmBoot + /// + Unknown = 0, + + /// + /// Cold boot. No Sync state + /// + ColdBoot = 1, + + /// + /// Warm boot. Sync state present + /// + WarmBoot = 2 + } +} diff --git a/src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs b/src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs index d05bb8f07f..0a99c4d6ef 100644 --- a/src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs +++ b/src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs @@ -1,4 +1,4 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. using System; @@ -7,6 +7,8 @@ using System.Linq; using System.Text; using System.Text.RegularExpressions; using Examine; +using Examine.Search; +using Lucene.Net.QueryParsers.Classic; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Mapping; @@ -103,17 +105,16 @@ namespace Umbraco.Cms.Infrastructure.Examine if (!_examineManager.TryGetIndex(indexName, out var index)) throw new InvalidOperationException("No index found by name " + indexName); - var internalSearcher = index.GetSearcher(); - if (!BuildQuery(sb, query, searchFrom, fields, type)) { totalFound = 0; return Enumerable.Empty(); } - var result = internalSearcher.CreateQuery().NativeQuery(sb.ToString()) - //only return the number of items specified to read up to the amount of records to fill from 0 -> the number of items on the page requested - .Execute(Convert.ToInt32(pageSize * (pageIndex + 1))); + var result = index.Searcher + .CreateQuery() + .NativeQuery(sb.ToString()) + .Execute(QueryOptions.SkipTake(Convert.ToInt32(pageSize * pageIndex), pageSize)); totalFound = result.TotalItemCount; @@ -143,7 +144,7 @@ namespace Umbraco.Cms.Infrastructure.Examine //strip quotes, escape string, the replace again query = query.Trim(Constants.CharArrays.DoubleQuoteSingleQuote); - query = Lucene.Net.QueryParsers.QueryParser.Escape(query); + query = QueryParser.Escape(query); //nothing to search if (searchFrom.IsNullOrWhiteSpace() && query.IsNullOrWhiteSpace()) @@ -186,7 +187,7 @@ namespace Umbraco.Cms.Infrastructure.Examine //update the query with the query term if (trimmed.IsNullOrWhiteSpace() == false) { - query = Lucene.Net.QueryParsers.QueryParser.Escape(query); + query = QueryParser.Escape(query); var querywords = query.Split(Constants.CharArrays.Space, StringSplitOptions.RemoveEmptyEntries); @@ -355,6 +356,8 @@ namespace Umbraco.Cms.Infrastructure.Examine sb.Append("\\,*"); } + // TODO: When/Where is this used? + /// /// Returns a collection of entities for media based on search results /// @@ -389,6 +392,8 @@ namespace Umbraco.Cms.Infrastructure.Examine } } + // TODO: When/Where is this used? + /// /// Returns a collection of entities for media based on search results /// @@ -397,6 +402,8 @@ namespace Umbraco.Cms.Infrastructure.Examine private IEnumerable MediaFromSearchResults(IEnumerable results) => _umbracoMapper.Map>(results); + // TODO: When/Where is this used? + /// /// Returns a collection of entities for content based on search results /// diff --git a/src/Umbraco.Examine.Lucene/ConfigurationEnabledDirectoryFactory.cs b/src/Umbraco.Examine.Lucene/ConfigurationEnabledDirectoryFactory.cs new file mode 100644 index 0000000000..e6e2ff9a82 --- /dev/null +++ b/src/Umbraco.Examine.Lucene/ConfigurationEnabledDirectoryFactory.cs @@ -0,0 +1,83 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System; +using System.IO; +using Examine; +using Examine.Lucene.Directories; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Composing; +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Hosting; +using Umbraco.Extensions; +using Constants = Umbraco.Cms.Core.Constants; + +namespace Umbraco.Cms.Infrastructure.Examine +{ + public class ConfigurationEnabledDirectoryFactory : IDirectoryFactory + { + private readonly IServiceProvider _services; + private readonly ITypeFinder _typeFinder; + private readonly IHostingEnvironment _hostingEnvironment; + private readonly ILockFactory _lockFactory; + private readonly IApplicationRoot _applicationRoot; + private readonly IndexCreatorSettings _settings; + + public ConfigurationEnabledDirectoryFactory( + IServiceProvider services, + ITypeFinder typeFinder, + IHostingEnvironment hostingEnvironment, + ILockFactory lockFactory, + IOptions settings, + IApplicationRoot applicationRoot) + { + _services = services; + _typeFinder = typeFinder; + _hostingEnvironment = hostingEnvironment; + _lockFactory = lockFactory; + _applicationRoot = applicationRoot; + _settings = settings.Value; + } + + public Lucene.Net.Store.Directory CreateDirectory(string indexName) => CreateFileSystemLuceneDirectory(indexName); + + /// + /// Creates a file system based Lucene with the correct locking guidelines for Umbraco + /// + /// + /// The folder name to store the index (single word, not a fully qualified folder) (i.e. Internal) + /// + /// + public virtual Lucene.Net.Store.Directory CreateFileSystemLuceneDirectory(string indexName) + { + var dirInfo = _applicationRoot.ApplicationRoot; + + if (!dirInfo.Exists) + { + Directory.CreateDirectory(dirInfo.FullName); + } + + //check if there's a configured directory factory, if so create it and use that to create the lucene dir + var configuredDirectoryFactory = _settings.LuceneDirectoryFactory; + + if (!configuredDirectoryFactory.IsNullOrWhiteSpace()) + { + //this should be a fully qualified type + Type factoryType = _typeFinder.GetTypeByName(configuredDirectoryFactory); + if (factoryType == null) + { + throw new InvalidOperationException("No directory type found for value: " + configuredDirectoryFactory); + } + + var directoryFactory = (IDirectoryFactory)ActivatorUtilities.CreateInstance(_services, factoryType); + + return directoryFactory.CreateDirectory(indexName); + } + + var fileSystemDirectoryFactory = new FileSystemDirectoryFactory(dirInfo, _lockFactory); + return fileSystemDirectoryFactory.CreateDirectory(indexName); + + } + } +} diff --git a/src/Umbraco.Examine.Lucene/DependencyInjection/ConfigureIndexOptions.cs b/src/Umbraco.Examine.Lucene/DependencyInjection/ConfigureIndexOptions.cs new file mode 100644 index 0000000000..677167f0ff --- /dev/null +++ b/src/Umbraco.Examine.Lucene/DependencyInjection/ConfigureIndexOptions.cs @@ -0,0 +1,46 @@ +using System; +using Examine; +using Examine.Lucene; +using Examine.Lucene.Analyzers; +using Lucene.Net.Analysis.Standard; +using Microsoft.Extensions.Options; +using Umbraco.Cms.Core; + +namespace Umbraco.Cms.Infrastructure.Examine.DependencyInjection +{ + /// + /// Configures the index options to construct the Examine indexes + /// + public sealed class ConfigureIndexOptions : IConfigureNamedOptions + { + private readonly IUmbracoIndexConfig _umbracoIndexConfig; + + public ConfigureIndexOptions(IUmbracoIndexConfig umbracoIndexConfig) + => _umbracoIndexConfig = umbracoIndexConfig; + + public void Configure(string name, LuceneDirectoryIndexOptions options) + { + switch (name) + { + case Constants.UmbracoIndexes.InternalIndexName: + options.Analyzer = new CultureInvariantWhitespaceAnalyzer(); + options.Validator = _umbracoIndexConfig.GetContentValueSetValidator(); + options.FieldDefinitions = new UmbracoFieldDefinitionCollection(); + break; + case Constants.UmbracoIndexes.ExternalIndexName: + options.Analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + options.Validator = _umbracoIndexConfig.GetPublishedContentValueSetValidator(); + options.FieldDefinitions = new UmbracoFieldDefinitionCollection(); + break; + case Constants.UmbracoIndexes.MembersIndexName: + options.Analyzer = new CultureInvariantWhitespaceAnalyzer(); + options.Validator = _umbracoIndexConfig.GetMemberValueSetValidator(); + options.FieldDefinitions = new UmbracoFieldDefinitionCollection(); + break; + } + } + + public void Configure(LuceneDirectoryIndexOptions options) + => throw new NotImplementedException("This is never called and is just part of the interface"); + } +} diff --git a/src/Umbraco.Examine.Lucene/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Examine.Lucene/DependencyInjection/UmbracoBuilderExtensions.cs new file mode 100644 index 0000000000..8eafde1a38 --- /dev/null +++ b/src/Umbraco.Examine.Lucene/DependencyInjection/UmbracoBuilderExtensions.cs @@ -0,0 +1,40 @@ +using Examine; +using Examine.Lucene.Directories; +using Microsoft.Extensions.DependencyInjection; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.Infrastructure.DependencyInjection; + +namespace Umbraco.Cms.Infrastructure.Examine.DependencyInjection +{ + public static class UmbracoBuilderExtensions + { + /// + /// Adds the Examine indexes for Umbraco + /// + /// + /// + public static IUmbracoBuilder AddExamineIndexes(this IUmbracoBuilder umbracoBuilder) + { + IServiceCollection services = umbracoBuilder.Services; + + services.AddSingleton(); + services.AddSingleton(); + + services.AddExamine(); + + // Create the indexes + services + .AddExamineLuceneIndex(Constants.UmbracoIndexes.InternalIndexName) + .AddExamineLuceneIndex(Constants.UmbracoIndexes.ExternalIndexName) + .AddExamineLuceneIndex(Constants.UmbracoIndexes.MembersIndexName) + .ConfigureOptions(); + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + return umbracoBuilder; + } + } +} diff --git a/src/Umbraco.Examine.Lucene/ExamineLuceneComponent.cs b/src/Umbraco.Examine.Lucene/ExamineLuceneComponent.cs deleted file mode 100644 index fe1826c989..0000000000 --- a/src/Umbraco.Examine.Lucene/ExamineLuceneComponent.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using Examine; -using Examine.LuceneEngine.Directories; -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.Runtime; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.Examine -{ - public sealed class ExamineLuceneComponent : IComponent - { - private readonly IndexRebuilder _indexRebuilder; - private readonly IExamineManager _examineManager; - private readonly IMainDom _mainDom; - private readonly ILoggerFactory _loggerFactory; - - public ExamineLuceneComponent(IndexRebuilder indexRebuilder, IExamineManager examineManager, IMainDom mainDom, ILoggerFactory loggerFactory) - { - _indexRebuilder = indexRebuilder; - _examineManager = examineManager; - _mainDom = mainDom; - _loggerFactory = loggerFactory; - } - - public void Initialize() - { - //we want to tell examine to use a different fs lock instead of the default NativeFSFileLock which could cause problems if the AppDomain - //terminates and in some rare cases would only allow unlocking of the file if IIS is forcefully terminated. Instead we'll rely on the simplefslock - //which simply checks the existence of the lock file - DirectoryFactory.DefaultLockFactory = d => - { - var simpleFsLockFactory = new NoPrefixSimpleFsLockFactory(d); - return simpleFsLockFactory; - }; - - _indexRebuilder.RebuildingIndexes += IndexRebuilder_RebuildingIndexes; - } - - /// - /// Handles event to ensure that all lucene based indexes are properly configured before rebuilding - /// - /// - /// - private void IndexRebuilder_RebuildingIndexes(object sender, IndexRebuildingEventArgs e) => _examineManager.ConfigureIndexes(_mainDom, _loggerFactory.CreateLogger()); - - public void Terminate() - { - } - } -} diff --git a/src/Umbraco.Examine.Lucene/ExamineLuceneComposer.cs b/src/Umbraco.Examine.Lucene/ExamineLuceneComposer.cs deleted file mode 100644 index 327ac4b4ba..0000000000 --- a/src/Umbraco.Examine.Lucene/ExamineLuceneComposer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using System.Runtime.InteropServices; -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.DependencyInjection; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.Examine -{ - // We want to run after core composers since we are replacing some items - [ComposeAfter(typeof(ICoreComposer))] - public sealed class ExamineLuceneComposer : ComponentComposer - { - public override void Compose(IUmbracoBuilder builder) - { - var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - if(!isWindows) return; - - - base.Compose(builder); - - builder.Services.AddUnique(); - builder.Services.AddUnique(); - builder.Services.AddUnique(); - builder.Services.AddUnique(); - } - } -} diff --git a/src/Umbraco.Examine.Lucene/ExamineLuceneFinalComponent.cs b/src/Umbraco.Examine.Lucene/ExamineLuceneFinalComponent.cs deleted file mode 100644 index b95165b121..0000000000 --- a/src/Umbraco.Examine.Lucene/ExamineLuceneFinalComponent.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using Examine; -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.Runtime; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.Examine -{ - public class ExamineLuceneFinalComponent : IComponent - { - private readonly ILoggerFactory _loggerFactory; - private readonly IExamineManager _examineManager; - private readonly IMainDom _mainDom; - - public ExamineLuceneFinalComponent(ILoggerFactory loggerFactory, IExamineManager examineManager, IMainDom mainDom) - { - _loggerFactory = loggerFactory; - _examineManager = examineManager; - _mainDom = mainDom; - } - - public void Initialize() - { - if (!_mainDom.IsMainDom) return; - - // Ensures all lucene based indexes are unlocked and ready to go - _examineManager.ConfigureIndexes(_mainDom, _loggerFactory.CreateLogger()); - } - - public void Terminate() - { - } - } -} diff --git a/src/Umbraco.Examine.Lucene/ExamineLuceneFinalComposer.cs b/src/Umbraco.Examine.Lucene/ExamineLuceneFinalComposer.cs deleted file mode 100644 index 518ffc2db8..0000000000 --- a/src/Umbraco.Examine.Lucene/ExamineLuceneFinalComposer.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using Umbraco.Cms.Core.Composing; - -namespace Umbraco.Cms.Infrastructure.Examine -{ - // examine's Lucene final composer composes after all user composers - // and *also* after ICoreComposer (in case IUserComposer is disabled) - [ComposeAfter(typeof(IUserComposer))] - [ComposeAfter(typeof(ICoreComposer))] - public class ExamineLuceneFinalComposer : ComponentComposer - { } -} diff --git a/src/Umbraco.Examine.Lucene/Extensions/ExamineExtensions.cs b/src/Umbraco.Examine.Lucene/Extensions/ExamineExtensions.cs index 9307c4cbf4..1a8bb36baa 100644 --- a/src/Umbraco.Examine.Lucene/Extensions/ExamineExtensions.cs +++ b/src/Umbraco.Examine.Lucene/Extensions/ExamineExtensions.cs @@ -1,19 +1,17 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. using System; using System.Linq; using System.Threading; using Examine; -using Examine.LuceneEngine.Providers; -using Lucene.Net.Analysis; +using Examine.Lucene.Providers; +using Lucene.Net.Analysis.Core; using Lucene.Net.Index; -using Lucene.Net.QueryParsers; -using Lucene.Net.Search; +using Lucene.Net.QueryParsers.Classic; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Runtime; using Umbraco.Cms.Infrastructure.Examine; -using Version = Lucene.Net.Util.Version; namespace Umbraco.Extensions { @@ -22,40 +20,19 @@ namespace Umbraco.Extensions /// public static class ExamineExtensions { - private static bool _isConfigured = false; - private static object _configuredInit = null; - private static object _isConfiguredLocker = new object(); - - /// - /// Called on startup to configure each index. - /// - /// - /// Configures and unlocks all Lucene based indexes registered with the . - /// - internal static void ConfigureIndexes(this IExamineManager examineManager, IMainDom mainDom, ILogger logger) - { - LazyInitializer.EnsureInitialized( - ref _configuredInit, - ref _isConfigured, - ref _isConfiguredLocker, - () => - { - examineManager.ConfigureLuceneIndexes(logger, !mainDom.IsMainDom); - return null; - }); - } - internal static bool TryParseLuceneQuery(string query) { // TODO: I'd assume there would be a more strict way to parse the query but not that i can find yet, for now we'll // also do this rudimentary check if (!query.Contains(":")) + { return false; + } try { //This will pass with a plain old string without any fields, need to figure out a way to have it properly parse - var parsed = new QueryParser(Version.LUCENE_30, UmbracoExamineFieldNames.NodeNameFieldName, new KeywordAnalyzer()).Parse(query); + var parsed = new QueryParser(LuceneInfo.CurrentVersion, UmbracoExamineFieldNames.NodeNameFieldName, new KeywordAnalyzer()).Parse(query); return true; } catch (ParseException) @@ -68,34 +45,6 @@ namespace Umbraco.Extensions } } - /// - /// Forcibly unlocks all lucene based indexes - /// - /// - /// This is not thread safe, use with care - /// - private static void ConfigureLuceneIndexes(this IExamineManager examineManager, ILogger logger, bool disableExamineIndexing) - { - foreach (var luceneIndexer in examineManager.Indexes.OfType()) - { - //We now need to disable waiting for indexing for Examine so that the appdomain is shutdown immediately and doesn't wait for pending - //indexing operations. We used to wait for indexing operations to complete but this can cause more problems than that is worth because - //that could end up halting shutdown for a very long time causing overlapping appdomains and many other problems. - luceneIndexer.WaitForIndexQueueOnShutdown = false; - - if (disableExamineIndexing) continue; //exit if not enabled, we don't need to unlock them if we're not maindom - - //we should check if the index is locked ... it shouldn't be! We are using simple fs lock now and we are also ensuring that - //the indexes are not operational unless MainDom is true - var dir = luceneIndexer.GetLuceneDirectory(); - if (IndexWriter.IsLocked(dir)) - { - logger.LogDebug("Forcing index {IndexerName} to be unlocked since it was left in a locked state", luceneIndexer.Name); - IndexWriter.Unlock(dir); - } - } - } - /// /// Checks if the index can be read/opened /// @@ -106,7 +55,7 @@ namespace Umbraco.Extensions { try { - using (indexer.GetIndexWriter().GetReader()) + using (indexer.IndexWriter.IndexWriter.GetReader(false)) { ex = null; return true; @@ -119,38 +68,5 @@ namespace Umbraco.Extensions } } - /// - /// Return the number of indexed documents in Lucene - /// - /// - /// - public static int GetIndexDocumentCount(this LuceneIndex indexer) - { - if (!((indexer.GetSearcher() as LuceneSearcher)?.GetLuceneSearcher() is IndexSearcher searcher)) - return 0; - - using (searcher) - using (var reader = searcher.IndexReader) - { - return reader.NumDocs(); - } - } - - /// - /// Return the total number of fields in the index - /// - /// - /// - public static int GetIndexFieldCount(this LuceneIndex indexer) - { - if (!((indexer.GetSearcher() as LuceneSearcher)?.GetLuceneSearcher() is IndexSearcher searcher)) - return 0; - - using (searcher) - using (var reader = searcher.IndexReader) - { - return reader.GetFieldNames(IndexReader.FieldOption.ALL).Count; - } - } } } diff --git a/src/Umbraco.Examine.Lucene/ILuceneDirectoryFactory.cs b/src/Umbraco.Examine.Lucene/ILuceneDirectoryFactory.cs deleted file mode 100644 index 70f3825667..0000000000 --- a/src/Umbraco.Examine.Lucene/ILuceneDirectoryFactory.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -namespace Umbraco.Cms.Infrastructure.Examine -{ - public interface ILuceneDirectoryFactory - { - Lucene.Net.Store.Directory CreateDirectory(string indexName); - } -} diff --git a/src/Umbraco.Examine.Lucene/LuceneFileSystemDirectoryFactory.cs b/src/Umbraco.Examine.Lucene/LuceneFileSystemDirectoryFactory.cs deleted file mode 100644 index 9e09c7e96e..0000000000 --- a/src/Umbraco.Examine.Lucene/LuceneFileSystemDirectoryFactory.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using System; -using System.IO; -using Examine.LuceneEngine.Directories; -using Lucene.Net.Store; -using Microsoft.Extensions.Options; -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Hosting; -using Umbraco.Extensions; -using Constants = Umbraco.Cms.Core.Constants; - -namespace Umbraco.Cms.Infrastructure.Examine -{ - public class LuceneFileSystemDirectoryFactory : ILuceneDirectoryFactory - { - private readonly ITypeFinder _typeFinder; - private readonly IHostingEnvironment _hostingEnvironment; - private readonly IndexCreatorSettings _settings; - - public LuceneFileSystemDirectoryFactory(ITypeFinder typeFinder, IHostingEnvironment hostingEnvironment, IOptions settings) - { - _typeFinder = typeFinder; - _hostingEnvironment = hostingEnvironment; - _settings = settings.Value; - } - - public Lucene.Net.Store.Directory CreateDirectory(string indexName) => CreateFileSystemLuceneDirectory(indexName); - - /// - /// Creates a file system based Lucene with the correct locking guidelines for Umbraco - /// - /// - /// The folder name to store the index (single word, not a fully qualified folder) (i.e. Internal) - /// - /// - public virtual Lucene.Net.Store.Directory CreateFileSystemLuceneDirectory(string folderName) - { - - var dirInfo = new DirectoryInfo(Path.Combine(_hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.TempData), "ExamineIndexes", folderName)); - if (!dirInfo.Exists) - System.IO.Directory.CreateDirectory(dirInfo.FullName); - - //check if there's a configured directory factory, if so create it and use that to create the lucene dir - var configuredDirectoryFactory = _settings.LuceneDirectoryFactory; - - if (!configuredDirectoryFactory.IsNullOrWhiteSpace()) - { - //this should be a fully qualified type - var factoryType = _typeFinder.GetTypeByName(configuredDirectoryFactory); - if (factoryType == null) throw new NullReferenceException("No directory type found for value: " + configuredDirectoryFactory); - var directoryFactory = (IDirectoryFactory)Activator.CreateInstance(factoryType); - return directoryFactory.CreateDirectory(dirInfo); - } - - //no dir factory, just create a normal fs directory - - var luceneDir = new SimpleFSDirectory(dirInfo); - - //we want to tell examine to use a different fs lock instead of the default NativeFSFileLock which could cause problems if the appdomain - //terminates and in some rare cases would only allow unlocking of the file if IIS is forcefully terminated. Instead we'll rely on the simplefslock - //which simply checks the existence of the lock file - // The full syntax of this is: new NoPrefixSimpleFsLockFactory(dirInfo) - // however, we are setting the DefaultLockFactory in startup so we'll use that instead since it can be managed globally. - luceneDir.SetLockFactory(DirectoryFactory.DefaultLockFactory(dirInfo)); - return luceneDir; - - - } - } -} diff --git a/src/Umbraco.Examine.Lucene/LuceneIndexCreator.cs b/src/Umbraco.Examine.Lucene/LuceneIndexCreator.cs deleted file mode 100644 index dc2acfa66d..0000000000 --- a/src/Umbraco.Examine.Lucene/LuceneIndexCreator.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using System.Collections.Generic; -using Examine; -using Microsoft.Extensions.Options; -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Hosting; - -namespace Umbraco.Cms.Infrastructure.Examine -{ - /// - /// - /// Abstract class for creating Lucene based Indexes - /// - public abstract class LuceneIndexCreator : IIndexCreator - { - private readonly ITypeFinder _typeFinder; - private readonly IHostingEnvironment _hostingEnvironment; - private readonly IndexCreatorSettings _settings; - - protected LuceneIndexCreator(ITypeFinder typeFinder, IHostingEnvironment hostingEnvironment, IOptions settings) - { - _typeFinder = typeFinder; - _hostingEnvironment = hostingEnvironment; - _settings = settings.Value; - } - - public abstract IEnumerable Create(); - } -} diff --git a/src/Umbraco.Examine.Lucene/LuceneIndexDiagnostics.cs b/src/Umbraco.Examine.Lucene/LuceneIndexDiagnostics.cs index 1cba0767eb..6ad23b5992 100644 --- a/src/Umbraco.Examine.Lucene/LuceneIndexDiagnostics.cs +++ b/src/Umbraco.Examine.Lucene/LuceneIndexDiagnostics.cs @@ -1,8 +1,10 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. +using System; using System.Collections.Generic; -using Examine.LuceneEngine.Providers; +using System.Threading.Tasks; +using Examine.Lucene.Providers; using Lucene.Net.Store; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core; @@ -25,37 +27,7 @@ namespace Umbraco.Cms.Infrastructure.Examine public LuceneIndex Index { get; } public ILogger Logger { get; } - public int DocumentCount - { - get - { - try - { - return Index.GetIndexDocumentCount(); - } - catch (AlreadyClosedException) - { - Logger.LogWarning("Cannot get GetIndexDocumentCount, the writer is already closed"); - return 0; - } - } - } - - public int FieldCount - { - get - { - try - { - return Index.GetIndexFieldCount(); - } - catch (AlreadyClosedException) - { - Logger.LogWarning("Cannot get GetIndexFieldCount, the writer is already closed"); - return 0; - } - } - } + public Attempt IsHealthy() { @@ -63,6 +35,10 @@ namespace Umbraco.Cms.Infrastructure.Examine return isHealthy ? Attempt.Succeed() : Attempt.Fail(indexError.Message); } + public long GetDocumentCount() => Index.GetDocumentCount(); + + public IEnumerable GetFieldNames() => Index.GetFieldNames(); + public virtual IReadOnlyDictionary Metadata { get diff --git a/src/Umbraco.Examine.Lucene/LuceneIndexDiagnosticsFactory.cs b/src/Umbraco.Examine.Lucene/LuceneIndexDiagnosticsFactory.cs index 322da710dc..bdfc299121 100644 --- a/src/Umbraco.Examine.Lucene/LuceneIndexDiagnosticsFactory.cs +++ b/src/Umbraco.Examine.Lucene/LuceneIndexDiagnosticsFactory.cs @@ -1,8 +1,8 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. using Examine; -using Examine.LuceneEngine.Providers; +using Examine.Lucene.Providers; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Hosting; @@ -28,9 +28,13 @@ namespace Umbraco.Cms.Infrastructure.Examine if (!(index is IIndexDiagnostics indexDiag)) { if (index is LuceneIndex luceneIndex) + { indexDiag = new LuceneIndexDiagnostics(luceneIndex, _loggerFactory.CreateLogger(), _hostingEnvironment); + } else + { indexDiag = base.Create(index); + } } return indexDiag; } diff --git a/src/Umbraco.Examine.Lucene/LuceneRAMDirectoryFactory.cs b/src/Umbraco.Examine.Lucene/LuceneRAMDirectoryFactory.cs index 1f9802f072..2b25350f09 100644 --- a/src/Umbraco.Examine.Lucene/LuceneRAMDirectoryFactory.cs +++ b/src/Umbraco.Examine.Lucene/LuceneRAMDirectoryFactory.cs @@ -1,27 +1,26 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. using System; +using System.IO; +using Examine.Lucene.Directories; using Lucene.Net.Store; +using Directory = Lucene.Net.Store.Directory; namespace Umbraco.Cms.Infrastructure.Examine { - public class LuceneRAMDirectoryFactory : ILuceneDirectoryFactory + public class LuceneRAMDirectoryFactory : IDirectoryFactory { public LuceneRAMDirectoryFactory() { - } - public Lucene.Net.Store.Directory CreateDirectory(string indexName) => new RandomIdRAMDirectory(); + public Directory CreateDirectory(string indexName) => new RandomIdRAMDirectory(); private class RandomIdRAMDirectory : RAMDirectory { private readonly string _lockId = Guid.NewGuid().ToString(); - public override string GetLockId() - { - return _lockId; - } + public override string GetLockID() => _lockId; } } } diff --git a/src/Umbraco.Examine.Lucene/NoPrefixSimpleFsLockFactory.cs b/src/Umbraco.Examine.Lucene/NoPrefixSimpleFsLockFactory.cs index 38d704e681..ed6f47c882 100644 --- a/src/Umbraco.Examine.Lucene/NoPrefixSimpleFsLockFactory.cs +++ b/src/Umbraco.Examine.Lucene/NoPrefixSimpleFsLockFactory.cs @@ -6,6 +6,7 @@ using Lucene.Net.Store; namespace Umbraco.Cms.Infrastructure.Examine { + /// /// A custom that ensures a prefixless lock prefix /// diff --git a/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj b/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj index ef67c424d8..2417178d69 100644 --- a/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj +++ b/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj @@ -1,56 +1,42 @@ - - - net472 - Umbraco.Cms.Infrastructure.Examine - Umbraco CMS - Umbraco.Examine.Lucene - - - false - - Umbraco.Cms.Examine.Lucene - - - - true - bin\Release\Umbraco.Examine.Lucene.xml - - - - - - - - - - - - - - - - - - - - - - - 1.0.0 - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - 3.5.4 - runtime; build; native; contentfiles; analyzers - all - - - all - - - - + + netstandard2.0 + Umbraco.Cms.Infrastructure.Examine + Umbraco CMS + Umbraco.Examine.Lucene + + Umbraco.Cms.Examine.Lucene + + + true + bin\Release\Umbraco.Examine.Lucene.xml + + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + 3.5.4 + runtime; build; native; contentfiles; analyzers + all + + + all + + + \ No newline at end of file diff --git a/src/Umbraco.Examine.Lucene/UmbracoApplicationRoot.cs b/src/Umbraco.Examine.Lucene/UmbracoApplicationRoot.cs new file mode 100644 index 0000000000..e99f986176 --- /dev/null +++ b/src/Umbraco.Examine.Lucene/UmbracoApplicationRoot.cs @@ -0,0 +1,23 @@ +using System.IO; +using Examine; +using Umbraco.Cms.Core.Hosting; + +namespace Umbraco.Cms.Infrastructure.Examine +{ + /// + /// Sets the Examine to be ExamineIndexes sub directory of the Umbraco TEMP folder + /// + public class UmbracoApplicationRoot : IApplicationRoot + { + private readonly IHostingEnvironment _hostingEnvironment; + + public UmbracoApplicationRoot(IHostingEnvironment hostingEnvironment) + => _hostingEnvironment = hostingEnvironment; + + public DirectoryInfo ApplicationRoot + => new DirectoryInfo( + Path.Combine( + _hostingEnvironment.MapPathContentRoot(Core.Constants.SystemDirectories.TempData), + "ExamineIndexes")); + } +} diff --git a/src/Umbraco.Examine.Lucene/UmbracoContentIndex.cs b/src/Umbraco.Examine.Lucene/UmbracoContentIndex.cs index 18b9945a6e..b3852254af 100644 --- a/src/Umbraco.Examine.Lucene/UmbracoContentIndex.cs +++ b/src/Umbraco.Examine.Lucene/UmbracoContentIndex.cs @@ -5,12 +5,10 @@ using System; using System.Collections.Generic; using System.Linq; using Examine; -using Examine.LuceneEngine; -using Lucene.Net.Analysis; -using Lucene.Net.Store; +using Examine.Lucene; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Hosting; -using Umbraco.Cms.Core.Logging; using Umbraco.Cms.Core.Services; namespace Umbraco.Cms.Infrastructure.Examine @@ -21,49 +19,38 @@ namespace Umbraco.Cms.Infrastructure.Examine public class UmbracoContentIndex : UmbracoExamineIndex, IUmbracoContentIndex, IDisposable { private readonly ILogger _logger; - protected ILocalizationService LanguageService { get; } - #region Constructors - - /// - /// Create an index at runtime - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// public UmbracoContentIndex( - string name, - Directory luceneDirectory, - FieldDefinitionCollection fieldDefinitions, - Analyzer defaultAnalyzer, - IProfilingLogger profilingLogger, - ILogger logger, ILoggerFactory loggerFactory, + string name, + IOptionsSnapshot indexOptions, IHostingEnvironment hostingEnvironment, IRuntimeState runtimeState, - ILocalizationService languageService, - IContentValueSetValidator validator, - IReadOnlyDictionary indexValueTypes = null) - : base(name, luceneDirectory, fieldDefinitions, defaultAnalyzer, profilingLogger, logger, loggerFactory ,hostingEnvironment, runtimeState, validator, indexValueTypes) + ILocalizationService languageService = null) + : base(loggerFactory, name, indexOptions, hostingEnvironment, runtimeState) { - if (validator == null) throw new ArgumentNullException(nameof(validator)); - _logger = logger; - LanguageService = languageService ?? throw new ArgumentNullException(nameof(languageService)); + LanguageService = languageService; + _logger = loggerFactory.CreateLogger(); - if (validator is IContentValueSetValidator contentValueSetValidator) + LuceneDirectoryIndexOptions namedOptions = indexOptions.Get(name); + if (namedOptions == null) + { + throw new InvalidOperationException($"No named {typeof(LuceneDirectoryIndexOptions)} options with name {name}"); + } + + if (namedOptions.Validator is IContentValueSetValidator contentValueSetValidator) + { PublishedValuesOnly = contentValueSetValidator.PublishedValuesOnly; + } } - #endregion + protected ILocalizationService LanguageService { get; } + + /// + /// Explicitly override because we need to do validation differently than the underlying logic + /// + /// + void IIndex.IndexItems(IEnumerable values) => PerformIndexItems(values, OnIndexOperationComplete); /// /// Special check for invalid paths @@ -75,45 +62,48 @@ namespace Umbraco.Cms.Infrastructure.Examine // We don't want to re-enumerate this list, but we need to split it into 2x enumerables: invalid and valid items. // The Invalid items will be deleted, these are items that have invalid paths (i.e. moved to the recycle bin, etc...) // Then we'll index the Value group all together. - // We return 0 or 1 here so we can order the results and do the invalid first and then the valid. var invalidOrValid = values.GroupBy(v => { - if (!v.Values.TryGetValue("path", out var paths) || paths.Count <= 0 || paths[0] == null) - return 0; + if (!v.Values.TryGetValue("path", out List paths) || paths.Count <= 0 || paths[0] == null) + { + return ValueSetValidationResult.Failed; + } - //we know this is an IContentValueSetValidator - var validator = (IContentValueSetValidator)ValueSetValidator; - var path = paths[0].ToString(); + ValueSetValidationResult validationResult = ValueSetValidator.Validate(v); - return (!validator.ValidatePath(path, v.Category) - || !validator.ValidateRecycleBin(path, v.Category) - || !validator.ValidateProtectedContent(path, v.Category)) - ? 0 - : 1; + return validationResult; }).ToList(); var hasDeletes = false; var hasUpdates = false; - foreach (var group in invalidOrValid.OrderBy(x => x.Key)) - { - if (group.Key == 0) - { - hasDeletes = true; - //these are the invalid items so we'll delete them - //since the path is not valid we need to delete this item in case it exists in the index already and has now - //been moved to an invalid parent. - base.PerformDeleteFromIndex(group.Select(x => x.Id), args => { /*noop*/ }); - } - else + // ordering by descending so that Filtered/Failed processes first + foreach (IGrouping group in invalidOrValid.OrderByDescending(x => x.Key)) + { + switch (group.Key) { - hasUpdates = true; - //these are the valid ones, so just index them all at once - base.PerformIndexItems(group.ToList(), onComplete); + case ValueSetValidationResult.Valid: + hasUpdates = true; + + //these are the valid ones, so just index them all at once + base.PerformIndexItems(group.ToList(), onComplete); + break; + case ValueSetValidationResult.Failed: + // don't index anything that is invalid + break; + case ValueSetValidationResult.Filtered: + hasDeletes = true; + + // these are the invalid/filtered items so we'll delete them + // since the path is not valid we need to delete this item in + // case it exists in the index already and has now + // been moved to an invalid parent. + base.PerformDeleteFromIndex(group.Select(x => x.Id), null); + break; } } - if (hasDeletes && !hasUpdates || !hasDeletes && !hasUpdates) + if ((hasDeletes && !hasUpdates) || (!hasDeletes && !hasUpdates)) { //we need to manually call the completed method onComplete(new IndexOperationEventArgs(this, 0)); @@ -133,21 +123,27 @@ namespace Umbraco.Cms.Infrastructure.Examine protected override void PerformDeleteFromIndex(IEnumerable itemIds, Action onComplete) { var idsAsList = itemIds.ToList(); - foreach (var nodeId in idsAsList) + + for (int i = 0; i < idsAsList.Count; i++) { + string nodeId = idsAsList[i]; + //find all descendants based on path var descendantPath = $@"\-1\,*{nodeId}\,*"; var rawQuery = $"{UmbracoExamineFieldNames.IndexPathFieldName}:{descendantPath}"; - var searcher = GetSearcher(); - var c = searcher.CreateQuery(); + var c = Searcher.CreateQuery(); var filtered = c.NativeQuery(rawQuery); var results = filtered.Execute(); _logger. LogDebug("DeleteFromIndex with query: {Query} (found {TotalItems} results)", rawQuery, results.TotalItemCount); - //need to queue a delete item for each one found - QueueIndexOperation(results.Select(r => new IndexOperation(new ValueSet(r.Id), IndexOperationType.Delete))); + var toRemove = results.Select(x => x.Id).ToList(); + // delete those descendants (ensure base. is used here so we aren't calling ourselves!) + base.PerformDeleteFromIndex(toRemove, null); + + // remove any ids from our list that were part of the descendants + idsAsList.RemoveAll(x => toRemove.Contains(x)); } base.PerformDeleteFromIndex(idsAsList, onComplete); diff --git a/src/Umbraco.Examine.Lucene/UmbracoExamineIndex.cs b/src/Umbraco.Examine.Lucene/UmbracoExamineIndex.cs index 851dfbd152..5ebcb4877a 100644 --- a/src/Umbraco.Examine.Lucene/UmbracoExamineIndex.cs +++ b/src/Umbraco.Examine.Lucene/UmbracoExamineIndex.cs @@ -4,20 +4,17 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading; using Examine; -using Examine.LuceneEngine; -using Examine.LuceneEngine.Providers; -using Lucene.Net.Analysis; +using Examine.Lucene; +using Examine.Lucene.Providers; using Lucene.Net.Documents; using Lucene.Net.Index; using Lucene.Net.Store; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Hosting; -using Umbraco.Cms.Core.Logging; using Umbraco.Cms.Core.Services; -using Directory = Lucene.Net.Store.Directory; namespace Umbraco.Cms.Infrastructure.Examine { @@ -27,61 +24,24 @@ namespace Umbraco.Cms.Infrastructure.Examine /// public abstract class UmbracoExamineIndex : LuceneIndex, IUmbracoIndex, IIndexDiagnostics { - private readonly ILogger _logger; - private readonly ILoggerFactory _loggerFactory; - + private readonly UmbracoExamineIndexDiagnostics _diagnostics; private readonly IRuntimeState _runtimeState; + private bool _hasLoggedInitLog = false; + private readonly ILogger _logger; - // note - // wrapping all operations that end up calling base.SafelyProcessQueueItems in a safe call - // context because they will fork a thread/task/whatever which should *not* capture our - // call context (and the database it can contain)! - // TODO: FIX Examine to not flow the ExecutionContext so callers don't need to worry about this! - - /// - /// Create a new - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// protected UmbracoExamineIndex( - string name, - Directory luceneDirectory, - FieldDefinitionCollection fieldDefinitions, - Analyzer defaultAnalyzer, - IProfilingLogger profilingLogger, - ILogger logger, ILoggerFactory loggerFactory, + string name, + IOptionsSnapshot indexOptions, IHostingEnvironment hostingEnvironment, - IRuntimeState runtimeState, - IValueSetValidator validator = null, - IReadOnlyDictionary indexValueTypes = null) - : base(name, luceneDirectory, fieldDefinitions, defaultAnalyzer, validator, indexValueTypes) + IRuntimeState runtimeState) + : base(loggerFactory, name, indexOptions) { - _logger = logger; - _loggerFactory = loggerFactory; _runtimeState = runtimeState; - ProfilingLogger = profilingLogger ?? throw new ArgumentNullException(nameof(profilingLogger)); - - //try to set the value of `LuceneIndexFolder` for diagnostic reasons - if (luceneDirectory is FSDirectory fsDir) - LuceneIndexFolder = fsDir.Directory; - - _diagnostics = new UmbracoExamineIndexDiagnostics(this, _loggerFactory.CreateLogger(), hostingEnvironment); + _diagnostics = new UmbracoExamineIndexDiagnostics(this, loggerFactory.CreateLogger(), hostingEnvironment); + _logger = loggerFactory.CreateLogger(); } - private readonly bool _configBased = false; - - protected IProfilingLogger ProfilingLogger { get; } - /// /// When set to true Umbraco will keep the index in sync with Umbraco data automatically /// @@ -89,14 +49,6 @@ namespace Umbraco.Cms.Infrastructure.Examine public bool PublishedValuesOnly { get; protected set; } = false; - /// - public IEnumerable GetFields() - { - //we know this is a LuceneSearcher - var searcher = (LuceneSearcher)GetSearcher(); - return searcher.GetAllIndexedFields(); - } - /// /// override to check if we can actually initialize. /// @@ -107,13 +59,7 @@ namespace Umbraco.Cms.Infrastructure.Examine { if (CanInitialize()) { - // Use ExecutionContext.SuppressFlow to prevent the current Execution Context (AsyncLocal) flow to child - // tasks executed in the base class so we don't leak Scopes. - // TODO: See notes at the top of this class - using (ExecutionContext.SuppressFlow()) - { - base.PerformDeleteFromIndex(itemIds, onComplete); - } + base.PerformDeleteFromIndex(itemIds, onComplete); } } @@ -121,13 +67,7 @@ namespace Umbraco.Cms.Infrastructure.Examine { if (CanInitialize()) { - // Use ExecutionContext.SuppressFlow to prevent the current Execution Context (AsyncLocal) flow to child - // tasks executed in the base class so we don't leak Scopes. - // TODO: See notes at the top of this class - using (ExecutionContext.SuppressFlow()) - { - base.PerformIndexItems(values, onComplete); - } + base.PerformIndexItems(values, onComplete); } } @@ -137,19 +77,15 @@ namespace Umbraco.Cms.Infrastructure.Examine /// protected bool CanInitialize() { - // only affects indexers that are config file based, if an index was created via code then - // this has no effect, it is assumed the index would not be created if it could not be initialized - return _configBased == false || _runtimeState.Level == RuntimeLevel.Run; - } + var canInit = _runtimeState.Level == RuntimeLevel.Run; - /// - /// overridden for logging - /// - /// - protected override void OnIndexingError(IndexingErrorEventArgs ex) - { - _logger.LogError(ex.InnerException, ex.Message); - base.OnIndexingError(ex); + if (!canInit && !_hasLoggedInitLog) + { + _hasLoggedInitLog = true; + _logger.LogWarning("Runtime state is not " + RuntimeLevel.Run + ", no indexing will occur"); + } + + return canInit; } /// @@ -167,31 +103,16 @@ namespace Umbraco.Cms.Infrastructure.Examine //remove the original value so we can store it the correct way d.RemoveField(f.Key); - d.Add(new Field( + d.Add(new StringField( f.Key, f.Value[0].ToString(), - Field.Store.YES, - Field.Index.NO, //don't index this field, we never want to search by it - Field.TermVector.NO)); + Field.Store.YES)); } } base.OnDocumentWriting(docArgs); } - /// - /// Overridden for logging. - /// - protected override void AddDocument(Document doc, ValueSet valueSet, IndexWriter writer) - { - _logger.LogDebug("Write lucene doc id:{DocumentId}, category:{DocumentCategory}, type:{DocumentItemType}", - valueSet.Id, - valueSet.Category, - valueSet.ItemType); - - base.AddDocument(doc, valueSet, writer); - } - protected override void OnTransformingIndexValues(IndexingItemEventArgs e) { base.OnTransformingIndexValues(e); @@ -210,15 +131,7 @@ namespace Umbraco.Cms.Infrastructure.Examine } } - #region IIndexDiagnostics - - private readonly UmbracoExamineIndexDiagnostics _diagnostics; - - public int DocumentCount => _diagnostics.DocumentCount; - public int FieldCount => _diagnostics.FieldCount; public Attempt IsHealthy() => _diagnostics.IsHealthy(); public virtual IReadOnlyDictionary Metadata => _diagnostics.Metadata; - - #endregion } } diff --git a/src/Umbraco.Examine.Lucene/UmbracoIndexesCreator.cs b/src/Umbraco.Examine.Lucene/UmbracoIndexesCreator.cs deleted file mode 100644 index aa7b30677f..0000000000 --- a/src/Umbraco.Examine.Lucene/UmbracoIndexesCreator.cs +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using System.Collections.Generic; -using Examine; -using Examine.LuceneEngine; -using Lucene.Net.Analysis.Standard; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Hosting; -using Umbraco.Cms.Core.Logging; -using Umbraco.Cms.Core.Services; -using Constants = Umbraco.Cms.Core.Constants; - -namespace Umbraco.Cms.Infrastructure.Examine -{ - /// - /// Creates the indexes used by Umbraco - /// - public class UmbracoIndexesCreator : LuceneIndexCreator, IUmbracoIndexesCreator - { - // TODO: we should inject the different IValueSetValidator so devs can just register them instead of overriding this class? - - public UmbracoIndexesCreator( - ITypeFinder typeFinder, - IProfilingLogger profilingLogger, - ILoggerFactory loggerFactory, - ILocalizationService languageService, - IPublicAccessService publicAccessService, - IMemberService memberService, - IUmbracoIndexConfig umbracoIndexConfig, - IHostingEnvironment hostingEnvironment, - IRuntimeState runtimeState, - IOptions settings, - ILuceneDirectoryFactory directoryFactory) : base(typeFinder, hostingEnvironment, settings) - { - ProfilingLogger = profilingLogger ?? throw new System.ArgumentNullException(nameof(profilingLogger)); - LoggerFactory = loggerFactory; - LanguageService = languageService ?? throw new System.ArgumentNullException(nameof(languageService)); - PublicAccessService = publicAccessService ?? throw new System.ArgumentNullException(nameof(publicAccessService)); - MemberService = memberService ?? throw new System.ArgumentNullException(nameof(memberService)); - UmbracoIndexConfig = umbracoIndexConfig; - HostingEnvironment = hostingEnvironment ?? throw new System.ArgumentNullException(nameof(hostingEnvironment)); - RuntimeState = runtimeState ?? throw new System.ArgumentNullException(nameof(runtimeState)); - DirectoryFactory = directoryFactory; - } - - protected IProfilingLogger ProfilingLogger { get; } - protected ILoggerFactory LoggerFactory { get; } - protected IHostingEnvironment HostingEnvironment { get; } - protected IRuntimeState RuntimeState { get; } - protected ILuceneDirectoryFactory DirectoryFactory { get; } - protected ILocalizationService LanguageService { get; } - protected IPublicAccessService PublicAccessService { get; } - protected IMemberService MemberService { get; } - protected IUmbracoIndexConfig UmbracoIndexConfig { get; } - - /// - /// Creates the Umbraco indexes - /// - /// - public override IEnumerable Create() - { - return new[] - { - CreateInternalIndex(), - CreateExternalIndex(), - CreateMemberIndex() - }; - } - - private IIndex CreateInternalIndex() - { - var index = new UmbracoContentIndex( - Constants.UmbracoIndexes.InternalIndexName, - DirectoryFactory.CreateDirectory(Constants.UmbracoIndexes.InternalIndexPath), - new UmbracoFieldDefinitionCollection(), - new CultureInvariantWhitespaceAnalyzer(), - ProfilingLogger, - LoggerFactory.CreateLogger(), - LoggerFactory, - HostingEnvironment, - RuntimeState, - LanguageService, - UmbracoIndexConfig.GetContentValueSetValidator() - ); - return index; - } - - private IIndex CreateExternalIndex() - { - var index = new UmbracoContentIndex( - Constants.UmbracoIndexes.ExternalIndexName, - DirectoryFactory.CreateDirectory(Constants.UmbracoIndexes.ExternalIndexPath), - new UmbracoFieldDefinitionCollection(), - new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30), - ProfilingLogger, - LoggerFactory.CreateLogger(), - LoggerFactory, - HostingEnvironment, - RuntimeState, - LanguageService, - UmbracoIndexConfig.GetPublishedContentValueSetValidator()); - return index; - } - - private IIndex CreateMemberIndex() - { - var index = new UmbracoMemberIndex( - Constants.UmbracoIndexes.MembersIndexName, - new UmbracoFieldDefinitionCollection(), - DirectoryFactory.CreateDirectory(Constants.UmbracoIndexes.MembersIndexPath), - new CultureInvariantWhitespaceAnalyzer(), - ProfilingLogger, - LoggerFactory.CreateLogger(), - LoggerFactory, - HostingEnvironment, - RuntimeState, - UmbracoIndexConfig.GetMemberValueSetValidator() - ); - return index; - } - } -} diff --git a/src/Umbraco.Examine.Lucene/UmbracoLockFactory.cs b/src/Umbraco.Examine.Lucene/UmbracoLockFactory.cs new file mode 100644 index 0000000000..89f61c1e53 --- /dev/null +++ b/src/Umbraco.Examine.Lucene/UmbracoLockFactory.cs @@ -0,0 +1,15 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.IO; +using Examine.Lucene.Directories; +using Lucene.Net.Store; + +namespace Umbraco.Cms.Infrastructure.Examine +{ + public class UmbracoLockFactory : ILockFactory + { + public LockFactory GetLockFactory(DirectoryInfo directory) + => new NoPrefixSimpleFsLockFactory(directory); + } +} diff --git a/src/Umbraco.Examine.Lucene/UmbracoMemberIndex.cs b/src/Umbraco.Examine.Lucene/UmbracoMemberIndex.cs index 3889209fdb..0792dd8a6f 100644 --- a/src/Umbraco.Examine.Lucene/UmbracoMemberIndex.cs +++ b/src/Umbraco.Examine.Lucene/UmbracoMemberIndex.cs @@ -1,13 +1,11 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. -using Examine; -using Lucene.Net.Analysis; +using Examine.Lucene; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Hosting; -using Umbraco.Cms.Core.Logging; using Umbraco.Cms.Core.Services; -using Directory = Lucene.Net.Store.Directory; namespace Umbraco.Cms.Infrastructure.Examine { @@ -16,31 +14,14 @@ namespace Umbraco.Cms.Infrastructure.Examine /// public class UmbracoMemberIndex : UmbracoExamineIndex, IUmbracoMemberIndex { - /// - /// Constructor to allow for creating an indexer at runtime - /// - /// - /// - /// - /// - /// - /// - /// - /// public UmbracoMemberIndex( - string name, - FieldDefinitionCollection fieldDefinitions, - Directory luceneDirectory, - Analyzer analyzer, - IProfilingLogger profilingLogger, - ILogger logger, ILoggerFactory loggerFactory, + string name, + IOptionsSnapshot indexOptions, IHostingEnvironment hostingEnvironment, - IRuntimeState runtimeState, - IValueSetValidator validator = null) : - base(name, luceneDirectory, fieldDefinitions, analyzer, profilingLogger, logger, loggerFactory, hostingEnvironment, runtimeState, validator) + IRuntimeState runtimeState) + : base(loggerFactory, name, indexOptions, hostingEnvironment, runtimeState) { } - } } diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs index 6eb08bd4d5..14457e9687 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs @@ -154,8 +154,6 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection builder.Services.AddUnique(); - // Register noop versions for examine to be overridden by examine - builder.Services.AddUnique(); builder.Services.AddUnique(); builder.Services.AddUnique(); @@ -170,7 +168,6 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection // Services required to run background jobs (with out the handler) builder.Services.AddUnique(); - builder.Services.AddUnique(); return builder; } diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.DistributedCache.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.DistributedCache.cs index 0757f2c725..05dba2cc0f 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.DistributedCache.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.DistributedCache.cs @@ -27,7 +27,8 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection /// public static IUmbracoBuilder AddDistributedCache(this IUmbracoBuilder builder) { - builder.SetDatabaseServerMessengerCallbacks(GetCallbacks); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); builder.SetServerMessenger(); builder.AddNotificationHandler(); builder.AddNotificationHandler(); @@ -59,24 +60,6 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection public static void SetServerRegistrar(this IUmbracoBuilder builder, IServerRoleAccessor registrar) => builder.Services.AddUnique(registrar); - /// - /// Sets the database server messenger options. - /// - /// The builder. - /// A function creating the options. - /// Use DatabaseServerRegistrarAndMessengerComposer.GetDefaultOptions to get the options that Umbraco would use by default. - public static void SetDatabaseServerMessengerCallbacks(this IUmbracoBuilder builder, Func factory) - => builder.Services.AddUnique(factory); - - /// - /// Sets the database server messenger options. - /// - /// The builder. - /// Options. - /// Use DatabaseServerRegistrarAndMessengerComposer.GetDefaultOptions to get the options that Umbraco would use by default. - public static void SetDatabaseServerMessengerOptions(this IUmbracoBuilder builder, DatabaseServerMessengerCallbacks options) - => builder.Services.AddUnique(options); - /// /// Sets the server messenger. /// @@ -101,36 +84,5 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection /// A server messenger. public static void SetServerMessenger(this IUmbracoBuilder builder, IServerMessenger registrar) => builder.Services.AddUnique(registrar); - - private static DatabaseServerMessengerCallbacks GetCallbacks(IServiceProvider factory) => new DatabaseServerMessengerCallbacks - { - // These callbacks will be executed if the server has not been synced - // (i.e. it is a new server or the lastsynced.txt file has been removed) - InitializingCallbacks = new Action[] - { - // rebuild the xml cache file if the server is not synced - () => - { - IPublishedSnapshotService publishedSnapshotService = factory.GetRequiredService(); - - // rebuild the published snapshot caches entirely, if the server is not synced - // this is equivalent to DistributedCache RefreshAll... but local only - // (we really should have a way to reuse RefreshAll... locally) - // note: refresh all content & media caches does refresh content types too - publishedSnapshotService.Notify(new[] { new DomainCacheRefresher.JsonPayload(0, DomainChangeTypes.RefreshAll) }); - publishedSnapshotService.Notify(new[] { new ContentCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) }, out _, out _); - publishedSnapshotService.Notify(new[] { new MediaCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) }, out _); - }, - - // rebuild indexes if the server is not synced - // NOTE: This will rebuild ALL indexes including the members, if developers want to target specific - // indexes then they can adjust this logic themselves. - () => - { - var indexRebuilder = factory.GetRequiredService(); - indexRebuilder.RebuildIndexes(false, TimeSpan.FromSeconds(5)); - } - } - }; } } diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Examine.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Examine.cs index 103d7a198d..d061a4372c 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Examine.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Examine.cs @@ -28,7 +28,8 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection builder.Services.AddSingleton(); builder.Services.AddSingleton(); - builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); builder.Services.AddUnique(); builder.Services.AddUnique(); builder.Services.AddUnique(factory => @@ -49,14 +50,15 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection false)); builder.Services.AddUnique, MediaValueSetBuilder>(); builder.Services.AddUnique, MemberValueSetBuilder>(); - builder.Services.AddUnique(); + builder.Services.AddUnique(); - builder.AddNotificationHandler(); - builder.AddNotificationHandler(); - builder.AddNotificationHandler(); - builder.AddNotificationHandler(); - builder.AddNotificationHandler(); - builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + + builder.AddNotificationHandler(); return builder; } diff --git a/src/Umbraco.Infrastructure/Examine/ContentIndexPopulator.cs b/src/Umbraco.Infrastructure/Examine/ContentIndexPopulator.cs index d9fd10f1d7..bd205e2009 100644 --- a/src/Umbraco.Infrastructure/Examine/ContentIndexPopulator.cs +++ b/src/Umbraco.Infrastructure/Examine/ContentIndexPopulator.cs @@ -1,7 +1,8 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Examine; +using Microsoft.Extensions.Logging; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Querying; @@ -27,6 +28,7 @@ namespace Umbraco.Cms.Infrastructure.Examine private readonly bool _publishedValuesOnly; private readonly int? _parentId; + private readonly ILogger _logger; /// /// Default constructor to lookup all content data @@ -34,20 +36,30 @@ namespace Umbraco.Cms.Infrastructure.Examine /// /// /// - public ContentIndexPopulator(IContentService contentService, IUmbracoDatabaseFactory umbracoDatabaseFactory, IContentValueSetBuilder contentValueSetBuilder) - : this(false, null, contentService, umbracoDatabaseFactory, contentValueSetBuilder) + public ContentIndexPopulator( + ILogger logger, + IContentService contentService, + IUmbracoDatabaseFactory umbracoDatabaseFactory, + IContentValueSetBuilder contentValueSetBuilder) + : this(logger, false, null, contentService, umbracoDatabaseFactory, contentValueSetBuilder) { } /// /// Optional constructor allowing specifying custom query parameters /// - public ContentIndexPopulator(bool publishedValuesOnly, int? parentId, IContentService contentService, IUmbracoDatabaseFactory umbracoDatabaseFactory, IValueSetBuilder contentValueSetBuilder) + public ContentIndexPopulator( + ILogger logger, + bool publishedValuesOnly, + int? parentId, + IContentService contentService, + IUmbracoDatabaseFactory umbracoDatabaseFactory, + IValueSetBuilder contentValueSetBuilder) { - if (umbracoDatabaseFactory == null) throw new ArgumentNullException(nameof(umbracoDatabaseFactory)); _contentService = contentService ?? throw new ArgumentNullException(nameof(contentService)); - _umbracoDatabaseFactory = umbracoDatabaseFactory; + _umbracoDatabaseFactory = umbracoDatabaseFactory ?? throw new ArgumentNullException(nameof(umbracoDatabaseFactory)); _contentValueSetBuilder = contentValueSetBuilder ?? throw new ArgumentNullException(nameof(contentValueSetBuilder)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _publishedValuesOnly = publishedValuesOnly; _parentId = parentId; } @@ -60,7 +72,11 @@ namespace Umbraco.Cms.Infrastructure.Examine protected override void PopulateIndexes(IReadOnlyList indexes) { - if (indexes.Count == 0) return; + if (indexes.Count == 0) + { + _logger.LogDebug($"{nameof(PopulateIndexes)} called with no indexes to populate. Typically means no index is registered with this populator."); + return; + } const int pageSize = 10000; var pageIndex = 0; @@ -144,9 +160,10 @@ namespace Umbraco.Cms.Infrastructure.Examine var valueSets = _contentValueSetBuilder.GetValueSets(indexableContent.ToArray()).ToList(); - // ReSharper disable once PossibleMultipleEnumeration - foreach (var index in indexes) + foreach (IIndex index in indexes) + { index.IndexItems(valueSets); + } } pageIndex++; diff --git a/src/Umbraco.Infrastructure/Examine/ContentValueSetValidator.cs b/src/Umbraco.Infrastructure/Examine/ContentValueSetValidator.cs index 463e8dee26..39d260a24d 100644 --- a/src/Umbraco.Infrastructure/Examine/ContentValueSetValidator.cs +++ b/src/Umbraco.Infrastructure/Examine/ContentValueSetValidator.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using Examine; using Umbraco.Cms.Core.Scoping; @@ -106,10 +106,14 @@ namespace Umbraco.Cms.Infrastructure.Examine if (valueSet.Category == IndexTypes.Content && PublishedValuesOnly) { if (!valueSet.Values.TryGetValue(UmbracoExamineFieldNames.PublishedFieldName, out var published)) + { return ValueSetValidationResult.Failed; + } if (!published[0].Equals("y")) + { return ValueSetValidationResult.Failed; + } //deal with variants, if there are unpublished variants than we need to remove them from the value set if (valueSet.Values.TryGetValue(UmbracoExamineFieldNames.VariesByCultureFieldName, out var variesByCulture) diff --git a/src/Umbraco.Infrastructure/Search/ExamineIndexModel.cs b/src/Umbraco.Infrastructure/Examine/ExamineIndexModel.cs similarity index 88% rename from src/Umbraco.Infrastructure/Search/ExamineIndexModel.cs rename to src/Umbraco.Infrastructure/Examine/ExamineIndexModel.cs index d14cef8ccf..ff9f499217 100644 --- a/src/Umbraco.Infrastructure/Search/ExamineIndexModel.cs +++ b/src/Umbraco.Infrastructure/Examine/ExamineIndexModel.cs @@ -1,7 +1,7 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Runtime.Serialization; -namespace Umbraco.Cms.Infrastructure.Search +namespace Umbraco.Cms.Infrastructure.Examine { [DataContract(Name = "indexer", Namespace = "")] public class ExamineIndexModel diff --git a/src/Umbraco.Infrastructure/Examine/ExamineIndexRebuilder.cs b/src/Umbraco.Infrastructure/Examine/ExamineIndexRebuilder.cs new file mode 100644 index 0000000000..d7719cfd40 --- /dev/null +++ b/src/Umbraco.Infrastructure/Examine/ExamineIndexRebuilder.cs @@ -0,0 +1,207 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Examine; +using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Runtime; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Infrastructure.HostedServices; + +namespace Umbraco.Cms.Infrastructure.Examine +{ + public class ExamineIndexRebuilder : IIndexRebuilder + { + private readonly IBackgroundTaskQueue _backgroundTaskQueue; + private readonly IMainDom _mainDom; + private readonly IRuntimeState _runtimeState; + private readonly ILogger _logger; + private readonly IExamineManager _examineManager; + private readonly IEnumerable _populators; + private readonly object _rebuildLocker = new(); + + /// + /// Initializes a new instance of the class. + /// + public ExamineIndexRebuilder( + IMainDom mainDom, + IRuntimeState runtimeState, + ILogger logger, + IExamineManager examineManager, + IEnumerable populators, + IBackgroundTaskQueue backgroundTaskQueue) + { + _mainDom = mainDom; + _runtimeState = runtimeState; + _logger = logger; + _examineManager = examineManager; + _populators = populators; + _backgroundTaskQueue = backgroundTaskQueue; + } + + public bool CanRebuild(string indexName) + { + if (!_examineManager.TryGetIndex(indexName, out IIndex index)) + { + throw new InvalidOperationException("No index found by name " + indexName); + } + + return _populators.Any(x => x.IsRegistered(index)); + } + + public virtual void RebuildIndex(string indexName, TimeSpan? delay = null, bool useBackgroundThread = true) + { + if (delay == null) + { + delay = TimeSpan.Zero; + } + + if (!CanRun()) + { + return; + } + + if (useBackgroundThread) + { + _logger.LogInformation($"Starting async background thread for rebuilding index {indexName}."); + + _backgroundTaskQueue.QueueBackgroundWorkItem( + cancellationToken => Task.Run(() => RebuildIndex(indexName, delay.Value, cancellationToken))); + } + else + { + RebuildIndex(indexName, delay.Value, CancellationToken.None); + } + } + + public virtual void RebuildIndexes(bool onlyEmptyIndexes, TimeSpan? delay = null, bool useBackgroundThread = true) + { + if (delay == null) + { + delay = TimeSpan.Zero; + } + + if (!CanRun()) + { + return; + } + + if (useBackgroundThread) + { + _logger.LogInformation($"Starting async background thread for {nameof(RebuildIndexes)}."); + + _backgroundTaskQueue.QueueBackgroundWorkItem( + cancellationToken => Task.Run(() => RebuildIndexes(onlyEmptyIndexes, delay.Value, cancellationToken))); + } + else + { + RebuildIndexes(onlyEmptyIndexes, delay.Value, CancellationToken.None); + } + } + + private bool CanRun() => _mainDom.IsMainDom && _runtimeState.Level >= RuntimeLevel.Run; + + private void RebuildIndex(string indexName, TimeSpan delay, CancellationToken cancellationToken) + { + if (delay > TimeSpan.Zero) + { + Thread.Sleep(delay); + } + + try + { + if (!Monitor.TryEnter(_rebuildLocker)) + { + _logger.LogWarning("Call was made to RebuildIndexes but the task runner for rebuilding is already running"); + } + else + { + if (!_examineManager.TryGetIndex(indexName, out IIndex index)) + { + throw new InvalidOperationException($"No index found with name {indexName}"); + } + + index.CreateIndex(); // clear the index + foreach (IIndexPopulator populator in _populators) + { + if (cancellationToken.IsCancellationRequested) + { + return; + } + + populator.Populate(index); + } + } + } + finally + { + if (Monitor.IsEntered(_rebuildLocker)) + { + Monitor.Exit(_rebuildLocker); + } + } + } + + private void RebuildIndexes(bool onlyEmptyIndexes, TimeSpan delay, CancellationToken cancellationToken) + { + if (delay > TimeSpan.Zero) + { + Thread.Sleep(delay); + } + + try + { + if (!Monitor.TryEnter(_rebuildLocker)) + { + _logger.LogWarning($"Call was made to {nameof(RebuildIndexes)} but the task runner for rebuilding is already running"); + } + else + { + IIndex[] indexes = (onlyEmptyIndexes + ? _examineManager.Indexes.Where(x => !x.IndexExists()) + : _examineManager.Indexes).ToArray(); + + if (indexes.Length == 0) + { + return; + } + + foreach (IIndex index in indexes) + { + index.CreateIndex(); // clear the index + } + + // run each populator over the indexes + foreach (IIndexPopulator populator in _populators) + { + if (cancellationToken.IsCancellationRequested) + { + return; + } + + try + { + populator.Populate(indexes); + } + catch (Exception e) + { + _logger.LogError(e, "Index populating failed for populator {Populator}", populator.GetType()); + } + } + } + } + finally + { + if (Monitor.IsEntered(_rebuildLocker)) + { + Monitor.Exit(_rebuildLocker); + } + } + } + } +} diff --git a/src/Umbraco.Infrastructure/Search/ExamineSearcherModel.cs b/src/Umbraco.Infrastructure/Examine/ExamineSearcherModel.cs similarity index 74% rename from src/Umbraco.Infrastructure/Search/ExamineSearcherModel.cs rename to src/Umbraco.Infrastructure/Examine/ExamineSearcherModel.cs index 8e6ea30c0c..c4b602e430 100644 --- a/src/Umbraco.Infrastructure/Search/ExamineSearcherModel.cs +++ b/src/Umbraco.Infrastructure/Examine/ExamineSearcherModel.cs @@ -1,6 +1,6 @@ -using System.Runtime.Serialization; +using System.Runtime.Serialization; -namespace Umbraco.Cms.Infrastructure.Search +namespace Umbraco.Cms.Infrastructure.Examine { [DataContract(Name = "searcher", Namespace = "")] public class ExamineSearcherModel diff --git a/src/Umbraco.Infrastructure/Examine/ExamineUmbracoIndexingHandler.cs b/src/Umbraco.Infrastructure/Examine/ExamineUmbracoIndexingHandler.cs new file mode 100644 index 0000000000..6c6d209e5a --- /dev/null +++ b/src/Umbraco.Infrastructure/Examine/ExamineUmbracoIndexingHandler.cs @@ -0,0 +1,394 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +using Examine; +using Examine.Search; +using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Logging; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Runtime; +using Umbraco.Cms.Core.Scoping; +using Umbraco.Cms.Infrastructure.HostedServices; +using Umbraco.Cms.Infrastructure.Search; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Infrastructure.Examine +{ + /// + /// Indexing handler for Examine indexes + /// + internal class ExamineUmbracoIndexingHandler : IUmbracoIndexingHandler + { + // the default enlist priority is 100 + // enlist with a lower priority to ensure that anything "default" runs after us + // but greater that SafeXmlReaderWriter priority which is 60 + private const int EnlistPriority = 80; + private readonly IMainDom _mainDom; + private readonly ILogger _logger; + private readonly IProfilingLogger _profilingLogger; + private readonly IScopeProvider _scopeProvider; + private readonly IExamineManager _examineManager; + private readonly IBackgroundTaskQueue _backgroundTaskQueue; + private readonly IContentValueSetBuilder _contentValueSetBuilder; + private readonly IPublishedContentValueSetBuilder _publishedContentValueSetBuilder; + private readonly IValueSetBuilder _mediaValueSetBuilder; + private readonly IValueSetBuilder _memberValueSetBuilder; + private readonly Lazy _enabled; + + public ExamineUmbracoIndexingHandler( + IMainDom mainDom, + ILogger logger, + IProfilingLogger profilingLogger, + IScopeProvider scopeProvider, + IExamineManager examineManager, + IBackgroundTaskQueue backgroundTaskQueue, + IContentValueSetBuilder contentValueSetBuilder, + IPublishedContentValueSetBuilder publishedContentValueSetBuilder, + IValueSetBuilder mediaValueSetBuilder, + IValueSetBuilder memberValueSetBuilder) + { + _mainDom = mainDom; + _logger = logger; + _profilingLogger = profilingLogger; + _scopeProvider = scopeProvider; + _examineManager = examineManager; + _backgroundTaskQueue = backgroundTaskQueue; + _contentValueSetBuilder = contentValueSetBuilder; + _publishedContentValueSetBuilder = publishedContentValueSetBuilder; + _mediaValueSetBuilder = mediaValueSetBuilder; + _memberValueSetBuilder = memberValueSetBuilder; + _enabled = new Lazy(IsEnabled); + } + + /// + /// Used to lazily check if Examine Index handling is enabled + /// + /// + private bool IsEnabled() + { + //let's deal with shutting down Examine with MainDom + var examineShutdownRegistered = _mainDom.Register(release: () => + { + using (_profilingLogger.TraceDuration("Examine shutting down")) + { + _examineManager.Dispose(); + } + }); + + if (!examineShutdownRegistered) + { + _logger.LogInformation("Examine shutdown not registered, this AppDomain is not the MainDom, Examine will be disabled"); + + //if we could not register the shutdown examine ourselves, it means we are not maindom! in this case all of examine should be disabled! + Suspendable.ExamineEvents.SuspendIndexers(_logger); + return false; //exit, do not continue + } + + _logger.LogDebug("Examine shutdown registered with MainDom"); + + var registeredIndexers = _examineManager.Indexes.OfType().Count(x => x.EnableDefaultEventHandler); + + _logger.LogInformation("Adding examine event handlers for {RegisteredIndexers} index providers.", registeredIndexers); + + // don't bind event handlers if we're not suppose to listen + if (registeredIndexers == 0) + { + return false; + } + + return true; + } + + /// + public bool Enabled => _enabled.Value; + + /// + public void DeleteIndexForEntity(int entityId, bool keepIfUnpublished) + { + var actions = DeferedActions.Get(_scopeProvider); + if (actions != null) + { + actions.Add(new DeferedDeleteIndex(this, entityId, keepIfUnpublished)); + } + else + { + DeferedDeleteIndex.Execute(this, entityId, keepIfUnpublished); + } + } + + /// + public void ReIndexForContent(IContent sender, bool isPublished) + { + var actions = DeferedActions.Get(_scopeProvider); + if (actions != null) + { + actions.Add(new DeferedReIndexForContent(_backgroundTaskQueue, this, sender, isPublished)); + } + else + { + DeferedReIndexForContent.Execute(_backgroundTaskQueue, this, sender, isPublished); + } + } + + /// + public void ReIndexForMedia(IMedia sender, bool isPublished) + { + var actions = DeferedActions.Get(_scopeProvider); + if (actions != null) + { + actions.Add(new DeferedReIndexForMedia(_backgroundTaskQueue, this, sender, isPublished)); + } + else + { + DeferedReIndexForMedia.Execute(_backgroundTaskQueue, this, sender, isPublished); + } + } + + /// + public void ReIndexForMember(IMember member) + { + var actions = DeferedActions.Get(_scopeProvider); + if (actions != null) + { + actions.Add(new DeferedReIndexForMember(_backgroundTaskQueue, this, member)); + } + else + { + DeferedReIndexForMember.Execute(_backgroundTaskQueue, this, member); + } + } + + /// + public void DeleteDocumentsForContentTypes(IReadOnlyCollection removedContentTypes) + { + const int pageSize = 500; + + //Delete all content of this content/media/member type that is in any content indexer by looking up matched examine docs + foreach (var id in removedContentTypes) + { + foreach (var index in _examineManager.Indexes.OfType()) + { + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + //paging with examine, see https://shazwazza.com/post/paging-with-examine/ + var results = index.Searcher + .CreateQuery() + .Field("nodeType", id.ToInvariantString()) + .Execute(QueryOptions.SkipTake(page * pageSize, pageSize)); + total = results.TotalItemCount; + var paged = results.Skip(page * pageSize); + + foreach (ISearchResult item in paged) + { + if (int.TryParse(item.Id, out int contentId)) + { + DeleteIndexForEntity(contentId, false); + } + } + + page++; + } + } + } + } + + #region Deferred Actions + private class DeferedActions + { + private readonly List _actions = new List(); + + public static DeferedActions Get(IScopeProvider scopeProvider) + { + IScopeContext scopeContext = scopeProvider.Context; + + return scopeContext?.Enlist("examineEvents", + () => new DeferedActions(), // creator + (completed, actions) => // action + { + if (completed) + { + actions.Execute(); + } + }, EnlistPriority); + } + + public void Add(DeferedAction action) => _actions.Add(action); + + private void Execute() + { + foreach (DeferedAction action in _actions) + { + action.Execute(); + } + } + } + + /// + /// An action that will execute at the end of the Scope being completed + /// + private abstract class DeferedAction + { + public virtual void Execute() + { } + } + + /// + /// Re-indexes an item on a background thread + /// + private class DeferedReIndexForContent : DeferedAction + { + private readonly IBackgroundTaskQueue _backgroundTaskQueue; + private readonly ExamineUmbracoIndexingHandler _ExamineUmbracoIndexingHandler; + private readonly IContent _content; + private readonly bool _isPublished; + + public DeferedReIndexForContent(IBackgroundTaskQueue backgroundTaskQueue, ExamineUmbracoIndexingHandler ExamineUmbracoIndexingHandler, IContent content, bool isPublished) + { + _backgroundTaskQueue = backgroundTaskQueue; + _ExamineUmbracoIndexingHandler = ExamineUmbracoIndexingHandler; + _content = content; + _isPublished = isPublished; + } + + public override void Execute() => Execute(_backgroundTaskQueue, _ExamineUmbracoIndexingHandler, _content, _isPublished); + + public static void Execute(IBackgroundTaskQueue backgroundTaskQueue, ExamineUmbracoIndexingHandler ExamineUmbracoIndexingHandler, IContent content, bool isPublished) + => backgroundTaskQueue.QueueBackgroundWorkItem(cancellationToken => + { + using IScope scope = ExamineUmbracoIndexingHandler._scopeProvider.CreateScope(autoComplete: true); + + // for content we have a different builder for published vs unpublished + // we don't want to build more value sets than is needed so we'll lazily build 2 one for published one for non-published + var builders = new Dictionary>> + { + [true] = new Lazy>(() => ExamineUmbracoIndexingHandler._publishedContentValueSetBuilder.GetValueSets(content).ToList()), + [false] = new Lazy>(() => ExamineUmbracoIndexingHandler._contentValueSetBuilder.GetValueSets(content).ToList()) + }; + + foreach (IUmbracoIndex index in ExamineUmbracoIndexingHandler._examineManager.Indexes.OfType() + //filter the indexers + .Where(x => isPublished || !x.PublishedValuesOnly) + .Where(x => x.EnableDefaultEventHandler)) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.CompletedTask; + } + + List valueSet = builders[index.PublishedValuesOnly].Value; + index.IndexItems(valueSet); + } + + return Task.CompletedTask; + }); + } + + /// + /// Re-indexes an item on a background thread + /// + private class DeferedReIndexForMedia : DeferedAction + { + private readonly IBackgroundTaskQueue _backgroundTaskQueue; + private readonly ExamineUmbracoIndexingHandler _ExamineUmbracoIndexingHandler; + private readonly IMedia _media; + private readonly bool _isPublished; + + public DeferedReIndexForMedia(IBackgroundTaskQueue backgroundTaskQueue, ExamineUmbracoIndexingHandler ExamineUmbracoIndexingHandler, IMedia media, bool isPublished) + { + _backgroundTaskQueue = backgroundTaskQueue; + _ExamineUmbracoIndexingHandler = ExamineUmbracoIndexingHandler; + _media = media; + _isPublished = isPublished; + } + + public override void Execute() => Execute(_backgroundTaskQueue, _ExamineUmbracoIndexingHandler, _media, _isPublished); + + public static void Execute(IBackgroundTaskQueue backgroundTaskQueue, ExamineUmbracoIndexingHandler ExamineUmbracoIndexingHandler, IMedia media, bool isPublished) => + // perform the ValueSet lookup on a background thread + backgroundTaskQueue.QueueBackgroundWorkItem(cancellationToken => + { + using IScope scope = ExamineUmbracoIndexingHandler._scopeProvider.CreateScope(autoComplete: true); + + var valueSet = ExamineUmbracoIndexingHandler._mediaValueSetBuilder.GetValueSets(media).ToList(); + + foreach (IUmbracoIndex index in ExamineUmbracoIndexingHandler._examineManager.Indexes.OfType() + //filter the indexers + .Where(x => isPublished || !x.PublishedValuesOnly) + .Where(x => x.EnableDefaultEventHandler)) + { + index.IndexItems(valueSet); + } + + return Task.CompletedTask; + }); + } + + /// + /// Re-indexes an item on a background thread + /// + private class DeferedReIndexForMember : DeferedAction + { + private readonly ExamineUmbracoIndexingHandler _ExamineUmbracoIndexingHandler; + private readonly IMember _member; + private readonly IBackgroundTaskQueue _backgroundTaskQueue; + + public DeferedReIndexForMember(IBackgroundTaskQueue backgroundTaskQueue, ExamineUmbracoIndexingHandler ExamineUmbracoIndexingHandler, IMember member) + { + _ExamineUmbracoIndexingHandler = ExamineUmbracoIndexingHandler; + _member = member; + _backgroundTaskQueue = backgroundTaskQueue; + } + + public override void Execute() => Execute(_backgroundTaskQueue, _ExamineUmbracoIndexingHandler, _member); + + public static void Execute(IBackgroundTaskQueue backgroundTaskQueue, ExamineUmbracoIndexingHandler ExamineUmbracoIndexingHandler, IMember member) => + // perform the ValueSet lookup on a background thread + backgroundTaskQueue.QueueBackgroundWorkItem(cancellationToken => + { + using IScope scope = ExamineUmbracoIndexingHandler._scopeProvider.CreateScope(autoComplete: true); + + var valueSet = ExamineUmbracoIndexingHandler._memberValueSetBuilder.GetValueSets(member).ToList(); + foreach (IUmbracoIndex index in ExamineUmbracoIndexingHandler._examineManager.Indexes.OfType() + //filter the indexers + .Where(x => x.EnableDefaultEventHandler)) + { + index.IndexItems(valueSet); + } + + return Task.CompletedTask; + }); + } + + private class DeferedDeleteIndex : DeferedAction + { + private readonly ExamineUmbracoIndexingHandler _ExamineUmbracoIndexingHandler; + private readonly int _id; + private readonly bool _keepIfUnpublished; + + public DeferedDeleteIndex(ExamineUmbracoIndexingHandler ExamineUmbracoIndexingHandler, int id, bool keepIfUnpublished) + { + _ExamineUmbracoIndexingHandler = ExamineUmbracoIndexingHandler; + _id = id; + _keepIfUnpublished = keepIfUnpublished; + } + + public override void Execute() => Execute(_ExamineUmbracoIndexingHandler, _id, _keepIfUnpublished); + + public static void Execute(ExamineUmbracoIndexingHandler ExamineUmbracoIndexingHandler, int id, bool keepIfUnpublished) + { + var strId = id.ToString(CultureInfo.InvariantCulture); + foreach (var index in ExamineUmbracoIndexingHandler._examineManager.Indexes.OfType() + .Where(x => x.PublishedValuesOnly || !keepIfUnpublished) + .Where(x => x.EnableDefaultEventHandler)) + { + index.DeleteFromIndex(strId); + } + } + } + #endregion + } +} diff --git a/src/Umbraco.Infrastructure/Examine/GenericIndexDiagnostics.cs b/src/Umbraco.Infrastructure/Examine/GenericIndexDiagnostics.cs index 8c05926483..2ff01c51dc 100644 --- a/src/Umbraco.Infrastructure/Examine/GenericIndexDiagnostics.cs +++ b/src/Umbraco.Infrastructure/Examine/GenericIndexDiagnostics.cs @@ -1,6 +1,7 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Examine; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Composing; @@ -15,12 +16,9 @@ namespace Umbraco.Cms.Infrastructure.Examine public class GenericIndexDiagnostics : IIndexDiagnostics { private readonly IIndex _index; - private static readonly string[] IgnoreProperties = { "Description" }; + private static readonly string[] s_ignoreProperties = { "Description" }; - public GenericIndexDiagnostics(IIndex index) - { - _index = index; - } + public GenericIndexDiagnostics(IIndex index) => _index = index; public int DocumentCount => -1; //unknown @@ -33,8 +31,7 @@ namespace Umbraco.Cms.Infrastructure.Examine try { - var searcher = _index.GetSearcher(); - var result = searcher.Search("test"); + var result = _index.Searcher.Search("test"); return Attempt.Succeed(); //if we can search we'll assume it's healthy } catch (Exception e) @@ -43,6 +40,10 @@ namespace Umbraco.Cms.Infrastructure.Examine } } + public long GetDocumentCount() => -1L; + + public IEnumerable GetFieldNames() => Enumerable.Empty(); + public IReadOnlyDictionary Metadata { get @@ -50,7 +51,7 @@ namespace Umbraco.Cms.Infrastructure.Examine var result = new Dictionary(); var props = TypeHelper.CachedDiscoverableProperties(_index.GetType(), mustWrite: false) - .Where(x => IgnoreProperties.InvariantContains(x.Name) == false) + .Where(x => s_ignoreProperties.InvariantContains(x.Name) == false) .OrderBy(x => x.Name); foreach (var p in props) diff --git a/src/Umbraco.Infrastructure/Examine/IIndexCreator.cs b/src/Umbraco.Infrastructure/Examine/IIndexCreator.cs deleted file mode 100644 index aadaa00f46..0000000000 --- a/src/Umbraco.Infrastructure/Examine/IIndexCreator.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.Generic; -using Examine; - -namespace Umbraco.Cms.Infrastructure.Examine -{ - /// - /// Creates 's - /// - public interface IIndexCreator - { - IEnumerable Create(); - } -} diff --git a/src/Umbraco.Infrastructure/Examine/IIndexDiagnostics.cs b/src/Umbraco.Infrastructure/Examine/IIndexDiagnostics.cs index a4e1c0ca4f..716b7731eb 100644 --- a/src/Umbraco.Infrastructure/Examine/IIndexDiagnostics.cs +++ b/src/Umbraco.Infrastructure/Examine/IIndexDiagnostics.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System.Collections.Generic; +using System.Threading.Tasks; +using Examine; using Umbraco.Cms.Core; namespace Umbraco.Cms.Infrastructure.Examine @@ -7,18 +9,8 @@ namespace Umbraco.Cms.Infrastructure.Examine /// /// Exposes diagnostic information about an index /// - public interface IIndexDiagnostics + public interface IIndexDiagnostics : IIndexStats { - /// - /// The number of documents in the index - /// - int DocumentCount { get; } - - /// - /// The number of fields in the index - /// - int FieldCount { get; } - /// /// If the index can be open/read /// diff --git a/src/Umbraco.Infrastructure/Examine/IIndexRebuilder.cs b/src/Umbraco.Infrastructure/Examine/IIndexRebuilder.cs new file mode 100644 index 0000000000..127a20d685 --- /dev/null +++ b/src/Umbraco.Infrastructure/Examine/IIndexRebuilder.cs @@ -0,0 +1,14 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Examine; + +namespace Umbraco.Cms.Infrastructure.Examine +{ + public interface IIndexRebuilder + { + bool CanRebuild(string indexName); + void RebuildIndex(string indexName, TimeSpan? delay = null, bool useBackgroundThread = true); + void RebuildIndexes(bool onlyEmptyIndexes, TimeSpan? delay = null, bool useBackgroundThread = true); + } +} diff --git a/src/Umbraco.Infrastructure/Examine/IUmbracoIndex.cs b/src/Umbraco.Infrastructure/Examine/IUmbracoIndex.cs index 8dfdf6d812..f2221e5c91 100644 --- a/src/Umbraco.Infrastructure/Examine/IUmbracoIndex.cs +++ b/src/Umbraco.Infrastructure/Examine/IUmbracoIndex.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Examine; namespace Umbraco.Cms.Infrastructure.Examine @@ -6,7 +6,7 @@ namespace Umbraco.Cms.Infrastructure.Examine /// /// A Marker interface for defining an Umbraco indexer /// - public interface IUmbracoIndex : IIndex + public interface IUmbracoIndex : IIndex, IIndexStats { /// /// When set to true Umbraco will keep the index in sync with Umbraco data automatically @@ -22,11 +22,5 @@ namespace Umbraco.Cms.Infrastructure.Examine /// * non-published Variants /// bool PublishedValuesOnly { get; } - - /// - /// Returns a list of all indexed fields - /// - /// - IEnumerable GetFields(); } } diff --git a/src/Umbraco.Infrastructure/Examine/IUmbracoIndexesCreator.cs b/src/Umbraco.Infrastructure/Examine/IUmbracoIndexesCreator.cs deleted file mode 100644 index df61901dba..0000000000 --- a/src/Umbraco.Infrastructure/Examine/IUmbracoIndexesCreator.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Umbraco.Cms.Infrastructure.Examine -{ - /// - /// - /// Used to create the Umbraco indexes - /// - public interface IUmbracoIndexesCreator : IIndexCreator - { - } -} diff --git a/src/Umbraco.Infrastructure/Examine/IndexDiagnosticsFactory.cs b/src/Umbraco.Infrastructure/Examine/IndexDiagnosticsFactory.cs index ca2e732071..a60a373e65 100644 --- a/src/Umbraco.Infrastructure/Examine/IndexDiagnosticsFactory.cs +++ b/src/Umbraco.Infrastructure/Examine/IndexDiagnosticsFactory.cs @@ -1,4 +1,4 @@ -using Examine; +using Examine; namespace Umbraco.Cms.Infrastructure.Examine { @@ -9,8 +9,11 @@ namespace Umbraco.Cms.Infrastructure.Examine { public virtual IIndexDiagnostics Create(IIndex index) { - if (!(index is IIndexDiagnostics indexDiag)) + if (index is not IIndexDiagnostics indexDiag) + { indexDiag = new GenericIndexDiagnostics(index); + } + return indexDiag; } } diff --git a/src/Umbraco.Infrastructure/Examine/IndexPopulator.cs b/src/Umbraco.Infrastructure/Examine/IndexPopulator.cs index 2feac0710a..d32470d875 100644 --- a/src/Umbraco.Infrastructure/Examine/IndexPopulator.cs +++ b/src/Umbraco.Infrastructure/Examine/IndexPopulator.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using Examine; using Umbraco.Cms.Core.Collections; diff --git a/src/Umbraco.Infrastructure/Examine/IndexRebuilder.cs b/src/Umbraco.Infrastructure/Examine/IndexRebuilder.cs deleted file mode 100644 index 9e4fe6fed0..0000000000 --- a/src/Umbraco.Infrastructure/Examine/IndexRebuilder.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Examine; -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core.Logging; - -namespace Umbraco.Cms.Infrastructure.Examine -{ - - /// - /// Utility to rebuild all indexes ensuring minimal data queries - /// - public class IndexRebuilder - { - private readonly IProfilingLogger _profilingLogger; - private readonly ILogger _logger; - private readonly IEnumerable _populators; - public IExamineManager ExamineManager { get; } - - public IndexRebuilder(IProfilingLogger profilingLogger , ILogger logger, IExamineManager examineManager, IEnumerable populators) - { - _profilingLogger = profilingLogger ; - _populators = populators; - _logger = logger; - ExamineManager = examineManager; - } - - public bool CanRebuild(IIndex index) - { - return _populators.Any(x => x.IsRegistered(index)); - } - - public void RebuildIndex(string indexName) - { - if (!ExamineManager.TryGetIndex(indexName, out var index)) - throw new InvalidOperationException($"No index found with name {indexName}"); - index.CreateIndex(); // clear the index - foreach (var populator in _populators) - { - populator.Populate(index); - } - } - - public void RebuildIndexes(bool onlyEmptyIndexes) - { - var indexes = (onlyEmptyIndexes - ? ExamineManager.Indexes.Where(x => !x.IndexExists()) - : ExamineManager.Indexes).ToArray(); - - if (indexes.Length == 0) return; - - OnRebuildingIndexes(new IndexRebuildingEventArgs(indexes)); - - foreach (var index in indexes) - { - index.CreateIndex(); // clear the index - } - - // run each populator over the indexes - foreach(var populator in _populators) - { - try - { - populator.Populate(indexes); - } - catch (Exception e) - { - _logger.LogError(e, "Index populating failed for populator {Populator}", populator.GetType()); - } - } - } - - /// - /// Event raised when indexes are being rebuilt - /// - public event EventHandler RebuildingIndexes; - - private void OnRebuildingIndexes(IndexRebuildingEventArgs args) => RebuildingIndexes?.Invoke(this, args); - } -} diff --git a/src/Umbraco.Infrastructure/Examine/IndexRebuildingEventArgs.cs b/src/Umbraco.Infrastructure/Examine/IndexRebuildingEventArgs.cs deleted file mode 100644 index fbe3dbcbe3..0000000000 --- a/src/Umbraco.Infrastructure/Examine/IndexRebuildingEventArgs.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using Examine; - -namespace Umbraco.Cms.Infrastructure.Examine -{ - public class IndexRebuildingEventArgs : EventArgs - { - public IndexRebuildingEventArgs(IEnumerable indexes) - { - Indexes = indexes; - } - - /// - /// The indexes being rebuilt - /// - public IEnumerable Indexes { get; } - } -} diff --git a/src/Umbraco.Infrastructure/Examine/MediaIndexPopulator.cs b/src/Umbraco.Infrastructure/Examine/MediaIndexPopulator.cs index 429285fa85..9f6e33f8dd 100644 --- a/src/Umbraco.Infrastructure/Examine/MediaIndexPopulator.cs +++ b/src/Umbraco.Infrastructure/Examine/MediaIndexPopulator.cs @@ -1,6 +1,7 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using Examine; +using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; @@ -11,6 +12,7 @@ namespace Umbraco.Cms.Infrastructure.Examine /// public class MediaIndexPopulator : IndexPopulator { + private readonly ILogger _logger; private readonly int? _parentId; private readonly IMediaService _mediaService; private readonly IValueSetBuilder _mediaValueSetBuilder; @@ -20,8 +22,8 @@ namespace Umbraco.Cms.Infrastructure.Examine /// /// /// - public MediaIndexPopulator(IMediaService mediaService, IValueSetBuilder mediaValueSetBuilder) - : this(null, mediaService, mediaValueSetBuilder) + public MediaIndexPopulator(ILogger logger, IMediaService mediaService, IValueSetBuilder mediaValueSetBuilder) + : this(logger, null, mediaService, mediaValueSetBuilder) { } @@ -31,8 +33,9 @@ namespace Umbraco.Cms.Infrastructure.Examine /// /// /// - public MediaIndexPopulator(int? parentId, IMediaService mediaService, IValueSetBuilder mediaValueSetBuilder) + public MediaIndexPopulator(ILogger logger, int? parentId, IMediaService mediaService, IValueSetBuilder mediaValueSetBuilder) { + _logger = logger; _parentId = parentId; _mediaService = mediaService; _mediaValueSetBuilder = mediaValueSetBuilder; @@ -40,7 +43,11 @@ namespace Umbraco.Cms.Infrastructure.Examine protected override void PopulateIndexes(IReadOnlyList indexes) { - if (indexes.Count == 0) return; + if (indexes.Count == 0) + { + _logger.LogDebug($"{nameof(PopulateIndexes)} called with no indexes to populate. Typically means no index is registered with this populator."); + return; + } const int pageSize = 10000; var pageIndex = 0; diff --git a/src/Umbraco.Infrastructure/Examine/NoopUmbracoIndexesCreator.cs b/src/Umbraco.Infrastructure/Examine/NoopUmbracoIndexesCreator.cs deleted file mode 100644 index e84fb96a74..0000000000 --- a/src/Umbraco.Infrastructure/Examine/NoopUmbracoIndexesCreator.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Examine; - -namespace Umbraco.Cms.Infrastructure.Examine -{ - public class NoopUmbracoIndexesCreator : IUmbracoIndexesCreator - { - public IEnumerable Create() - { - return Enumerable.Empty(); - } - } -} diff --git a/src/Umbraco.Infrastructure/Examine/PublishedContentIndexPopulator.cs b/src/Umbraco.Infrastructure/Examine/PublishedContentIndexPopulator.cs index 4b55337670..f9ccaffdbc 100644 --- a/src/Umbraco.Infrastructure/Examine/PublishedContentIndexPopulator.cs +++ b/src/Umbraco.Infrastructure/Examine/PublishedContentIndexPopulator.cs @@ -1,4 +1,5 @@ -using Umbraco.Cms.Core.Services; +using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.Persistence; namespace Umbraco.Cms.Infrastructure.Examine @@ -13,8 +14,8 @@ namespace Umbraco.Cms.Infrastructure.Examine /// public class PublishedContentIndexPopulator : ContentIndexPopulator { - public PublishedContentIndexPopulator(IContentService contentService, IUmbracoDatabaseFactory umbracoDatabaseFactory, IPublishedContentValueSetBuilder contentValueSetBuilder) : - base(true, null, contentService, umbracoDatabaseFactory, contentValueSetBuilder) + public PublishedContentIndexPopulator(ILogger logger, IContentService contentService, IUmbracoDatabaseFactory umbracoDatabaseFactory, IPublishedContentValueSetBuilder contentValueSetBuilder) : + base(logger, true, null, contentService, umbracoDatabaseFactory, contentValueSetBuilder) { } } diff --git a/src/Umbraco.Infrastructure/Examine/RebuildOnStartupHandler.cs b/src/Umbraco.Infrastructure/Examine/RebuildOnStartupHandler.cs new file mode 100644 index 0000000000..60f7478c3f --- /dev/null +++ b/src/Umbraco.Infrastructure/Examine/RebuildOnStartupHandler.cs @@ -0,0 +1,60 @@ +using System; +using System.Threading; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Core.Sync; + +namespace Umbraco.Cms.Infrastructure.Examine +{ + /// + /// Handles how the indexes are rebuilt on startup + /// + /// + /// On the first HTTP request this will rebuild the Examine indexes if they are empty. + /// If it is a cold boot, they are all rebuilt. + /// + public sealed class RebuildOnStartupHandler : INotificationHandler + { + private readonly ISyncBootStateAccessor _syncBootStateAccessor; + private readonly ExamineIndexRebuilder _backgroundIndexRebuilder; + + // These must be static because notification handlers are transient. + // this does unfortunatley mean that one RebuildOnStartupHandler instance + // will be created for each front-end request even though we only use the first one. + // TODO: Is there a better way to acheive this without allocating? We cannot remove + // a handler from the notification system. It's not a huge deal but would be better + // with less objects. + private static bool _isReady; + private static bool _isReadSet; + private static object _isReadyLock; + + public RebuildOnStartupHandler( + ISyncBootStateAccessor syncBootStateAccessor, + ExamineIndexRebuilder backgroundIndexRebuilder) + { + _syncBootStateAccessor = syncBootStateAccessor; + _backgroundIndexRebuilder = backgroundIndexRebuilder; + } + + /// + /// On first http request schedule an index rebuild for any empty indexes (or all if it's a cold boot) + /// + /// + public void Handle(UmbracoRequestBeginNotification notification) + => LazyInitializer.EnsureInitialized( + ref _isReady, + ref _isReadSet, + ref _isReadyLock, + () => + { + SyncBootState bootState = _syncBootStateAccessor.GetSyncBootState(); + + _backgroundIndexRebuilder.RebuildIndexes( + // if it's not a cold boot, only rebuild empty ones + bootState != SyncBootState.ColdBoot, + TimeSpan.FromMinutes(1)); + + return true; + }); + } +} diff --git a/src/Umbraco.Infrastructure/Examine/UmbracoExamineExtensions.cs b/src/Umbraco.Infrastructure/Examine/UmbracoExamineExtensions.cs index 90f012f08a..0d341d1d9b 100644 --- a/src/Umbraco.Infrastructure/Examine/UmbracoExamineExtensions.cs +++ b/src/Umbraco.Infrastructure/Examine/UmbracoExamineExtensions.cs @@ -1,5 +1,6 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Text.RegularExpressions; +using System.Threading.Tasks; using Examine; using Examine.Search; using Umbraco.Cms.Infrastructure.Examine; @@ -16,8 +17,6 @@ namespace Umbraco.Extensions /// internal static readonly Regex CultureIsoCodeFieldNameMatchExpression = new Regex("^([_\\w]+)_([a-z]{2}-[a-z0-9]{2,4})$", RegexOptions.Compiled); - - //TODO: We need a public method here to just match a field name against CultureIsoCodeFieldNameMatchExpression /// @@ -28,14 +27,19 @@ namespace Umbraco.Extensions /// public static IEnumerable GetCultureFields(this IUmbracoIndex index, string culture) { - var allFields = index.GetFields(); - // ReSharper disable once LoopCanBeConvertedToQuery + IEnumerable allFields = index.GetFieldNames(); + + var results = new List(); foreach (var field in allFields) { var match = CultureIsoCodeFieldNameMatchExpression.Match(field); if (match.Success && match.Groups.Count == 3 && culture.InvariantEquals(match.Groups[2].Value)) - yield return field; + { + results.Add(field); + } } + + return results; } /// @@ -46,8 +50,8 @@ namespace Umbraco.Extensions /// public static IEnumerable GetCultureAndInvariantFields(this IUmbracoIndex index, string culture) { - var allFields = index.GetFields(); - // ReSharper disable once LoopCanBeConvertedToQuery + IEnumerable allFields = index.GetFieldNames(); + foreach (var field in allFields) { var match = CultureIsoCodeFieldNameMatchExpression.Match(field); @@ -59,7 +63,6 @@ namespace Umbraco.Extensions { yield return field; //matches no culture field (invariant) } - } } diff --git a/src/Umbraco.Infrastructure/HostedServices/QueuedHostedService.cs b/src/Umbraco.Infrastructure/HostedServices/QueuedHostedService.cs index db933fec31..c15a37855e 100644 --- a/src/Umbraco.Infrastructure/HostedServices/QueuedHostedService.cs +++ b/src/Umbraco.Infrastructure/HostedServices/QueuedHostedService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Hosting; @@ -35,8 +35,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices { while (!stoppingToken.IsCancellationRequested) { - var workItem = - await TaskQueue.DequeueAsync(stoppingToken); + Func workItem = await TaskQueue.DequeueAsync(stoppingToken); try { diff --git a/src/Umbraco.Infrastructure/HostedServices/RecurringHostedServiceBase.cs b/src/Umbraco.Infrastructure/HostedServices/RecurringHostedServiceBase.cs index 1ec13f334e..131b81322a 100644 --- a/src/Umbraco.Infrastructure/HostedServices/RecurringHostedServiceBase.cs +++ b/src/Umbraco.Infrastructure/HostedServices/RecurringHostedServiceBase.cs @@ -47,12 +47,25 @@ namespace Umbraco.Cms.Infrastructure.HostedServices /// Executes the task. /// /// The task state. - public async void ExecuteAsync(object state) => - // Delegate work to method returning a task, that can be called and asserted in a unit test. - // Without this there can be behaviour where tests pass, but an error within them causes the test - // running process to crash. - // Hat-tip: https://stackoverflow.com/a/14207615/489433 - await PerformExecuteAsync(state); + public async void ExecuteAsync(object state) + { + try + { + // First, stop the timer, we do not want tasks to execute in parallel + _timer?.Change(Timeout.Infinite, 0); + + // Delegate work to method returning a task, that can be called and asserted in a unit test. + // Without this there can be behaviour where tests pass, but an error within them causes the test + // running process to crash. + // Hat-tip: https://stackoverflow.com/a/14207615/489433 + await PerformExecuteAsync(state); + } + finally + { + // Resume now that the task is complete + _timer?.Change((int)_delay.TotalMilliseconds, (int)_period.TotalMilliseconds); + } + } public abstract Task PerformExecuteAsync(object state); diff --git a/src/Umbraco.Infrastructure/PublishedContentQuery.cs b/src/Umbraco.Infrastructure/PublishedContentQuery.cs index 1d13748aeb..47b98d8dc0 100644 --- a/src/Umbraco.Infrastructure/PublishedContentQuery.cs +++ b/src/Umbraco.Infrastructure/PublishedContentQuery.cs @@ -1,7 +1,8 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using System.Xml.XPath; using Examine; using Examine.Search; @@ -260,7 +261,7 @@ namespace Umbraco.Cms.Infrastructure $"No index found by name {indexName} or is not of type {typeof(IUmbracoIndex)}"); } - var query = umbIndex.GetSearcher().CreateQuery(IndexTypes.Content); + var query = umbIndex.Searcher.CreateQuery(IndexTypes.Content); IQueryExecutor queryExecutor; if (culture == "*") @@ -286,7 +287,7 @@ namespace Umbraco.Cms.Infrastructure var results = skip == 0 && take == 0 ? queryExecutor.Execute() - : queryExecutor.Execute(skip + take); + : queryExecutor.Execute(QueryOptions.SkipTake(skip, take)); totalRecords = results.TotalItemCount; @@ -316,7 +317,7 @@ namespace Umbraco.Cms.Infrastructure var results = skip == 0 && take == 0 ? query.Execute() - : query.Execute(skip + take); + : query.Execute(QueryOptions.SkipTake(skip, take)); totalRecords = results.TotalItemCount; diff --git a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs index e004313ac3..e2b20ced8f 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs @@ -119,10 +119,10 @@ namespace Umbraco.Cms.Infrastructure.Runtime } - await _eventAggregator.PublishAsync(new UmbracoApplicationStartingNotification(State.Level), cancellationToken); - // create & initialize the components _components.Initialize(); + + await _eventAggregator.PublishAsync(new UmbracoApplicationStartingNotification(State.Level), cancellationToken); } private void DoUnattendedUpgrade() diff --git a/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs b/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs index c2c3262047..1c02898334 100644 --- a/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs +++ b/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs @@ -56,7 +56,7 @@ namespace Umbraco.Cms.Infrastructure.Runtime dbProviderFactoryCreator, databaseSchemaCreatorFactory); - MainDomKey = MainDomKeyPrefix + "-" + (NetworkHelper.MachineName + MainDom.GetMainDomId(_hostingEnvironment)).GenerateHash(); + MainDomKey = MainDomKeyPrefix + "-" + (Environment.MachineName + MainDom.GetMainDomId(_hostingEnvironment)).GenerateHash(); } public async Task AcquireLockAsync(int millisecondsTimeout) diff --git a/src/Umbraco.Infrastructure/Search/BackgroundIndexRebuilder.cs b/src/Umbraco.Infrastructure/Search/BackgroundIndexRebuilder.cs deleted file mode 100644 index 2fbceb2f9a..0000000000 --- a/src/Umbraco.Infrastructure/Search/BackgroundIndexRebuilder.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core.Runtime; -using Umbraco.Cms.Infrastructure.Examine; -using Umbraco.Cms.Infrastructure.HostedServices; - -namespace Umbraco.Cms.Infrastructure.Search -{ - /// - /// Utility to rebuild all indexes on a background thread - /// - public class BackgroundIndexRebuilder - { - private readonly IndexRebuilder _indexRebuilder; - private readonly IBackgroundTaskQueue _backgroundTaskQueue; - - private readonly IMainDom _mainDom; - private readonly ILogger _logger; - - private volatile bool _isRunning = false; - private static readonly object s_rebuildLocker = new object(); - - /// - /// Initializes a new instance of the class. - /// - public BackgroundIndexRebuilder( - IMainDom mainDom, - ILogger logger, - IndexRebuilder indexRebuilder, - IBackgroundTaskQueue backgroundTaskQueue) - { - _mainDom = mainDom; - _logger = logger; - _indexRebuilder = indexRebuilder; - _backgroundTaskQueue = backgroundTaskQueue; - } - - - /// - /// Called to rebuild empty indexes on startup - /// - public virtual void RebuildIndexes(bool onlyEmptyIndexes, TimeSpan? delay = null) - { - - lock (s_rebuildLocker) - { - if (_isRunning) - { - _logger.LogWarning("Call was made to RebuildIndexes but the task runner for rebuilding is already running"); - return; - } - - _logger.LogInformation("Starting initialize async background thread."); - - _backgroundTaskQueue.QueueBackgroundWorkItem(cancellationToken => RebuildIndexes(onlyEmptyIndexes, delay ?? TimeSpan.Zero, cancellationToken)); - - } - } - - private Task RebuildIndexes(bool onlyEmptyIndexes, TimeSpan delay, CancellationToken cancellationToken) - { - if (!_mainDom.IsMainDom) - { - return Task.CompletedTask; - } - - if (delay > TimeSpan.Zero) - { - Thread.Sleep(delay); - } - - _isRunning = true; - _indexRebuilder.RebuildIndexes(onlyEmptyIndexes); - _isRunning = false; - return Task.CompletedTask; - } - } -} diff --git a/src/Umbraco.Infrastructure/Search/ExamineNotificationHandler.cs b/src/Umbraco.Infrastructure/Search/ExamineNotificationHandler.cs deleted file mode 100644 index d0541cfd97..0000000000 --- a/src/Umbraco.Infrastructure/Search/ExamineNotificationHandler.cs +++ /dev/null @@ -1,838 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Threading.Tasks; -using Examine; -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Events; -using Umbraco.Cms.Core.Logging; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Notifications; -using Umbraco.Cms.Core.Runtime; -using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Changes; -using Umbraco.Cms.Core.Sync; -using Umbraco.Cms.Infrastructure.Examine; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.Search -{ - public sealed class ExamineNotificationHandler : - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler - { - private readonly IExamineManager _examineManager; - private readonly IContentValueSetBuilder _contentValueSetBuilder; - private readonly IPublishedContentValueSetBuilder _publishedContentValueSetBuilder; - private readonly IValueSetBuilder _mediaValueSetBuilder; - private readonly IValueSetBuilder _memberValueSetBuilder; - private readonly BackgroundIndexRebuilder _backgroundIndexRebuilder; - private readonly TaskHelper _taskHelper; - private readonly IRuntimeState _runtimeState; - private readonly IScopeProvider _scopeProvider; - private readonly ServiceContext _services; - private readonly IMainDom _mainDom; - private readonly IProfilingLogger _profilingLogger; - private readonly ILogger _logger; - private readonly IUmbracoIndexesCreator _indexCreator; - private static bool s_deactivate_handlers; - - // the default enlist priority is 100 - // enlist with a lower priority to ensure that anything "default" runs after us - // but greater that SafeXmlReaderWriter priority which is 60 - private const int EnlistPriority = 80; - - public ExamineNotificationHandler(IMainDom mainDom, - IExamineManager examineManager, - IProfilingLogger profilingLogger, - ILogger logger, - IScopeProvider scopeProvider, - IUmbracoIndexesCreator indexCreator, - ServiceContext services, - IContentValueSetBuilder contentValueSetBuilder, - IPublishedContentValueSetBuilder publishedContentValueSetBuilder, - IValueSetBuilder mediaValueSetBuilder, - IValueSetBuilder memberValueSetBuilder, - BackgroundIndexRebuilder backgroundIndexRebuilder, - TaskHelper taskHelper, - IRuntimeState runtimeState) - { - _services = services; - _scopeProvider = scopeProvider; - _examineManager = examineManager; - _contentValueSetBuilder = contentValueSetBuilder; - _publishedContentValueSetBuilder = publishedContentValueSetBuilder; - _mediaValueSetBuilder = mediaValueSetBuilder; - _memberValueSetBuilder = memberValueSetBuilder; - _backgroundIndexRebuilder = backgroundIndexRebuilder; - _taskHelper = taskHelper; - _runtimeState = runtimeState; - _mainDom = mainDom; - _profilingLogger = profilingLogger; - _logger = logger; - _indexCreator = indexCreator; - } - public void Handle(UmbracoApplicationStartingNotification notification) - { - //let's deal with shutting down Examine with MainDom - var examineShutdownRegistered = _mainDom.Register(release: () => - { - using (_profilingLogger.TraceDuration("Examine shutting down")) - { - _examineManager.Dispose(); - } - }); - - if (!examineShutdownRegistered) - { - _logger.LogInformation("Examine shutdown not registered, this AppDomain is not the MainDom, Examine will be disabled"); - - //if we could not register the shutdown examine ourselves, it means we are not maindom! in this case all of examine should be disabled! - Suspendable.ExamineEvents.SuspendIndexers(_logger); - return; //exit, do not continue - } - - //create the indexes and register them with the manager - foreach (IIndex index in _indexCreator.Create()) - { - _examineManager.AddIndex(index); - } - - _logger.LogDebug("Examine shutdown registered with MainDom"); - - var registeredIndexers = _examineManager.Indexes.OfType().Count(x => x.EnableDefaultEventHandler); - - _logger.LogInformation("Adding examine event handlers for {RegisteredIndexers} index providers.", registeredIndexers); - - // don't bind event handlers if we're not suppose to listen - if (registeredIndexers == 0) - { - s_deactivate_handlers = true; - } - - if (_mainDom.IsMainDom && _runtimeState.Level >= RuntimeLevel.Run) - { - _backgroundIndexRebuilder.RebuildIndexes(true); - } - } - - - #region Cache refresher updated event handlers - - /// - /// Updates indexes based on content changes - /// - /// - /// - public void Handle(ContentCacheRefresherNotification args) - { - if (s_deactivate_handlers) - { - return; - } - if (Suspendable.ExamineEvents.CanIndex == false) - { - return; - } - - if (args.MessageType != MessageType.RefreshByPayload) - { - throw new NotSupportedException(); - } - - var contentService = _services.ContentService; - - foreach (var payload in (ContentCacheRefresher.JsonPayload[])args.MessageObject) - { - if (payload.ChangeTypes.HasType(TreeChangeTypes.Remove)) - { - // delete content entirely (with descendants) - // false: remove entirely from all indexes - DeleteIndexForEntity(payload.Id, false); - } - else if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshAll)) - { - // ExamineEvents does not support RefreshAll - // just ignore that payload - // so what?! - - // TODO: Rebuild the index at this point? - } - else // RefreshNode or RefreshBranch (maybe trashed) - { - // don't try to be too clever - refresh entirely - // there has to be race conditions in there ;-( - - var content = contentService.GetById(payload.Id); - if (content == null) - { - // gone fishing, remove entirely from all indexes (with descendants) - DeleteIndexForEntity(payload.Id, false); - continue; - } - - IContent published = null; - if (content.Published && contentService.IsPathPublished(content)) - { - published = content; - } - - if (published == null) - { - DeleteIndexForEntity(payload.Id, true); - } - - // just that content - ReIndexForContent(content, published != null); - - // branch - if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch)) - { - var masked = published == null ? null : new List(); - const int pageSize = 500; - var page = 0; - var total = long.MaxValue; - while (page * pageSize < total) - { - var descendants = contentService.GetPagedDescendants(content.Id, page++, pageSize, out total, - //order by shallowest to deepest, this allows us to check it's published state without checking every item - ordering: Ordering.By("Path", Direction.Ascending)); - - foreach (var descendant in descendants) - { - published = null; - if (masked != null) // else everything is masked - { - if (masked.Contains(descendant.ParentId) || !descendant.Published) - { - masked.Add(descendant.Id); - } - else - { - published = descendant; - } - } - - ReIndexForContent(descendant, published != null); - } - } - } - } - - // NOTE - // - // DeleteIndexForEntity is handled by UmbracoContentIndexer.DeleteFromIndex() which takes - // care of also deleting the descendants - // - // ReIndexForContent is NOT taking care of descendants so we have to reload everything - // again in order to process the branch - we COULD improve that by just reloading the - // XML from database instead of reloading content & re-serializing! - // - // BUT ... pretty sure it is! see test "Index_Delete_Index_Item_Ensure_Heirarchy_Removed" - } - } - - public void Handle(MemberCacheRefresherNotification args) - { - if (s_deactivate_handlers) - { - return; - } - - if (Suspendable.ExamineEvents.CanIndex == false) - { - return; - } - - switch (args.MessageType) - { - case MessageType.RefreshById: - var c1 = _services.MemberService.GetById((int)args.MessageObject); - if (c1 != null) - { - ReIndexForMember(c1); - } - break; - case MessageType.RemoveById: - - // This is triggered when the item is permanently deleted - - DeleteIndexForEntity((int)args.MessageObject, false); - break; - case MessageType.RefreshByInstance: - if (args.MessageObject is IMember c3) - { - ReIndexForMember(c3); - } - break; - case MessageType.RemoveByInstance: - - // This is triggered when the item is permanently deleted - - if (args.MessageObject is IMember c4) - { - DeleteIndexForEntity(c4.Id, false); - } - break; - case MessageType.RefreshByPayload: - var payload = (MemberCacheRefresher.JsonPayload[])args.MessageObject; - foreach (var p in payload) - { - if (p.Removed) - { - DeleteIndexForEntity(p.Id, false); - } - else - { - var m = _services.MemberService.GetById(p.Id); - if (m != null) - { - ReIndexForMember(m); - } - } - } - break; - case MessageType.RefreshAll: - case MessageType.RefreshByJson: - default: - //We don't support these, these message types will not fire for unpublished content - break; - } - } - - public void Handle(MediaCacheRefresherNotification args) - { - if (s_deactivate_handlers) - { - return; - } - - if (Suspendable.ExamineEvents.CanIndex == false) - { - return; - } - - if (args.MessageType != MessageType.RefreshByPayload) - { - throw new NotSupportedException(); - } - - var mediaService = _services.MediaService; - - foreach (var payload in (MediaCacheRefresher.JsonPayload[])args.MessageObject) - { - if (payload.ChangeTypes.HasType(TreeChangeTypes.Remove)) - { - // remove from *all* indexes - DeleteIndexForEntity(payload.Id, false); - } - else if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshAll)) - { - // ExamineEvents does not support RefreshAll - // just ignore that payload - // so what?! - } - else // RefreshNode or RefreshBranch (maybe trashed) - { - var media = mediaService.GetById(payload.Id); - if (media == null) - { - // gone fishing, remove entirely - DeleteIndexForEntity(payload.Id, false); - continue; - } - - if (media.Trashed) - { - DeleteIndexForEntity(payload.Id, true); - } - - // just that media - ReIndexForMedia(media, !media.Trashed); - - // branch - if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch)) - { - const int pageSize = 500; - var page = 0; - var total = long.MaxValue; - while (page * pageSize < total) - { - var descendants = mediaService.GetPagedDescendants(media.Id, page++, pageSize, out total); - foreach (var descendant in descendants) - { - ReIndexForMedia(descendant, !descendant.Trashed); - } - } - } - } - } - } - - public void Handle(LanguageCacheRefresherNotification args) - { - if (s_deactivate_handlers) - { - return; - } - - if (!(args.MessageObject is LanguageCacheRefresher.JsonPayload[] payloads)) - { - return; - } - - if (payloads.Length == 0) - { - return; - } - - var removedOrCultureChanged = payloads.Any(x => - x.ChangeType == LanguageCacheRefresher.JsonPayload.LanguageChangeType.ChangeCulture - || x.ChangeType == LanguageCacheRefresher.JsonPayload.LanguageChangeType.Remove); - - if (removedOrCultureChanged) - { - //if a lang is removed or it's culture has changed, we need to rebuild the indexes since - //field names and values in the index have a string culture value. - _backgroundIndexRebuilder.RebuildIndexes(false); - } - } - - /// - /// Updates indexes based on content type changes - /// - /// - /// - public void Handle(ContentTypeCacheRefresherNotification args) - { - if (s_deactivate_handlers) - { - return; - } - - if (Suspendable.ExamineEvents.CanIndex == false) - { - return; - } - - if (args.MessageType != MessageType.RefreshByPayload) - { - throw new NotSupportedException(); - } - - var changedIds = new Dictionary removedIds, List refreshedIds, List otherIds)>(); - - foreach (var payload in (ContentTypeCacheRefresher.JsonPayload[])args.MessageObject) - { - if (!changedIds.TryGetValue(payload.ItemType, out var idLists)) - { - idLists = (removedIds: new List(), refreshedIds: new List(), otherIds: new List()); - changedIds.Add(payload.ItemType, idLists); - } - - if (payload.ChangeTypes.HasType(ContentTypeChangeTypes.Remove)) - { - idLists.removedIds.Add(payload.Id); - } - else if (payload.ChangeTypes.HasType(ContentTypeChangeTypes.RefreshMain)) - { - idLists.refreshedIds.Add(payload.Id); - } - else if (payload.ChangeTypes.HasType(ContentTypeChangeTypes.RefreshOther)) - { - idLists.otherIds.Add(payload.Id); - } - } - - const int pageSize = 500; - - foreach (var ci in changedIds) - { - if (ci.Value.refreshedIds.Count > 0 || ci.Value.otherIds.Count > 0) - { - switch (ci.Key) - { - case var itemType when itemType == typeof(IContentType).Name: - RefreshContentOfContentTypes(ci.Value.refreshedIds.Concat(ci.Value.otherIds).Distinct().ToArray()); - break; - case var itemType when itemType == typeof(IMediaType).Name: - RefreshMediaOfMediaTypes(ci.Value.refreshedIds.Concat(ci.Value.otherIds).Distinct().ToArray()); - break; - case var itemType when itemType == typeof(IMemberType).Name: - RefreshMemberOfMemberTypes(ci.Value.refreshedIds.Concat(ci.Value.otherIds).Distinct().ToArray()); - break; - } - } - - //Delete all content of this content/media/member type that is in any content indexer by looking up matched examine docs - foreach (var id in ci.Value.removedIds) - { - foreach (var index in _examineManager.Indexes.OfType()) - { - var searcher = index.GetSearcher(); - - var page = 0; - var total = long.MaxValue; - while (page * pageSize < total) - { - //paging with examine, see https://shazwazza.com/post/paging-with-examine/ - var results = searcher.CreateQuery().Field("nodeType", id.ToInvariantString()).Execute(maxResults: pageSize * (page + 1)); - total = results.TotalItemCount; - var paged = results.Skip(page * pageSize); - - foreach (ISearchResult item in paged) - { - if (int.TryParse(item.Id, out int contentId)) - { - DeleteIndexForEntity(contentId, false); - } - } - - page++; - } - } - } - } - } - - private void RefreshMemberOfMemberTypes(int[] memberTypeIds) - { - const int pageSize = 500; - - IEnumerable memberTypes = _services.MemberTypeService.GetAll(memberTypeIds); - foreach (IMemberType memberType in memberTypes) - { - var page = 0; - var total = long.MaxValue; - while (page * pageSize < total) - { - IEnumerable memberToRefresh = _services.MemberService.GetAll( - page++, pageSize, out total, "LoginName", Direction.Ascending, - memberType.Alias); - - foreach (IMember c in memberToRefresh) - { - ReIndexForMember(c); - } - } - } - } - - private void RefreshMediaOfMediaTypes(int[] mediaTypeIds) - { - const int pageSize = 500; - var page = 0; - var total = long.MaxValue; - while (page * pageSize < total) - { - IEnumerable mediaToRefresh = _services.MediaService.GetPagedOfTypes( - //Re-index all content of these types - mediaTypeIds, - page++, pageSize, out total, null, - Ordering.By("Path", Direction.Ascending)); - - foreach (IMedia c in mediaToRefresh) - { - ReIndexForMedia(c, c.Trashed == false); - } - } - } - - private void RefreshContentOfContentTypes(int[] contentTypeIds) - { - const int pageSize = 500; - var page = 0; - var total = long.MaxValue; - while (page * pageSize < total) - { - IEnumerable contentToRefresh = _services.ContentService.GetPagedOfTypes( - //Re-index all content of these types - contentTypeIds, - page++, pageSize, out total, null, - //order by shallowest to deepest, this allows us to check it's published state without checking every item - Ordering.By("Path", Direction.Ascending)); - - //track which Ids have their paths are published - var publishChecked = new Dictionary(); - - foreach (IContent c in contentToRefresh) - { - var isPublished = false; - if (c.Published) - { - if (!publishChecked.TryGetValue(c.ParentId, out isPublished)) - { - //nothing by parent id, so query the service and cache the result for the next child to check against - isPublished = _services.ContentService.IsPathPublished(c); - publishChecked[c.Id] = isPublished; - } - } - - ReIndexForContent(c, isPublished); - } - } - } - - #endregion - - #region ReIndex/Delete for entity - private void ReIndexForContent(IContent sender, bool isPublished) - { - var actions = DeferedActions.Get(_scopeProvider); - if (actions != null) - { - actions.Add(new DeferedReIndexForContent(_taskHelper, this, sender, isPublished)); - } - else - { - DeferedReIndexForContent.Execute(_taskHelper, this, sender, isPublished); - } - } - - private void ReIndexForMember(IMember member) - { - var actions = DeferedActions.Get(_scopeProvider); - if (actions != null) - { - actions.Add(new DeferedReIndexForMember(_taskHelper, this, member)); - } - else - { - DeferedReIndexForMember.Execute(_taskHelper, this, member); - } - } - - private void ReIndexForMedia(IMedia sender, bool isPublished) - { - var actions = DeferedActions.Get(_scopeProvider); - if (actions != null) - { - actions.Add(new DeferedReIndexForMedia(_taskHelper, this, sender, isPublished)); - } - else - { - DeferedReIndexForMedia.Execute(_taskHelper, this, sender, isPublished); - } - } - - /// - /// Remove items from an index - /// - /// - /// - /// If true, indicates that we will only delete this item from indexes that don't support unpublished content. - /// If false it will delete this from all indexes regardless. - /// - private void DeleteIndexForEntity(int entityId, bool keepIfUnpublished) - { - var actions = DeferedActions.Get(_scopeProvider); - if (actions != null) - { - actions.Add(new DeferedDeleteIndex(this, entityId, keepIfUnpublished)); - } - else - { - DeferedDeleteIndex.Execute(this, entityId, keepIfUnpublished); - } - } - #endregion - - #region Deferred Actions - private class DeferedActions - { - private readonly List _actions = new List(); - - public static DeferedActions Get(IScopeProvider scopeProvider) - { - IScopeContext scopeContext = scopeProvider.Context; - - return scopeContext?.Enlist("examineEvents", - () => new DeferedActions(), // creator - (completed, actions) => // action - { - if (completed) - { - actions.Execute(); - } - }, EnlistPriority); - } - - public void Add(DeferedAction action) => _actions.Add(action); - - private void Execute() - { - foreach (DeferedAction action in _actions) - { - action.Execute(); - } - } - } - - /// - /// An action that will execute at the end of the Scope being completed - /// - private abstract class DeferedAction - { - public virtual void Execute() - { } - } - - /// - /// Re-indexes an item on a background thread - /// - private class DeferedReIndexForContent : DeferedAction - { - private readonly TaskHelper _taskHelper; - private readonly ExamineNotificationHandler _ExamineNotificationHandler; - private readonly IContent _content; - private readonly bool _isPublished; - - public DeferedReIndexForContent(TaskHelper taskHelper, ExamineNotificationHandler ExamineNotificationHandler, IContent content, bool isPublished) - { - _taskHelper = taskHelper; - _ExamineNotificationHandler = ExamineNotificationHandler; - _content = content; - _isPublished = isPublished; - } - - public override void Execute() => Execute(_taskHelper, _ExamineNotificationHandler, _content, _isPublished); - - public static void Execute(TaskHelper taskHelper, ExamineNotificationHandler ExamineNotificationHandler, IContent content, bool isPublished) - => taskHelper.RunBackgroundTask(() => - { - using IScope scope = ExamineNotificationHandler._scopeProvider.CreateScope(autoComplete: true); - - // for content we have a different builder for published vs unpublished - // we don't want to build more value sets than is needed so we'll lazily build 2 one for published one for non-published - var builders = new Dictionary>> - { - [true] = new Lazy>(() => ExamineNotificationHandler._publishedContentValueSetBuilder.GetValueSets(content).ToList()), - [false] = new Lazy>(() => ExamineNotificationHandler._contentValueSetBuilder.GetValueSets(content).ToList()) - }; - - foreach (IUmbracoIndex index in ExamineNotificationHandler._examineManager.Indexes.OfType() - //filter the indexers - .Where(x => isPublished || !x.PublishedValuesOnly) - .Where(x => x.EnableDefaultEventHandler)) - { - List valueSet = builders[index.PublishedValuesOnly].Value; - index.IndexItems(valueSet); - } - - return Task.CompletedTask; - }); - } - - /// - /// Re-indexes an item on a background thread - /// - private class DeferedReIndexForMedia : DeferedAction - { - private readonly TaskHelper _taskHelper; - private readonly ExamineNotificationHandler _ExamineNotificationHandler; - private readonly IMedia _media; - private readonly bool _isPublished; - - public DeferedReIndexForMedia(TaskHelper taskHelper, ExamineNotificationHandler ExamineNotificationHandler, IMedia media, bool isPublished) - { - _taskHelper = taskHelper; - _ExamineNotificationHandler = ExamineNotificationHandler; - _media = media; - _isPublished = isPublished; - } - - public override void Execute() => Execute(_taskHelper, _ExamineNotificationHandler, _media, _isPublished); - - public static void Execute(TaskHelper taskHelper, ExamineNotificationHandler ExamineNotificationHandler, IMedia media, bool isPublished) => - // perform the ValueSet lookup on a background thread - taskHelper.RunBackgroundTask(() => - { - using IScope scope = ExamineNotificationHandler._scopeProvider.CreateScope(autoComplete: true); - - var valueSet = ExamineNotificationHandler._mediaValueSetBuilder.GetValueSets(media).ToList(); - - foreach (IUmbracoIndex index in ExamineNotificationHandler._examineManager.Indexes.OfType() - //filter the indexers - .Where(x => isPublished || !x.PublishedValuesOnly) - .Where(x => x.EnableDefaultEventHandler)) - { - index.IndexItems(valueSet); - } - - return Task.CompletedTask; - }); - } - - /// - /// Re-indexes an item on a background thread - /// - private class DeferedReIndexForMember : DeferedAction - { - private readonly ExamineNotificationHandler _ExamineNotificationHandler; - private readonly IMember _member; - private readonly TaskHelper _taskHelper; - - public DeferedReIndexForMember(TaskHelper taskHelper, ExamineNotificationHandler ExamineNotificationHandler, IMember member) - { - _ExamineNotificationHandler = ExamineNotificationHandler; - _member = member; - _taskHelper = taskHelper; - } - - public override void Execute() => Execute(_taskHelper, _ExamineNotificationHandler, _member); - - public static void Execute(TaskHelper taskHelper, ExamineNotificationHandler ExamineNotificationHandler, IMember member) => - // perform the ValueSet lookup on a background thread - taskHelper.RunBackgroundTask(() => - { - using IScope scope = ExamineNotificationHandler._scopeProvider.CreateScope(autoComplete: true); - - var valueSet = ExamineNotificationHandler._memberValueSetBuilder.GetValueSets(member).ToList(); - foreach (IUmbracoIndex index in ExamineNotificationHandler._examineManager.Indexes.OfType() - //filter the indexers - .Where(x => x.EnableDefaultEventHandler)) - { - index.IndexItems(valueSet); - } - - return Task.CompletedTask; - }); - } - - private class DeferedDeleteIndex : DeferedAction - { - private readonly ExamineNotificationHandler _ExamineNotificationHandler; - private readonly int _id; - private readonly bool _keepIfUnpublished; - - public DeferedDeleteIndex(ExamineNotificationHandler ExamineNotificationHandler, int id, bool keepIfUnpublished) - { - _ExamineNotificationHandler = ExamineNotificationHandler; - _id = id; - _keepIfUnpublished = keepIfUnpublished; - } - - public override void Execute() => Execute(_ExamineNotificationHandler, _id, _keepIfUnpublished); - - public static void Execute(ExamineNotificationHandler ExamineNotificationHandler, int id, bool keepIfUnpublished) - { - var strId = id.ToString(CultureInfo.InvariantCulture); - foreach (var index in ExamineNotificationHandler._examineManager.Indexes.OfType() - .Where(x => x.PublishedValuesOnly || !keepIfUnpublished) - .Where(x => x.EnableDefaultEventHandler)) - { - index.DeleteFromIndex(strId); - } - } - } - #endregion - } -} diff --git a/src/Umbraco.Infrastructure/Search/ExamineUserComponent.cs b/src/Umbraco.Infrastructure/Search/ExamineUserComponent.cs deleted file mode 100644 index 6c39da44c7..0000000000 --- a/src/Umbraco.Infrastructure/Search/ExamineUserComponent.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.Runtime; - -namespace Umbraco.Cms.Infrastructure.Search -{ - /// - /// An abstract class for custom index authors to inherit from - /// - public abstract class ExamineUserComponent : IComponent - { - private readonly IMainDom _mainDom; - - public ExamineUserComponent(IMainDom mainDom) - { - _mainDom = mainDom; - } - - /// - /// Initialize the component, eagerly exits if ExamineComponent.ExamineEnabled == false - /// - public void Initialize() - { - if (!_mainDom.IsMainDom) return; - - InitializeComponent(); - } - - /// - /// Abstract method which executes to initialize this component if ExamineComponent.ExamineEnabled == true - /// - protected abstract void InitializeComponent(); - - public virtual void Terminate() - { - } - } -} diff --git a/src/Umbraco.Infrastructure/Search/IUmbracoIndexingHandler.cs b/src/Umbraco.Infrastructure/Search/IUmbracoIndexingHandler.cs new file mode 100644 index 0000000000..24c82c055d --- /dev/null +++ b/src/Umbraco.Infrastructure/Search/IUmbracoIndexingHandler.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using Umbraco.Cms.Core.Models; + +namespace Umbraco.Cms.Infrastructure.Search +{ + public interface IUmbracoIndexingHandler + { + /// + /// Returns true if the indexing handler is enabled + /// + /// + /// If this is false then there will be no data lookups executed to populate indexes + /// when service changes are made. + /// + bool Enabled { get; } + + void ReIndexForContent(IContent sender, bool isPublished); + void ReIndexForMember(IMember member); + void ReIndexForMedia(IMedia sender, bool isPublished); + + /// + /// Deletes all documents for the content type Ids + /// + /// + void DeleteDocumentsForContentTypes(IReadOnlyCollection removedContentTypes); + + /// + /// Remove items from an index + /// + /// + /// + /// If true, indicates that we will only delete this item from indexes that don't support unpublished content. + /// If false it will delete this from all indexes regardless. + /// + void DeleteIndexForEntity(int entityId, bool keepIfUnpublished); + } +} diff --git a/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.Content.cs b/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.Content.cs new file mode 100644 index 0000000000..ebebdb7f34 --- /dev/null +++ b/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.Content.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Services.Changes; +using Umbraco.Cms.Core.Sync; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Infrastructure.Search +{ + public sealed class ContentIndexingNotificationHandler : INotificationHandler + { + private readonly IUmbracoIndexingHandler _umbracoIndexingHandler; + private readonly IContentService _contentService; + + public ContentIndexingNotificationHandler(IUmbracoIndexingHandler umbracoIndexingHandler, IContentService contentService) + { + _umbracoIndexingHandler = umbracoIndexingHandler ?? throw new ArgumentNullException(nameof(umbracoIndexingHandler)); + _contentService = contentService ?? throw new ArgumentNullException(nameof(contentService)); + } + + /// + /// Updates indexes based on content changes + /// + /// + /// + public void Handle(ContentCacheRefresherNotification args) + { + if (!_umbracoIndexingHandler.Enabled) + { + return; + } + if (Suspendable.ExamineEvents.CanIndex == false) + { + return; + } + + if (args.MessageType != MessageType.RefreshByPayload) + { + throw new NotSupportedException(); + } + + foreach (var payload in (ContentCacheRefresher.JsonPayload[])args.MessageObject) + { + if (payload.ChangeTypes.HasType(TreeChangeTypes.Remove)) + { + // delete content entirely (with descendants) + // false: remove entirely from all indexes + _umbracoIndexingHandler.DeleteIndexForEntity(payload.Id, false); + } + else if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshAll)) + { + // ExamineEvents does not support RefreshAll + // just ignore that payload + // so what?! + + // TODO: Rebuild the index at this point? + } + else // RefreshNode or RefreshBranch (maybe trashed) + { + // don't try to be too clever - refresh entirely + // there has to be race conditions in there ;-( + + var content = _contentService.GetById(payload.Id); + if (content == null) + { + // gone fishing, remove entirely from all indexes (with descendants) + _umbracoIndexingHandler.DeleteIndexForEntity(payload.Id, false); + continue; + } + + IContent published = null; + if (content.Published && _contentService.IsPathPublished(content)) + { + published = content; + } + + if (published == null) + { + _umbracoIndexingHandler.DeleteIndexForEntity(payload.Id, true); + } + + // just that content + _umbracoIndexingHandler.ReIndexForContent(content, published != null); + + // branch + if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch)) + { + var masked = published == null ? null : new List(); + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + var descendants = _contentService.GetPagedDescendants(content.Id, page++, pageSize, out total, + //order by shallowest to deepest, this allows us to check it's published state without checking every item + ordering: Ordering.By("Path", Direction.Ascending)); + + foreach (var descendant in descendants) + { + published = null; + if (masked != null) // else everything is masked + { + if (masked.Contains(descendant.ParentId) || !descendant.Published) + { + masked.Add(descendant.Id); + } + else + { + published = descendant; + } + } + + _umbracoIndexingHandler.ReIndexForContent(descendant, published != null); + } + } + } + } + + // NOTE + // + // DeleteIndexForEntity is handled by UmbracoContentIndexer.DeleteFromIndex() which takes + // care of also deleting the descendants + // + // ReIndexForContent is NOT taking care of descendants so we have to reload everything + // again in order to process the branch - we COULD improve that by just reloading the + // XML from database instead of reloading content & re-serializing! + // + // BUT ... pretty sure it is! see test "Index_Delete_Index_Item_Ensure_Heirarchy_Removed" + } + } + } +} diff --git a/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.ContentType.cs b/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.ContentType.cs new file mode 100644 index 0000000000..9bdc9fa3c4 --- /dev/null +++ b/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.ContentType.cs @@ -0,0 +1,180 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Services.Changes; +using Umbraco.Cms.Core.Sync; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Infrastructure.Search +{ + public sealed class ContentTypeIndexingNotificationHandler : INotificationHandler + { + private readonly IUmbracoIndexingHandler _umbracoIndexingHandler; + private readonly IContentService _contentService; + private readonly IMemberService _memberService; + private readonly IMediaService _mediaService; + private readonly IMemberTypeService _memberTypeService; + + public ContentTypeIndexingNotificationHandler(IUmbracoIndexingHandler umbracoIndexingHandler, IContentService contentService, IMemberService memberService, IMediaService mediaService, IMemberTypeService memberTypeService) + { + _umbracoIndexingHandler = umbracoIndexingHandler ?? throw new ArgumentNullException(nameof(umbracoIndexingHandler)); + _contentService = contentService ?? throw new ArgumentNullException(nameof(contentService)); + _memberService = memberService ?? throw new ArgumentNullException(nameof(memberService)); + _mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); + _memberTypeService = memberTypeService ?? throw new ArgumentNullException(nameof(memberTypeService)); + } + + /// + /// Updates indexes based on content type changes + /// + /// + /// + public void Handle(ContentTypeCacheRefresherNotification args) + { + if (!_umbracoIndexingHandler.Enabled) + { + return; + } + + if (Suspendable.ExamineEvents.CanIndex == false) + { + return; + } + + if (args.MessageType != MessageType.RefreshByPayload) + { + throw new NotSupportedException(); + } + + var changedIds = new Dictionary removedIds, List refreshedIds, List otherIds)>(); + + foreach (var payload in (ContentTypeCacheRefresher.JsonPayload[])args.MessageObject) + { + if (!changedIds.TryGetValue(payload.ItemType, out var idLists)) + { + idLists = (removedIds: new List(), refreshedIds: new List(), otherIds: new List()); + changedIds.Add(payload.ItemType, idLists); + } + + if (payload.ChangeTypes.HasType(ContentTypeChangeTypes.Remove)) + { + idLists.removedIds.Add(payload.Id); + } + else if (payload.ChangeTypes.HasType(ContentTypeChangeTypes.RefreshMain)) + { + idLists.refreshedIds.Add(payload.Id); + } + else if (payload.ChangeTypes.HasType(ContentTypeChangeTypes.RefreshOther)) + { + idLists.otherIds.Add(payload.Id); + } + } + + foreach (var ci in changedIds) + { + if (ci.Value.refreshedIds.Count > 0 || ci.Value.otherIds.Count > 0) + { + switch (ci.Key) + { + case var itemType when itemType == typeof(IContentType).Name: + RefreshContentOfContentTypes(ci.Value.refreshedIds.Concat(ci.Value.otherIds).Distinct().ToArray()); + break; + case var itemType when itemType == typeof(IMediaType).Name: + RefreshMediaOfMediaTypes(ci.Value.refreshedIds.Concat(ci.Value.otherIds).Distinct().ToArray()); + break; + case var itemType when itemType == typeof(IMemberType).Name: + RefreshMemberOfMemberTypes(ci.Value.refreshedIds.Concat(ci.Value.otherIds).Distinct().ToArray()); + break; + } + } + + //Delete all content of this content/media/member type that is in any content indexer by looking up matched examine docs + _umbracoIndexingHandler.DeleteDocumentsForContentTypes(ci.Value.removedIds); + } + } + + private void RefreshMemberOfMemberTypes(int[] memberTypeIds) + { + const int pageSize = 500; + + IEnumerable memberTypes = _memberTypeService.GetAll(memberTypeIds); + foreach (IMemberType memberType in memberTypes) + { + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + IEnumerable memberToRefresh = _memberService.GetAll( + page++, pageSize, out total, "LoginName", Direction.Ascending, + memberType.Alias); + + foreach (IMember c in memberToRefresh) + { + _umbracoIndexingHandler.ReIndexForMember(c); + } + } + } + } + + private void RefreshMediaOfMediaTypes(int[] mediaTypeIds) + { + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + IEnumerable mediaToRefresh = _mediaService.GetPagedOfTypes( + //Re-index all content of these types + mediaTypeIds, + page++, pageSize, out total, null, + Ordering.By("Path", Direction.Ascending)); + + foreach (IMedia c in mediaToRefresh) + { + _umbracoIndexingHandler.ReIndexForMedia(c, c.Trashed == false); + } + } + } + + private void RefreshContentOfContentTypes(int[] contentTypeIds) + { + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + IEnumerable contentToRefresh = _contentService.GetPagedOfTypes( + //Re-index all content of these types + contentTypeIds, + page++, pageSize, out total, null, + //order by shallowest to deepest, this allows us to check it's published state without checking every item + Ordering.By("Path", Direction.Ascending)); + + //track which Ids have their paths are published + var publishChecked = new Dictionary(); + + foreach (IContent c in contentToRefresh) + { + var isPublished = false; + if (c.Published) + { + if (!publishChecked.TryGetValue(c.ParentId, out isPublished)) + { + //nothing by parent id, so query the service and cache the result for the next child to check against + isPublished = _contentService.IsPathPublished(c); + publishChecked[c.Id] = isPublished; + } + } + + _umbracoIndexingHandler.ReIndexForContent(c, isPublished); + } + } + } + } +} diff --git a/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.Language.cs b/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.Language.cs new file mode 100644 index 0000000000..2f7d5f66ca --- /dev/null +++ b/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.Language.cs @@ -0,0 +1,50 @@ +using System; +using System.Linq; +using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Infrastructure.Examine; + +namespace Umbraco.Cms.Infrastructure.Search +{ + public sealed class LanguageIndexingNotificationHandler : INotificationHandler + { + private readonly IUmbracoIndexingHandler _umbracoIndexingHandler; + private readonly IIndexRebuilder _indexRebuilder; + + public LanguageIndexingNotificationHandler(IUmbracoIndexingHandler umbracoIndexingHandler, IIndexRebuilder indexRebuilder) + { + _umbracoIndexingHandler = umbracoIndexingHandler ?? throw new ArgumentNullException(nameof(umbracoIndexingHandler)); + _indexRebuilder = indexRebuilder ?? throw new ArgumentNullException(nameof(indexRebuilder)); + } + + public void Handle(LanguageCacheRefresherNotification args) + { + if (!_umbracoIndexingHandler.Enabled) + { + return; + } + + if (!(args.MessageObject is LanguageCacheRefresher.JsonPayload[] payloads)) + { + return; + } + + if (payloads.Length == 0) + { + return; + } + + var removedOrCultureChanged = payloads.Any(x => + x.ChangeType == LanguageCacheRefresher.JsonPayload.LanguageChangeType.ChangeCulture + || x.ChangeType == LanguageCacheRefresher.JsonPayload.LanguageChangeType.Remove); + + if (removedOrCultureChanged) + { + //if a lang is removed or it's culture has changed, we need to rebuild the indexes since + //field names and values in the index have a string culture value. + _indexRebuilder.RebuildIndexes(false); + } + } + } +} diff --git a/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.Media.cs b/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.Media.cs new file mode 100644 index 0000000000..8b37d047de --- /dev/null +++ b/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.Media.cs @@ -0,0 +1,90 @@ +using System; +using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Services.Changes; +using Umbraco.Cms.Core.Sync; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Infrastructure.Search +{ + public sealed class MediaIndexingNotificationHandler : INotificationHandler + { + private readonly IUmbracoIndexingHandler _umbracoIndexingHandler; + private readonly IMediaService _mediaService; + + public MediaIndexingNotificationHandler(IUmbracoIndexingHandler umbracoIndexingHandler, IMediaService mediaService) + { + _umbracoIndexingHandler = umbracoIndexingHandler ?? throw new ArgumentNullException(nameof(umbracoIndexingHandler)); + _mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); + } + + public void Handle(MediaCacheRefresherNotification args) + { + if (!_umbracoIndexingHandler.Enabled) + { + return; + } + + if (Suspendable.ExamineEvents.CanIndex == false) + { + return; + } + + if (args.MessageType != MessageType.RefreshByPayload) + { + throw new NotSupportedException(); + } + + foreach (var payload in (MediaCacheRefresher.JsonPayload[])args.MessageObject) + { + if (payload.ChangeTypes.HasType(TreeChangeTypes.Remove)) + { + // remove from *all* indexes + _umbracoIndexingHandler.DeleteIndexForEntity(payload.Id, false); + } + else if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshAll)) + { + // ExamineEvents does not support RefreshAll + // just ignore that payload + // so what?! + } + else // RefreshNode or RefreshBranch (maybe trashed) + { + var media = _mediaService.GetById(payload.Id); + if (media == null) + { + // gone fishing, remove entirely + _umbracoIndexingHandler.DeleteIndexForEntity(payload.Id, false); + continue; + } + + if (media.Trashed) + { + _umbracoIndexingHandler.DeleteIndexForEntity(payload.Id, true); + } + + // just that media + _umbracoIndexingHandler.ReIndexForMedia(media, !media.Trashed); + + // branch + if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch)) + { + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + var descendants = _mediaService.GetPagedDescendants(media.Id, page++, pageSize, out total); + foreach (var descendant in descendants) + { + _umbracoIndexingHandler.ReIndexForMedia(descendant, !descendant.Trashed); + } + } + } + } + } + } + } +} diff --git a/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.Member.cs b/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.Member.cs new file mode 100644 index 0000000000..389b839c67 --- /dev/null +++ b/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.Member.cs @@ -0,0 +1,90 @@ +using System; +using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Sync; + +namespace Umbraco.Cms.Infrastructure.Search +{ + public sealed class MemberIndexingNotificationHandler : INotificationHandler + { + private readonly IUmbracoIndexingHandler _umbracoIndexingHandler; + private readonly IMemberService _memberService; + + public MemberIndexingNotificationHandler(IUmbracoIndexingHandler umbracoIndexingHandler, IMemberService memberService) + { + _umbracoIndexingHandler = umbracoIndexingHandler ?? throw new ArgumentNullException(nameof(umbracoIndexingHandler)); + _memberService = memberService ?? throw new ArgumentNullException(nameof(memberService)); + } + + public void Handle(MemberCacheRefresherNotification args) + { + if (!_umbracoIndexingHandler.Enabled) + { + return; + } + + if (Suspendable.ExamineEvents.CanIndex == false) + { + return; + } + + switch (args.MessageType) + { + case MessageType.RefreshById: + var c1 = _memberService.GetById((int)args.MessageObject); + if (c1 != null) + { + _umbracoIndexingHandler.ReIndexForMember(c1); + } + break; + case MessageType.RemoveById: + + // This is triggered when the item is permanently deleted + + _umbracoIndexingHandler.DeleteIndexForEntity((int)args.MessageObject, false); + break; + case MessageType.RefreshByInstance: + if (args.MessageObject is IMember c3) + { + _umbracoIndexingHandler.ReIndexForMember(c3); + } + break; + case MessageType.RemoveByInstance: + + // This is triggered when the item is permanently deleted + + if (args.MessageObject is IMember c4) + { + _umbracoIndexingHandler.DeleteIndexForEntity(c4.Id, false); + } + break; + case MessageType.RefreshByPayload: + var payload = (MemberCacheRefresher.JsonPayload[])args.MessageObject; + foreach (var p in payload) + { + if (p.Removed) + { + _umbracoIndexingHandler.DeleteIndexForEntity(p.Id, false); + } + else + { + var m = _memberService.GetById(p.Id); + if (m != null) + { + _umbracoIndexingHandler.ReIndexForMember(m); + } + } + } + break; + case MessageType.RefreshAll: + case MessageType.RefreshByJson: + default: + //We don't support these, these message types will not fire for unpublished content + break; + } + } + } +} diff --git a/src/Umbraco.Infrastructure/Services/Implement/CacheInstructionService.cs b/src/Umbraco.Infrastructure/Services/Implement/CacheInstructionService.cs index 0a6e945a23..e0c0f56244 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/CacheInstructionService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/CacheInstructionService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Newtonsoft.Json; @@ -22,8 +23,6 @@ namespace Umbraco.Cms.Core.Services.Implement /// public class CacheInstructionService : RepositoryService, ICacheInstructionService { - private readonly IServerRoleAccessor _serverRoleAccessor; - private readonly CacheRefresherCollection _cacheRefreshers; private readonly ICacheInstructionRepository _cacheInstructionRepository; private readonly IProfilingLogger _profilingLogger; private readonly ILogger _logger; @@ -36,16 +35,12 @@ namespace Umbraco.Cms.Core.Services.Implement IScopeProvider provider, ILoggerFactory loggerFactory, IEventMessagesFactory eventMessagesFactory, - IServerRoleAccessor serverRoleAccessor, - CacheRefresherCollection cacheRefreshers, ICacheInstructionRepository cacheInstructionRepository, IProfilingLogger profilingLogger, ILogger logger, IOptions globalSettings) : base(provider, loggerFactory, eventMessagesFactory) { - _serverRoleAccessor = serverRoleAccessor; - _cacheRefreshers = cacheRefreshers; _cacheInstructionRepository = cacheInstructionRepository; _profilingLogger = profilingLogger; _logger = logger; @@ -57,7 +52,7 @@ namespace Umbraco.Cms.Core.Services.Implement { using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { - if (lastId == 0) + if (lastId <= 0) { var count = _cacheInstructionRepository.CountAll(); @@ -79,7 +74,6 @@ namespace Umbraco.Cms.Core.Services.Implement return false; } } - /// public bool IsInstructionCountOverLimit(int lastId, int limit, out int count) { @@ -133,22 +127,28 @@ namespace Umbraco.Cms.Core.Services.Implement new CacheInstruction(0, DateTime.UtcNow, JsonConvert.SerializeObject(instructions, Formatting.None), localIdentity, instructions.Sum(x => x.JsonIdCount)); /// - public CacheInstructionServiceProcessInstructionsResult ProcessInstructions(bool released, string localIdentity, DateTime lastPruned, int lastId) + public ProcessInstructionsResult ProcessInstructions( + CacheRefresherCollection cacheRefreshers, + ServerRole serverRole, + CancellationToken cancellationToken, + string localIdentity, + DateTime lastPruned, + int lastId) { using (_profilingLogger.DebugDuration("Syncing from database...")) using (IScope scope = ScopeProvider.CreateScope()) { - var numberOfInstructionsProcessed = ProcessDatabaseInstructions(released, localIdentity, ref lastId); + var numberOfInstructionsProcessed = ProcessDatabaseInstructions(cacheRefreshers, cancellationToken, localIdentity, ref lastId); // Check for pruning throttling. - if (released || (DateTime.UtcNow - lastPruned) <= _globalSettings.DatabaseServerMessenger.TimeBetweenPruneOperations) + if (cancellationToken.IsCancellationRequested || (DateTime.UtcNow - lastPruned) <= _globalSettings.DatabaseServerMessenger.TimeBetweenPruneOperations) { scope.Complete(); - return CacheInstructionServiceProcessInstructionsResult.AsCompleted(numberOfInstructionsProcessed, lastId); + return ProcessInstructionsResult.AsCompleted(numberOfInstructionsProcessed, lastId); } var instructionsWerePruned = false; - switch (_serverRoleAccessor.CurrentServerRole) + switch (serverRole) { case ServerRole.Single: case ServerRole.Master: @@ -160,8 +160,8 @@ namespace Umbraco.Cms.Core.Services.Implement scope.Complete(); return instructionsWerePruned - ? CacheInstructionServiceProcessInstructionsResult.AsCompletedAndPruned(numberOfInstructionsProcessed, lastId) - : CacheInstructionServiceProcessInstructionsResult.AsCompleted(numberOfInstructionsProcessed, lastId); + ? ProcessInstructionsResult.AsCompletedAndPruned(numberOfInstructionsProcessed, lastId) + : ProcessInstructionsResult.AsCompleted(numberOfInstructionsProcessed, lastId); } } @@ -172,7 +172,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// Thread safety: this is NOT thread safe. Because it is NOT meant to run multi-threaded. /// /// Number of instructions processed. - private int ProcessDatabaseInstructions(bool released, string localIdentity, ref int lastId) + private int ProcessDatabaseInstructions(CacheRefresherCollection cacheRefreshers, CancellationToken cancellationToken, string localIdentity, ref int lastId) { // NOTE: // We 'could' recurse to ensure that no remaining instructions are pending in the table before proceeding but I don't think that @@ -205,7 +205,7 @@ namespace Umbraco.Cms.Core.Services.Implement { // If this flag gets set it means we're shutting down! In this case, we need to exit asap and cannot // continue processing anything otherwise we'll hold up the app domain shutdown. - if (released) + if (cancellationToken.IsCancellationRequested) { break; } @@ -227,7 +227,7 @@ namespace Umbraco.Cms.Core.Services.Implement List instructionBatch = GetAllInstructions(jsonInstructions); // Process as per-normal. - var success = ProcessDatabaseInstructions(instructionBatch, instruction, processed, released, ref lastId); + var success = ProcessDatabaseInstructions(cacheRefreshers, instructionBatch, instruction, processed, cancellationToken, ref lastId); // If they couldn't be all processed (i.e. we're shutting down) then exit. if (success == false) @@ -295,12 +295,18 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// Returns true if all instructions in the batch were processed, otherwise false if they could not be due to the app being shut down /// - private bool ProcessDatabaseInstructions(IReadOnlyCollection instructionBatch, CacheInstruction instruction, HashSet processed, bool released, ref int lastId) + private bool ProcessDatabaseInstructions( + CacheRefresherCollection cacheRefreshers, + IReadOnlyCollection instructionBatch, + CacheInstruction instruction, + HashSet processed, + CancellationToken cancellationToken, + ref int lastId) { // Execute remote instructions & update lastId. try { - var result = NotifyRefreshers(instructionBatch, processed, released); + var result = NotifyRefreshers(cacheRefreshers, instructionBatch, processed, cancellationToken); if (result) { // If all instructions were processed, set the last id. @@ -330,12 +336,16 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// Returns true if all instructions were processed, otherwise false if the processing was interrupted (i.e. by app shutdown). /// - private bool NotifyRefreshers(IEnumerable instructions, HashSet processed, bool released) + private bool NotifyRefreshers( + CacheRefresherCollection cacheRefreshers, + IEnumerable instructions, + HashSet processed, + CancellationToken cancellationToken) { foreach (RefreshInstruction instruction in instructions) { // Check if the app is shutting down, we need to exit if this happens. - if (released) + if (cancellationToken.IsCancellationRequested) { return false; } @@ -349,22 +359,22 @@ namespace Umbraco.Cms.Core.Services.Implement switch (instruction.RefreshType) { case RefreshMethodType.RefreshAll: - RefreshAll(instruction.RefresherId); + RefreshAll(cacheRefreshers, instruction.RefresherId); break; case RefreshMethodType.RefreshByGuid: - RefreshByGuid(instruction.RefresherId, instruction.GuidId); + RefreshByGuid(cacheRefreshers, instruction.RefresherId, instruction.GuidId); break; case RefreshMethodType.RefreshById: - RefreshById(instruction.RefresherId, instruction.IntId); + RefreshById(cacheRefreshers, instruction.RefresherId, instruction.IntId); break; case RefreshMethodType.RefreshByIds: - RefreshByIds(instruction.RefresherId, instruction.JsonIds); + RefreshByIds(cacheRefreshers, instruction.RefresherId, instruction.JsonIds); break; case RefreshMethodType.RefreshByJson: - RefreshByJson(instruction.RefresherId, instruction.JsonPayload); + RefreshByJson(cacheRefreshers, instruction.RefresherId, instruction.JsonPayload); break; case RefreshMethodType.RemoveById: - RemoveById(instruction.RefresherId, instruction.IntId); + RemoveById(cacheRefreshers, instruction.RefresherId, instruction.IntId); break; } @@ -374,48 +384,48 @@ namespace Umbraco.Cms.Core.Services.Implement return true; } - private void RefreshAll(Guid uniqueIdentifier) + private void RefreshAll(CacheRefresherCollection cacheRefreshers, Guid uniqueIdentifier) { - ICacheRefresher refresher = GetRefresher(uniqueIdentifier); + ICacheRefresher refresher = GetRefresher(cacheRefreshers, uniqueIdentifier); refresher.RefreshAll(); } - private void RefreshByGuid(Guid uniqueIdentifier, Guid id) + private void RefreshByGuid(CacheRefresherCollection cacheRefreshers, Guid uniqueIdentifier, Guid id) { - ICacheRefresher refresher = GetRefresher(uniqueIdentifier); + ICacheRefresher refresher = GetRefresher(cacheRefreshers, uniqueIdentifier); refresher.Refresh(id); } - private void RefreshById(Guid uniqueIdentifier, int id) + private void RefreshById(CacheRefresherCollection cacheRefreshers, Guid uniqueIdentifier, int id) { - ICacheRefresher refresher = GetRefresher(uniqueIdentifier); + ICacheRefresher refresher = GetRefresher(cacheRefreshers, uniqueIdentifier); refresher.Refresh(id); } - private void RefreshByIds(Guid uniqueIdentifier, string jsonIds) + private void RefreshByIds(CacheRefresherCollection cacheRefreshers, Guid uniqueIdentifier, string jsonIds) { - ICacheRefresher refresher = GetRefresher(uniqueIdentifier); + ICacheRefresher refresher = GetRefresher(cacheRefreshers, uniqueIdentifier); foreach (var id in JsonConvert.DeserializeObject(jsonIds)) { refresher.Refresh(id); } } - private void RefreshByJson(Guid uniqueIdentifier, string jsonPayload) + private void RefreshByJson(CacheRefresherCollection cacheRefreshers, Guid uniqueIdentifier, string jsonPayload) { - IJsonCacheRefresher refresher = GetJsonRefresher(uniqueIdentifier); + IJsonCacheRefresher refresher = GetJsonRefresher(cacheRefreshers, uniqueIdentifier); refresher.Refresh(jsonPayload); } - private void RemoveById(Guid uniqueIdentifier, int id) + private void RemoveById(CacheRefresherCollection cacheRefreshers, Guid uniqueIdentifier, int id) { - ICacheRefresher refresher = GetRefresher(uniqueIdentifier); + ICacheRefresher refresher = GetRefresher(cacheRefreshers, uniqueIdentifier); refresher.Remove(id); } - private ICacheRefresher GetRefresher(Guid id) + private ICacheRefresher GetRefresher(CacheRefresherCollection cacheRefreshers, Guid id) { - ICacheRefresher refresher = _cacheRefreshers[id]; + ICacheRefresher refresher = cacheRefreshers[id]; if (refresher == null) { throw new InvalidOperationException("Cache refresher with ID \"" + id + "\" does not exist."); @@ -424,7 +434,7 @@ namespace Umbraco.Cms.Core.Services.Implement return refresher; } - private IJsonCacheRefresher GetJsonRefresher(Guid id) => GetJsonRefresher(GetRefresher(id)); + private IJsonCacheRefresher GetJsonRefresher(CacheRefresherCollection cacheRefreshers, Guid id) => GetJsonRefresher(GetRefresher(cacheRefreshers, id)); private static IJsonCacheRefresher GetJsonRefresher(ICacheRefresher refresher) { diff --git a/src/Umbraco.Infrastructure/Services/Implement/ServerRegistrationService.cs b/src/Umbraco.Infrastructure/Services/Implement/ServerRegistrationService.cs index 9c03f9aabc..248de428a8 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ServerRegistrationService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ServerRegistrationService.cs @@ -154,7 +154,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// Gets the local server identity. /// - private string GetCurrentServerIdentity() => NetworkHelper.MachineName // eg DOMAIN\SERVER + private string GetCurrentServerIdentity() => Environment.MachineName // eg DOMAIN\SERVER + "/" + _hostingEnvironment.ApplicationId; // eg /LM/S3SVC/11/ROOT; } } diff --git a/src/Umbraco.Infrastructure/Suspendable.cs b/src/Umbraco.Infrastructure/Suspendable.cs index e96baa44e4..022a641094 100644 --- a/src/Umbraco.Infrastructure/Suspendable.cs +++ b/src/Umbraco.Infrastructure/Suspendable.cs @@ -2,7 +2,6 @@ using Microsoft.Extensions.Logging; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Infrastructure.Examine; -using Umbraco.Cms.Infrastructure.Search; namespace Umbraco.Cms.Infrastructure { @@ -81,7 +80,7 @@ namespace Umbraco.Cms.Infrastructure s_suspended = true; } - public static void ResumeIndexers(IndexRebuilder indexRebuilder, ILogger logger, BackgroundIndexRebuilder backgroundIndexRebuilder) + public static void ResumeIndexers(ExamineIndexRebuilder backgroundIndexRebuilder) { s_suspended = false; diff --git a/src/Umbraco.Infrastructure/Sync/BatchedDatabaseServerMessenger.cs b/src/Umbraco.Infrastructure/Sync/BatchedDatabaseServerMessenger.cs index 940ebfe0cd..9bae34cf3e 100644 --- a/src/Umbraco.Infrastructure/Sync/BatchedDatabaseServerMessenger.cs +++ b/src/Umbraco.Infrastructure/Sync/BatchedDatabaseServerMessenger.cs @@ -27,15 +27,18 @@ namespace Umbraco.Cms.Infrastructure.Sync /// public BatchedDatabaseServerMessenger( IMainDom mainDom, + CacheRefresherCollection cacheRefreshers, + IServerRoleAccessor serverRoleAccessor, ILogger logger, - DatabaseServerMessengerCallbacks callbacks, + ISyncBootStateAccessor syncBootStateAccessor, IHostingEnvironment hostingEnvironment, ICacheInstructionService cacheInstructionService, IJsonSerializer jsonSerializer, IRequestCache requestCache, IRequestAccessor requestAccessor, + LastSyncedFileManager lastSyncedFileManager, IOptions globalSettings) - : base(mainDom, logger, true, callbacks, hostingEnvironment, cacheInstructionService, jsonSerializer, globalSettings) + : base(mainDom, cacheRefreshers, serverRoleAccessor, logger, true, syncBootStateAccessor, hostingEnvironment, cacheInstructionService, jsonSerializer, lastSyncedFileManager, globalSettings) { _requestCache = requestCache; _requestAccessor = requestAccessor; diff --git a/src/Umbraco.Infrastructure/Sync/DatabaseServerMessenger.cs b/src/Umbraco.Infrastructure/Sync/DatabaseServerMessenger.cs index 0b2076a3a7..ee8793f5c9 100644 --- a/src/Umbraco.Infrastructure/Sync/DatabaseServerMessenger.cs +++ b/src/Umbraco.Infrastructure/Sync/DatabaseServerMessenger.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Globalization; -using System.IO; using System.Linq; using System.Threading; using Microsoft.Extensions.Logging; @@ -15,7 +13,6 @@ using Umbraco.Cms.Core.Runtime; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Sync; -using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Sync { @@ -31,57 +28,61 @@ namespace Umbraco.Cms.Infrastructure.Sync */ private readonly IMainDom _mainDom; + private readonly CacheRefresherCollection _cacheRefreshers; + private readonly IServerRoleAccessor _serverRoleAccessor; + private readonly ISyncBootStateAccessor _syncBootStateAccessor; private readonly ManualResetEvent _syncIdle; private readonly object _locko = new object(); private readonly IHostingEnvironment _hostingEnvironment; - - private readonly Lazy _distCacheFilePath; - private int _lastId = -1; + private readonly LastSyncedFileManager _lastSyncedFileManager; private DateTime _lastSync; private DateTime _lastPruned; - private readonly Lazy _initialized; + private readonly Lazy _initialized; private bool _syncing; - private bool _released; + private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); + private readonly CancellationToken _cancellationToken; /// /// Initializes a new instance of the class. /// protected DatabaseServerMessenger( IMainDom mainDom, + CacheRefresherCollection cacheRefreshers, + IServerRoleAccessor serverRoleAccessor, ILogger logger, bool distributedEnabled, - DatabaseServerMessengerCallbacks callbacks, + ISyncBootStateAccessor syncBootStateAccessor, IHostingEnvironment hostingEnvironment, ICacheInstructionService cacheInstructionService, IJsonSerializer jsonSerializer, + LastSyncedFileManager lastSyncedFileManager, IOptions globalSettings) : base(distributedEnabled) { + _cancellationToken = _cancellationTokenSource.Token; _mainDom = mainDom; + _cacheRefreshers = cacheRefreshers; + _serverRoleAccessor = serverRoleAccessor; _hostingEnvironment = hostingEnvironment; Logger = logger; - Callbacks = callbacks ?? throw new ArgumentNullException(nameof(callbacks)); + _syncBootStateAccessor = syncBootStateAccessor; CacheInstructionService = cacheInstructionService; JsonSerializer = jsonSerializer; + _lastSyncedFileManager = lastSyncedFileManager; GlobalSettings = globalSettings.Value; _lastPruned = _lastSync = DateTime.UtcNow; _syncIdle = new ManualResetEvent(true); - _distCacheFilePath = new Lazy(() => GetDistCacheFilePath(hostingEnvironment)); // See notes on _localIdentity - LocalIdentity = NetworkHelper.MachineName // eg DOMAIN\SERVER + LocalIdentity = Environment.MachineName // eg DOMAIN\SERVER + "/" + hostingEnvironment.ApplicationId // eg /LM/S3SVC/11/ROOT + " [P" + Process.GetCurrentProcess().Id // eg 1234 + "/D" + AppDomain.CurrentDomain.Id // eg 22 + "] " + Guid.NewGuid().ToString("N").ToUpper(); // make it truly unique - _initialized = new Lazy(EnsureInitialized); + _initialized = new Lazy(InitializeWithMainDom); } - private string DistCacheFilePath => _distCacheFilePath.Value; - - public DatabaseServerMessengerCallbacks Callbacks { get; } - public GlobalSettings GlobalSettings { get; } protected ILogger Logger { get; } @@ -102,12 +103,17 @@ namespace Umbraco.Cms.Infrastructure.Sync /// protected string LocalIdentity { get; } + /// + /// Returns true if initialization was successfull (i.e. Is MainDom) + /// + protected bool EnsureInitialized() => _initialized.Value.HasValue; + #region Messenger // we don't care if there are servers listed or not, // if distributed call is enabled we will make the call protected override bool RequiresDistributed(ICacheRefresher refresher, MessageType dispatchType) - => _initialized.Value && DistributedEnabled; + => EnsureInitialized() && DistributedEnabled; protected override void DeliverRemote( ICacheRefresher refresher, @@ -134,7 +140,7 @@ namespace Umbraco.Cms.Infrastructure.Sync /// /// Boots the messenger. /// - private bool EnsureInitialized() + private SyncBootState? InitializeWithMainDom() { // weight:10, must release *before* the published snapshot service, because once released // the service will *not* be able to properly handle our notifications anymore. @@ -145,15 +151,15 @@ namespace Umbraco.Cms.Infrastructure.Sync { lock (_locko) { - _released = true; // no more syncs + _cancellationTokenSource.Cancel(); // no more syncs } - // Wait a max of 5 seconds and then return, so that we don't block + // Wait a max of 3 seconds and then return, so that we don't block // the entire MainDom callbacks chain and prevent the AppDomain from // properly releasing MainDom - a timeout here means that one refresher // is taking too much time processing, however when it's done we will // not update lastId and stop everything. - var idle = _syncIdle.WaitOne(5000); + var idle = _syncIdle.WaitOne(3000); if (idle == false) { Logger.LogWarning("The wait lock timed out, application is shutting down. The current instruction batch will be re-processed."); @@ -163,17 +169,11 @@ namespace Umbraco.Cms.Infrastructure.Sync if (registered == false) { - return false; + // return null if we cannot initialize + return null; } - ReadLastSynced(); // get _lastId - - if (CacheInstructionService.IsColdBootRequired(_lastId)) - { - _lastId = -1; // reset _lastId if instructions are missing - } - - return Initialize(); // boot + return InitializeColdBootState(); } // @@ -183,70 +183,32 @@ namespace Umbraco.Cms.Infrastructure.Sync /// Thread safety: this is NOT thread safe. Because it is NOT meant to run multi-threaded. /// Callers MUST ensure thread-safety. /// - private bool Initialize() + private SyncBootState InitializeColdBootState() { lock (_locko) { - if (_released) + if (_cancellationToken.IsCancellationRequested) { - return false; + return SyncBootState.Unknown; } - var coldboot = false; + SyncBootState syncState = _syncBootStateAccessor.GetSyncBootState(); - // Never synced before. - if (_lastId < 0) - { - // We haven't synced - in this case we aren't going to sync the whole thing, we will assume this is a new - // server and it will need to rebuild it's own caches, e.g. Lucene or the XML cache file. - Logger.LogWarning("No last synced Id found, this generally means this is a new server/install." - + " The server will build its caches and indexes, and then adjust its last synced Id to the latest found in" - + " the database and maintain cache updates based on that Id."); - - coldboot = true; - } - else - { - // Check for how many instructions there are to process, each row contains a count of the number of instructions contained in each - // row so we will sum these numbers to get the actual count. - var limit = GlobalSettings.DatabaseServerMessenger.MaxProcessingInstructionCount; - if (CacheInstructionService.IsInstructionCountOverLimit(_lastId, limit, out int count)) - { - // Too many instructions, proceed to cold boot. - Logger.LogWarning( - "The instruction count ({InstructionCount}) exceeds the specified MaxProcessingInstructionCount ({MaxProcessingInstructionCount})." - + " The server will skip existing instructions, rebuild its caches and indexes entirely, adjust its last synced Id" - + " to the latest found in the database and maintain cache updates based on that Id.", - count, limit); - - coldboot = true; - } - } - - if (coldboot) + if (syncState == SyncBootState.ColdBoot) { // Get the last id in the db and store it. // Note: Do it BEFORE initializing otherwise some instructions might get lost // when doing it before. Some instructions might run twice but this is not an issue. var maxId = CacheInstructionService.GetMaxInstructionId(); - // If there is a max currently, or if we've never synced. - if (maxId > 0 || _lastId < 0) + // if there is a max currently, or if we've never synced + if (maxId > 0 || _lastSyncedFileManager.LastSyncedId < 0) { - SaveLastSynced(maxId); - } - - // Execute initializing callbacks. - if (Callbacks.InitializingCallbacks != null) - { - foreach (Action callback in Callbacks.InitializingCallbacks) - { - callback(); - } + _lastSyncedFileManager.SaveLastSyncedId(maxId); } } - return true; + return syncState; } } @@ -255,7 +217,7 @@ namespace Umbraco.Cms.Infrastructure.Sync /// public override void Sync() { - if (!_initialized.Value) + if (!EnsureInitialized()) { return; } @@ -268,7 +230,7 @@ namespace Umbraco.Cms.Infrastructure.Sync } // Don't continue if we are released - if (_released) + if (_cancellationToken.IsCancellationRequested) { return; } @@ -286,7 +248,14 @@ namespace Umbraco.Cms.Infrastructure.Sync try { - CacheInstructionServiceProcessInstructionsResult result = CacheInstructionService.ProcessInstructions(_released, LocalIdentity, _lastPruned, _lastId); + ProcessInstructionsResult result = CacheInstructionService.ProcessInstructions( + _cacheRefreshers, + _serverRoleAccessor.CurrentServerRole, + _cancellationToken, + LocalIdentity, + _lastPruned, + _lastSyncedFileManager.LastSyncedId); + if (result.InstructionsWerePruned) { _lastPruned = _lastSync; @@ -294,7 +263,7 @@ namespace Umbraco.Cms.Infrastructure.Sync if (result.LastId > 0) { - SaveLastSynced(result.LastId); + _lastSyncedFileManager.SaveLastSyncedId(result.LastId); } } finally @@ -309,60 +278,6 @@ namespace Umbraco.Cms.Infrastructure.Sync } } - /// - /// Reads the last-synced id from file into memory. - /// - /// - /// Thread safety: this is NOT thread safe. Because it is NOT meant to run multi-threaded. - /// - private void ReadLastSynced() - { - if (File.Exists(DistCacheFilePath) == false) - { - return; - } - - var content = File.ReadAllText(DistCacheFilePath); - if (int.TryParse(content, out var last)) - { - _lastId = last; - } - } - - /// - /// Updates the in-memory last-synced id and persists it to file. - /// - /// The id. - /// - /// Thread safety: this is NOT thread safe. Because it is NOT meant to run multi-threaded. - /// - private void SaveLastSynced(int id) - { - File.WriteAllText(DistCacheFilePath, id.ToString(CultureInfo.InvariantCulture)); - _lastId = id; - } - - private string GetDistCacheFilePath(IHostingEnvironment hostingEnvironment) - { - var fileName = _hostingEnvironment.ApplicationId.ReplaceNonAlphanumericChars(string.Empty) + "-lastsynced.txt"; - - var distCacheFilePath = Path.Combine(hostingEnvironment.LocalTempPath, "DistCache", fileName); - - //ensure the folder exists - var folder = Path.GetDirectoryName(distCacheFilePath); - if (folder == null) - { - throw new InvalidOperationException("The folder could not be determined for the file " + distCacheFilePath); - } - - if (Directory.Exists(folder) == false) - { - Directory.CreateDirectory(folder); - } - - return distCacheFilePath; - } - #endregion } } diff --git a/src/Umbraco.Infrastructure/Sync/LastSyncedFileManager.cs b/src/Umbraco.Infrastructure/Sync/LastSyncedFileManager.cs new file mode 100644 index 0000000000..3b3351fd93 --- /dev/null +++ b/src/Umbraco.Infrastructure/Sync/LastSyncedFileManager.cs @@ -0,0 +1,89 @@ +using System; +using System.Globalization; +using System.IO; +using System.Threading; +using Umbraco.Cms.Core.Hosting; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Infrastructure.Sync +{ + public sealed class LastSyncedFileManager + { + private string _distCacheFile; + private bool _lastIdReady; + private object _lastIdLock; + private int _lastId; + private readonly IHostingEnvironment _hostingEnvironment; + + public LastSyncedFileManager(IHostingEnvironment hostingEnvironment) + => _hostingEnvironment = hostingEnvironment; + + /// + /// Persists the last-synced id to file. + /// + /// The id. + public void SaveLastSyncedId(int id) + { + lock (_lastIdLock) + { + if (!_lastIdReady) + { + throw new InvalidOperationException("Cannot save the last synced id before it is read"); + } + + File.WriteAllText(DistCacheFilePath, id.ToString(CultureInfo.InvariantCulture)); + _lastId = id; + } + } + + /// + /// Returns the last-synced id. + /// + public int LastSyncedId => LazyInitializer.EnsureInitialized( + ref _lastId, + ref _lastIdReady, + ref _lastIdLock, + () => + { + // On first load, read from file, else it will return the in-memory _lastId value + + var distCacheFilePath = DistCacheFilePath; + + if (File.Exists(distCacheFilePath)) + { + var content = File.ReadAllText(distCacheFilePath); + if (int.TryParse(content, out var last)) + { + return last; + } + } + + return -1; + }); + + /// + /// Gets the dist cache file path (once). + /// + /// + public string DistCacheFilePath => LazyInitializer.EnsureInitialized(ref _distCacheFile, () => + { + var fileName = (Environment.MachineName + _hostingEnvironment.ApplicationId).GenerateHash() + "-lastsynced.txt"; + + var distCacheFilePath = Path.Combine(_hostingEnvironment.LocalTempPath, "DistCache", fileName); + + //ensure the folder exists + var folder = Path.GetDirectoryName(distCacheFilePath); + if (folder == null) + { + throw new InvalidOperationException("The folder could not be determined for the file " + distCacheFilePath); + } + + if (Directory.Exists(folder) == false) + { + Directory.CreateDirectory(folder); + } + + return distCacheFilePath; + }); + } +} diff --git a/src/Umbraco.Infrastructure/Sync/SyncBootStateAccessor.cs b/src/Umbraco.Infrastructure/Sync/SyncBootStateAccessor.cs new file mode 100644 index 0000000000..9a77c57965 --- /dev/null +++ b/src/Umbraco.Infrastructure/Sync/SyncBootStateAccessor.cs @@ -0,0 +1,84 @@ +using System.Threading; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Sync; + +namespace Umbraco.Cms.Infrastructure.Sync +{ + public class SyncBootStateAccessor : ISyncBootStateAccessor + { + private readonly ILogger _logger; + private readonly LastSyncedFileManager _lastSyncedFileManager; + private readonly GlobalSettings _globalSettings; + private readonly ICacheInstructionService _cacheInstructionService; + + private SyncBootState _syncBootState; + private bool _syncBootStateReady; + private object _syncBootStateLock; + + public SyncBootStateAccessor( + ILogger logger, + LastSyncedFileManager lastSyncedFileManager, + IOptions globalSettings, + ICacheInstructionService cacheInstructionService) + { + _logger = logger; + _lastSyncedFileManager = lastSyncedFileManager; + _globalSettings = globalSettings.Value; + _cacheInstructionService = cacheInstructionService; + } + + public SyncBootState GetSyncBootState() + => LazyInitializer.EnsureInitialized( + ref _syncBootState, + ref _syncBootStateReady, + ref _syncBootStateLock, + () => InitializeColdBootState(_lastSyncedFileManager.LastSyncedId)); + + private SyncBootState InitializeColdBootState(int lastId) + { + var coldboot = false; + + // Never synced before. + if (lastId < 0) + { + // We haven't synced - in this case we aren't going to sync the whole thing, we will assume this is a new + // server and it will need to rebuild it's own caches, e.g. Lucene or the XML cache file. + _logger.LogWarning("No last synced Id found, this generally means this is a new server/install. " + + "A cold boot will be triggered."); + + coldboot = true; + } + else + { + if (_cacheInstructionService.IsColdBootRequired(lastId)) + { + _logger.LogWarning("Last synced Id found {LastSyncedId} but was not found in the database. This generally means this server/install " + + " has been idle for too long and the instructions in the database have been pruned. A cold boot will be triggered.", lastId); + + coldboot = true; + } + else + { + // Check for how many instructions there are to process, each row contains a count of the number of instructions contained in each + // row so we will sum these numbers to get the actual count. + var limit = _globalSettings.DatabaseServerMessenger.MaxProcessingInstructionCount; + if (_cacheInstructionService.IsInstructionCountOverLimit(lastId, limit, out int count)) + { + // Too many instructions, proceed to cold boot. + _logger.LogWarning( + "The instruction count ({InstructionCount}) exceeds the specified MaxProcessingInstructionCount ({MaxProcessingInstructionCount}). " + + "A cold boot will be triggered.", + count, limit); + + coldboot = true; + } + } + } + + return coldboot ? SyncBootState.ColdBoot : SyncBootState.WarmBoot; + } + } +} diff --git a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj index 9dbacfa267..54970e58a9 100644 --- a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj +++ b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj @@ -49,7 +49,7 @@ - + all diff --git a/src/Umbraco.PublishedCache.NuCache/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.PublishedCache.NuCache/DependencyInjection/UmbracoBuilderExtensions.cs index 82e62b2328..113a2245d8 100644 --- a/src/Umbraco.PublishedCache.NuCache/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.PublishedCache.NuCache/DependencyInjection/UmbracoBuilderExtensions.cs @@ -29,9 +29,6 @@ namespace Umbraco.Extensions // must register default options, required in the service ctor builder.Services.TryAddTransient(factory => new PublishedSnapshotServiceOptions()); builder.SetPublishedSnapshotService(); - - // Add as itself - builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); // replace this service since we want to improve the content/media diff --git a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshot.cs b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshot.cs index ead279a199..fc4c64d552 100644 --- a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshot.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshot.cs @@ -14,9 +14,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache #region Constructors - public PublishedSnapshot(PublishedSnapshotService service, bool defaultPreview) + public PublishedSnapshot(IPublishedSnapshotService service, bool defaultPreview) { - _service = service; + _service = service as PublishedSnapshotService; _defaultPreview = defaultPreview; } @@ -38,7 +38,17 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache } } - private PublishedSnapshotElements Elements => _elements ?? (_elements = _service.GetElements(_defaultPreview)); + private PublishedSnapshotElements Elements + { + get + { + if (_service == null) + { + throw new InvalidOperationException($"The {typeof(PublishedSnapshot)} cannot be used when the {typeof(IPublishedSnapshotService)} is not the default type {typeof(PublishedSnapshotService)}"); + } + return _elements ??= _service.GetElements(_defaultPreview); + } + } public void Resync() { diff --git a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs index 916fb2da5e..9c08a2fc5a 100644 --- a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs @@ -19,6 +19,7 @@ using Umbraco.Cms.Core.Runtime; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.Changes; +using Umbraco.Cms.Core.Sync; using Umbraco.Cms.Infrastructure.PublishedCache.DataSource; using Umbraco.Cms.Infrastructure.PublishedCache.Persistence; using Umbraco.Extensions; @@ -29,6 +30,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache { internal class PublishedSnapshotService : IPublishedSnapshotService { + private readonly PublishedSnapshotServiceOptions _options; + private readonly ISyncBootStateAccessor _syncBootStateAccessor; + private readonly IMainDom _mainDom; private readonly ServiceContext _serviceContext; private readonly IPublishedContentTypeFactory _publishedContentTypeFactory; private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; @@ -39,7 +43,6 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache private readonly ILogger _logger; private readonly ILoggerFactory _loggerFactory; private readonly GlobalSettings _globalSettings; - private readonly IEntityXmlSerializer _entitySerializer; private readonly IPublishedModelFactory _publishedModelFactory; private readonly IDefaultCultureAccessor _defaultCultureAccessor; private readonly IHostingEnvironment _hostingEnvironment; @@ -49,9 +52,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache private bool _isReadSet; private object _isReadyLock; - private readonly ContentStore _contentStore; - private readonly ContentStore _mediaStore; - private readonly SnapDictionary _domainStore; + private ContentStore _contentStore; + private ContentStore _mediaStore; + private SnapDictionary _domainStore; private readonly object _storesLock = new object(); private readonly object _elementsLock = new object(); @@ -73,6 +76,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache public PublishedSnapshotService( PublishedSnapshotServiceOptions options, + ISyncBootStateAccessor syncBootStateAccessor, IMainDom mainDom, ServiceContext serviceContext, IPublishedContentTypeFactory publishedContentTypeFactory, @@ -84,11 +88,13 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache INuCacheContentService publishedContentService, IDefaultCultureAccessor defaultCultureAccessor, IOptions globalSettings, - IEntityXmlSerializer entitySerializer, IPublishedModelFactory publishedModelFactory, IHostingEnvironment hostingEnvironment, IOptions config) { + _options = options; + _syncBootStateAccessor = syncBootStateAccessor; + _mainDom = mainDom; _serviceContext = serviceContext; _publishedContentTypeFactory = publishedContentTypeFactory; _publishedSnapshotAccessor = publishedSnapshotAccessor; @@ -102,41 +108,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache _globalSettings = globalSettings.Value; _hostingEnvironment = hostingEnvironment; _config = config.Value; - - // we need an Xml serializer here so that the member cache can support XPath, - // for members this is done by navigating the serialized-to-xml member - _entitySerializer = entitySerializer; _publishedModelFactory = publishedModelFactory; - - // lock this entire call, we only want a single thread to be accessing the stores at once and within - // the call below to mainDom.Register, a callback may occur on a threadpool thread to MainDomRelease - // at the same time as we are trying to write to the stores. MainDomRelease also locks on _storesLock so - // it will not be able to close the stores until we are done populating (if the store is empty) - lock (_storesLock) - { - if (!options.IgnoreLocalDb) - { - mainDom.Register(MainDomRegister, MainDomRelease); - - // stores are created with a db so they can write to it, but they do not read from it, - // stores need to be populated, happens in OnResolutionFrozen which uses _localDbExists to - // figure out whether it can read the databases or it should populate them from sql - - _logger.LogInformation("Creating the content store, localContentDbExists? {LocalContentDbExists}", _localContentDbExists); - _contentStore = new ContentStore(_publishedSnapshotAccessor, _variationContextAccessor, _loggerFactory.CreateLogger("ContentStore"), _loggerFactory, _publishedModelFactory, _localContentDb); - _logger.LogInformation("Creating the media store, localMediaDbExists? {LocalMediaDbExists}", _localMediaDbExists); - _mediaStore = new ContentStore(_publishedSnapshotAccessor, _variationContextAccessor, _loggerFactory.CreateLogger("ContentStore"), _loggerFactory, _publishedModelFactory, _localMediaDb); - } - else - { - _logger.LogInformation("Creating the content store (local db ignored)"); - _contentStore = new ContentStore(_publishedSnapshotAccessor, _variationContextAccessor, _loggerFactory.CreateLogger("ContentStore"), _loggerFactory, _publishedModelFactory); - _logger.LogInformation("Creating the media store (local db ignored)"); - _mediaStore = new ContentStore(_publishedSnapshotAccessor, _variationContextAccessor, _loggerFactory.CreateLogger("ContentStore"), _loggerFactory, _publishedModelFactory); - } - - _domainStore = new SnapDictionary(); - } } protected PublishedSnapshot CurrentPublishedSnapshot => (PublishedSnapshot)_publishedSnapshotAccessor.PublishedSnapshot; @@ -144,13 +116,29 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // NOTE: These aren't used within this object but are made available internally to improve the IdKey lookup performance // when nucache is enabled. // TODO: Does this need to be here? - internal int GetDocumentId(Guid udi) => GetId(_contentStore, udi); + internal int GetDocumentId(Guid udi) + { + EnsureCaches(); + return GetId(_contentStore, udi); + } - internal int GetMediaId(Guid udi) => GetId(_mediaStore, udi); + internal int GetMediaId(Guid udi) + { + EnsureCaches(); + return GetId(_mediaStore, udi); + } - internal Guid GetDocumentUid(int id) => GetUid(_contentStore, id); + internal Guid GetDocumentUid(int id) + { + EnsureCaches(); + return GetUid(_contentStore, id); + } - internal Guid GetMediaUid(int id) => GetUid(_mediaStore, id); + internal Guid GetMediaUid(int id) + { + EnsureCaches(); + return GetUid(_mediaStore, id); + } private int GetId(ContentStore store, Guid uid) => store.LiveSnapshot.Get(uid)?.Id ?? 0; @@ -249,7 +237,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache } /// - /// Populates the stores + /// Lazily populates the stores only when they are first requested /// internal void EnsureCaches() => LazyInitializer.EnsureInitialized( ref _isReady, @@ -257,15 +245,43 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache ref _isReadyLock, () => { - // even though we are ready locked here we want to ensure that the stores lock is also locked + // lock this entire call, we only want a single thread to be accessing the stores at once and within + // the call below to mainDom.Register, a callback may occur on a threadpool thread to MainDomRelease + // at the same time as we are trying to write to the stores. MainDomRelease also locks on _storesLock so + // it will not be able to close the stores until we are done populating (if the store is empty) lock (_storesLock) { + if (!_options.IgnoreLocalDb) + { + _mainDom.Register(MainDomRegister, MainDomRelease); + + // stores are created with a db so they can write to it, but they do not read from it, + // stores need to be populated, happens in OnResolutionFrozen which uses _localDbExists to + // figure out whether it can read the databases or it should populate them from sql + + _logger.LogInformation("Creating the content store, localContentDbExists? {LocalContentDbExists}", _localContentDbExists); + _contentStore = new ContentStore(_publishedSnapshotAccessor, _variationContextAccessor, _loggerFactory.CreateLogger("ContentStore"), _loggerFactory, _publishedModelFactory, _localContentDb); + _logger.LogInformation("Creating the media store, localMediaDbExists? {LocalMediaDbExists}", _localMediaDbExists); + _mediaStore = new ContentStore(_publishedSnapshotAccessor, _variationContextAccessor, _loggerFactory.CreateLogger("ContentStore"), _loggerFactory, _publishedModelFactory, _localMediaDb); + } + else + { + _logger.LogInformation("Creating the content store (local db ignored)"); + _contentStore = new ContentStore(_publishedSnapshotAccessor, _variationContextAccessor, _loggerFactory.CreateLogger("ContentStore"), _loggerFactory, _publishedModelFactory); + _logger.LogInformation("Creating the media store (local db ignored)"); + _mediaStore = new ContentStore(_publishedSnapshotAccessor, _variationContextAccessor, _loggerFactory.CreateLogger("ContentStore"), _loggerFactory, _publishedModelFactory); + } + + _domainStore = new SnapDictionary(); + var okContent = false; var okMedia = false; + SyncBootState bootState = _syncBootStateAccessor.GetSyncBootState(); + try { - if (_localContentDbExists) + if (bootState != SyncBootState.ColdBoot && _localContentDbExists) { okContent = LockAndLoadContent(() => LoadContentFromLocalDbLocked(true)); if (!okContent) @@ -274,7 +290,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache } } - if (_localMediaDbExists) + if (bootState != SyncBootState.ColdBoot && _localMediaDbExists) { okMedia = LockAndLoadMedia(() => LoadMediaFromLocalDbLocked(true)); if (!okMedia) diff --git a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotStatus.cs b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotStatus.cs index df4f803006..6a75e3e021 100644 --- a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotStatus.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotStatus.cs @@ -11,9 +11,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache private readonly PublishedSnapshotService _service; private readonly INuCacheContentService _publishedContentService; - public PublishedSnapshotStatus(PublishedSnapshotService service, INuCacheContentService publishedContentService) + public PublishedSnapshotStatus(IPublishedSnapshotService service, INuCacheContentService publishedContentService) { - _service = service; + _service = service as PublishedSnapshotService; _publishedContentService = publishedContentService; } @@ -23,6 +23,12 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache /// public string GetStatus() { + if (_service == null) + { + return $"The current {typeof(IPublishedSnapshotService)} is not the default type. A status cannot be determined."; + } + + // TODO: This should be private _service.EnsureCaches(); var dbCacheIsOk = _publishedContentService.VerifyContentDbCache() diff --git a/src/Umbraco.Core/TaskHelper.cs b/src/Umbraco.Tests.Common/TaskHelper.cs similarity index 95% rename from src/Umbraco.Core/TaskHelper.cs rename to src/Umbraco.Tests.Common/TaskHelper.cs index ba9f865eba..8b22f7b47d 100644 --- a/src/Umbraco.Core/TaskHelper.cs +++ b/src/Umbraco.Tests.Common/TaskHelper.cs @@ -7,7 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -namespace Umbraco.Cms.Core +namespace Umbraco.Cms.Tests.Common { /// /// Helper class to not repeat common patterns with Task. @@ -24,7 +24,7 @@ namespace Umbraco.Cms.Core public void RunBackgroundTask(Func fn) => ExecuteBackgroundTask(fn); // for tests, returning the Task as a public API indicates it can be awaited that is not what we want to do - internal Task ExecuteBackgroundTask(Func fn) + public Task ExecuteBackgroundTask(Func fn) { // it is also possible to use UnsafeQueueUserWorkItem which does not flow the execution context, // however that seems more difficult to use for async operations. @@ -45,7 +45,7 @@ namespace Umbraco.Cms.Core public void RunLongRunningBackgroundTask(Func fn) => ExecuteLongRunningBackgroundTask(fn); // for tests, returning the Task as a public API indicates it can be awaited that is not what we want to do - internal Task ExecuteLongRunningBackgroundTask(Func fn) + public Task ExecuteLongRunningBackgroundTask(Func fn) { // it is also possible to use UnsafeQueueUserWorkItem which does not flow the execution context, // however that seems more difficult to use for async operations. diff --git a/src/Umbraco.Tests.Integration/Implementations/TestHostingEnvironment.cs b/src/Umbraco.Tests.Common/Testing/TestHostingEnvironment.cs similarity index 62% rename from src/Umbraco.Tests.Integration/Implementations/TestHostingEnvironment.cs rename to src/Umbraco.Tests.Common/Testing/TestHostingEnvironment.cs index 8980a91cff..e34161a3c2 100644 --- a/src/Umbraco.Tests.Integration/Implementations/TestHostingEnvironment.cs +++ b/src/Umbraco.Tests.Common/Testing/TestHostingEnvironment.cs @@ -7,15 +7,22 @@ using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Web.Common.AspNetCore; using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment; -namespace Umbraco.Cms.Tests.Integration.Implementations +namespace Umbraco.Cms.Tests.Common.Testing { - public class TestHostingEnvironment : AspNetCoreHostingEnvironment, Cms.Core.Hosting.IHostingEnvironment + public class TestHostingEnvironment : AspNetCoreHostingEnvironment, IHostingEnvironment { - public TestHostingEnvironment(IOptionsMonitor hostingSettings,IOptionsMonitor webRoutingSettings, IWebHostEnvironment webHostEnvironment) - : base(hostingSettings,webRoutingSettings, webHostEnvironment) + public TestHostingEnvironment( + IOptionsMonitor hostingSettings, + IOptionsMonitor webRoutingSettings, + IWebHostEnvironment webHostEnvironment) + : base(null, hostingSettings, webRoutingSettings, webHostEnvironment) { } + // override + string IHostingEnvironment.ApplicationId { get; } = "TestApplication"; + + /// /// Gets a value indicating whether we are hosted. /// diff --git a/src/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj b/src/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj index 373c319218..58527c4cfa 100644 --- a/src/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj +++ b/src/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj @@ -1,7 +1,7 @@ - + - netstandard2.0 + net5.0 Umbraco.Cms.Tests.Common Umbraco.Cms.Tests Umbraco CMS Test Tools @@ -24,5 +24,6 @@ + diff --git a/src/Umbraco.Tests.Integration/ComponentRuntimeTests.cs b/src/Umbraco.Tests.Integration/ComponentRuntimeTests.cs index ddac52872f..baa194b17e 100644 --- a/src/Umbraco.Tests.Integration/ComponentRuntimeTests.cs +++ b/src/Umbraco.Tests.Integration/ComponentRuntimeTests.cs @@ -10,6 +10,7 @@ using Umbraco.Cms.Core.Runtime; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Cms.Tests.Integration.Testing; +using Umbraco.Extensions; namespace Umbraco.Cms.Tests.Integration { @@ -18,7 +19,11 @@ namespace Umbraco.Cms.Tests.Integration public class ComponentRuntimeTests : UmbracoIntegrationTest { // ensure composers are added - protected override void CustomTestSetup(IUmbracoBuilder builder) => builder.AddComposers(); + protected override void CustomTestSetup(IUmbracoBuilder builder) + { + builder.AddNuCache(); + builder.AddComposers(); + } /// /// This will boot up umbraco with components enabled to show they initialize and shutdown diff --git a/src/Umbraco.Tests.Integration/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Tests.Integration/DependencyInjection/UmbracoBuilderExtensions.cs index 5cc94ce4c9..bb0da4d08a 100644 --- a/src/Umbraco.Tests.Integration/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Tests.Integration/DependencyInjection/UmbracoBuilderExtensions.cs @@ -2,8 +2,11 @@ // See LICENSE for more details. using System; +using System.Collections.Generic; using System.IO; using System.Linq; +using Examine; +using Examine.Lucene.Directories; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -21,7 +24,6 @@ using Umbraco.Cms.Core.WebAssets; using Umbraco.Cms.Infrastructure.Examine; using Umbraco.Cms.Infrastructure.HostedServices; using Umbraco.Cms.Infrastructure.PublishedCache; -using Umbraco.Cms.Infrastructure.Search; using Umbraco.Cms.Tests.Common.TestHelpers.Stubs; using Umbraco.Cms.Tests.Integration.Implementations; using Umbraco.Extensions; @@ -43,7 +45,7 @@ namespace Umbraco.Cms.Tests.Integration.DependencyInjection builder.Services.AddUnique(Mock.Of()); builder.Services.AddUnique(testHelper.MainDom); - builder.Services.AddUnique(); + builder.Services.AddUnique(); builder.Services.AddUnique(factory => Mock.Of()); // we don't want persisted nucache files in tests @@ -51,7 +53,7 @@ namespace Umbraco.Cms.Tests.Integration.DependencyInjection #if IS_WINDOWS // ensure all lucene indexes are using RAM directory (no file system) - builder.Services.AddUnique(); + builder.Services.AddUnique(); #endif // replace this service so that it can lookup the correct file locations @@ -97,18 +99,18 @@ namespace Umbraco.Cms.Tests.Integration.DependencyInjection } // replace the default so there is no background index rebuilder - private class TestBackgroundIndexRebuilder : BackgroundIndexRebuilder + private class TestBackgroundIndexRebuilder : ExamineIndexRebuilder { - public TestBackgroundIndexRebuilder( - IMainDom mainDom, - ILogger logger, - IndexRebuilder indexRebuilder, - IBackgroundTaskQueue backgroundTaskQueue) - : base(mainDom, logger, indexRebuilder, backgroundTaskQueue) + public TestBackgroundIndexRebuilder(IMainDom mainDom, IRuntimeState runtimeState, ILogger logger, IExamineManager examineManager, IEnumerable populators, IBackgroundTaskQueue backgroundTaskQueue) : base(mainDom, runtimeState, logger, examineManager, populators, backgroundTaskQueue) { } - public override void RebuildIndexes(bool onlyEmptyIndexes, TimeSpan? delay = null) + public override void RebuildIndex(string indexName, TimeSpan? delay = null, bool useBackgroundThread = true) + { + // noop + } + + public override void RebuildIndexes(bool onlyEmptyIndexes, TimeSpan? delay = null, bool useBackgroundThread = true) { // noop } diff --git a/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs b/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs index 8e897011d2..90c5e0eb02 100644 --- a/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs +++ b/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs @@ -12,8 +12,10 @@ using System.Reflection; using System.Threading; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Hosting.Internal; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Moq; @@ -31,6 +33,7 @@ using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Runtime; using Umbraco.Cms.Infrastructure.Persistence; using Umbraco.Cms.Tests.Common; +using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Cms.Web.Common.AspNetCore; using Umbraco.Extensions; using File = System.IO.File; diff --git a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs index 635a17a2b1..b91a034420 100644 --- a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs +++ b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs @@ -150,6 +150,7 @@ namespace Umbraco.Cms.Tests.Integration.TestServerTest .AddConfiguration() .AddUmbracoCore() .AddWebComponents() + .AddNuCache() .AddRuntimeMinifier() .AddBackOfficeCore() .AddBackOfficeAuthentication() diff --git a/src/Umbraco.Tests.Integration/Testing/IntegrationTestComponent.cs b/src/Umbraco.Tests.Integration/Testing/IntegrationTestComponent.cs index 277510fc9e..c0b490e0e5 100644 --- a/src/Umbraco.Tests.Integration/Testing/IntegrationTestComponent.cs +++ b/src/Umbraco.Tests.Integration/Testing/IntegrationTestComponent.cs @@ -2,7 +2,7 @@ // See LICENSE for more details. using Examine; -using Examine.LuceneEngine.Providers; +using Examine.Lucene.Providers; using Umbraco.Cms.Core.Composing; namespace Umbraco.Cms.Tests.Integration.Testing @@ -31,7 +31,7 @@ namespace Umbraco.Cms.Tests.Integration.Testing { if (index is LuceneIndex luceneIndex) { - luceneIndex.ProcessNonAsync(); + luceneIndex.WithThreadingMode(IndexThreadingMode.Synchronous); } } } diff --git a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs index dbf047cf48..f0eac637fd 100644 --- a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs +++ b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs @@ -138,12 +138,13 @@ namespace Umbraco.Cms.Tests.Integration.Testing Log.Logger = new LoggerConfiguration() .WriteTo.File(path, rollingInterval: RollingInterval.Day) + .MinimumLevel.Debug() .CreateLogger(); builder.AddSerilog(Log.Logger); }); case UmbracoTestOptions.Logger.Console: - return Microsoft.Extensions.Logging.LoggerFactory.Create(builder => builder.AddConsole()); + return Microsoft.Extensions.Logging.LoggerFactory.Create(builder => builder.AddConsole().SetMinimumLevel(LogLevel.Debug)); } } catch diff --git a/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineBaseTest.cs b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineBaseTest.cs new file mode 100644 index 0000000000..8840988ac6 --- /dev/null +++ b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineBaseTest.cs @@ -0,0 +1,135 @@ +using System; +using System.Data; +using Examine.Lucene.Providers; +using Examine.Search; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using NPoco; +using NUnit.Framework; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Persistence.Querying; +using Umbraco.Cms.Core.Scoping; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Infrastructure.Examine; +using Umbraco.Cms.Infrastructure.Persistence; +using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; +using Umbraco.Cms.Tests.Integration.Testing; + +namespace Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine +{ + [TestFixture] + public abstract class ExamineBaseTest : UmbracoIntegrationTest + { + protected IndexInitializer IndexInitializer => Services.GetRequiredService(); + + protected IHostingEnvironment HostingEnvironment => Services.GetRequiredService(); + + protected IRuntimeState RunningRuntimeState { get; } = Mock.Of(x => x.Level == RuntimeLevel.Run); + + public override void ConfigureServices(IServiceCollection services) + { + base.ConfigureServices(services); + services.AddSingleton(); + } + + /// + /// Used to create and manage a testable index + /// + /// + /// + /// + /// + /// + /// + protected IDisposable GetSynchronousContentIndex( + bool publishedValuesOnly, + out UmbracoContentIndex index, + out ContentIndexPopulator contentRebuilder, + out ContentValueSetBuilder contentValueSetBuilder, + int? parentId = null, + IContentService contentService = null) + { + contentValueSetBuilder = IndexInitializer.GetContentValueSetBuilder(publishedValuesOnly); + + ISqlContext sqlContext = Mock.Of(x => x.Query() == Mock.Of>()); + IUmbracoDatabaseFactory dbFactory = Mock.Of(x => x.SqlContext == sqlContext); + + if (contentService == null) + { + contentService = IndexInitializer.GetMockContentService(); + } + + contentRebuilder = IndexInitializer.GetContentIndexRebuilder(contentService, publishedValuesOnly, dbFactory); + + var luceneDir = new RandomIdRAMDirectory(); + + ContentValueSetValidator validator; + + // if only published values then we'll change the validator for tests to + // ensure we don't support protected nodes and that we + // mock the public access service for the special protected node. + if (publishedValuesOnly) + { + var publicAccessServiceMock = new Mock(); + publicAccessServiceMock.Setup(x => x.IsProtected(It.IsAny())) + .Returns((string path) => + { + if (path.EndsWith("," + ExamineDemoDataContentService.ProtectedNode)) + { + return Attempt.Succeed(); + } + return Attempt.Fail(); + }); + + var scopeProviderMock = new Mock(); + scopeProviderMock.Setup(x => x.CreateScope( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Returns(Mock.Of); + + validator = new ContentValueSetValidator( + publishedValuesOnly, + false, + publicAccessServiceMock.Object, + scopeProviderMock.Object, + parentId); + } + else + { + validator = new ContentValueSetValidator(publishedValuesOnly, parentId); + } + + index = IndexInitializer.GetUmbracoIndexer( + HostingEnvironment, + RunningRuntimeState, + luceneDir, + validator: validator); + + IDisposable syncMode = index.WithThreadingMode(IndexThreadingMode.Synchronous); + + return new DisposableWrapper(syncMode, index, luceneDir); + } + + private class DisposableWrapper : IDisposable + { + private readonly IDisposable[] _disposables; + + public DisposableWrapper(params IDisposable[] disposables) => _disposables = disposables; + + public void Dispose() + { + foreach (IDisposable d in _disposables) + { + d.Dispose(); + } + } + } + } +} diff --git a/src/Umbraco.Tests/UmbracoExamine/ExamineDemoDataContentService.cs b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineDemoDataContentService.cs similarity index 94% rename from src/Umbraco.Tests/UmbracoExamine/ExamineDemoDataContentService.cs rename to src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineDemoDataContentService.cs index ca11680f68..8323acf9bf 100644 --- a/src/Umbraco.Tests/UmbracoExamine/ExamineDemoDataContentService.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineDemoDataContentService.cs @@ -1,7 +1,7 @@ -using System.Xml.Linq; +using System.Xml.Linq; using System.Xml.XPath; -namespace Umbraco.Tests.UmbracoExamine +namespace Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine { // TODO: This is ultra hack and still left over from legacy but still works for testing atm public class ExamineDemoDataContentService diff --git a/src/Umbraco.Tests/UmbracoExamine/ExamineDemoDataMediaService.cs b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineDemoDataMediaService.cs similarity index 88% rename from src/Umbraco.Tests/UmbracoExamine/ExamineDemoDataMediaService.cs rename to src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineDemoDataMediaService.cs index 035a31b240..a7172248db 100644 --- a/src/Umbraco.Tests/UmbracoExamine/ExamineDemoDataMediaService.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineDemoDataMediaService.cs @@ -1,11 +1,11 @@ -using System; +using System; using System.IO; using System.Linq; using System.Text; using System.Xml.Linq; using System.Xml.XPath; -namespace Umbraco.Tests.UmbracoExamine +namespace Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine { // TODO: This is ultra hack and still left over from legacy but still works for testing atm internal class ExamineDemoDataMediaService diff --git a/src/Umbraco.Tests/UmbracoExamine/ExamineExtensions.cs b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineExtensions.cs similarity index 98% rename from src/Umbraco.Tests/UmbracoExamine/ExamineExtensions.cs rename to src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineExtensions.cs index 9cca58719e..ee8aea385f 100644 --- a/src/Umbraco.Tests/UmbracoExamine/ExamineExtensions.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineExtensions.cs @@ -1,11 +1,11 @@ -using Examine; +using Examine; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Xml.Linq; -namespace Umbraco.Tests.UmbracoExamine +namespace Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine { /// /// LEGACY!! Static methods to help query umbraco xml diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexInitializer.cs similarity index 53% rename from src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs rename to src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexInitializer.cs index 6dfc0a39ce..b7aa9fafe1 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexInitializer.cs @@ -1,11 +1,14 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Examine; +using Examine.Lucene; +using Examine.Lucene.Directories; using Lucene.Net.Analysis; using Lucene.Net.Analysis.Standard; using Lucene.Net.Store; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Moq; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Logging; @@ -14,48 +17,74 @@ using Umbraco.Cms.Core.Models.Membership; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Scoping; +using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Examine; using Umbraco.Cms.Infrastructure.Persistence; -using Umbraco.Tests.TestHelpers; using IContentService = Umbraco.Cms.Core.Services.IContentService; using IMediaService = Umbraco.Cms.Core.Services.IMediaService; -using Version = Lucene.Net.Util.Version; -namespace Umbraco.Tests.UmbracoExamine +namespace Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine { /// /// Used internally by test classes to initialize a new index from the template /// - internal static class IndexInitializer + public class IndexInitializer { - public static ContentValueSetBuilder GetContentValueSetBuilder(PropertyEditorCollection propertyEditors, IScopeProvider scopeProvider, bool publishedValuesOnly) + private readonly IShortStringHelper _shortStringHelper; + private readonly IJsonSerializer _jsonSerializer; + private readonly PropertyEditorCollection _propertyEditors; + private readonly IScopeProvider _scopeProvider; + private readonly ILoggerFactory _loggerFactory; + + public IndexInitializer( + IShortStringHelper shortStringHelper, + IJsonSerializer jsonSerializer, + PropertyEditorCollection propertyEditors, + IScopeProvider scopeProvider, + ILoggerFactory loggerFactory) + { + _shortStringHelper = shortStringHelper; + _jsonSerializer = jsonSerializer; + _propertyEditors = propertyEditors; + _scopeProvider = scopeProvider; + _loggerFactory = loggerFactory; + } + + public ContentValueSetBuilder GetContentValueSetBuilder(bool publishedValuesOnly) { var contentValueSetBuilder = new ContentValueSetBuilder( - propertyEditors, - new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider(TestHelper.ShortStringHelper) }), + _propertyEditors, + new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider(_shortStringHelper) }), GetMockUserService(), - TestHelper.ShortStringHelper, - scopeProvider, + _shortStringHelper, + _scopeProvider, publishedValuesOnly); return contentValueSetBuilder; } - public static ContentIndexPopulator GetContentIndexRebuilder(PropertyEditorCollection propertyEditors, IContentService contentService, IScopeProvider scopeProvider, IUmbracoDatabaseFactory umbracoDatabaseFactory, bool publishedValuesOnly) + public ContentIndexPopulator GetContentIndexRebuilder(IContentService contentService, bool publishedValuesOnly, IUmbracoDatabaseFactory umbracoDatabaseFactory) { - var contentValueSetBuilder = GetContentValueSetBuilder(propertyEditors, scopeProvider, publishedValuesOnly); - var contentIndexDataSource = new ContentIndexPopulator(publishedValuesOnly, null, contentService, umbracoDatabaseFactory, contentValueSetBuilder); + var contentValueSetBuilder = GetContentValueSetBuilder(publishedValuesOnly); + var contentIndexDataSource = new ContentIndexPopulator( + _loggerFactory.CreateLogger(), + publishedValuesOnly, + null, + contentService, + umbracoDatabaseFactory, + contentValueSetBuilder); return contentIndexDataSource; } - public static MediaIndexPopulator GetMediaIndexRebuilder(PropertyEditorCollection propertyEditors, IMediaService mediaService) + public MediaIndexPopulator GetMediaIndexRebuilder(IMediaService mediaService) { - var mediaValueSetBuilder = new MediaValueSetBuilder(propertyEditors, new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider(TestHelper.ShortStringHelper) }), GetMockUserService(), Mock.Of>(), TestHelper.ShortStringHelper, TestHelper.JsonSerializer); + var mediaValueSetBuilder = new MediaValueSetBuilder(_propertyEditors, new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider(_shortStringHelper) }), GetMockUserService(), Mock.Of>(), _shortStringHelper, _jsonSerializer); var mediaIndexDataSource = new MediaIndexPopulator(null, mediaService, mediaValueSetBuilder); return mediaIndexDataSource; } + public static IContentService GetMockContentService() { long longTotalRecs; @@ -64,23 +93,25 @@ namespace Umbraco.Tests.UmbracoExamine var allRecs = demoData.GetLatestContentByXPath("//*[@isDoc]") .Root .Elements() - .Select(x => Mock.Of( + .Select((xmlElement, index) => Mock.Of( m => - m.Id == (int)x.Attribute("id") && - m.ParentId == (int)x.Attribute("parentID") && - m.Level == (int)x.Attribute("level") && + m.Id == (int)xmlElement.Attribute("id") && + // have every second one published and include the special one + m.Published == ((ExamineDemoDataContentService.ProtectedNode == (int)xmlElement.Attribute("id")) || (index % 2 == 0 ? true : false)) && + m.ParentId == (int)xmlElement.Attribute("parentID") && + m.Level == (int)xmlElement.Attribute("level") && m.CreatorId == 0 && - m.SortOrder == (int)x.Attribute("sortOrder") && - m.CreateDate == (DateTime)x.Attribute("createDate") && - m.UpdateDate == (DateTime)x.Attribute("updateDate") && - m.Name == (string)x.Attribute(UmbracoExamineFieldNames.NodeNameFieldName) && - m.GetCultureName(It.IsAny()) == (string)x.Attribute(UmbracoExamineFieldNames.NodeNameFieldName) && - m.Path == (string)x.Attribute("path") && + m.SortOrder == (int)xmlElement.Attribute("sortOrder") && + m.CreateDate == (DateTime)xmlElement.Attribute("createDate") && + m.UpdateDate == (DateTime)xmlElement.Attribute("updateDate") && + m.Name == (string)xmlElement.Attribute(UmbracoExamineFieldNames.NodeNameFieldName) && + m.GetCultureName(It.IsAny()) == (string)xmlElement.Attribute(UmbracoExamineFieldNames.NodeNameFieldName) && + m.Path == (string)xmlElement.Attribute("path") && m.Properties == new PropertyCollection() && m.ContentType == Mock.Of(mt => mt.Icon == "test" && - mt.Alias == x.Name.LocalName && - mt.Id == (int)x.Attribute("nodeType")))) + mt.Alias == xmlElement.Name.LocalName && + mt.Id == (int)xmlElement.Attribute("nodeType")))) .ToArray(); @@ -90,10 +121,7 @@ namespace Umbraco.Tests.UmbracoExamine == allRecs); } - public static IUserService GetMockUserService() - { - return Mock.Of(x => x.GetProfileById(It.IsAny()) == Mock.Of(p => p.Id == 0 && p.Name == "admin")); - } + public IUserService GetMockUserService() => Mock.Of(x => x.GetProfileById(It.IsAny()) == Mock.Of(p => p.Id == 0 && p.Name == "admin")); public static IMediaService GetMockMediaService() { @@ -136,30 +164,23 @@ namespace Umbraco.Tests.UmbracoExamine return mediaServiceMock.Object; } - public static ILocalizationService GetMockLocalizationService() - { - return Mock.Of(x => x.GetAllLanguages() == Array.Empty()); - } + public ILocalizationService GetMockLocalizationService() => Mock.Of(x => x.GetAllLanguages() == Array.Empty()); - public static IMediaTypeService GetMockMediaTypeService() + public static IMediaTypeService GetMockMediaTypeService(IShortStringHelper shortStringHelper) { var mediaTypeServiceMock = new Mock(); mediaTypeServiceMock.Setup(x => x.GetAll()) .Returns(new List { - new MediaType(TestHelper.ShortStringHelper, -1) {Alias = "Folder", Name = "Folder", Id = 1031, Icon = "icon-folder"}, - new MediaType(TestHelper.ShortStringHelper, -1) {Alias = "Image", Name = "Image", Id = 1032, Icon = "icon-picture"} + new MediaType(shortStringHelper, -1) {Alias = "Folder", Name = "Folder", Id = 1031, Icon = "icon-folder"}, + new MediaType(shortStringHelper, -1) {Alias = "Image", Name = "Image", Id = 1032, Icon = "icon-picture"} }); return mediaTypeServiceMock.Object; } - public static IProfilingLogger GetMockProfilingLogger() - { - return new ProfilingLogger(Mock.Of>(), Mock.Of()); - } + public IProfilingLogger GetMockProfilingLogger() => new ProfilingLogger(Mock.Of>(), Mock.Of()); - public static UmbracoContentIndex GetUmbracoIndexer( - IProfilingLogger profilingLogger, + public UmbracoContentIndex GetUmbracoIndexer( IHostingEnvironment hostingEnvironment, IRuntimeState runtimeState, Directory luceneDir, @@ -171,40 +192,50 @@ namespace Umbraco.Tests.UmbracoExamine languageService = GetMockLocalizationService(); if (analyzer == null) - analyzer = new StandardAnalyzer(Version.LUCENE_30); + analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); if (validator == null) validator = new ContentValueSetValidator(true); - var i = new UmbracoContentIndex( + var options = GetOptions( "testIndexer", - luceneDir, - new UmbracoFieldDefinitionCollection(), - analyzer, - profilingLogger, - Mock.Of>(), - Mock.Of(), + new LuceneDirectoryIndexOptions + { + Analyzer = analyzer, + Validator = validator, + DirectoryFactory = new GenericDirectoryFactory(s => luceneDir), + FieldDefinitions = new UmbracoFieldDefinitionCollection() + }); + + var i = new UmbracoContentIndex( + _loggerFactory, + "testIndexer", + options, hostingEnvironment, runtimeState, - languageService, - validator); + languageService); i.IndexingError += IndexingError; + i.IndexOperationComplete += I_IndexOperationComplete; return i; } + private void I_IndexOperationComplete(object sender, IndexOperationEventArgs e) + { + + } + //public static MultiIndexSearcher GetMultiSearcher(Directory pdfDir, Directory simpleDir, Directory conventionDir, Directory cwsDir) //{ // var i = new MultiIndexSearcher("testSearcher", new[] { pdfDir, simpleDir, conventionDir, cwsDir }, new StandardAnalyzer(Version.LUCENE_29)); // return i; //} + public static IOptionsSnapshot GetOptions(string indexName, LuceneDirectoryIndexOptions options) + => Mock.Of>(x => x.Get(indexName) == options); - internal static void IndexingError(object sender, IndexingErrorEventArgs e) - { - throw new ApplicationException(e.Message, e.InnerException); - } + internal void IndexingError(object sender, IndexingErrorEventArgs e) => throw new ApplicationException(e.Message, e.Exception); } diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexTest.cs similarity index 54% rename from src/Umbraco.Tests/UmbracoExamine/IndexTest.cs rename to src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexTest.cs index 3daf185cd4..f6362a8156 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexTest.cs @@ -1,52 +1,67 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; -using System.Threading; using Examine; -using Examine.LuceneEngine.Providers; -using Lucene.Net.Index; -using Lucene.Net.Search; -using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; using NUnit.Framework; using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Infrastructure.Examine; +using Umbraco.Cms.Tests.Common.Builders; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Extensions; -using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.TestHelpers.Entities; -namespace Umbraco.Tests.UmbracoExamine +namespace Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine { + /// /// Tests the standard indexing capabilities /// [TestFixture] - [Apartment(ApartmentState.STA)] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] + [UmbracoTest(Database = UmbracoTestOptions.Database.None)] public class IndexTest : ExamineBaseTest { [Test] - public void Index_Property_Data_With_Value_Indexer() + public void GivenValidationParentNode_WhenContentIndexedUnderDifferentParent_DocumentIsNotIndexed() { - var contentValueSetBuilder = IndexInitializer.GetContentValueSetBuilder(Factory.GetRequiredService(), ScopeProvider, false); - - using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, HostingEnvironment, RuntimeState, luceneDir, - validator: new ContentValueSetValidator(false))) - using (indexer.ProcessNonAsync()) + using (GetSynchronousContentIndex(false, out UmbracoContentIndex index, out _, out _, 999)) { - indexer.CreateIndex(); + var searcher = index.Searcher; - var contentType = MockedContentTypes.CreateBasicContentType(); + var contentService = new ExamineDemoDataContentService(); + //get a node from the data repo + var node = contentService.GetPublishedContentByXPath("//*[string-length(@id)>0 and number(@id)>0]") + .Root + .Elements() + .First(); + + ValueSet valueSet = node.ConvertToValueSet(IndexTypes.Content); + + // Ignored since the path isn't under 999 + index.IndexItems(new[] { valueSet }); + Assert.AreEqual(0, searcher.CreateQuery().Id(valueSet.Id).Execute().TotalItemCount); + + // Change so that it's under 999 and verify + valueSet.Values["path"] = new List { "-1,999," + valueSet.Id }; + index.IndexItems(new[] { valueSet }); + Assert.AreEqual(1, searcher.CreateQuery().Id(valueSet.Id).Execute().TotalItemCount); + } + } + + [Test] + public void GivenIndexingDocument_WhenGridPropertyData_ThenDataIndexedInSegregatedFields() + { + using (GetSynchronousContentIndex(false, out UmbracoContentIndex index, out _, out ContentValueSetBuilder contentValueSetBuilder, null)) + { + index.CreateIndex(); + + ContentType contentType = ContentTypeBuilder.CreateBasicContentType(); contentType.AddPropertyType(new PropertyType(TestHelper.ShortStringHelper, "test", ValueStorageType.Ntext) { Alias = "grid", Name = "Grid", PropertyEditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.Grid }); - var content = MockedContent.CreateBasicContent(contentType); + Content content = ContentBuilder.CreateBasicContent(contentType); content.Id = 555; content.Path = "-1,555"; var gridVal = new GridValue @@ -100,15 +115,13 @@ namespace Umbraco.Tests.UmbracoExamine var json = JsonConvert.SerializeObject(gridVal); content.Properties["grid"].SetValue(json); - var valueSet = contentValueSetBuilder.GetValueSets(content); - indexer.IndexItems(valueSet); + IEnumerable valueSet = contentValueSetBuilder.GetValueSets(content); + index.IndexItems(valueSet); - var searcher = indexer.GetSearcher(); - - var results = searcher.CreateQuery().Id(555).Execute(); + ISearchResults results = index.Searcher.CreateQuery().Id(555).Execute(); Assert.AreEqual(1, results.TotalItemCount); - var result = results.First(); + ISearchResult result = results.First(); Assert.IsTrue(result.Values.ContainsKey("grid.row1")); Assert.AreEqual("value1", result.AllValues["grid.row1"][0]); Assert.AreEqual("value2", result.AllValues["grid.row1"][1]); @@ -120,95 +133,64 @@ namespace Umbraco.Tests.UmbracoExamine } [Test] - public void Rebuild_Index() - { - var contentRebuilder = IndexInitializer.GetContentIndexRebuilder(Factory.GetRequiredService(), IndexInitializer.GetMockContentService(), ScopeProvider, UmbracoDatabaseFactory,false); - var mediaRebuilder = IndexInitializer.GetMediaIndexRebuilder(Factory.GetRequiredService(), IndexInitializer.GetMockMediaService()); + public void GivenEmptyIndex_WhenUsingWithContentAndMediaPopulators_ThenIndexPopulated() + { + var mediaRebuilder = IndexInitializer.GetMediaIndexRebuilder(IndexInitializer.GetMockMediaService()); - using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, HostingEnvironment, RuntimeState, luceneDir, - validator: new ContentValueSetValidator(false))) - using (indexer.ProcessNonAsync()) + using (GetSynchronousContentIndex(false, out UmbracoContentIndex index, out ContentIndexPopulator contentRebuilder, out _, null)) { - - var searcher = indexer.GetSearcher(); - //create the whole thing - contentRebuilder.Populate(indexer); - mediaRebuilder.Populate(indexer); + contentRebuilder.Populate(index); + mediaRebuilder.Populate(index); - var result = searcher.CreateQuery().All().Execute(); + var result = index.Searcher.CreateQuery().All().Execute(); Assert.AreEqual(29, result.TotalItemCount); } } - ///// /// /// Check that the node signalled as protected in the content service is not present in the index. /// [Test] - public void Index_Protected_Content_Not_Indexed() + public void GivenPublishedContentIndex_WhenProtectedContentIndexed_ThenItIsIgnored() { - var rebuilder = IndexInitializer.GetContentIndexRebuilder(Factory.GetRequiredService(), IndexInitializer.GetMockContentService(), ScopeProvider, UmbracoDatabaseFactory,false); - - - using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, HostingEnvironment, RuntimeState, luceneDir)) - using (indexer.ProcessNonAsync()) - using (var searcher = ((LuceneSearcher)indexer.GetSearcher()).GetLuceneSearcher()) + using (GetSynchronousContentIndex(true, out UmbracoContentIndex index, out ContentIndexPopulator contentRebuilder, out _, null)) { //create the whole thing - rebuilder.Populate(indexer); + contentRebuilder.Populate(index); + Assert.Greater( + index.Searcher.CreateQuery().All().Execute().TotalItemCount, + 0); - var protectedQuery = new BooleanQuery(); - protectedQuery.Add( - new BooleanClause( - new TermQuery(new Term(ExamineFieldNames.CategoryFieldName, IndexTypes.Content)), - Occur.MUST)); - - protectedQuery.Add( - new BooleanClause( - new TermQuery(new Term(ExamineFieldNames.ItemIdFieldName, ExamineDemoDataContentService.ProtectedNode.ToString())), - Occur.MUST)); - - var collector = TopScoreDocCollector.Create(100, true); - - searcher.Search(protectedQuery, collector); - - Assert.AreEqual(0, collector.TotalHits, "Protected node should not be indexed"); + Assert.AreEqual( + 0, + index.Searcher.CreateQuery().Id(ExamineDemoDataContentService.ProtectedNode.ToString()).Execute().TotalItemCount); } - } [Test] - public void Index_Move_Media_From_Non_Indexable_To_Indexable_ParentID() + public void GivenMediaUnderNonIndexableParent_WhenMediaMovedUnderIndexableParent_ThenItIsIncludedInTheIndex() { // create a validator with // publishedValuesOnly false - // parentId 1116 (only content under that parent will be indexed) - var validator = new ContentValueSetValidator(false, 1116); - - using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, HostingEnvironment, RuntimeState, luceneDir, validator: validator)) - using (indexer.ProcessNonAsync()) + // parentId 1116 (only content under that parent will be indexed) + using (GetSynchronousContentIndex(false, out UmbracoContentIndex index, out ContentIndexPopulator contentRebuilder, out _, 1116)) { - var searcher = indexer.GetSearcher(); - //get a node from the data repo (this one exists underneath 2222) var node = _mediaService.GetLatestMediaByXpath("//*[string-length(@id)>0 and number(@id)>0]") .Root.Elements() - .First(x => (int) x.Attribute("id") == 2112); + .First(x => (int)x.Attribute("id") == 2112); var currPath = (string)node.Attribute("path"); //should be : -1,1111,2222,2112 Assert.AreEqual("-1,1111,2222,2112", currPath); //ensure it's indexed - indexer.IndexItem(node.ConvertToValueSet(IndexTypes.Media)); + index.IndexItem(node.ConvertToValueSet(IndexTypes.Media)); //it will not exist because it exists under 2222 - var results = searcher.CreateQuery().Id(2112).Execute(); + var results = index.Searcher.CreateQuery().Id(2112).Execute(); Assert.AreEqual(0, results.Count()); //now mimic moving 2112 to 1116 @@ -217,38 +199,34 @@ namespace Umbraco.Tests.UmbracoExamine node.SetAttributeValue("parentID", "1116"); //now reindex the node, this should first delete it and then WILL add it because of the parent id constraint - indexer.IndexItems(new[] { node.ConvertToValueSet(IndexTypes.Media) }); + index.IndexItems(new[] { node.ConvertToValueSet(IndexTypes.Media) }); //now ensure it exists - results = searcher.CreateQuery().Id(2112).Execute(); + results = index.Searcher.CreateQuery().Id(2112).Execute(); Assert.AreEqual(1, results.Count()); } } [Test] - public void Index_Move_Media_To_Non_Indexable_ParentID() + public void GivenMediaUnderIndexableParent_WhenMediaMovedUnderNonIndexableParent_ThenItIsRemovedFromTheIndex() { // create a validator with // publishedValuesOnly false // parentId 2222 (only content under that parent will be indexed) - var validator = new ContentValueSetValidator(false, 2222); - - using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer1 = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, HostingEnvironment, RuntimeState, luceneDir, validator: validator)) - using (indexer1.ProcessNonAsync()) + using (GetSynchronousContentIndex(false, out UmbracoContentIndex index, out ContentIndexPopulator contentRebuilder, out _, 2222)) { - var searcher = indexer1.GetSearcher(); + var searcher = index.Searcher; //get a node from the data repo (this one exists underneath 2222) var node = _mediaService.GetLatestMediaByXpath("//*[string-length(@id)>0 and number(@id)>0]") .Root.Elements() - .First(x => (int) x.Attribute("id") == 2112); + .First(x => (int)x.Attribute("id") == 2112); var currPath = (string)node.Attribute("path"); //should be : -1,1111,2222,2112 Assert.AreEqual("-1,1111,2222,2112", currPath); //ensure it's indexed - indexer1.IndexItem(node.ConvertToValueSet(IndexTypes.Media)); + index.IndexItem(node.ConvertToValueSet(IndexTypes.Media)); //it will exist because it exists under 2222 var results = searcher.CreateQuery().Id(2112).Execute(); @@ -259,7 +237,7 @@ namespace Umbraco.Tests.UmbracoExamine node.SetAttributeValue("parentID", "1116"); //now reindex the node, this should first delete it and then NOT add it because of the parent id constraint - indexer1.IndexItems(new[] { node.ConvertToValueSet(IndexTypes.Media) }); + index.IndexItems(new[] { node.ConvertToValueSet(IndexTypes.Media) }); //now ensure it's deleted results = searcher.CreateQuery().Id(2112).Execute(); @@ -273,38 +251,34 @@ namespace Umbraco.Tests.UmbracoExamine /// We then call the Examine method to re-index Content and do some comparisons to ensure that it worked correctly. /// [Test] - public void Index_Reindex_Content() + public void GivenEmptyIndex_WhenIndexedWithContentPopulator_ThenTheIndexIsPopulated() { - var rebuilder = IndexInitializer.GetContentIndexRebuilder(Factory.GetRequiredService(), IndexInitializer.GetMockContentService(), ScopeProvider, UmbracoDatabaseFactory,false); - using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, HostingEnvironment, RuntimeState, luceneDir, - validator: new ContentValueSetValidator(false))) - using (indexer.ProcessNonAsync()) + using (GetSynchronousContentIndex(false, out UmbracoContentIndex index, out ContentIndexPopulator contentRebuilder, out _, null)) { - - var searcher = indexer.GetSearcher(); - //create the whole thing - rebuilder.Populate(indexer); + contentRebuilder.Populate(index); - var result = searcher.CreateQuery().Field(ExamineFieldNames.CategoryFieldName, IndexTypes.Content).Execute(); + var result = index.Searcher + .CreateQuery() + .Field(ExamineFieldNames.CategoryFieldName, IndexTypes.Content) + .Execute(); Assert.AreEqual(21, result.TotalItemCount); //delete all content - foreach (var r in result) - { - indexer.DeleteFromIndex(r.Id); - } - + index.DeleteFromIndex(result.Select(x => x.Id)); //ensure it's all gone - result = searcher.CreateQuery().Field(ExamineFieldNames.CategoryFieldName, IndexTypes.Content).Execute(); + result = index.Searcher.CreateQuery().Field(ExamineFieldNames.CategoryFieldName, IndexTypes.Content).Execute(); Assert.AreEqual(0, result.TotalItemCount); //call our indexing methods - rebuilder.Populate(indexer); + contentRebuilder.Populate(index); + + result = index.Searcher + .CreateQuery() + .Field(ExamineFieldNames.CategoryFieldName, IndexTypes.Content) + .Execute(); - result = searcher.CreateQuery().Field(ExamineFieldNames.CategoryFieldName, IndexTypes.Content).Execute(); Assert.AreEqual(21, result.TotalItemCount); } } @@ -313,26 +287,24 @@ namespace Umbraco.Tests.UmbracoExamine /// This will delete an item from the index and ensure that all children of the node are deleted too! /// [Test] - public void Index_Delete_Index_Item_Ensure_Heirarchy_Removed() + public void GivenPopulatedIndex_WhenDocumentDeleted_ThenItsHierarchyIsAlsoDeleted() { - - var rebuilder = IndexInitializer.GetContentIndexRebuilder(Factory.GetRequiredService(), IndexInitializer.GetMockContentService(), ScopeProvider, UmbracoDatabaseFactory,false); - - using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, HostingEnvironment, RuntimeState, luceneDir)) - using (indexer.ProcessNonAsync()) + using (GetSynchronousContentIndex(false, out UmbracoContentIndex index, out ContentIndexPopulator contentRebuilder, out _, null)) { - var searcher = indexer.GetSearcher(); + var searcher = index.Searcher; //create the whole thing - rebuilder.Populate(indexer); + contentRebuilder.Populate(index); + + var results = searcher.CreateQuery().Id(1141).Execute(); + Assert.AreEqual(1, results.Count()); //now delete a node that has children - indexer.DeleteFromIndex(1140.ToString()); + index.DeleteFromIndex(1140.ToString()); //this node had children: 1141 & 1142, let's ensure they are also removed - var results = searcher.CreateQuery().Id(1141).Execute(); + results = searcher.CreateQuery().Id(1141).Execute(); Assert.AreEqual(0, results.Count()); results = searcher.CreateQuery().Id(1142).Execute(); diff --git a/src/Umbraco.Tests/Web/PublishedContentQueryTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/PublishedContentQueryTests.cs similarity index 62% rename from src/Umbraco.Tests/Web/PublishedContentQueryTests.cs rename to src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/PublishedContentQueryTests.cs index cc34cd4aba..f2269916a4 100644 --- a/src/Umbraco.Tests/Web/PublishedContentQueryTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/PublishedContentQueryTests.cs @@ -1,33 +1,43 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Examine; -using Examine.LuceneEngine.Providers; +using Examine.Lucene; +using Examine.Lucene.Directories; +using Examine.Lucene.Providers; using Lucene.Net.Store; +using Microsoft.Extensions.Logging; using Moq; using NUnit.Framework; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Infrastructure; using Umbraco.Cms.Infrastructure.Examine; -using Umbraco.Tests.TestHelpers; -using Umbraco.Web; +using Umbraco.Cms.Tests.Common.Testing; -namespace Umbraco.Tests.Web +namespace Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine { [TestFixture] - public class PublishedContentQueryTests + [UmbracoTest(Database = UmbracoTestOptions.Database.None)] + public class PublishedContentQueryTests : ExamineBaseTest { private class TestIndex : LuceneIndex, IUmbracoIndex { private readonly string[] _fieldNames; - public TestIndex(string name, Directory luceneDirectory, string[] fieldNames) - : base(name, luceneDirectory, null, null, null, null) + public TestIndex(ILoggerFactory loggerFactory, string name, Directory luceneDirectory, string[] fieldNames) + : base( + loggerFactory, + name, + IndexInitializer.GetOptions(name, new LuceneDirectoryIndexOptions + { + DirectoryFactory = new GenericDirectoryFactory(s => luceneDirectory) + })) { _fieldNames = fieldNames; } + public bool EnableDefaultEventHandler => throw new NotImplementedException(); public bool PublishedValuesOnly => throw new NotImplementedException(); public IEnumerable GetFields() => _fieldNames; @@ -35,29 +45,29 @@ namespace Umbraco.Tests.Web private TestIndex CreateTestIndex(Directory luceneDirectory, string[] fieldNames) { - var indexer = new TestIndex("TestIndex", luceneDirectory, fieldNames); + var index = new TestIndex(LoggerFactory, "TestIndex", luceneDirectory, fieldNames); - using (indexer.ProcessNonAsync()) + using (index.WithThreadingMode(IndexThreadingMode.Synchronous)) { //populate with some test data - indexer.IndexItem(new ValueSet("1", "content", new Dictionary + index.IndexItem(new ValueSet("1", "content", new Dictionary { [fieldNames[0]] = "Hello world, there are products here", [UmbracoExamineFieldNames.VariesByCultureFieldName] = "n" })); - indexer.IndexItem(new ValueSet("2", "content", new Dictionary + index.IndexItem(new ValueSet("2", "content", new Dictionary { [fieldNames[1]] = "Hello world, there are products here", [UmbracoExamineFieldNames.VariesByCultureFieldName] = "y" })); - indexer.IndexItem(new ValueSet("3", "content", new Dictionary + index.IndexItem(new ValueSet("3", "content", new Dictionary { [fieldNames[2]] = "Hello world, there are products here", [UmbracoExamineFieldNames.VariesByCultureFieldName] = "y" })); } - return indexer; + return index; } private PublishedContentQuery CreatePublishedContentQuery(IIndex indexer) @@ -75,10 +85,10 @@ namespace Umbraco.Tests.Web return new PublishedContentQuery(snapshot, variationContextAccessor, examineManager.Object); } - [TestCase("fr-fr", ExpectedResult = "1, 3", TestName = "Search Culture: fr-fr. Must return both fr-fr and invariant results")] - [TestCase("en-us", ExpectedResult = "1, 2", TestName = "Search Culture: en-us. Must return both en-us and invariant results")] - [TestCase("*", ExpectedResult = "1, 2, 3", TestName = "Search Culture: *. Must return all cultures and all invariant results")] - [TestCase(null, ExpectedResult = "1", TestName = "Search Culture: null. Must return only invariant results")] + [TestCase("fr-fr", ExpectedResult = "1, 3", Description = "Search Culture: fr-fr. Must return both fr-fr and invariant results")] + [TestCase("en-us", ExpectedResult = "1, 2", Description = "Search Culture: en-us. Must return both en-us and invariant results")] + [TestCase("*", ExpectedResult = "1, 2, 3", Description = "Search Culture: *. Must return all cultures and all invariant results")] + [TestCase(null, ExpectedResult = "1", Description = "Search Culture: null. Must return only invariant results")] public string Search(string culture) { using (var luceneDir = new RandomIdRAMDirectory()) diff --git a/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/RandomIdRAMDirectory.cs b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/RandomIdRAMDirectory.cs new file mode 100644 index 0000000000..3d8fc1f192 --- /dev/null +++ b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/RandomIdRAMDirectory.cs @@ -0,0 +1,11 @@ +using System; +using Lucene.Net.Store; + +namespace Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine +{ + public class RandomIdRAMDirectory : RAMDirectory + { + private readonly string _lockId = Guid.NewGuid().ToString(); + public override string GetLockID() => _lockId; + } +} diff --git a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/SearchTests.cs similarity index 69% rename from src/Umbraco.Tests/UmbracoExamine/SearchTests.cs rename to src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/SearchTests.cs index c4698fcdf2..2aefc593db 100644 --- a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/SearchTests.cs @@ -1,23 +1,22 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Examine; +using Examine.Lucene.Providers; using Examine.Search; -using Microsoft.Extensions.DependencyInjection; using Moq; using NUnit.Framework; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Querying; -using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.Examine; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Extensions; -namespace Umbraco.Tests.UmbracoExamine +namespace Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine { [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, Logger = UmbracoTestOptions.Logger.Console)] public class SearchTests : ExamineBaseTest { @@ -54,17 +53,14 @@ namespace Umbraco.Tests.UmbracoExamine == allRecs); - var propertyEditors = Factory.GetRequiredService(); - var rebuilder = IndexInitializer.GetContentIndexRebuilder(propertyEditors, contentService, ScopeProvider, UmbracoDatabaseFactory,true); - - using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, HostingEnvironment, RuntimeState, luceneDir)) - using (indexer.ProcessNonAsync()) + using (GetSynchronousContentIndex(false, out UmbracoContentIndex index, out ContentIndexPopulator contentRebuilder, out _, null, contentService)) { - indexer.CreateIndex(); - rebuilder.Populate(indexer); + index.CreateIndex(); + contentRebuilder.Populate(index); - var searcher = indexer.GetSearcher(); + var searcher = index.Searcher; + + Assert.Greater(searcher.CreateQuery().All().Execute().TotalItemCount, 0); var numberSortedCriteria = searcher.CreateQuery() .ParentId(1148) @@ -99,23 +95,5 @@ namespace Umbraco.Tests.UmbracoExamine return true; } - //[Test] - //public void Test_Index_Type_With_German_Analyzer() - //{ - // using (var luceneDir = new RandomIdRamDirectory()) - // { - // var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir, - // new GermanAnalyzer()); - // indexer.RebuildIndex(); - // var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir); - // } - //} - - //private readonly TestContentService _contentService = new TestContentService(); - //private readonly TestMediaService _mediaService = new TestMediaService(); - //private static UmbracoExamineSearcher _searcher; - //private static UmbracoContentIndexer _indexer; - //private Lucene.Net.Store.Directory _luceneDir; - } } diff --git a/src/Umbraco.Tests/UmbracoExamine/TestFiles.Designer.cs b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/TestFiles.Designer.cs similarity index 93% rename from src/Umbraco.Tests/UmbracoExamine/TestFiles.Designer.cs rename to src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/TestFiles.Designer.cs index b60dc487de..166d329208 100644 --- a/src/Umbraco.Tests/UmbracoExamine/TestFiles.Designer.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/TestFiles.Designer.cs @@ -8,7 +8,7 @@ // //------------------------------------------------------------------------------ -namespace Umbraco.Tests.UmbracoExamine { +namespace Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine { using System; @@ -19,7 +19,7 @@ namespace Umbraco.Tests.UmbracoExamine { // 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.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class TestFiles { @@ -39,7 +39,7 @@ namespace Umbraco.Tests.UmbracoExamine { 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.UmbracoExamine.TestFiles", typeof(TestFiles).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine.TestFiles", typeof(TestFiles).Assembly); resourceMan = temp; } return resourceMan; diff --git a/src/Umbraco.Tests/UmbracoExamine/TestFiles.resx b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/TestFiles.resx similarity index 96% rename from src/Umbraco.Tests/UmbracoExamine/TestFiles.resx rename to src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/TestFiles.resx index e23540252a..b5ed853136 100644 --- a/src/Umbraco.Tests/UmbracoExamine/TestFiles.resx +++ b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/TestFiles.resx @@ -1,4 +1,4 @@ - + - -## Umbraco version - -I am seeing this issue on Umbraco version: - - -Reproduction ------------- - -If you're filing a bug, please describe how to reproduce it. Include as much -relevant information as possible, such as: - -### Bug summary - - - -### Specifics - - - -### Steps to reproduce - - - -### Expected result - - - -### Actual result - - diff --git a/.github/ISSUE_TEMPLATE/2_Feature_request.md b/.github/ISSUE_TEMPLATE/2_Feature_request.md deleted file mode 100644 index 16ec2568dd..0000000000 --- a/.github/ISSUE_TEMPLATE/2_Feature_request.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -name: 📮 Feature Request -about: Open a feature request, if you want to propose a new feature. ---- - -A brief description of your feature request goes here. - - - - -How can you help? -------------------------------- - - diff --git a/.github/ISSUE_TEMPLATE/3_BugNetCore.md b/.github/ISSUE_TEMPLATE/3_BugNetCore.md deleted file mode 100644 index 989904d4d8..0000000000 --- a/.github/ISSUE_TEMPLATE/3_BugNetCore.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -name: 🌟 .Net Core Bug Report -about: For bugs specifically for the upcoming .NET Core release of Umbraco, don't use this if you're working with Umbraco version 7 or 8 -labels: project/net-core ---- - -ℹ️ If this bug **also** appears on the current version 8 of Umbraco then please [report it as a regular bug](https://github.com/umbraco/Umbraco-CMS/issues/new?template=1_Bug.md), fixes in version 8 will be merged to the .NET Core version. - -A brief description of the issue goes here. - - - - -Reproduction ------------- - -If you're filing a bug, please describe how to reproduce it. Include as much -relevant information as possible, such as: - -### Bug summary - - - -### Specifics - - - -### Steps to reproduce - - - -### Expected result - - - -### Actual result - - diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 37d1be9158..d5418ad270 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,4 +1,4 @@ -blank_issues_enabled: true +blank_issues_enabled: false contact_links: - name: ⁉️ Support Question url: https://our.umbraco.com @@ -8,4 +8,4 @@ contact_links: about: Documentation issues should be reported on the Umbraco documentation repository. - name: 🔐 Security Issue url: https://umbraco.com/about-us/trust-center/security-and-umbraco/how-to-report-a-vulnerability-in-umbraco/ - about: Discovered a Security Issue in Umbraco? \ No newline at end of file + about: Discovered a Security Issue in Umbraco? From f63c85a16c427d4357d4be2d17f4c99db7364aa1 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 18 May 2021 19:07:17 +0200 Subject: [PATCH 103/182] Update version and artifact name generated by azure pipeline (#10277) * Update version and artifact name generated by azure pipeline * Fix issue with version * Add build tags * test nightly builds * fix negation + Prefix with Umbraco * use updatereleasename instead * test if build number is the issue * determine releases by tags * use date and rev instead of build nuber * build id * more testing * more test * more test * build without buildId if ResourceTrigger is reason * more test * more trials * determine build type from source branch regex * test * test * test * test * test * test * test * use date in nightlies/previews * use date in nightlies/previews * addd build id to nightlies --- build/azure-pipelines.yml | 55 +++--- src/Umbraco.Web.UI.Client/package-lock.json | 198 +++++--------------- 2 files changed, 79 insertions(+), 174 deletions(-) diff --git a/build/azure-pipelines.yml b/build/azure-pipelines.yml index e05dfc8974..893c62509a 100644 --- a/build/azure-pipelines.yml +++ b/build/azure-pipelines.yml @@ -1,3 +1,4 @@ +name: $(TeamProject)_$(Build.DefinitionName)_$(SourceBranchName)_$(Date:yyyyMMdd)$(Rev:.r) variables: buildConfiguration: Release SA_PASSWORD: UmbracoIntegration123! @@ -321,8 +322,7 @@ stages: restoreSolution: '*\src\umbraco.sln' feedsToUse: config - task: PowerShell@1 - displayName: Update Version - condition: 'eq(variables[''Umbraco.IsReleaseBuild''], ''false'')' + displayName: Update Version and Artifact Name inputs: scriptType: inlineScript inlineScript: > @@ -333,40 +333,45 @@ stages: $version = $ubuild.GetUmbracoVersion() - if ($version.Comment -ne "") - { - # 8.0.0-beta.33.1234 - $continuous = "$($version.Semver).$(Build.BuildNumber)" + $isRelease = [regex]::matches($env:BUILD_SOURCEBRANCH,"v\d+\/\d+.\d+.*") + + + if ($isRelease.Count -gt 0){ + $continuous = $version.Semver + Write-Host "##vso[build.addbuildtag]Release build" } else { - # 8.0.0-alpha.1234 - $continuous = "$($version.Release)-alpha.$(Build.BuildNumber)" - } - $ubuild.SetUmbracoVersion($continuous) + $date = (Get-Date).ToString("yyyyMMdd") + $continuous = "$($version.release)-preview$date.$(Build.BuildId)" + $ubuild.SetUmbracoVersion($continuous) + + #Update the version in templates also + + $templatePath = + 'build/templates/UmbracoProject/.template.config/template.json' + + $a = Get-Content $templatePath -raw | ConvertFrom-Json + + $a.symbols.version.defaultValue = $continuous + + $a | ConvertTo-Json -depth 32| set-content $templatePath - #Update the version in templates also + $templatePath = + 'build/templates/UmbracoPackage/.template.config/template.json' - $templatePath = - 'build/templates/UmbracoProject/.template.config/template.json' + $a = Get-Content $templatePath -raw | ConvertFrom-Json - $a = Get-Content $templatePath -raw | ConvertFrom-Json + $a.symbols.version.defaultValue = $continuous - $a.symbols.version.defaultValue = $continuous + $a | ConvertTo-Json -depth 32| set-content $templatePath + Write-Host "##vso[build.addbuildtag]Continuous build" - $a | ConvertTo-Json -depth 32| set-content $templatePath + } - $templatePath = - 'build/templates/UmbracoPackage/.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 "##vso[build.updatebuildnumber]$continuous.$(Build.BuildId)" Write-Host "Building: $continuous" - task: PowerShell@1 diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 6f2edfa2ae..9bcf82dee7 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -1842,8 +1842,7 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "optional": true + "dev": true }, "base64id": { "version": "1.0.0", @@ -2052,8 +2051,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true, - "optional": true + "dev": true }, "got": { "version": "8.3.2", @@ -2131,7 +2129,6 @@ "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz", "integrity": "sha1-2N0ZeVldLcATnh/ka4tkbLPN8Dg=", "dev": true, - "optional": true, "requires": { "p-finally": "^1.0.0" } @@ -2173,7 +2170,6 @@ "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", "dev": true, - "optional": true, "requires": { "readable-stream": "^2.3.5", "safe-buffer": "^5.1.1" @@ -2183,15 +2179,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true, - "optional": true + "dev": true }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, - "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -2207,7 +2201,6 @@ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, - "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -2348,7 +2341,6 @@ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, - "optional": true, "requires": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -2374,8 +2366,7 @@ "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", - "dev": true, - "optional": true + "dev": true }, "buffer-equal": { "version": "1.0.0", @@ -2572,7 +2563,6 @@ "resolved": "https://registry.npmjs.org/caw/-/caw-2.0.1.tgz", "integrity": "sha1-bDygcfwZRyCIPC3F2psHS/x+npU=", "dev": true, - "optional": true, "requires": { "get-proxy": "^2.0.0", "isurl": "^1.0.0-alpha5", @@ -3096,7 +3086,6 @@ "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", "integrity": "sha1-D96NCRIA616AjK8l/mGMAvSOTvo=", "dev": true, - "optional": true, "requires": { "ini": "^1.3.4", "proto-list": "~1.2.1" @@ -3152,7 +3141,6 @@ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", "integrity": "sha1-4TDK9+cnkIfFYWwgB9BIVpiYT70=", "dev": true, - "optional": true, "requires": { "safe-buffer": "5.1.2" } @@ -3594,7 +3582,6 @@ "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz", "integrity": "sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==", "dev": true, - "optional": true, "requires": { "decompress-tar": "^4.0.0", "decompress-tarbz2": "^4.0.0", @@ -3611,7 +3598,6 @@ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", "integrity": "sha1-ecEDO4BRW9bSTsmTPoYMp17ifww=", "dev": true, - "optional": true, "requires": { "pify": "^3.0.0" }, @@ -3620,8 +3606,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true, - "optional": true + "dev": true } } } @@ -3632,7 +3617,6 @@ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", "dev": true, - "optional": true, "requires": { "mimic-response": "^1.0.0" } @@ -3642,7 +3626,6 @@ "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", "integrity": "sha1-cYy9P8sWIJcW5womuE57pFkuWvE=", "dev": true, - "optional": true, "requires": { "file-type": "^5.2.0", "is-stream": "^1.1.0", @@ -3653,8 +3636,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=", - "dev": true, - "optional": true + "dev": true } } }, @@ -3663,7 +3645,6 @@ "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", "integrity": "sha1-MIKluIDqQEOBY0nzeLVsUWvho5s=", "dev": true, - "optional": true, "requires": { "decompress-tar": "^4.1.0", "file-type": "^6.1.0", @@ -3676,8 +3657,7 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", "integrity": "sha1-5QzXXTVv/tTjBtxPW89Sp5kDqRk=", - "dev": true, - "optional": true + "dev": true } } }, @@ -3686,7 +3666,6 @@ "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", "integrity": "sha1-wJvDXE0R894J8tLaU+neI+fOHu4=", "dev": true, - "optional": true, "requires": { "decompress-tar": "^4.1.1", "file-type": "^5.2.0", @@ -3697,8 +3676,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=", - "dev": true, - "optional": true + "dev": true } } }, @@ -3707,7 +3685,6 @@ "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", "integrity": "sha1-3qrM39FK6vhVePczroIQ+bSEj2k=", "dev": true, - "optional": true, "requires": { "file-type": "^3.8.0", "get-stream": "^2.2.0", @@ -3719,15 +3696,13 @@ "version": "3.9.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=", - "dev": true, - "optional": true + "dev": true }, "get-stream": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=", "dev": true, - "optional": true, "requires": { "object-assign": "^4.0.1", "pinkie-promise": "^2.0.0" @@ -3737,8 +3712,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, - "optional": true + "dev": true } } }, @@ -4026,8 +4000,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true, - "optional": true + "dev": true } } }, @@ -4044,8 +4017,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "dev": true, - "optional": true + "dev": true }, "duplexify": { "version": "3.7.1", @@ -4690,7 +4662,6 @@ "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", "dev": true, - "optional": true, "requires": { "cross-spawn": "^6.0.0", "get-stream": "^4.0.0", @@ -4832,7 +4803,6 @@ "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", "integrity": "sha1-C5jmTtgvWs8PKTG6v2khLvUt3Tc=", "dev": true, - "optional": true, "requires": { "mime-db": "^1.28.0" } @@ -4842,7 +4812,6 @@ "resolved": "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz", "integrity": "sha1-cHgZgdGD7hXROZPIgiBFxQbI8KY=", "dev": true, - "optional": true, "requires": { "ext-list": "^2.0.0", "sort-keys-length": "^1.0.0" @@ -5080,7 +5049,6 @@ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", "dev": true, - "optional": true, "requires": { "pend": "~1.2.0" } @@ -5119,15 +5087,13 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", "integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=", - "dev": true, - "optional": true + "dev": true }, "filenamify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-2.1.0.tgz", "integrity": "sha1-iPr0lfsbR6v9YSMAACoWIoxnfuk=", "dev": true, - "optional": true, "requires": { "filename-reserved-regex": "^2.0.0", "strip-outer": "^1.0.0", @@ -5476,8 +5442,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha1-a+Dem+mYzhavivwkSXue6bfM2a0=", - "dev": true, - "optional": true + "dev": true }, "fs-mkdirp-stream": { "version": "1.0.0", @@ -5524,8 +5489,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -5546,14 +5510,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5568,20 +5530,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -5698,8 +5657,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -5711,7 +5669,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -5726,7 +5683,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5734,14 +5690,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -5760,7 +5714,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -5841,8 +5794,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -5854,7 +5806,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -5940,8 +5891,7 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -5977,7 +5927,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -5997,7 +5946,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6041,14 +5989,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -6075,7 +6021,6 @@ "resolved": "https://registry.npmjs.org/get-proxy/-/get-proxy-2.1.0.tgz", "integrity": "sha1-NJ8rTZHUTE1NTpy6KtkBQ/rF75M=", "dev": true, - "optional": true, "requires": { "npm-conf": "^1.1.0" } @@ -6084,15 +6029,13 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true, - "optional": true + "dev": true }, "get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dev": true, - "optional": true, "requires": { "pump": "^3.0.0" }, @@ -6102,7 +6045,6 @@ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, - "optional": true, "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -6215,8 +6157,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "optional": true + "dev": true }, "pump": { "version": "3.0.0", @@ -7419,8 +7360,7 @@ "version": "1.4.2", "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", "integrity": "sha1-FAn5i8ACR9pF2mfO4KNvKC/yZFU=", - "dev": true, - "optional": true + "dev": true }, "has-symbols": { "version": "1.0.0", @@ -7433,7 +7373,6 @@ "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", "integrity": "sha1-oEWrOD17SyASoAFIqwql8pAETU0=", "dev": true, - "optional": true, "requires": { "has-symbol-support-x": "^1.4.1" } @@ -7639,8 +7578,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "optional": true + "dev": true }, "ignore": { "version": "4.0.6", @@ -7780,8 +7718,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true + "dev": true }, "svgo": { "version": "1.3.2", @@ -7853,7 +7790,6 @@ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", "dev": true, - "optional": true, "requires": { "repeating": "^2.0.0" } @@ -8180,8 +8116,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", - "dev": true, - "optional": true + "dev": true }, "is-fullwidth-code-point": { "version": "1.0.0", @@ -8231,8 +8166,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", "integrity": "sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=", - "dev": true, - "optional": true + "dev": true }, "is-negated-glob": { "version": "1.0.0", @@ -8270,15 +8204,13 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.2.tgz", "integrity": "sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==", - "dev": true, - "optional": true + "dev": true }, "is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true, - "optional": true + "dev": true }, "is-plain-object": { "version": "2.0.4", @@ -8348,15 +8280,13 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", "integrity": "sha1-13hIi9CkZmo76KFIK58rqv7eqLQ=", - "dev": true, - "optional": true + "dev": true }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true, - "optional": true + "dev": true }, "is-svg": { "version": "3.0.0", @@ -8451,7 +8381,6 @@ "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", "integrity": "sha1-sn9PSfPNqj6kSgpbfzRi5u3DnWc=", "dev": true, - "optional": true, "requires": { "has-to-string-tag-x": "^1.2.0", "is-object": "^1.0.1" @@ -9347,8 +9276,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", "integrity": "sha1-b54wtHCE2XGnyCD/FabFFnt0wm8=", - "dev": true, - "optional": true + "dev": true }, "lpad-align": { "version": "1.1.2", @@ -9418,8 +9346,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true, - "optional": true + "dev": true }, "map-visit": { "version": "1.0.0", @@ -9587,8 +9514,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", "integrity": "sha1-SSNTiHju9CBjy4o+OweYeBSHqxs=", - "dev": true, - "optional": true + "dev": true }, "minimatch": { "version": "3.0.4", @@ -12935,7 +12861,6 @@ "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", "integrity": "sha1-JWzEe9DiGMJZxOlVC/QTvCGSr/k=", "dev": true, - "optional": true, "requires": { "config-chain": "^1.1.11", "pify": "^3.0.0" @@ -12945,8 +12870,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true, - "optional": true + "dev": true } } }, @@ -12955,7 +12879,6 @@ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, - "optional": true, "requires": { "path-key": "^2.0.0" } @@ -13324,8 +13247,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true, - "optional": true + "dev": true }, "p-is-promise": { "version": "1.1.0", @@ -13362,7 +13284,6 @@ "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", "dev": true, - "optional": true, "requires": { "p-finally": "^1.0.0" } @@ -13553,8 +13474,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", - "dev": true, - "optional": true + "dev": true }, "performance-now": { "version": "2.1.0", @@ -14061,8 +13981,7 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", - "dev": true, - "optional": true + "dev": true }, "prr": { "version": "1.0.1", @@ -14420,7 +14339,6 @@ "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "dev": true, - "optional": true, "requires": { "is-finite": "^1.0.0" } @@ -14775,7 +14693,6 @@ "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz", "integrity": "sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==", "dev": true, - "optional": true, "requires": { "commander": "^2.8.1" } @@ -15170,7 +15087,6 @@ "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", "dev": true, - "optional": true, "requires": { "is-plain-obj": "^1.0.0" } @@ -15180,7 +15096,6 @@ "resolved": "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz", "integrity": "sha1-nLb09OnkgVWmqgZx7dM2/xR5oYg=", "dev": true, - "optional": true, "requires": { "sort-keys": "^1.0.0" } @@ -15528,7 +15443,6 @@ "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", "integrity": "sha1-SYdzYmT8NEzyD2w0rKnRPR1O1sU=", "dev": true, - "optional": true, "requires": { "is-natural-number": "^4.0.1" } @@ -15537,8 +15451,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true, - "optional": true + "dev": true }, "strip-final-newline": { "version": "2.0.0", @@ -15568,7 +15481,6 @@ "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", "integrity": "sha1-sv0qv2YEudHmATBXGV34Nrip1jE=", "dev": true, - "optional": true, "requires": { "escape-string-regexp": "^1.0.2" } @@ -15694,7 +15606,6 @@ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", "integrity": "sha1-jqVdqzeXIlPZqa+Q/c1VmuQ1xVU=", "dev": true, - "optional": true, "requires": { "bl": "^1.0.0", "buffer-alloc": "^1.2.0", @@ -15709,15 +15620,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true, - "optional": true + "dev": true }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, - "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -15733,7 +15642,6 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, - "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -15744,15 +15652,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", "integrity": "sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=", - "dev": true, - "optional": true + "dev": true }, "tempfile": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-2.0.0.tgz", "integrity": "sha1-awRGhWqbERTRhW/8vlCczLCXcmU=", "dev": true, - "optional": true, "requires": { "temp-dir": "^1.0.0", "uuid": "^3.0.1" @@ -15866,8 +15772,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", - "dev": true, - "optional": true + "dev": true }, "timers-ext": { "version": "0.1.7", @@ -15924,8 +15829,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", "integrity": "sha1-STvUj2LXxD/N7TE6A9ytsuEhOoA=", - "dev": true, - "optional": true + "dev": true }, "to-fast-properties": { "version": "2.0.0", @@ -16027,7 +15931,6 @@ "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", "dev": true, - "optional": true, "requires": { "escape-string-regexp": "^1.0.2" } @@ -16163,7 +16066,6 @@ "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", "dev": true, - "optional": true, "requires": { "buffer": "^5.2.1", "through": "^2.3.8" @@ -16372,8 +16274,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=", - "dev": true, - "optional": true + "dev": true }, "use": { "version": "3.1.1", @@ -16867,7 +16768,6 @@ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", "dev": true, - "optional": true, "requires": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" From 4c78702da033a26c94f06a303b4fc12b0491998a Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 18 May 2021 19:19:58 +0200 Subject: [PATCH 104/182] Do not restart after install, instead stop and restart the runtime. Also signin the admin user after install. --- src/Umbraco.Core/Services/IRuntime.cs | 3 ++ .../Runtime/CoreRuntime.cs | 2 + .../AutoFixture/AutoMoqDataAttribute.cs | 2 +- .../Umbraco.Web.Common/FileNameTests.cs | 2 +- .../Routing/InstallAreaRoutesTests.cs | 2 +- .../Controllers/PackageInstallController.cs | 1 + .../UmbracoBuilderExtensions.cs | 9 +++++ .../Extensions/LinkGeneratorExtensions.cs | 25 ++++++++++++ .../UmbracoApplicationBuilder.Installer.cs | 2 +- ...CreateUnattendedUserNotificationHandler.cs | 2 +- .../Install/InstallApiController.cs | 35 +++++++++++++---- .../Install/InstallAreaRoutes.cs | 2 +- .../Install/InstallAuthorizeAttribute.cs | 2 +- .../Install/InstallController.cs | 4 +- .../Routing/BackOfficeAreaRoutes.cs | 5 --- .../Routing/PreviewRoutes.cs | 2 - .../UmbracoBuilderExtensions.cs | 8 ---- .../Extensions/LinkGeneratorExtensions.cs | 16 -------- .../Middleware/UmbracoRequestMiddleware.cs | 12 ++++-- .../Profiler/InitializeWebProfiling.cs | 3 +- .../Profiler/WebProfiler.cs | 38 ++++++++++++++----- .../src/installer/installer.service.js | 18 ++++----- 22 files changed, 122 insertions(+), 73 deletions(-) create mode 100644 src/Umbraco.Web.BackOffice/Extensions/LinkGeneratorExtensions.cs rename src/{Umbraco.Web.Common => Umbraco.Web.BackOffice}/Extensions/UmbracoApplicationBuilder.Installer.cs (95%) rename src/{Umbraco.Web.Common => Umbraco.Web.BackOffice}/Install/CreateUnattendedUserNotificationHandler.cs (98%) rename src/{Umbraco.Web.Common => Umbraco.Web.BackOffice}/Install/InstallApiController.cs (89%) rename src/{Umbraco.Web.Common => Umbraco.Web.BackOffice}/Install/InstallAreaRoutes.cs (98%) rename src/{Umbraco.Web.Common => Umbraco.Web.BackOffice}/Install/InstallAuthorizeAttribute.cs (97%) rename src/{Umbraco.Web.Common => Umbraco.Web.BackOffice}/Install/InstallController.cs (99%) diff --git a/src/Umbraco.Core/Services/IRuntime.cs b/src/Umbraco.Core/Services/IRuntime.cs index b10c9fe90f..3455835c8e 100644 --- a/src/Umbraco.Core/Services/IRuntime.cs +++ b/src/Umbraco.Core/Services/IRuntime.cs @@ -1,3 +1,4 @@ +using System.Threading; using Microsoft.Extensions.Hosting; namespace Umbraco.Cms.Core.Services @@ -11,5 +12,7 @@ namespace Umbraco.Cms.Core.Services /// Gets the runtime state. /// IRuntimeState State { get; } + + CancellationToken CancellationToken { get; } } } diff --git a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs index e2b20ced8f..b198b88f59 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs @@ -66,10 +66,12 @@ namespace Umbraco.Cms.Infrastructure.Runtime /// Gets the state of the Umbraco runtime. /// public IRuntimeState State { get; } + public CancellationToken CancellationToken { get; private set; } /// public async Task StartAsync(CancellationToken cancellationToken) { + CancellationToken = cancellationToken; StaticApplicationLogging.Initialize(_loggerFactory); AppDomain.CurrentDomain.UnhandledException += (_, args) => diff --git a/src/Umbraco.Tests.UnitTests/AutoFixture/AutoMoqDataAttribute.cs b/src/Umbraco.Tests.UnitTests/AutoFixture/AutoMoqDataAttribute.cs index d7fd849e71..3e728a96c9 100644 --- a/src/Umbraco.Tests.UnitTests/AutoFixture/AutoMoqDataAttribute.cs +++ b/src/Umbraco.Tests.UnitTests/AutoFixture/AutoMoqDataAttribute.cs @@ -17,8 +17,8 @@ using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Web.BackOffice.Controllers; +using Umbraco.Cms.Web.BackOffice.Install; using Umbraco.Cms.Web.BackOffice.Routing; -using Umbraco.Cms.Web.Common.Install; using Umbraco.Cms.Web.Common.Security; namespace Umbraco.Cms.Tests.UnitTests.AutoFixture diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/FileNameTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/FileNameTests.cs index 98d4eaee3c..4d7d0a0f26 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/FileNameTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/FileNameTests.cs @@ -15,7 +15,7 @@ using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Tests.UnitTests.AutoFixture; using Umbraco.Cms.Web.BackOffice.Controllers; -using Umbraco.Cms.Web.Common.Install; +using Umbraco.Cms.Web.BackOffice.Install; namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common { diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/InstallAreaRoutesTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/InstallAreaRoutesTests.cs index 0833ba4576..24a272a4fa 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/InstallAreaRoutesTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/InstallAreaRoutesTests.cs @@ -9,7 +9,7 @@ using NUnit.Framework; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Web.Common.Install; +using Umbraco.Cms.Web.BackOffice.Install; using Umbraco.Extensions; using static Umbraco.Cms.Core.Constants.Web.Routing; using Constants = Umbraco.Cms.Core.Constants; diff --git a/src/Umbraco.Web.BackOffice/Controllers/PackageInstallController.cs b/src/Umbraco.Web.BackOffice/Controllers/PackageInstallController.cs index 9404e9a292..1c874732c4 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/PackageInstallController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/PackageInstallController.cs @@ -317,6 +317,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers model.IsRestarting = true; + return model; } diff --git a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs index 23a7e3e9b1..3572ee53fc 100644 --- a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs @@ -14,6 +14,7 @@ using Umbraco.Cms.Infrastructure.Examine.DependencyInjection; using Umbraco.Cms.Infrastructure.WebAssets; using Umbraco.Cms.Web.BackOffice.Controllers; using Umbraco.Cms.Web.BackOffice.Filters; +using Umbraco.Cms.Web.BackOffice.Install; using Umbraco.Cms.Web.BackOffice.Middleware; using Umbraco.Cms.Web.BackOffice.ModelsBuilder; using Umbraco.Cms.Web.BackOffice.Routing; @@ -54,6 +55,13 @@ namespace Umbraco.Extensions .AddExamine() .AddExamineIndexes(); + public static IUmbracoBuilder AddUnattedInstallCreateUser(this IUmbracoBuilder builder) + { + builder.AddNotificationAsyncHandler(); + return builder; + } + + /// /// Adds Umbraco preview support /// @@ -75,6 +83,7 @@ namespace Umbraco.Extensions builder.Services.AddSingleton(); builder.Services.ConfigureOptions(); builder.Services.AddUnique(); + builder.Services.AddUnique(); builder.Services.AddUnique(); builder.Services.AddUnique(); builder.AddNotificationAsyncHandler(); diff --git a/src/Umbraco.Web.BackOffice/Extensions/LinkGeneratorExtensions.cs b/src/Umbraco.Web.BackOffice/Extensions/LinkGeneratorExtensions.cs new file mode 100644 index 0000000000..23c44e2be4 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Extensions/LinkGeneratorExtensions.cs @@ -0,0 +1,25 @@ +using Microsoft.AspNetCore.Routing; +using Umbraco.Cms.Web.BackOffice.Install; + +namespace Umbraco.Extensions +{ + public static class BackofficeLinkGeneratorExtensions + { + /// + /// Returns the URL for the installer + /// + public static string GetInstallerUrl(this LinkGenerator linkGenerator) + => linkGenerator.GetPathByAction(nameof(InstallController.Index), ControllerExtensions.GetControllerName(), new { area = Cms.Core.Constants.Web.Mvc.InstallArea }); + + /// + /// Returns the URL for the installer api + /// + public static string GetInstallerApiUrl(this LinkGenerator linkGenerator) + => linkGenerator.GetPathByAction( + nameof(InstallApiController.GetSetup), + ControllerExtensions.GetControllerName(), + new { area = Cms.Core.Constants.Web.Mvc.InstallArea }).TrimEnd(nameof(InstallApiController.GetSetup)); + + + } +} diff --git a/src/Umbraco.Web.Common/Extensions/UmbracoApplicationBuilder.Installer.cs b/src/Umbraco.Web.BackOffice/Extensions/UmbracoApplicationBuilder.Installer.cs similarity index 95% rename from src/Umbraco.Web.Common/Extensions/UmbracoApplicationBuilder.Installer.cs rename to src/Umbraco.Web.BackOffice/Extensions/UmbracoApplicationBuilder.Installer.cs index 3859814775..2be8c6bb28 100644 --- a/src/Umbraco.Web.Common/Extensions/UmbracoApplicationBuilder.Installer.cs +++ b/src/Umbraco.Web.BackOffice/Extensions/UmbracoApplicationBuilder.Installer.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; +using Umbraco.Cms.Web.BackOffice.Install; using Umbraco.Cms.Web.Common.ApplicationBuilder; -using Umbraco.Cms.Web.Common.Install; namespace Umbraco.Extensions { diff --git a/src/Umbraco.Web.Common/Install/CreateUnattendedUserNotificationHandler.cs b/src/Umbraco.Web.BackOffice/Install/CreateUnattendedUserNotificationHandler.cs similarity index 98% rename from src/Umbraco.Web.Common/Install/CreateUnattendedUserNotificationHandler.cs rename to src/Umbraco.Web.BackOffice/Install/CreateUnattendedUserNotificationHandler.cs index df25dda78a..3990f42aa7 100644 --- a/src/Umbraco.Web.Common/Install/CreateUnattendedUserNotificationHandler.cs +++ b/src/Umbraco.Web.BackOffice/Install/CreateUnattendedUserNotificationHandler.cs @@ -12,7 +12,7 @@ using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; using Umbraco.Extensions; -namespace Umbraco.Cms.Web.Common.Install +namespace Umbraco.Cms.Web.BackOffice.Install { public class CreateUnattendedUserNotificationHandler : INotificationAsyncHandler { diff --git a/src/Umbraco.Web.Common/Install/InstallApiController.cs b/src/Umbraco.Web.BackOffice/Install/InstallApiController.cs similarity index 89% rename from src/Umbraco.Web.Common/Install/InstallApiController.cs rename to src/Umbraco.Web.BackOffice/Install/InstallApiController.cs index 6efe11c2a0..bbdd183d90 100644 --- a/src/Umbraco.Web.Common/Install/InstallApiController.cs +++ b/src/Umbraco.Web.BackOffice/Install/InstallApiController.cs @@ -6,18 +6,20 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; -using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Install; using Umbraco.Cms.Core.Install.Models; using Umbraco.Cms.Core.Logging; +using Umbraco.Cms.Core.Security; +using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.Install; using Umbraco.Cms.Infrastructure.Migrations.Install; +using Umbraco.Cms.Web.BackOffice.Security; using Umbraco.Cms.Web.Common.ActionsResults; using Umbraco.Cms.Web.Common.Attributes; using Umbraco.Cms.Web.Common.Filters; using Umbraco.Extensions; -namespace Umbraco.Cms.Web.Common.Install +namespace Umbraco.Cms.Web.BackOffice.Install { [UmbracoApiController] [AngularJsonOnlyConfiguration] @@ -27,20 +29,31 @@ namespace Umbraco.Cms.Web.Common.Install { private readonly DatabaseBuilder _databaseBuilder; private readonly InstallStatusTracker _installStatusTracker; - private readonly IUmbracoApplicationLifetime _umbracoApplicationLifetime; + private readonly IRuntime _runtime; + private readonly IBackOfficeUserManager _backOfficeUserManager; + private readonly IBackOfficeSignInManager _backOfficeSignInManager; private readonly InstallStepCollection _installSteps; private readonly ILogger _logger; private readonly IProfilingLogger _proflog; - public InstallApiController(DatabaseBuilder databaseBuilder, IProfilingLogger proflog, ILogger logger, - InstallHelper installHelper, InstallStepCollection installSteps, InstallStatusTracker installStatusTracker, - IUmbracoApplicationLifetime umbracoApplicationLifetime) + public InstallApiController( + DatabaseBuilder databaseBuilder, + IProfilingLogger proflog, + ILogger logger, + InstallHelper installHelper, + InstallStepCollection installSteps, + InstallStatusTracker installStatusTracker, + IRuntime runtime, + IBackOfficeUserManager backOfficeUserManager, + IBackOfficeSignInManager backOfficeSignInManager) { _databaseBuilder = databaseBuilder ?? throw new ArgumentNullException(nameof(databaseBuilder)); _proflog = proflog ?? throw new ArgumentNullException(nameof(proflog)); _installSteps = installSteps; _installStatusTracker = installStatusTracker; - _umbracoApplicationLifetime = umbracoApplicationLifetime; + _runtime = runtime; + _backOfficeUserManager = backOfficeUserManager; + _backOfficeSignInManager = backOfficeSignInManager; InstallHelper = installHelper; _logger = logger; } @@ -86,7 +99,13 @@ namespace Umbraco.Cms.Web.Common.Install [HttpPost] public async Task CompleteInstall() { - _umbracoApplicationLifetime.Restart(); + + await _runtime.StopAsync(_runtime.CancellationToken); + await _runtime.StartAsync(_runtime.CancellationToken); + + var identityUser = await _backOfficeUserManager.FindByIdAsync(Core.Constants.Security.SuperUserIdAsString); + _backOfficeSignInManager.SignInAsync(identityUser, false); + return NoContent(); } diff --git a/src/Umbraco.Web.Common/Install/InstallAreaRoutes.cs b/src/Umbraco.Web.BackOffice/Install/InstallAreaRoutes.cs similarity index 98% rename from src/Umbraco.Web.Common/Install/InstallAreaRoutes.cs rename to src/Umbraco.Web.BackOffice/Install/InstallAreaRoutes.cs index 6a89d3b770..28871db452 100644 --- a/src/Umbraco.Web.Common/Install/InstallAreaRoutes.cs +++ b/src/Umbraco.Web.BackOffice/Install/InstallAreaRoutes.cs @@ -7,7 +7,7 @@ using Umbraco.Cms.Core.Services; using Umbraco.Cms.Web.Common.Routing; using Umbraco.Extensions; -namespace Umbraco.Cms.Web.Common.Install +namespace Umbraco.Cms.Web.BackOffice.Install { public class InstallAreaRoutes : IAreaRoutes diff --git a/src/Umbraco.Web.Common/Install/InstallAuthorizeAttribute.cs b/src/Umbraco.Web.BackOffice/Install/InstallAuthorizeAttribute.cs similarity index 97% rename from src/Umbraco.Web.Common/Install/InstallAuthorizeAttribute.cs rename to src/Umbraco.Web.BackOffice/Install/InstallAuthorizeAttribute.cs index 71482db274..ee55f22888 100644 --- a/src/Umbraco.Web.Common/Install/InstallAuthorizeAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Install/InstallAuthorizeAttribute.cs @@ -5,7 +5,7 @@ using Microsoft.Extensions.Logging; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Services; -namespace Umbraco.Cms.Web.Common.Install +namespace Umbraco.Cms.Web.BackOffice.Install { /// /// Ensures authorization occurs for the installer if it has already completed. diff --git a/src/Umbraco.Web.Common/Install/InstallController.cs b/src/Umbraco.Web.BackOffice/Install/InstallController.cs similarity index 99% rename from src/Umbraco.Web.Common/Install/InstallController.cs rename to src/Umbraco.Web.BackOffice/Install/InstallController.cs index 4bee2c0a0f..721e6718fe 100644 --- a/src/Umbraco.Web.Common/Install/InstallController.cs +++ b/src/Umbraco.Web.BackOffice/Install/InstallController.cs @@ -13,10 +13,10 @@ using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.WebAssets; using Umbraco.Cms.Infrastructure.Install; -using Umbraco.Extensions; using Umbraco.Cms.Web.Common.Filters; +using Umbraco.Extensions; -namespace Umbraco.Cms.Web.Common.Install +namespace Umbraco.Cms.Web.BackOffice.Install { /// diff --git a/src/Umbraco.Web.BackOffice/Routing/BackOfficeAreaRoutes.cs b/src/Umbraco.Web.BackOffice/Routing/BackOfficeAreaRoutes.cs index 2a83b45fd5..13d57ccc43 100644 --- a/src/Umbraco.Web.BackOffice/Routing/BackOfficeAreaRoutes.cs +++ b/src/Umbraco.Web.BackOffice/Routing/BackOfficeAreaRoutes.cs @@ -47,12 +47,7 @@ namespace Umbraco.Cms.Web.BackOffice.Routing switch (_runtimeState.Level) { case RuntimeLevel.Install: - // a new install so we don't route the back office - break; case RuntimeLevel.Upgrade: - // for upgrades we only need to route the back office and auth controllers - MapMinimalBackOffice(endpoints); - break; case RuntimeLevel.Run: MapMinimalBackOffice(endpoints); diff --git a/src/Umbraco.Web.BackOffice/Routing/PreviewRoutes.cs b/src/Umbraco.Web.BackOffice/Routing/PreviewRoutes.cs index a207a727fb..bda08d0d87 100644 --- a/src/Umbraco.Web.BackOffice/Routing/PreviewRoutes.cs +++ b/src/Umbraco.Web.BackOffice/Routing/PreviewRoutes.cs @@ -35,9 +35,7 @@ namespace Umbraco.Cms.Web.BackOffice.Routing switch (_runtimeState.Level) { case RuntimeLevel.Install: - break; case RuntimeLevel.Upgrade: - break; case RuntimeLevel.Run: endpoints.MapHub(GetPreviewHubRoute()); endpoints.MapUmbracoRoute(_umbracoPathSegment, Constants.Web.Mvc.BackOfficeArea, null); diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs index 58ea578ec3..02f6a04ef9 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs @@ -48,7 +48,6 @@ using Umbraco.Cms.Web.Common.ApplicationModels; using Umbraco.Cms.Web.Common.AspNetCore; using Umbraco.Cms.Web.Common.Controllers; using Umbraco.Cms.Web.Common.DependencyInjection; -using Umbraco.Cms.Web.Common.Install; using Umbraco.Cms.Web.Common.Localization; using Umbraco.Cms.Web.Common.Macros; using Umbraco.Cms.Web.Common.Middleware; @@ -298,8 +297,6 @@ namespace Umbraco.Extensions builder.WithCollectionBuilder() .Add(umbracoApiControllerTypes); - builder.Services.AddUnique(); - builder.Services.AddUnique(); builder.Services.AddUnique(); builder.Services.AddUnique(); @@ -320,11 +317,6 @@ namespace Umbraco.Extensions return builder; } - public static IUmbracoBuilder AddUnattedInstallCreateUser(this IUmbracoBuilder builder) - { - builder.AddNotificationAsyncHandler(); - return builder; - } // TODO: Does this need to exist and/or be public? public static IUmbracoBuilder AddWebServer(this IUmbracoBuilder builder) diff --git a/src/Umbraco.Web.Common/Extensions/LinkGeneratorExtensions.cs b/src/Umbraco.Web.Common/Extensions/LinkGeneratorExtensions.cs index e0a6802a8d..c30eefd918 100644 --- a/src/Umbraco.Web.Common/Extensions/LinkGeneratorExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/LinkGeneratorExtensions.cs @@ -10,7 +10,6 @@ using Umbraco.Cms.Core; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Web.Mvc; using Umbraco.Cms.Web.Common.Controllers; -using Umbraco.Cms.Web.Common.Install; namespace Umbraco.Extensions { @@ -39,21 +38,6 @@ namespace Umbraco.Extensions return linkGenerator.GetPathByAction("Default", ControllerExtensions.GetControllerName(backOfficeControllerType), values: new { area = Cms.Core.Constants.Web.Mvc.BackOfficeApiArea }); } - /// - /// Returns the URL for the installer - /// - public static string GetInstallerUrl(this LinkGenerator linkGenerator) - => linkGenerator.GetPathByAction(nameof(InstallController.Index), ControllerExtensions.GetControllerName(), new { area = Cms.Core.Constants.Web.Mvc.InstallArea }); - - /// - /// Returns the URL for the installer api - /// - public static string GetInstallerApiUrl(this LinkGenerator linkGenerator) - => linkGenerator.GetPathByAction( - nameof(InstallApiController.GetSetup), - ControllerExtensions.GetControllerName(), - new { area = Cms.Core.Constants.Web.Mvc.InstallArea }).TrimEnd(nameof(InstallApiController.GetSetup)); - /// /// Return the Url for a Web Api service /// diff --git a/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs b/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs index c8a7abf727..fe0029bd80 100644 --- a/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs +++ b/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs @@ -17,6 +17,7 @@ using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Security; +using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Web; using Umbraco.Cms.Infrastructure.PublishedCache; using Umbraco.Cms.Infrastructure.WebAssets; @@ -48,6 +49,7 @@ namespace Umbraco.Cms.Web.Common.Middleware private readonly IHostingEnvironment _hostingEnvironment; private readonly UmbracoRequestPaths _umbracoRequestPaths; private readonly BackOfficeWebAssets _backOfficeWebAssets; + private readonly IRuntimeState _runtimeState; private readonly SmidgeOptions _smidgeOptions; private readonly WebProfiler _profiler; @@ -74,7 +76,8 @@ namespace Umbraco.Cms.Web.Common.Middleware IHostingEnvironment hostingEnvironment, UmbracoRequestPaths umbracoRequestPaths, BackOfficeWebAssets backOfficeWebAssets, - IOptions smidgeOptions) + IOptions smidgeOptions, + IRuntimeState runtimeState) { _logger = logger; _umbracoContextFactory = umbracoContextFactory; @@ -84,6 +87,7 @@ namespace Umbraco.Cms.Web.Common.Middleware _hostingEnvironment = hostingEnvironment; _umbracoRequestPaths = umbracoRequestPaths; _backOfficeWebAssets = backOfficeWebAssets; + _runtimeState = runtimeState; _smidgeOptions = smidgeOptions.Value; _profiler = profiler as WebProfiler; // Ignore if not a WebProfiler } @@ -102,7 +106,7 @@ namespace Umbraco.Cms.Web.Common.Middleware // Profiling start needs to be one of the first things that happens. // Also MiniProfiler.Current becomes null if it is handled by the event aggregator due to async/await - _profiler?.UmbracoApplicationBeginRequest(context); + _profiler?.UmbracoApplicationBeginRequest(context, _runtimeState.Level); EnsureContentCacheInitialized(); @@ -162,7 +166,7 @@ namespace Umbraco.Cms.Web.Common.Middleware // Profiling end needs to be last of the first things that happens. // Also MiniProfiler.Current becomes null if it is handled by the event aggregator due to async/await - _profiler?.UmbracoApplicationEndRequest(context); + _profiler?.UmbracoApplicationEndRequest(context, _runtimeState.Level); } /// @@ -213,7 +217,7 @@ namespace Umbraco.Cms.Web.Common.Middleware } // ensure this is disposed by DI at the end of the request - IHttpScopeReference httpScopeReference = request.HttpContext.RequestServices.GetRequiredService(); + IHttpScopeReference httpScopeReference = request.HttpContext.RequestServices.GetRequiredService(); httpScopeReference.Register(); } diff --git a/src/Umbraco.Web.Common/Profiler/InitializeWebProfiling.cs b/src/Umbraco.Web.Common/Profiler/InitializeWebProfiling.cs index 755282bcf4..45901314a6 100644 --- a/src/Umbraco.Web.Common/Profiler/InitializeWebProfiling.cs +++ b/src/Umbraco.Web.Common/Profiler/InitializeWebProfiling.cs @@ -2,6 +2,7 @@ // See LICENSE for more details. using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Logging; using Umbraco.Cms.Core.Notifications; @@ -44,7 +45,7 @@ namespace Umbraco.Cms.Web.Common.Profiler /// public void Handle(UmbracoApplicationStartingNotification notification) { - if (_profile) + if (_profile && notification.RuntimeLevel == RuntimeLevel.Run) { // Stop the profiling of the booting process _profiler.StopBoot(); diff --git a/src/Umbraco.Web.Common/Profiler/WebProfiler.cs b/src/Umbraco.Web.Common/Profiler/WebProfiler.cs index 899373f35e..caf0b2fae0 100644 --- a/src/Umbraco.Web.Common/Profiler/WebProfiler.cs +++ b/src/Umbraco.Web.Common/Profiler/WebProfiler.cs @@ -3,7 +3,9 @@ using System.Linq; using System.Threading; using Microsoft.AspNetCore.Http; using StackExchange.Profiling; +using Umbraco.Cms.Core; using Umbraco.Cms.Core.Logging; +using Umbraco.Cms.Core.Services; using Umbraco.Extensions; namespace Umbraco.Cms.Web.Common.Profiler @@ -38,30 +40,46 @@ namespace Umbraco.Cms.Web.Common.Profiler public void Stop(bool discardResults = false) => MiniProfilerContext.Value?.Stop(discardResults); - public void UmbracoApplicationBeginRequest(HttpContext context) + public void UmbracoApplicationBeginRequest(HttpContext context, RuntimeLevel runtimeLevel) { + if (runtimeLevel < RuntimeLevel.Run) + { + return; + } + if (ShouldProfile(context.Request)) + { Start(); + } } - public void UmbracoApplicationEndRequest(HttpContext context) + public void UmbracoApplicationEndRequest(HttpContext context, RuntimeLevel runtimeLevel) { + if (runtimeLevel < RuntimeLevel.Run) + { + return; + } + if (ShouldProfile(context.Request)) { Stop(); - // if this is the first request, append the startup profiler - var first = Interlocked.Exchange(ref _first, 1) == 0; - if (first) + if (MiniProfilerContext.Value is not null) { + // if this is the first request, append the startup profiler + var first = Interlocked.Exchange(ref _first, 1) == 0; + if (first) + { - var startupDuration = _startupProfiler.Root.DurationMilliseconds.GetValueOrDefault(); - MiniProfilerContext.Value.DurationMilliseconds += startupDuration; - MiniProfilerContext.Value.GetTimingHierarchy().First().DurationMilliseconds += startupDuration; - MiniProfilerContext.Value.Root.AddChild(_startupProfiler.Root); + var startupDuration = _startupProfiler.Root.DurationMilliseconds.GetValueOrDefault(); + MiniProfilerContext.Value.DurationMilliseconds += startupDuration; + MiniProfilerContext.Value.GetTimingHierarchy().First().DurationMilliseconds += startupDuration; + MiniProfilerContext.Value.Root.AddChild(_startupProfiler.Root); - _startupProfiler = null; + _startupProfiler = null; + } } + } } diff --git a/src/Umbraco.Web.UI.Client/src/installer/installer.service.js b/src/Umbraco.Web.UI.Client/src/installer/installer.service.js index 2f0ae4b8cf..27b1381f02 100644 --- a/src/Umbraco.Web.UI.Client/src/installer/installer.service.js +++ b/src/Umbraco.Web.UI.Client/src/installer/installer.service.js @@ -30,7 +30,7 @@ angular.module("umbraco.install").factory('installerService', function ($rootSco "At least 4 people have the Umbraco logo tattooed on them", "'Umbraco' is the Danish name for an allen key", "Umbraco has been around since 2005, that's a looong time in IT", - "More than 700 people from all over the world meet each year in Denmark in May for our annual conference CodeGarden", + "More than 700 people from all over the world meet each year in Denmark in May for our annual conference CodeGarden", "While you are installing Umbraco someone else on the other side of the planet is probably doing it too", "You can extend Umbraco without modifying the source code using either JavaScript or C#", "Umbraco has been installed in more than 198 countries" @@ -47,7 +47,7 @@ angular.module("umbraco.install").factory('installerService', function ($rootSco } return null; } - /* Returns the description for the given step name */ + /* Returns the description for the given step name */ function getDescriptionForStepName(steps, name) { var found = _.find(steps, function(i) { return i.name == name; @@ -166,8 +166,8 @@ angular.module("umbraco.install").factory('installerService', function ($rootSco service.status.current = step; }, - /** - Finds the next step containing a view. If one is found it stores it as the current step + /** + Finds the next step containing a view. If one is found it stores it as the current step and retreives the step information and returns it, otherwise returns null . */ findNextStep : function(){ @@ -315,12 +315,10 @@ angular.module("umbraco.install").factory('installerService', function ($rootSco clearInterval(factTimer); } - $timeout(function(){ - window.location.href = Umbraco.Sys.ServerVariables.umbracoBaseUrl; - }, 5000); + window.location.href = Umbraco.Sys.ServerVariables.umbracoBaseUrl; }, handleErrorResponse); - - + + } }; @@ -354,6 +352,6 @@ angular.module("umbraco.install").factory('installerService', function ($rootSco service.switchToConfiguration(); } - + return service; }); From da895ae65b0239e36b4542ed71a76cde759e757b Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 18 May 2021 19:30:07 +0200 Subject: [PATCH 105/182] Added RestartAsync to IRuntime instead of cancellation token --- src/Umbraco.Core/Services/IRuntime.cs | 3 ++- src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs | 9 +++++++-- .../Install/InstallApiController.cs | 3 +-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Core/Services/IRuntime.cs b/src/Umbraco.Core/Services/IRuntime.cs index 3455835c8e..a28ebe7a0d 100644 --- a/src/Umbraco.Core/Services/IRuntime.cs +++ b/src/Umbraco.Core/Services/IRuntime.cs @@ -1,4 +1,5 @@ using System.Threading; +using System.Threading.Tasks; using Microsoft.Extensions.Hosting; namespace Umbraco.Cms.Core.Services @@ -13,6 +14,6 @@ namespace Umbraco.Cms.Core.Services /// IRuntimeState State { get; } - CancellationToken CancellationToken { get; } + Task RestartAsync(); } } diff --git a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs index b198b88f59..3f5e7c80bf 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs @@ -31,6 +31,7 @@ namespace Umbraco.Cms.Infrastructure.Runtime private readonly IHostingEnvironment _hostingEnvironment; private readonly DatabaseBuilder _databaseBuilder; private readonly IUmbracoVersion _umbracoVersion; + private CancellationToken _cancellationToken; /// /// Initializes a new instance of the class. @@ -66,12 +67,16 @@ namespace Umbraco.Cms.Infrastructure.Runtime /// Gets the state of the Umbraco runtime. /// public IRuntimeState State { get; } - public CancellationToken CancellationToken { get; private set; } + public async Task RestartAsync() + { + await StopAsync(_cancellationToken); + await StartAsync(_cancellationToken); + } /// public async Task StartAsync(CancellationToken cancellationToken) { - CancellationToken = cancellationToken; + _cancellationToken = cancellationToken; StaticApplicationLogging.Initialize(_loggerFactory); AppDomain.CurrentDomain.UnhandledException += (_, args) => diff --git a/src/Umbraco.Web.BackOffice/Install/InstallApiController.cs b/src/Umbraco.Web.BackOffice/Install/InstallApiController.cs index bbdd183d90..9d97883bba 100644 --- a/src/Umbraco.Web.BackOffice/Install/InstallApiController.cs +++ b/src/Umbraco.Web.BackOffice/Install/InstallApiController.cs @@ -100,8 +100,7 @@ namespace Umbraco.Cms.Web.BackOffice.Install public async Task CompleteInstall() { - await _runtime.StopAsync(_runtime.CancellationToken); - await _runtime.StartAsync(_runtime.CancellationToken); + await _runtime.RestartAsync(); var identityUser = await _backOfficeUserManager.FindByIdAsync(Core.Constants.Security.SuperUserIdAsString); _backOfficeSignInManager.SignInAsync(identityUser, false); From 8131d1a9c998829ecd6ebaa1212ad7e20a627a3e Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 18 May 2021 19:33:54 +0200 Subject: [PATCH 106/182] Added xml docs --- src/Umbraco.Core/Services/IRuntime.cs | 3 +++ src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/Umbraco.Core/Services/IRuntime.cs b/src/Umbraco.Core/Services/IRuntime.cs index a28ebe7a0d..caa430ce1f 100644 --- a/src/Umbraco.Core/Services/IRuntime.cs +++ b/src/Umbraco.Core/Services/IRuntime.cs @@ -14,6 +14,9 @@ namespace Umbraco.Cms.Core.Services /// IRuntimeState State { get; } + /// + /// Stops and Starts the runtime using the original cancellation token. + /// Task RestartAsync(); } } diff --git a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs index 3f5e7c80bf..560612f6b5 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs @@ -67,6 +67,8 @@ namespace Umbraco.Cms.Infrastructure.Runtime /// Gets the state of the Umbraco runtime. /// public IRuntimeState State { get; } + + /// public async Task RestartAsync() { await StopAsync(_cancellationToken); From 972d9d94f46a068d3107936d7ff5face38dd1900 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 18 May 2021 19:49:50 +0200 Subject: [PATCH 107/182] Fixed tests --- .../Routing/BackOfficeAreaRoutesTests.cs | 22 +++++-------------- .../Routing/PreviewRoutesTests.cs | 10 ++++----- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/BackOfficeAreaRoutesTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/BackOfficeAreaRoutesTests.cs index d785225ad2..60185a1846 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/BackOfficeAreaRoutesTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/BackOfficeAreaRoutesTests.cs @@ -24,7 +24,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.Routing [TestFixture] public class BackOfficeAreaRoutesTests { - [TestCase(RuntimeLevel.Install)] + [TestCase(RuntimeLevel.BootFailed)] [TestCase(RuntimeLevel.Unknown)] [TestCase(RuntimeLevel.Boot)] @@ -38,22 +38,12 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.Routing } [Test] - public void RuntimeState_Upgrade() + [TestCase(RuntimeLevel.Run)] + [TestCase(RuntimeLevel.Upgrade)] + [TestCase(RuntimeLevel.Install)] + public void RuntimeState_All_Routes(RuntimeLevel level) { - BackOfficeAreaRoutes routes = GetBackOfficeAreaRoutes(RuntimeLevel.Upgrade); - var endpoints = new TestRouteBuilder(); - routes.CreateRoutes(endpoints); - - Assert.AreEqual(1, endpoints.DataSources.Count); - EndpointDataSource route = endpoints.DataSources.First(); - Assert.AreEqual(2, route.Endpoints.Count); - AssertMinimalBackOfficeRoutes(route); - } - - [Test] - public void RuntimeState_Run() - { - BackOfficeAreaRoutes routes = GetBackOfficeAreaRoutes(RuntimeLevel.Run); + BackOfficeAreaRoutes routes = GetBackOfficeAreaRoutes(level); var endpoints = new TestRouteBuilder(); routes.CreateRoutes(endpoints); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/PreviewRoutesTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/PreviewRoutesTests.cs index e4da74c707..e87f1a297c 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/PreviewRoutesTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/PreviewRoutesTests.cs @@ -22,11 +22,9 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.Routing [TestFixture] public class PreviewRoutesTests { - [TestCase(RuntimeLevel.Install)] [TestCase(RuntimeLevel.BootFailed)] [TestCase(RuntimeLevel.Unknown)] [TestCase(RuntimeLevel.Boot)] - [TestCase(RuntimeLevel.Upgrade)] public void RuntimeState_No_Routes(RuntimeLevel level) { PreviewRoutes routes = GetRoutes(level); @@ -36,10 +34,12 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.Routing Assert.AreEqual(0, endpoints.DataSources.Count); } - [Test] - public void RuntimeState_Run() + [TestCase(RuntimeLevel.Run)] + [TestCase(RuntimeLevel.Upgrade)] + [TestCase(RuntimeLevel.Install)] + public void RuntimeState_All_Routes(RuntimeLevel level) { - PreviewRoutes routes = GetRoutes(RuntimeLevel.Run); + PreviewRoutes routes = GetRoutes(level); var endpoints = new TestRouteBuilder(); routes.CreateRoutes(endpoints); From 92cdf4e6e82aa95f7161c3775b17196daed28eb9 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Wed, 19 May 2021 10:20:01 +0200 Subject: [PATCH 108/182] Update CONTRIBUTING.md --- .github/CONTRIBUTING.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index f4e237a1f2..d156e9de88 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -44,6 +44,15 @@ We have [documented what we consider small and large changes](CONTRIBUTION_GUIDE Remember, it is always worth working on an issue from the `Up for grabs` list or even asking for some feedback before you send us a PR. This way, your PR will not be closed as unwanted. +#### Ownership and copyright + +It is your responsibility to make sure that you're allowed to share the code you're providing us. +For example, you should have permission from your employer or customer to share code. + +Similarly, if your contribution is copied or adapted from somewhere else, make sure that the license allows you to reuse that for a contribution to Umbraco-CMS. + +If you're not sure, leave a note on your contribution and we will be happy to guide you. + ### What can I start with? Unsure where to begin contributing to Umbraco? You can start by looking through [these `Up for grabs` issues](https://github.com/umbraco/Umbraco-CMS/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+label%3Acommunity%2Fup-for-grabs+) From 14558aea067294b017f911827aafea50ac152b9b Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Wed, 19 May 2021 10:21:44 +0200 Subject: [PATCH 109/182] Update CONTRIBUTING.md --- .github/CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index d156e9de88..8f969bf235 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -16,6 +16,7 @@ This project and everyone participating in it, is governed by the [our Code of C [Contributing code changes](#contributing-code-changes) * [Guidelines for contributions we welcome](#guidelines-for-contributions-we-welcome) + * [Ownership and copyright](#ownership-and-copyright) * [What can I start with?](#what-can-i-start-with) * [How do I begin?](#how-do-i-begin) * [Pull requests](#pull-requests) From 017b56eee1b45cdca44e14510e72dd675412b0b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 19 May 2021 12:06:17 +0200 Subject: [PATCH 110/182] #10274 Variant sorting should take into account that there might not be any language (#10278) (#10284) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Niels Lyngsø Co-authored-by: Mads Rasmussen --- .../forms/umbfocuslock.directive.js | 69 ++++++++++--------- .../services/contenteditinghelper.service.js | 15 ++-- 2 files changed, 46 insertions(+), 38 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbfocuslock.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbfocuslock.directive.js index 03d376e36a..e1639dde26 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbfocuslock.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbfocuslock.directive.js @@ -40,7 +40,9 @@ function getDomNodes(){ infiniteEditorsWrapper = document.querySelector('.umb-editors'); - infiniteEditors = Array.from(infiniteEditorsWrapper.querySelectorAll('.umb-editor')); + if(infiniteEditorsWrapper) { + infiniteEditors = Array.from(infiniteEditorsWrapper.querySelectorAll('.umb-editor') || []); + } } function getFocusableElements(targetElm) { @@ -84,22 +86,24 @@ var defaultFocusedElement = getAutoFocusElement(focusableElements); var lastKnownElement; - // If an inifite editor is being closed then we reset the focus to the element that triggered the the overlay + // If an infinite editor is being closed then we reset the focus to the element that triggered the the overlay if(closingEditor){ - var lastItemIndex = $rootScope.lastKnownFocusableElements.length - 1; - var editorInfo = infiniteEditors[0].querySelector('.editor-info'); // If there is only one editor open, search for the "editor-info" inside it and set focus on it // This is relevant when a property editor has been selected and the editor where we selected it from // is closed taking us back to the first layer // Otherwise set it to the last element in the lastKnownFocusedElements array - if(infiniteEditors.length === 1 && editorInfo !== null){ - lastKnownElement = editorInfo; + if(infiniteEditors && infiniteEditors.length === 1){ + var editorInfo = infiniteEditors[0].querySelector('.editor-info'); + if(infiniteEditors && infiniteEditors.length === 1 && editorInfo !== null) { + lastKnownElement = editorInfo; - // Clear the array - clearLastKnownFocusedElements(); + // Clear the array + clearLastKnownFocusedElements(); + } } else { + var lastItemIndex = $rootScope.lastKnownFocusableElements.length - 1; lastKnownElement = $rootScope.lastKnownFocusableElements[lastItemIndex]; // Remove the last item from the array so we always set the correct lastKnowFocus for each layer @@ -149,20 +153,24 @@ } function cleanupEventHandlers() { - var activeEditor = infiniteEditors[infiniteEditors.length - 1]; - var inactiveEditors = infiniteEditors.filter(editor => editor !== activeEditor); + //if we're in infinite editing mode + if(infiniteEditors.length > 0) { + var activeEditor = infiniteEditors[infiniteEditors.length - 1]; + var inactiveEditors = infiniteEditors.filter(editor => editor !== activeEditor); - if(inactiveEditors.length > 0) { - for (var index = 0; index < inactiveEditors.length; index++) { - var inactiveEditor = inactiveEditors[index]; + if(inactiveEditors.length > 0) { + for (var index = 0; index < inactiveEditors.length; index++) { + var inactiveEditor = inactiveEditors[index]; - // Remove event handlers from inactive editors - inactiveEditor.removeEventListener('keydown', handleKeydown); + // Remove event handlers from inactive editors + inactiveEditor.removeEventListener('keydown', handleKeydown); + } + } + else { + // Why is this one only begin called if there is no other infinite editors, wouldn't it make sense always to clean this up? + // Remove event handlers from the active editor + activeEditor.removeEventListener('keydown', handleKeydown); } - } - else { - // Remove event handlers from the active editor - activeEditor.removeEventListener('keydown', handleKeydown); } } @@ -173,10 +181,7 @@ // Fetch the DOM nodes we need getDomNodes(); - // Cleanup event handlers if we're in infinite editing mode - if(infiniteEditors.length > 0){ - cleanupEventHandlers(); - } + cleanupEventHandlers(); getFocusableElements(targetElm); @@ -204,17 +209,19 @@ // Make sure to disconnect the observer so we potentially don't end up with having many active ones disconnectObserver = true; - // Pass the correct editor in order to find the focusable elements - var newTarget = infiniteEditors[infiniteEditors.length - 2]; + if(infiniteEditors && infiniteEditors.length > 1) { + // Pass the correct editor in order to find the focusable elements + var newTarget = infiniteEditors[infiniteEditors.length - 2]; - if(infiniteEditors.length > 1){ - // Setting closing till true will let us re-apply the last known focus to then opened layer that then becomes - // active - closingEditor = true; + if(infiniteEditors.length > 1) { + // Setting closing till true will let us re-apply the last known focus to then opened layer that then becomes + // active + closingEditor = true; - onInit(newTarget); + onInit(newTarget); - return; + return; + } } // Clear lastKnownFocusableElements diff --git a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js index bab665579c..67e466af35 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js @@ -789,10 +789,10 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt */ sortVariants: function (a, b) { const statesOrder = {'PublishedPendingChanges':1, 'Published': 1, 'Draft': 2, 'NotCreated': 3}; - const compareDefault = (a,b) => (!a.language.isDefault ? 1 : -1) - (!b.language.isDefault ? 1 : -1); + const compareDefault = (a,b) => (a.language && a.language.isDefault ? -1 : 1) - (b.language && b.language.isDefault ? -1 : 1); // Make sure mandatory variants goes on top, unless they are published, cause then they already goes to the top and then we want to mix them with other published variants. - const compareMandatory = (a,b) => (a.state === 'PublishedPendingChanges' || a.state === 'Published') ? 0 : (!a.language.isMandatory ? 1 : -1) - (!b.language.isMandatory ? 1 : -1); + const compareMandatory = (a,b) => (a.state === 'PublishedPendingChanges' || a.state === 'Published') ? 0 : (a.language && a.language.isMandatory ? -1 : 1) - (b.language && b.language.isMandatory ? -1 : 1); const compareState = (a, b) => (statesOrder[a.state] || 99) - (statesOrder[b.state] || 99); const compareName = (a, b) => a.displayName.localeCompare(b.displayName); @@ -813,17 +813,18 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt */ getSortedVariantsAndSegments: function (variantsAndSegments) { const sortedVariants = variantsAndSegments.filter(variant => !variant.segment).sort(this.sortVariants); - let segments = variantsAndSegments.filter(variant => variant.segment); + let variantsWithSegments = variantsAndSegments.filter(variant => variant.segment); let sortedAvailableVariants = []; sortedVariants.forEach((variant) => { - const sortedMatchedSegments = segments.filter(segment => segment.language.culture === variant.language.culture).sort(this.sortVariants); - segments = segments.filter(segment => segment.language.culture !== variant.language.culture); + const sortedMatchedSegments = variantsWithSegments.filter(segment => segment.language && variant.language && segment.language.culture === variant.language.culture).sort(this.sortVariants); + // remove variants for this culture + variantsWithSegments = variantsWithSegments.filter(segment => !segment.language || segment.language && variant.language && segment.language.culture !== variant.language.culture); sortedAvailableVariants = [...sortedAvailableVariants, ...[variant], ...sortedMatchedSegments]; }) - // if we have segments without a parent language variant we need to add the remaining segments to the array - sortedAvailableVariants = [...sortedAvailableVariants, ...segments.sort(this.sortVariants)]; + // if we have segments without a parent language variant we need to add the remaining variantsWithSegments to the array + sortedAvailableVariants = [...sortedAvailableVariants, ...variantsWithSegments.sort(this.sortVariants)]; return sortedAvailableVariants; } From 36c49b0a2592758e2f525588047fe3b89b976844 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 18 May 2021 11:08:00 +0200 Subject: [PATCH 111/182] #10274 Variant sorting should take into account that there might not be any language (#10278) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Variant sorting should take into account that there might not be any language available * fix languages Co-authored-by: Niels Lyngsø Co-authored-by: Mads Rasmussen (cherry picked from commit e19a5989e9a9c77f1d35622fe70b0fe573df59c9) --- .../services/contenteditinghelper.service.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js index 8524b960c6..26f8dc86ea 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js @@ -775,10 +775,10 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt */ sortVariants: function (a, b) { const statesOrder = {'PublishedPendingChanges':1, 'Published': 1, 'Draft': 2, 'NotCreated': 3}; - const compareDefault = (a,b) => (!a.language.isDefault ? 1 : -1) - (!b.language.isDefault ? 1 : -1); + const compareDefault = (a,b) => (a.language && a.language.isDefault ? -1 : 1) - (b.language && b.language.isDefault ? -1 : 1); // Make sure mandatory variants goes on top, unless they are published, cause then they already goes to the top and then we want to mix them with other published variants. - const compareMandatory = (a,b) => (a.state === 'PublishedPendingChanges' || a.state === 'Published') ? 0 : (!a.language.isMandatory ? 1 : -1) - (!b.language.isMandatory ? 1 : -1); + const compareMandatory = (a,b) => (a.state === 'PublishedPendingChanges' || a.state === 'Published') ? 0 : (a.language && a.language.isMandatory ? -1 : 1) - (b.language && b.language.isMandatory ? -1 : 1); const compareState = (a, b) => (statesOrder[a.state] || 99) - (statesOrder[b.state] || 99); const compareName = (a, b) => a.displayName.localeCompare(b.displayName); @@ -799,17 +799,18 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt */ getSortedVariantsAndSegments: function (variantsAndSegments) { const sortedVariants = variantsAndSegments.filter(variant => !variant.segment).sort(this.sortVariants); - let segments = variantsAndSegments.filter(variant => variant.segment); + let variantsWithSegments = variantsAndSegments.filter(variant => variant.segment); let sortedAvailableVariants = []; sortedVariants.forEach((variant) => { - const sortedMatchedSegments = segments.filter(segment => segment.language.culture === variant.language.culture).sort(this.sortVariants); - segments = segments.filter(segment => segment.language.culture !== variant.language.culture); + const sortedMatchedSegments = variantsWithSegments.filter(segment => segment.language && variant.language && segment.language.culture === variant.language.culture).sort(this.sortVariants); + // remove variants for this culture + variantsWithSegments = variantsWithSegments.filter(segment => !segment.language || segment.language && variant.language && segment.language.culture !== variant.language.culture); sortedAvailableVariants = [...sortedAvailableVariants, ...[variant], ...sortedMatchedSegments]; }) - // if we have segments without a parent language variant we need to add the remaining segments to the array - sortedAvailableVariants = [...sortedAvailableVariants, ...segments.sort(this.sortVariants)]; + // if we have segments without a parent language variant we need to add the remaining variantsWithSegments to the array + sortedAvailableVariants = [...sortedAvailableVariants, ...variantsWithSegments.sort(this.sortVariants)]; return sortedAvailableVariants; } From 7c2f95613030b2e52f3ce66629916ef095ac3601 Mon Sep 17 00:00:00 2001 From: Mike Chambers Date: Tue, 27 Apr 2021 13:56:31 +0100 Subject: [PATCH 112/182] Update ModelsBuilderComposer.cs issue #10186 typo in IsExternalModelsBuilderInstalled -> Umbraco.ModelsBuider (cherry picked from commit b783399c5cd730cfcbff3fdcedf2f7a4ab2eb25c) --- .../Compose/ModelsBuilderComposer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.ModelsBuilder.Embedded/Compose/ModelsBuilderComposer.cs b/src/Umbraco.ModelsBuilder.Embedded/Compose/ModelsBuilderComposer.cs index 01010cca66..fee9b6f62e 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/Compose/ModelsBuilderComposer.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/Compose/ModelsBuilderComposer.cs @@ -47,7 +47,7 @@ namespace Umbraco.ModelsBuilder.Embedded.Compose { var assemblyNames = new[] { - "Umbraco.ModelsBuider", + "Umbraco.ModelsBuilder", "ModelsBuilder.Umbraco" }; From 9730a18c9b3bad19d9415af49e17977bda375ae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 19 May 2021 12:06:17 +0200 Subject: [PATCH 113/182] #10274 Variant sorting should take into account that there might not be any language (#10278) (#10284) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Niels Lyngsø Co-authored-by: Mads Rasmussen (cherry picked from commit 017b56eee1b45cdca44e14510e72dd675412b0b5) --- .../forms/umbfocuslock.directive.js | 69 ++++++++++--------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbfocuslock.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbfocuslock.directive.js index 03d376e36a..e1639dde26 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbfocuslock.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbfocuslock.directive.js @@ -40,7 +40,9 @@ function getDomNodes(){ infiniteEditorsWrapper = document.querySelector('.umb-editors'); - infiniteEditors = Array.from(infiniteEditorsWrapper.querySelectorAll('.umb-editor')); + if(infiniteEditorsWrapper) { + infiniteEditors = Array.from(infiniteEditorsWrapper.querySelectorAll('.umb-editor') || []); + } } function getFocusableElements(targetElm) { @@ -84,22 +86,24 @@ var defaultFocusedElement = getAutoFocusElement(focusableElements); var lastKnownElement; - // If an inifite editor is being closed then we reset the focus to the element that triggered the the overlay + // If an infinite editor is being closed then we reset the focus to the element that triggered the the overlay if(closingEditor){ - var lastItemIndex = $rootScope.lastKnownFocusableElements.length - 1; - var editorInfo = infiniteEditors[0].querySelector('.editor-info'); // If there is only one editor open, search for the "editor-info" inside it and set focus on it // This is relevant when a property editor has been selected and the editor where we selected it from // is closed taking us back to the first layer // Otherwise set it to the last element in the lastKnownFocusedElements array - if(infiniteEditors.length === 1 && editorInfo !== null){ - lastKnownElement = editorInfo; + if(infiniteEditors && infiniteEditors.length === 1){ + var editorInfo = infiniteEditors[0].querySelector('.editor-info'); + if(infiniteEditors && infiniteEditors.length === 1 && editorInfo !== null) { + lastKnownElement = editorInfo; - // Clear the array - clearLastKnownFocusedElements(); + // Clear the array + clearLastKnownFocusedElements(); + } } else { + var lastItemIndex = $rootScope.lastKnownFocusableElements.length - 1; lastKnownElement = $rootScope.lastKnownFocusableElements[lastItemIndex]; // Remove the last item from the array so we always set the correct lastKnowFocus for each layer @@ -149,20 +153,24 @@ } function cleanupEventHandlers() { - var activeEditor = infiniteEditors[infiniteEditors.length - 1]; - var inactiveEditors = infiniteEditors.filter(editor => editor !== activeEditor); + //if we're in infinite editing mode + if(infiniteEditors.length > 0) { + var activeEditor = infiniteEditors[infiniteEditors.length - 1]; + var inactiveEditors = infiniteEditors.filter(editor => editor !== activeEditor); - if(inactiveEditors.length > 0) { - for (var index = 0; index < inactiveEditors.length; index++) { - var inactiveEditor = inactiveEditors[index]; + if(inactiveEditors.length > 0) { + for (var index = 0; index < inactiveEditors.length; index++) { + var inactiveEditor = inactiveEditors[index]; - // Remove event handlers from inactive editors - inactiveEditor.removeEventListener('keydown', handleKeydown); + // Remove event handlers from inactive editors + inactiveEditor.removeEventListener('keydown', handleKeydown); + } + } + else { + // Why is this one only begin called if there is no other infinite editors, wouldn't it make sense always to clean this up? + // Remove event handlers from the active editor + activeEditor.removeEventListener('keydown', handleKeydown); } - } - else { - // Remove event handlers from the active editor - activeEditor.removeEventListener('keydown', handleKeydown); } } @@ -173,10 +181,7 @@ // Fetch the DOM nodes we need getDomNodes(); - // Cleanup event handlers if we're in infinite editing mode - if(infiniteEditors.length > 0){ - cleanupEventHandlers(); - } + cleanupEventHandlers(); getFocusableElements(targetElm); @@ -204,17 +209,19 @@ // Make sure to disconnect the observer so we potentially don't end up with having many active ones disconnectObserver = true; - // Pass the correct editor in order to find the focusable elements - var newTarget = infiniteEditors[infiniteEditors.length - 2]; + if(infiniteEditors && infiniteEditors.length > 1) { + // Pass the correct editor in order to find the focusable elements + var newTarget = infiniteEditors[infiniteEditors.length - 2]; - if(infiniteEditors.length > 1){ - // Setting closing till true will let us re-apply the last known focus to then opened layer that then becomes - // active - closingEditor = true; + if(infiniteEditors.length > 1) { + // Setting closing till true will let us re-apply the last known focus to then opened layer that then becomes + // active + closingEditor = true; - onInit(newTarget); + onInit(newTarget); - return; + return; + } } // Clear lastKnownFocusableElements From 779c94c66f499730bb2449409f4febfa61dae1e7 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Wed, 19 May 2021 13:29:02 +0200 Subject: [PATCH 114/182] Update CONTRIBUTING.md --- .github/CONTRIBUTING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 8f969bf235..f83861acd9 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -54,6 +54,8 @@ Similarly, if your contribution is copied or adapted from somewhere else, make s If you're not sure, leave a note on your contribution and we will be happy to guide you. +When your contribution has been accepted, it will be MIT licensed from that time onwards. + ### What can I start with? Unsure where to begin contributing to Umbraco? You can start by looking through [these `Up for grabs` issues](https://github.com/umbraco/Umbraco-CMS/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+label%3Acommunity%2Fup-for-grabs+) From a2c461bcdba057418a7a996b050b20d4a2c1ffd9 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Wed, 19 May 2021 13:30:53 +0200 Subject: [PATCH 115/182] Update CONTRIBUTING.md --- .github/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index f83861acd9..3432ac472a 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -54,7 +54,7 @@ Similarly, if your contribution is copied or adapted from somewhere else, make s If you're not sure, leave a note on your contribution and we will be happy to guide you. -When your contribution has been accepted, it will be MIT licensed from that time onwards. +When your contribution has been accepted, it will be [MIT licensed](https://github.com/umbraco/Umbraco-CMS/blob/v8/contrib/LICENSE.md) from that time onwards. ### What can I start with? From df6d4a603e60beda26928c7b4cf5d395675d9a1e Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 19 May 2021 15:05:30 +0200 Subject: [PATCH 116/182] Fix lucene reference for linux too --- src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj b/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj index 2a66eec917..a453e7b92c 100644 --- a/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj +++ b/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj @@ -42,7 +42,7 @@ - + From 3f596e27ccba6f30bd17779a94a41f9633d6f68e Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Wed, 19 May 2021 16:31:52 +0200 Subject: [PATCH 117/182] Bump version to 8.13.1 --- src/SolutionInfo.cs | 4 ++-- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index 1009ec125b..451a4b0d77 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -18,5 +18,5 @@ using System.Resources; [assembly: AssemblyVersion("8.0.0")] // these are FYI and changed automatically -[assembly: AssemblyFileVersion("8.13.0")] -[assembly: AssemblyInformationalVersion("8.13.0")] +[assembly: AssemblyFileVersion("8.13.1")] +[assembly: AssemblyInformationalVersion("8.13.1")] diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 58baadcb35..f69fdf7026 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -348,9 +348,9 @@ False True - 8130 + 8131 / - http://localhost:8130 + http://localhost:8131 False False From cb6f74774d7c35eab0dcfd9f8343a5bf1eec6590 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 19 May 2021 19:12:13 +0200 Subject: [PATCH 118/182] No not use explicit nuspec file for Examine.Lucene anymore :) --- .../NuSpecs/UmbracoCms.Examine.Lucene.nuspec | 49 ------------------- build/build.ps1 | 7 --- src/umbraco.sln | 1 - 3 files changed, 57 deletions(-) delete mode 100644 build/NuSpecs/UmbracoCms.Examine.Lucene.nuspec diff --git a/build/NuSpecs/UmbracoCms.Examine.Lucene.nuspec b/build/NuSpecs/UmbracoCms.Examine.Lucene.nuspec deleted file mode 100644 index 19d60f27a9..0000000000 --- a/build/NuSpecs/UmbracoCms.Examine.Lucene.nuspec +++ /dev/null @@ -1,49 +0,0 @@ - - - - Umbraco.Cms.Examine.Lucene - 9.0.0 - Umbraco CMS Examine Binaries - Umbraco HQ - Umbraco HQ - MIT - https://umbraco.com/ - https://umbraco.com/dist/nuget/logo-small.png - false - Contains the Examine assemblies needed to run Umbraco Cms. This package only contains assemblies and can be used for package development. Use the UmbracoCms package to setup Umbraco in Visual Studio as an ASP.NET Core project. - Contains dll files required to run Examine. - en-US - umbraco - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/build/build.ps1 b/build/build.ps1 index be9a8f0f4f..ba99e1e660 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -433,13 +433,6 @@ -Verbosity detailed -outputDirectory "$($this.BuildOutput)" > "$($this.BuildTemp)\nupack.cms.log" if (-not $?) { throw "Failed to pack NuGet UmbracoCms." } - &$this.BuildEnv.NuGet Pack "$nuspecs\UmbracoCms.Examine.Lucene.nuspec" ` - -Properties BuildTmp="$($this.BuildTemp)" ` - -Version "$($this.Version.Semver.ToString())" ` - -Verbosity detailed ` - -outputDirectory "$($this.BuildOutput)" > "$($this.BuildTemp)\nupack.examine.lucene.log" - if (-not $?) { throw "Failed to pack Nuget UmbracoCms.Lucene.nuspec"} - &$this.BuildEnv.NuGet Pack "$nuspecs\UmbracoCms.SqlCe.nuspec" ` -Properties BuildTmp="$($this.BuildTemp)" ` -Version "$($this.Version.Semver.ToString())" ` diff --git a/src/umbraco.sln b/src/umbraco.sln index 3da24dcca5..b021dfccff 100644 --- a/src/umbraco.sln +++ b/src/umbraco.sln @@ -39,7 +39,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuSpecs", "NuSpecs", "{227C ProjectSection(SolutionItems) = preProject ..\build\NuSpecs\UmbracoCms.nuspec = ..\build\NuSpecs\UmbracoCms.nuspec ..\build\NuSpecs\UmbracoCms.SqlCe.nuspec = ..\build\NuSpecs\UmbracoCms.SqlCe.nuspec - ..\build\NuSpecs\UmbracoCms.Examine.Lucene.nuspec = ..\build\NuSpecs\UmbracoCms.Examine.Lucene.nuspec ..\build\NuSpecs\UmbracoCms.StaticAssets.nuspec = ..\build\NuSpecs\UmbracoCms.StaticAssets.nuspec EndProjectSection EndProject From 80ee7b398cb951738db15bd2959f2370e2aa7a08 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 19 May 2021 20:40:06 +0200 Subject: [PATCH 119/182] Acquire the maindom before we do unattended installs --- src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs index e2b20ced8f..df4d09bf62 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs @@ -91,6 +91,9 @@ namespace Umbraco.Cms.Infrastructure.Runtime AppDomain.CurrentDomain.SetData("DataDirectory", _hostingEnvironment?.MapPathContentRoot(Constants.SystemDirectories.Data)); + // acquire the main domain - if this fails then anything that should be registered with MainDom will not operate + AcquireMainDom(); + DoUnattendedInstall(); DetermineRuntimeLevel(); @@ -105,8 +108,7 @@ namespace Umbraco.Cms.Infrastructure.Runtime 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(); + // if level is Run and reason is UpgradeMigrations, that means we need to perform an unattended upgrade if (State.Reason == RuntimeLevelReason.UpgradeMigrations && State.Level == RuntimeLevel.Run) From 025a7aca29326aada1c7ccb122d816a1c03d843c Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 19 May 2021 21:44:33 +0200 Subject: [PATCH 120/182] Updated nuget packages --- src/Umbraco.Core/Umbraco.Core.csproj | 2 +- .../Umbraco.Infrastructure.csproj | 12 ++++++------ .../Umbraco.PublishedCache.NuCache.csproj | 2 +- src/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj | 2 +- .../Umbraco.Tests.Integration.csproj | 4 ++-- .../Umbraco.Tests.UnitTests.csproj | 3 ++- .../DependencyInjection/UmbracoBuilderExtensions.cs | 3 --- .../Umbraco.Web.BackOffice.csproj | 7 +++---- .../DependencyInjection/UmbracoBuilder.ImageSharp.cs | 1 - src/Umbraco.Web.Common/Umbraco.Web.Common.csproj | 8 ++++---- .../Umbraco.Web.UI.NetCore.csproj | 6 +----- src/Umbraco.Web/Umbraco.Web.csproj | 5 +---- 12 files changed, 22 insertions(+), 33 deletions(-) diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 4538427d62..89b878ac6f 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -16,7 +16,7 @@ - + diff --git a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj index 54970e58a9..9bd8aca606 100644 --- a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj +++ b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj @@ -18,8 +18,8 @@ - - + + @@ -27,19 +27,19 @@ - + - + - + - + diff --git a/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj b/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj index b9c72f69dd..cf3ad2018f 100644 --- a/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj +++ b/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj @@ -19,7 +19,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all diff --git a/src/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj b/src/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj index 58527c4cfa..c0eae36505 100644 --- a/src/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj +++ b/src/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj @@ -16,7 +16,7 @@ - + diff --git a/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj b/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj index efbd5bf807..992fc40eeb 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj +++ b/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj @@ -68,8 +68,8 @@ - - + + diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj b/src/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj index 7d27f7ed75..5b13bc25ff 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj @@ -17,7 +17,8 @@ - + + diff --git a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs index 23a7e3e9b1..7284f6857b 100644 --- a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs @@ -1,9 +1,6 @@ using System.Linq; -using Ganss.XSS; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; diff --git a/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj b/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj index a453e7b92c..8de2b2e248 100644 --- a/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj +++ b/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj @@ -18,14 +18,13 @@ - - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + all diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs index e2b6bba733..96bf7017cb 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs @@ -44,7 +44,6 @@ namespace Umbraco.Extensions options.OnPrepareResponseAsync = _ => Task.CompletedTask; }) .SetRequestParser() - .SetMemoryAllocator(provider => ArrayPoolMemoryAllocator.CreateWithMinimalPooling()) .Configure(options => { options.CacheFolder = imagingSettings.Cache.CacheFolder; diff --git a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj index 74da42bbcc..2da18ce77b 100644 --- a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj +++ b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj @@ -25,16 +25,16 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + 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 e19bd9f54b..4474217cdb 100644 --- a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj +++ b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj @@ -62,20 +62,16 @@ - - - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - all diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 31b04c18f2..603790caa8 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -97,9 +97,6 @@ - - - 3.5.4 runtime; build; native; contentfiles; analyzers @@ -243,4 +240,4 @@ - \ No newline at end of file + From 710ecf2537a8630d00db793877d5c169c5cf8095 Mon Sep 17 00:00:00 2001 From: Bjarne Fyrstenborg Date: Thu, 20 May 2021 01:00:23 +0200 Subject: [PATCH 121/182] Add option to remove/cancel added crops (#10267) * Add option to remove/cancel added crops * Move vm functions to top * Only show cancel button for empty/new crop --- .../prevalue/mediapicker3.crops.controller.js | 20 ++++++--- .../prevalue/mediapicker3.crops.html | 4 +- ...umbMediaPicker3PropertyEditor.component.js | 44 +++++++++---------- 3 files changed, 38 insertions(+), 30 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/prevalue/mediapicker3.crops.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/prevalue/mediapicker3.crops.controller.js index 922370a032..b26c6f8549 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/prevalue/mediapicker3.crops.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/prevalue/mediapicker3.crops.controller.js @@ -22,7 +22,7 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.MediaPicker3.CropC crop.editMode = true; }; - $scope.addNewCrop = function (evt) { + $scope.addNewCrop = function (evt) { evt.preventDefault(); var crop = {}; @@ -30,7 +30,8 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.MediaPicker3.CropC $scope.model.value.push(crop); $scope.validate(crop); - } + }; + $scope.setChanges = function (crop) { $scope.validate(crop); if( @@ -42,22 +43,31 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.MediaPicker3.CropC window.dispatchEvent(new Event('resize.umbImageGravity')); } }; + + $scope.isEmpty = function (crop) { + return !crop.label && !crop.alias && !crop.width && !crop.height; + }; + $scope.useForAlias = function (crop) { if (crop.alias == null || crop.alias === "") { crop.alias = (crop.label || "").toCamelCase(); } }; - $scope.validate = function(crop) { + + $scope.validate = function (crop) { $scope.validateWidth(crop); $scope.validateHeight(crop); $scope.validateAlias(crop); - } + }; + $scope.validateWidth = function (crop) { crop.hasWidthError = !(Utilities.isNumber(crop.width) && crop.width > 0); }; + $scope.validateHeight = function (crop) { crop.hasHeightError = !(Utilities.isNumber(crop.height) && crop.height > 0); }; + $scope.validateAlias = function (crop, $event) { var exists = $scope.model.value.find( x => crop !== x && crop.alias === x.alias); if (exists !== undefined || crop.alias === "") { @@ -67,7 +77,6 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.MediaPicker3.CropC // everything was good: crop.hasAliasError = false; } - }; $scope.confirmChanges = function (crop, event) { @@ -76,6 +85,7 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.MediaPicker3.CropC event.preventDefault(); } }; + $scope.focusNextField = function (event) { if (event.keyCode == 13) { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/prevalue/mediapicker3.crops.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/prevalue/mediapicker3.crops.html index 46b9ddb15f..de7e7b1767 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/prevalue/mediapicker3.crops.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/prevalue/mediapicker3.crops.html @@ -20,7 +20,7 @@ -
      +
      @@ -33,7 +33,6 @@
      -
      +
      diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umbMediaPicker3PropertyEditor.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umbMediaPicker3PropertyEditor.component.js index 675381d46e..96f3126288 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umbMediaPicker3PropertyEditor.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker3/umbMediaPicker3PropertyEditor.component.js @@ -40,8 +40,13 @@ vm.loading = true; + vm.activeMediaEntry = null; vm.supportCopy = clipboardService.isSupported(); + vm.addMediaAt = addMediaAt; + vm.editMedia = editMedia; + vm.removeMedia = removeMedia; + vm.copyMedia = copyMedia; vm.labels = {}; @@ -121,7 +126,6 @@ } } - vm.addMediaAt = addMediaAt; function addMediaAt(createIndex, $event) { var mediaPicker = { startNodeId: vm.model.config.startNodeId, @@ -164,7 +168,7 @@ } } - if(vm.model.config.filter) { + if (vm.model.config.filter) { mediaPicker.filter = vm.model.config.filter; } @@ -182,6 +186,7 @@ // To be used by infinite editor. (defined here cause we need configuration from property editor) function changeMediaFor(mediaEntry, onSuccess) { + var mediaPicker = { startNodeId: vm.model.config.startNodeId, startNodeIsVirtual: vm.model.config.startNodeIsVirtual, @@ -199,16 +204,16 @@ mediaEntry.focalPoint = null; updateMediaEntryData(mediaEntry); - if(onSuccess) { + if (onSuccess) { onSuccess(); } }, close: function () { editorService.close(); } - } + }; - if(vm.model.config.filter) { + if (vm.model.config.filter) { mediaPicker.filter = vm.model.config.filter; } @@ -238,26 +243,23 @@ } }); mediaEntry.crops = newCrops; - } - vm.removeMedia = removeMedia; function removeMedia(media) { var index = vm.model.value.indexOf(media); - if(index !== -1) { + if (index !== -1) { vm.model.value.splice(index, 1); } } + function deleteAllMedias() { vm.model.value = []; } - vm.activeMediaEntry = null; function setActiveMedia(mediaEntryOrNull) { vm.activeMediaEntry = mediaEntryOrNull; } - - vm.editMedia = editMedia; + function editMedia(mediaEntry, options, $event) { if($event) @@ -304,13 +306,13 @@ editorService.open(mediaEditorModel); } - var getDocumentNameAndIcon = function() { + var getDocumentNameAndIcon = function () { // get node name var contentNodeName = "?"; var contentNodeIcon = null; - if(vm.umbVariantContent) { + if (vm.umbVariantContent) { contentNodeName = vm.umbVariantContent.editor.content.name; - if(vm.umbVariantContentEditors) { + if (vm.umbVariantContentEditors) { contentNodeIcon = vm.umbVariantContentEditors.content.icon.split(" ")[0]; } else if (vm.umbElementEditorContent) { contentNodeIcon = vm.umbElementEditorContent.model.documentType.icon.split(" ")[0]; @@ -324,9 +326,9 @@ name: contentNodeName, icon: contentNodeIcon } - } + }; - var requestCopyAllMedias = function() { + var requestCopyAllMedias = function () { var mediaKeys = vm.model.value.map(x => x.mediaKey) entityResource.getByIds(mediaKeys, "Media").then(function (entities) { @@ -338,18 +340,18 @@ var documentInfo = getDocumentNameAndIcon(); - localizationService.localize("clipboard_labelForArrayOfItemsFrom", [vm.model.label, documentInfo.name]).then(function(localizedLabel) { + localizationService.localize("clipboard_labelForArrayOfItemsFrom", [vm.model.label, documentInfo.name]).then(function (localizedLabel) { clipboardService.copyArray(clipboardService.TYPES.MEDIA, aliases, vm.model.value, localizedLabel, documentInfo.icon || "icon-thumbnail-list", vm.model.id); }); }); - } + }; - vm.copyMedia = copyMedia; function copyMedia(mediaEntry) { entityResource.getById(mediaEntry.mediaKey, "Media").then(function (mediaEntity) { clipboardService.copy(clipboardService.TYPES.MEDIA, mediaEntity.metaData.ContentTypeAlias, mediaEntry, mediaEntity.name, mediaEntity.icon, mediaEntry.key); }); } + function requestPasteFromClipboard(createIndex, pasteEntry, pasteType) { if (pasteEntry === undefined) { @@ -362,9 +364,7 @@ updateMediaEntryData(pasteEntry); vm.model.value.splice(createIndex, 0, pasteEntry); - return true; - } function requestRemoveAllMedia() { @@ -383,7 +383,6 @@ }); } - vm.sortableOptions = { cursor: "grabbing", handle: "umb-media-card", @@ -397,7 +396,6 @@ } }; - function onAmountOfMediaChanged() { // enable/disable property actions From 9bf01741b64bdbd43ced0d6ed28d20c450e330e8 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Thu, 20 May 2021 09:29:45 +0200 Subject: [PATCH 122/182] Fix invalid XML --- src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index 08448fa355..8e33f102c7 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -1907,7 +1907,6 @@ Mange hilsner fra Umbraco robotten Tilføj indhold Tilføj %0% Feltet %0% bruger editor %1% som ikke er supporteret for blokke. - Hvad er Indholdsskabeloner? From 6596bcf557a8b6baad4894a1d0cae122d2cf5df9 Mon Sep 17 00:00:00 2001 From: Mole Date: Thu, 20 May 2021 12:36:57 +0200 Subject: [PATCH 123/182] Inject notification publisher from CreateScope --- .../Scoping/IScopeProvider.cs | 4 ++++ src/Umbraco.Infrastructure/Scoping/Scope.cs | 21 +++++++++++++++---- .../Scoping/ScopeProvider.cs | 8 ++++--- .../UmbracoExamine/ExamineBaseTest.cs | 1 + .../HostedServices/LogScrubberTests.cs | 2 +- .../Mapping/MappingTests.cs | 1 + .../Migrations/MigrationTests.cs | 2 ++ .../Security/MemberUserStoreTests.cs | 2 +- .../Controllers/MemberControllerUnitTests.cs | 1 + 9 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Infrastructure/Scoping/IScopeProvider.cs b/src/Umbraco.Infrastructure/Scoping/IScopeProvider.cs index 12c081ef81..d1f82d2189 100644 --- a/src/Umbraco.Infrastructure/Scoping/IScopeProvider.cs +++ b/src/Umbraco.Infrastructure/Scoping/IScopeProvider.cs @@ -19,6 +19,7 @@ namespace Umbraco.Cms.Core.Scoping /// The transaction isolation level. /// The repositories cache mode. /// An optional events dispatcher. + /// An optional notification publisher. /// A value indicating whether to scope the filesystems. /// A value indicating whether this scope should always be registered in the call context. /// A value indicating whether this scope is auto-completed. @@ -35,6 +36,7 @@ namespace Umbraco.Cms.Core.Scoping IsolationLevel isolationLevel = IsolationLevel.Unspecified, RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified, IEventDispatcher eventDispatcher = null, + IScopedNotificationPublisher scopedNotificationPublisher = null, bool? scopeFileSystems = null, bool callContext = false, bool autoComplete = false); @@ -46,6 +48,7 @@ namespace Umbraco.Cms.Core.Scoping /// The transaction isolation level. /// The repositories cache mode. /// An optional events dispatcher. + /// An option notification publisher. /// A value indicating whether to scope the filesystems. /// /// A detached scope is not ambient and has no parent. @@ -56,6 +59,7 @@ namespace Umbraco.Cms.Core.Scoping IsolationLevel isolationLevel = IsolationLevel.Unspecified, RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified, IEventDispatcher eventDispatcher = null, + IScopedNotificationPublisher scopedNotificationPublisher = null, bool? scopeFileSystems = null); /// diff --git a/src/Umbraco.Infrastructure/Scoping/Scope.cs b/src/Umbraco.Infrastructure/Scoping/Scope.cs index 9d58cab2b4..b50b8ec0e3 100644 --- a/src/Umbraco.Infrastructure/Scoping/Scope.cs +++ b/src/Umbraco.Infrastructure/Scoping/Scope.cs @@ -39,7 +39,6 @@ namespace Umbraco.Cms.Core.Scoping private EventMessages _messages; private ICompletable _fscope; private IEventDispatcher _eventDispatcher; - // eventually this may need to be injectable - for now we'll create it explicitly and let future needs determine if it should be injectable private IScopedNotificationPublisher _notificationPublisher; private readonly object _dictionaryLocker; @@ -65,6 +64,7 @@ namespace Umbraco.Cms.Core.Scoping IsolationLevel isolationLevel = IsolationLevel.Unspecified, RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified, IEventDispatcher eventDispatcher = null, + IScopedNotificationPublisher notificationPublisher = null, bool? scopeFileSystems = null, bool callContext = false, bool autoComplete = false) @@ -80,6 +80,7 @@ namespace Umbraco.Cms.Core.Scoping _isolationLevel = isolationLevel; _repositoryCacheMode = repositoryCacheMode; _eventDispatcher = eventDispatcher; + _notificationPublisher = notificationPublisher; _scopeFileSystem = scopeFileSystems; _callContext = callContext; _autoComplete = autoComplete; @@ -140,6 +141,12 @@ namespace Umbraco.Cms.Core.Scoping throw new ArgumentException("Value cannot be specified on nested scope.", nameof(eventDispatcher)); } + // Only the outermost scope can specify the notification publisher + if (_notificationPublisher != null) + { + throw new ArgumentException("Value cannot be specified on nested scope.", nameof(notificationPublisher)); + } + // cannot specify a different fs scope! // can be 'true' only on outer scope (and false does not make much sense) if (scopeFileSystems != null && parent._scopeFileSystem != scopeFileSystems) @@ -172,10 +179,11 @@ namespace Umbraco.Cms.Core.Scoping IsolationLevel isolationLevel = IsolationLevel.Unspecified, RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified, IEventDispatcher eventDispatcher = null, + IScopedNotificationPublisher scopedNotificationPublisher = null, bool? scopeFileSystems = null, bool callContext = false, bool autoComplete = false) - : this(scopeProvider, coreDebugSettings, mediaFileManager, eventAggregator, logger, fileSystems, null, scopeContext, detachable, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete) + : this(scopeProvider, coreDebugSettings, mediaFileManager, eventAggregator, logger, fileSystems, null, scopeContext, detachable, isolationLevel, repositoryCacheMode, eventDispatcher, scopedNotificationPublisher, scopeFileSystems, callContext, autoComplete) { } // initializes a new scope in a nested scopes chain, with its parent @@ -190,10 +198,11 @@ namespace Umbraco.Cms.Core.Scoping IsolationLevel isolationLevel = IsolationLevel.Unspecified, RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified, IEventDispatcher eventDispatcher = null, + IScopedNotificationPublisher notificationPublisher = null, bool? scopeFileSystems = null, bool callContext = false, bool autoComplete = false) - : this(scopeProvider, coreDebugSettings, mediaFileManager, eventAggregator, logger, fileSystems, parent, null, false, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete) + : this(scopeProvider, coreDebugSettings, mediaFileManager, eventAggregator, logger, fileSystems, parent, null, false, isolationLevel, repositoryCacheMode, eventDispatcher, notificationPublisher, scopeFileSystems, callContext, autoComplete) { } public Guid InstanceId { get; } = Guid.NewGuid(); @@ -406,7 +415,11 @@ namespace Umbraco.Cms.Core.Scoping get { EnsureNotDisposed(); - if (ParentScope != null) return ParentScope.Notifications; + if (ParentScope != null) + { + return ParentScope.Notifications; + } + return _notificationPublisher ?? (_notificationPublisher = new ScopedNotificationPublisher(_eventAggregator)); } } diff --git a/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs b/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs index cc365beb9b..1da8b7bf1c 100644 --- a/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs +++ b/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs @@ -380,8 +380,9 @@ namespace Umbraco.Cms.Core.Scoping IsolationLevel isolationLevel = IsolationLevel.Unspecified, RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified, IEventDispatcher eventDispatcher = null, + IScopedNotificationPublisher scopedNotificationPublisher = null, bool? scopeFileSystems = null) - => new Scope(this, _coreDebugSettings, _mediaFileManager, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, true, null, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems); + => new Scope(this, _coreDebugSettings, _mediaFileManager, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, true, null, isolationLevel, repositoryCacheMode, eventDispatcher, scopedNotificationPublisher, scopeFileSystems); /// public void AttachScope(IScope other, bool callContext = false) @@ -451,6 +452,7 @@ namespace Umbraco.Cms.Core.Scoping IsolationLevel isolationLevel = IsolationLevel.Unspecified, RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified, IEventDispatcher eventDispatcher = null, + IScopedNotificationPublisher notificationPublisher = null, bool? scopeFileSystems = null, bool callContext = false, bool autoComplete = false) @@ -460,7 +462,7 @@ namespace Umbraco.Cms.Core.Scoping { IScopeContext ambientContext = AmbientContext; ScopeContext newContext = ambientContext == null ? new ScopeContext() : null; - var scope = new Scope(this, _coreDebugSettings, _mediaFileManager, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, false, newContext, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete); + var scope = new Scope(this, _coreDebugSettings, _mediaFileManager, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, false, newContext, isolationLevel, repositoryCacheMode, eventDispatcher, notificationPublisher, scopeFileSystems, callContext, autoComplete); // assign only if scope creation did not throw! PushAmbientScope(scope); if (newContext != null) @@ -470,7 +472,7 @@ namespace Umbraco.Cms.Core.Scoping return scope; } - var nested = new Scope(this, _coreDebugSettings, _mediaFileManager, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, ambientScope, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete); + var nested = new Scope(this, _coreDebugSettings, _mediaFileManager, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, ambientScope, isolationLevel, repositoryCacheMode, eventDispatcher, notificationPublisher, scopeFileSystems, callContext, autoComplete); PushAmbientScope(nested); return nested; } diff --git a/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineBaseTest.cs b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineBaseTest.cs index 8840988ac6..a2a82d57fb 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineBaseTest.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineBaseTest.cs @@ -89,6 +89,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/HostedServices/LogScrubberTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/HostedServices/LogScrubberTests.cs index fc87457248..a3a7b95210 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/HostedServices/LogScrubberTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/HostedServices/LogScrubberTests.cs @@ -76,7 +76,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.HostedServices var mockScope = new Mock(); var mockScopeProvider = new Mock(); mockScopeProvider - .Setup(x => x.CreateScope(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Setup(x => x.CreateScope(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(mockScope.Object); var mockLogger = new Mock>(); var mockProfilingLogger = new Mock(); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Mapping/MappingTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Mapping/MappingTests.cs index 9ccafbb38a..0db64a6ec9 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Mapping/MappingTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Mapping/MappingTests.cs @@ -30,6 +30,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Mapping It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Migrations/MigrationTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Migrations/MigrationTests.cs index 97af07d1ed..f118de46e0 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Migrations/MigrationTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Migrations/MigrationTests.cs @@ -30,6 +30,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Migrations IsolationLevel isolationLevel = IsolationLevel.Unspecified, RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified, IEventDispatcher eventDispatcher = null, + IScopedNotificationPublisher notificationPublisher = null, bool? scopeFileSystems = null, bool callContext = false, bool autoComplete = false) => _scope; @@ -38,6 +39,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Migrations IsolationLevel isolationLevel = IsolationLevel.Unspecified, RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified, IEventDispatcher eventDispatcher = null, + IScopedNotificationPublisher notificationPublisher = null, bool? scopeFileSystems = null) => throw new NotImplementedException(); public void AttachScope(IScope scope, bool callContext = false) => throw new NotImplementedException(); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberUserStoreTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberUserStoreTests.cs index 1740123612..3fdb3c27f9 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberUserStoreTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberUserStoreTests.cs @@ -29,7 +29,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security var mockScope = new Mock(); var mockScopeProvider = new Mock(); mockScopeProvider - .Setup(x => x.CreateScope(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Setup(x => x.CreateScope(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(mockScope.Object); return new MemberUserStore( diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MemberControllerUnitTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MemberControllerUnitTests.cs index f4075aa7d6..8006bda3a4 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MemberControllerUnitTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MemberControllerUnitTests.cs @@ -508,6 +508,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()) == Mock.Of()); From ee66455057520c116bcef03aa549043c009c995a Mon Sep 17 00:00:00 2001 From: Mole Date: Thu, 20 May 2021 13:00:59 +0200 Subject: [PATCH 124/182] Add unit tests for custom scoped notification publisher --- .../ScopedNotificationPublisherTests.cs | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 src/Umbraco.Tests.UnitTests/Umbraco.Core/Scoping/ScopedNotificationPublisherTests.cs diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Scoping/ScopedNotificationPublisherTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Scoping/ScopedNotificationPublisherTests.cs new file mode 100644 index 0000000000..e8b72bdd5e --- /dev/null +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Scoping/ScopedNotificationPublisherTests.cs @@ -0,0 +1,96 @@ +using System; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.IO; +using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Core.Scoping; +using Umbraco.Cms.Core.Strings; +using Umbraco.Cms.Infrastructure.Persistence; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Scoping +{ + [TestFixture] + public class ScopedNotificationPublisherTests + { + + [Test] + public void ScopeUsesInjectedNotificationPublisher() + { + var notificationPublisherMock = new Mock(); + ScopeProvider scopeProvider = GetScopeProvider(out var eventAggregatorMock); + + using (IScope scope = scopeProvider.CreateScope(notificationPublisher: notificationPublisherMock.Object)) + { + scope.Notifications.Publish(Mock.Of()); + scope.Notifications.PublishCancelable(Mock.Of()); + + notificationPublisherMock.Verify(x => x.Publish(It.IsAny()), Times.Once); + notificationPublisherMock.Verify(x => x.PublishCancelable(It.IsAny()), Times.Once); + + // Ensure that the custom scope provider is till used in inner scope. + using (IScope innerScope = scopeProvider.CreateScope()) + { + innerScope.Notifications.Publish(Mock.Of()); + innerScope.Notifications.PublishCancelable(Mock.Of()); + + notificationPublisherMock.Verify(x => x.Publish(It.IsAny()), Times.Exactly(2)); + notificationPublisherMock.Verify(x => x.PublishCancelable(It.IsAny()), Times.Exactly(2)); + } + + // Ensure scope exit is not called until outermost scope is being disposed + notificationPublisherMock.Verify(x => x.ScopeExit(It.IsAny()), Times.Never()); + } + + notificationPublisherMock.Verify(x => x.ScopeExit(It.IsAny()), Times.Once()); + // Ensure that the event aggregator isn't used directly. + eventAggregatorMock.Verify(x => x.Publish(It.IsAny()), Times.Never); + eventAggregatorMock.Verify(x => x.PublishCancelable(It.IsAny()), Times.Never); + } + + [Test] + public void SpecifyingNotificationPublishInInnerScopeCausesError() + { + var notificationPublisherMock = new Mock(); + ScopeProvider scopeProvider = GetScopeProvider(out var eventAggregatorMock); + + using (var scope = scopeProvider.CreateScope()) + { + Assert.Throws(() => scopeProvider.CreateScope(notificationPublisher: notificationPublisherMock.Object)); + } + } + + private ScopeProvider GetScopeProvider(out Mock eventAggregatorMock) + { + NullLoggerFactory loggerFactory = NullLoggerFactory.Instance; + + var fileSystems = new FileSystems( + loggerFactory, + Mock.Of(), + Options.Create(new GlobalSettings()), + Mock.Of()); + + var mediaFileManager = new MediaFileManager(Mock.Of(), Mock.Of(), + loggerFactory.CreateLogger(), Mock.Of()); + + eventAggregatorMock = new Mock(); + + return new ScopeProvider( + Mock.Of(), + fileSystems, + Options.Create(new CoreDebugSettings()), + mediaFileManager, + loggerFactory.CreateLogger(), + loggerFactory, + Mock.Of(), + eventAggregatorMock.Object + ); + } + } +} From 9b2cf8422d6e4a034871799e6f8249128e1ed52d Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Thu, 20 May 2021 13:16:50 +0200 Subject: [PATCH 125/182] Removed unused ctor (just for tests) + Fixed a couple of handlers that is not intended to execute before runtime level = run. --- ...abaseServerMessengerNotificationHandler.cs | 18 ++++++++-------- .../Examine/RebuildOnStartupHandler.cs | 16 ++++++++++++-- .../Persistence/UmbracoDatabaseFactory.cs | 21 ------------------- 3 files changed, 23 insertions(+), 32 deletions(-) diff --git a/src/Umbraco.Infrastructure/Cache/DatabaseServerMessengerNotificationHandler.cs b/src/Umbraco.Infrastructure/Cache/DatabaseServerMessengerNotificationHandler.cs index e627284f8b..092b4c6b99 100644 --- a/src/Umbraco.Infrastructure/Cache/DatabaseServerMessengerNotificationHandler.cs +++ b/src/Umbraco.Infrastructure/Cache/DatabaseServerMessengerNotificationHandler.cs @@ -38,19 +38,19 @@ namespace Umbraco.Cms.Core.Cache /// public void Handle(UmbracoApplicationStartingNotification notification) { + if (_runtimeState.Level < RuntimeLevel.Run) + { + return; + } + if (_databaseFactory.CanConnect == false) { _logger.LogWarning("Cannot connect to the database, distributed calls will not be enabled for this server."); - } - else if (_runtimeState.Level != RuntimeLevel.Run) - { - _logger.LogWarning("Distributed calls are not available outside the Run runtime level"); - } - else - { - // Sync on startup, this will run through the messenger's initialization sequence - _messenger?.Sync(); + return; } + + // Sync on startup, this will run through the messenger's initialization sequence + _messenger?.Sync(); } /// diff --git a/src/Umbraco.Infrastructure/Examine/RebuildOnStartupHandler.cs b/src/Umbraco.Infrastructure/Examine/RebuildOnStartupHandler.cs index 60f7478c3f..05b016dbb6 100644 --- a/src/Umbraco.Infrastructure/Examine/RebuildOnStartupHandler.cs +++ b/src/Umbraco.Infrastructure/Examine/RebuildOnStartupHandler.cs @@ -1,7 +1,9 @@ using System; using System.Threading; +using Umbraco.Cms.Core; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Sync; namespace Umbraco.Cms.Infrastructure.Examine @@ -17,6 +19,7 @@ namespace Umbraco.Cms.Infrastructure.Examine { private readonly ISyncBootStateAccessor _syncBootStateAccessor; private readonly ExamineIndexRebuilder _backgroundIndexRebuilder; + private readonly IRuntimeState _runtimeState; // These must be static because notification handlers are transient. // this does unfortunatley mean that one RebuildOnStartupHandler instance @@ -30,10 +33,12 @@ namespace Umbraco.Cms.Infrastructure.Examine public RebuildOnStartupHandler( ISyncBootStateAccessor syncBootStateAccessor, - ExamineIndexRebuilder backgroundIndexRebuilder) + ExamineIndexRebuilder backgroundIndexRebuilder, + IRuntimeState runtimeState) { _syncBootStateAccessor = syncBootStateAccessor; _backgroundIndexRebuilder = backgroundIndexRebuilder; + _runtimeState = runtimeState; } /// @@ -41,7 +46,13 @@ namespace Umbraco.Cms.Infrastructure.Examine /// /// public void Handle(UmbracoRequestBeginNotification notification) - => LazyInitializer.EnsureInitialized( + { + if (_runtimeState.Level < RuntimeLevel.Run) + { + return; + } + + LazyInitializer.EnsureInitialized( ref _isReady, ref _isReadSet, ref _isReadyLock, @@ -56,5 +67,6 @@ namespace Umbraco.Cms.Infrastructure.Examine return true; }); + } } } diff --git a/src/Umbraco.Infrastructure/Persistence/UmbracoDatabaseFactory.cs b/src/Umbraco.Infrastructure/Persistence/UmbracoDatabaseFactory.cs index 944195cb82..10d6fa2278 100644 --- a/src/Umbraco.Infrastructure/Persistence/UmbracoDatabaseFactory.cs +++ b/src/Umbraco.Infrastructure/Persistence/UmbracoDatabaseFactory.cs @@ -112,27 +112,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence Configure(settings.ConnectionString, settings.ProviderName); } - /// - /// Initializes a new instance of the . - /// - /// Used in tests. - public UmbracoDatabaseFactory(ILogger logger, ILoggerFactory loggerFactory, string connectionString, string providerName, Lazy mappers, IDbProviderFactoryCreator dbProviderFactoryCreator, DatabaseSchemaCreatorFactory databaseSchemaCreatorFactory) - { - _mappers = mappers ?? throw new ArgumentNullException(nameof(mappers)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _loggerFactory = loggerFactory; - _dbProviderFactoryCreator = dbProviderFactoryCreator ?? throw new ArgumentNullException(nameof(dbProviderFactoryCreator)); - _databaseSchemaCreatorFactory = databaseSchemaCreatorFactory ?? throw new ArgumentNullException(nameof(databaseSchemaCreatorFactory)); - - if (string.IsNullOrWhiteSpace(connectionString) || string.IsNullOrWhiteSpace(providerName)) - { - logger.LogDebug("Missing connection string or provider name, defer configuration."); - return; // not configured - } - - Configure(connectionString, providerName); - } - #endregion /// From edef73fd39a4133d055abbb9d60ddd2904295a9f Mon Sep 17 00:00:00 2001 From: Nathan Woulfe Date: Fri, 21 May 2021 11:21:44 +1000 Subject: [PATCH 126/182] Fix getBlueprintScaffold endpoint --- .../src/common/resources/content.resource.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 0f359f0689..73bcf170f4 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 @@ -694,7 +694,7 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { $http.get( umbRequestHelper.getApiUrl( "contentApiBaseUrl", - "GetEmpty", + "GetEmptyBlueprint", { blueprintId: blueprintId, parentId: parentId })), 'Failed to retrieve blueprint for id ' + blueprintId) .then(function (result) { From 71a1817a061983bbdb0d8ece856a634e4f028ff2 Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Tue, 11 May 2021 17:28:11 +0200 Subject: [PATCH 127/182] Fixed issue with data types not being cleared in cache refresh operations. --- src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs b/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs index c9caf5aaa7..37d36fca45 100644 --- a/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs @@ -3,6 +3,7 @@ using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Persistence.Repositories.Implement; using Umbraco.Core.PropertyEditors.ValueConverters; using Umbraco.Core.Services; using Umbraco.Web.PublishedCache; @@ -56,6 +57,11 @@ namespace Umbraco.Web.Cache foreach (var payload in payloads) { _idkMap.ClearCache(payload.Id); + + if (dataTypeCache.Success) + { + dataTypeCache.Result.Clear(RepositoryCacheKeys.GetKey(payload.Id)); + } } // TODO: not sure I like these? From 12328ba2f761b4c20b50df3dc83bb85b5f652cf6 Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Tue, 11 May 2021 17:28:11 +0200 Subject: [PATCH 128/182] Fixed issue with data types not being cleared in cache refresh operations. (cherry picked from commit 71a1817a061983bbdb0d8ece856a634e4f028ff2) --- src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs b/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs index c9caf5aaa7..37d36fca45 100644 --- a/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs @@ -3,6 +3,7 @@ using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Persistence.Repositories.Implement; using Umbraco.Core.PropertyEditors.ValueConverters; using Umbraco.Core.Services; using Umbraco.Web.PublishedCache; @@ -56,6 +57,11 @@ namespace Umbraco.Web.Cache foreach (var payload in payloads) { _idkMap.ClearCache(payload.Id); + + if (dataTypeCache.Success) + { + dataTypeCache.Result.Clear(RepositoryCacheKeys.GetKey(payload.Id)); + } } // TODO: not sure I like these? From 008480a17b652bc2c267ab2bfd68c4ae70162394 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Fri, 21 May 2021 15:01:51 +0200 Subject: [PATCH 129/182] Can't use optimized version of this method call until 8.14 --- src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs b/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs index 37d36fca45..431f560400 100644 --- a/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs @@ -60,7 +60,7 @@ namespace Umbraco.Web.Cache if (dataTypeCache.Success) { - dataTypeCache.Result.Clear(RepositoryCacheKeys.GetKey(payload.Id)); + dataTypeCache.Result.Clear(RepositoryCacheKeys.GetKey(payload.Id)); } } From 6aca1ca12ea568655fa215a4504b8347590cdbb0 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Sun, 23 May 2021 13:33:28 +0100 Subject: [PATCH 130/182] Fix for invalid chars used in dotnet new templates creations to ensure namespace is valid & compiles --- build/templates/UmbracoProject/UmbracoProject.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/build/templates/UmbracoProject/UmbracoProject.csproj b/build/templates/UmbracoProject/UmbracoProject.csproj index 3f229fde99..8702d8d02d 100644 --- a/build/templates/UmbracoProject/UmbracoProject.csproj +++ b/build/templates/UmbracoProject/UmbracoProject.csproj @@ -1,6 +1,7 @@ net5.0 + UmbracoProject From e02ccebf754d200c2df8fe7b2b8c0d7b6a6d4930 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Mon, 24 May 2021 14:39:06 +0100 Subject: [PATCH 131/182] No need for a specific namespace replacer it seems sourceName prop is used for that --- .../UmbracoProject/.template.config/template.json | 12 +----------- build/templates/UmbracoProject/UmbracoProject.csproj | 2 +- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/build/templates/UmbracoProject/.template.config/template.json b/build/templates/UmbracoProject/.template.config/template.json index ff4cdfd54c..4f2454706d 100644 --- a/build/templates/UmbracoProject/.template.config/template.json +++ b/build/templates/UmbracoProject/.template.config/template.json @@ -29,7 +29,7 @@ "continueOnError": true } ], - "sourceName": "UmbracoProject", + "sourceName": "Umbraco.Cms.Web.UI.NetCore", "symbols": { "version": { "type": "parameter", @@ -38,16 +38,6 @@ "description": "The version of Umbraco to load using NuGet", "replaces": "UMBRACO_VERSION_FROM_TEMPLATE" }, - "namespaceReplacer": { - "type": "generated", - "generator": "coalesce", - "parameters": { - "sourceVariableName": "name", - "defaultValue": "UmbracoProject", - "fallbackVariableName": "name" - }, - "replaces":"Umbraco.Cms.Web.UI.NetCore" - }, "PackageTestSiteName": { "type": "parameter", "datatype":"text", diff --git a/build/templates/UmbracoProject/UmbracoProject.csproj b/build/templates/UmbracoProject/UmbracoProject.csproj index 8702d8d02d..9798a40ff2 100644 --- a/build/templates/UmbracoProject/UmbracoProject.csproj +++ b/build/templates/UmbracoProject/UmbracoProject.csproj @@ -1,7 +1,7 @@ net5.0 - UmbracoProject + Umbraco.Cms.Web.UI.NetCore From 43ee6f288ea0b4249a6f411b03f29273fa809837 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 24 May 2021 11:25:03 -0700 Subject: [PATCH 132/182] fixes initialize flag --- src/Umbraco.Web/Search/ExamineFinalComponent.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Search/ExamineFinalComponent.cs b/src/Umbraco.Web/Search/ExamineFinalComponent.cs index 22dcb43cc0..335d19baf0 100644 --- a/src/Umbraco.Web/Search/ExamineFinalComponent.cs +++ b/src/Umbraco.Web/Search/ExamineFinalComponent.cs @@ -56,6 +56,8 @@ namespace Umbraco.Web.Search // double check lock, we must only do this once if (!_initialized) { + _initialized = true; + if (!_mainDom.IsMainDom) return; var bootState = _syncBootStateAccessor.GetSyncBootState(); @@ -63,7 +65,8 @@ namespace Umbraco.Web.Search _examineManager.ConfigureIndexes(_mainDom, _logger); // if it's a cold boot, rebuild all indexes including non-empty ones - _indexRebuilder.RebuildIndexes(bootState != SyncBootState.ColdBoot, 0); + // delay one minute since a cold boot also triggers nucache rebuilds + _indexRebuilder.RebuildIndexes(bootState != SyncBootState.ColdBoot, 60000); } } } From be3bfe1b6370d5d2cfded7a569c9fa1a969baa20 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 24 May 2021 12:32:57 -0700 Subject: [PATCH 133/182] Removes Booting event, backports lazy changes for PublishedSnapshotService from v9. --- .../Sync/ISyncBootStateAccessor.cs | 5 - .../NuCache/PublishedSnapshotService.cs | 202 ++++++++++-------- .../Search/ExamineFinalComponent.cs | 22 +- 3 files changed, 119 insertions(+), 110 deletions(-) diff --git a/src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs b/src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs index 249b038cae..8d4cda093c 100644 --- a/src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs +++ b/src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs @@ -12,10 +12,5 @@ namespace Umbraco.Core.Sync /// /// SyncBootState GetSyncBootState(); - - /// - /// Raised when the boot state is known - /// - event EventHandler Booting; // TODO: This should be removed in netcore } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index b35a3d0edb..f70160cde8 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -36,6 +36,8 @@ namespace Umbraco.Web.PublishedCache.NuCache internal class PublishedSnapshotService : PublishedSnapshotServiceBase { + private readonly PublishedSnapshotServiceOptions _options; + private readonly IMainDom _mainDom; private readonly ServiceContext _serviceContext; private readonly IPublishedContentTypeFactory _publishedContentTypeFactory; private readonly IScopeProvider _scopeProvider; @@ -50,12 +52,13 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly IDefaultCultureAccessor _defaultCultureAccessor; private readonly UrlSegmentProviderCollection _urlSegmentProviders; - // volatile because we read it with no lock - private volatile bool _isReady; + private bool _isReady; + private bool _isReadSet; + private object _isReadyLock; - private readonly ContentStore _contentStore; - private readonly ContentStore _mediaStore; - private readonly SnapDictionary _domainStore; + private ContentStore _contentStore; + private ContentStore _mediaStore; + private SnapDictionary _domainStore; private readonly object _storesLock = new object(); private readonly object _elementsLock = new object(); @@ -88,9 +91,12 @@ namespace Umbraco.Web.PublishedCache.NuCache ISyncBootStateAccessor syncBootStateAccessor) : base(publishedSnapshotAccessor, variationContextAccessor) { + //if (Interlocked.Increment(ref _singletonCheck) > 1) // throw new Exception("Singleton must be instantiated only once!"); + _options = options; + _mainDom = mainDom; _serviceContext = serviceContext; _publishedContentTypeFactory = publishedContentTypeFactory; _dataSource = dataSource; @@ -123,44 +129,6 @@ namespace Umbraco.Web.PublishedCache.NuCache if (runtime.Level != RuntimeLevel.Run) return; - // lock this entire call, we only want a single thread to be accessing the stores at once and within - // the call below to mainDom.Register, a callback may occur on a threadpool thread to MainDomRelease - // at the same time as we are trying to write to the stores. MainDomRelease also locks on _storesLock so - // it will not be able to close the stores until we are done populating (if the store is empty) - lock (_storesLock) - { - if (options.IgnoreLocalDb == false) - { - var registered = mainDom.Register(MainDomRegister, MainDomRelease); - - // stores are created with a db so they can write to it, but they do not read from it, - // stores need to be populated, happens in OnResolutionFrozen which uses _localDbExists to - // figure out whether it can read the databases or it should populate them from sql - - _logger.Info("Creating the content store, localContentDbExists? {LocalContentDbExists}", _localContentDbExists); - _contentStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger, _localContentDb); - _logger.Info("Creating the media store, localMediaDbExists? {LocalMediaDbExists}", _localMediaDbExists); - _mediaStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger, _localMediaDb); - } - else - { - _logger.Info("Creating the content store (local db ignored)"); - _contentStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger); - _logger.Info("Creating the media store (local db ignored)"); - _mediaStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger); - } - - _domainStore = new SnapDictionary(); - - _syncBootStateAccessor.Booting += (sender, args) => - { - LoadCachesOnStartup(args); - }; - } - - Guid GetUid(ContentStore store, int id) => store.LiveSnapshot.Get(id)?.Uid ?? default; - int GetId(ContentStore store, Guid uid) => store.LiveSnapshot.Get(uid)?.Id ?? default; - if (idkMap != null) { idkMap.SetMapper(UmbracoObjectTypes.Document, id => GetUid(_contentStore, id), uid => GetId(_contentStore, uid)); @@ -168,6 +136,18 @@ namespace Umbraco.Web.PublishedCache.NuCache } } + private int GetId(ContentStore store, Guid uid) + { + EnsureCaches(); + return store.LiveSnapshot.Get(uid)?.Id ?? default; + } + + private Guid GetUid(ContentStore store, int id) + { + EnsureCaches(); + return store.LiveSnapshot.Get(id)?.Uid ?? default; + } + /// /// Install phase of /// @@ -219,51 +199,82 @@ namespace Umbraco.Web.PublishedCache.NuCache } /// - /// Populates the stores + /// Lazily populates the stores only when they are first requested /// - /// This is called inside of a lock for _storesLock - private void LoadCachesOnStartup(SyncBootState bootState) - { - // TODO: This is super ugly that this does this as part of the ctor. - // In netcore this will be different, the initialization will happen - // outside of the ctor. - - var okContent = false; - var okMedia = false; - - try + internal void EnsureCaches() => LazyInitializer.EnsureInitialized( + ref _isReady, + ref _isReadSet, + ref _isReadyLock, + () => { - if (bootState != SyncBootState.ColdBoot && _localContentDbExists) + // lock this entire call, we only want a single thread to be accessing the stores at once and within + // the call below to mainDom.Register, a callback may occur on a threadpool thread to MainDomRelease + // at the same time as we are trying to write to the stores. MainDomRelease also locks on _storesLock so + // it will not be able to close the stores until we are done populating (if the store is empty) + lock (_storesLock) { - okContent = LockAndLoadContent(scope => LoadContentFromLocalDbLocked(true)); - if (!okContent) - _logger.Warn("Loading content from local db raised warnings, will reload from database."); + if (!_options.IgnoreLocalDb) + { + var registered = _mainDom.Register(MainDomRegister, MainDomRelease); + + // stores are created with a db so they can write to it, but they do not read from it, + // stores need to be populated, happens in OnResolutionFrozen which uses _localDbExists to + // figure out whether it can read the databases or it should populate them from sql + + _logger.Info("Creating the content store, localContentDbExists? {LocalContentDbExists}", _localContentDbExists); + _contentStore = new ContentStore(PublishedSnapshotAccessor, VariationContextAccessor, _logger, _localContentDb); + _logger.Info("Creating the media store, localMediaDbExists? {LocalMediaDbExists}", _localMediaDbExists); + _mediaStore = new ContentStore(PublishedSnapshotAccessor, VariationContextAccessor, _logger, _localMediaDb); + } + else + { + _logger.Info("Creating the content store (local db ignored)"); + _contentStore = new ContentStore(PublishedSnapshotAccessor, VariationContextAccessor, _logger); + _logger.Info("Creating the media store (local db ignored)"); + _mediaStore = new ContentStore(PublishedSnapshotAccessor, VariationContextAccessor, _logger); + } + + _domainStore = new SnapDictionary(); + + SyncBootState bootState = _syncBootStateAccessor.GetSyncBootState(); + + var okContent = false; + var okMedia = false; + + try + { + if (bootState != SyncBootState.ColdBoot && _localContentDbExists) + { + okContent = LockAndLoadContent(scope => LoadContentFromLocalDbLocked(true)); + if (!okContent) + _logger.Warn("Loading content from local db raised warnings, will reload from database."); + } + + if (bootState != SyncBootState.ColdBoot && _localMediaDbExists) + { + okMedia = LockAndLoadMedia(scope => LoadMediaFromLocalDbLocked(true)); + if (!okMedia) + _logger.Warn("Loading media from local db raised warnings, will reload from database."); + } + + if (!okContent) + LockAndLoadContent(scope => LoadContentFromDatabaseLocked(scope, true)); + + if (!okMedia) + LockAndLoadMedia(scope => LoadMediaFromDatabaseLocked(scope, true)); + + LockAndLoadDomains(); + } + catch (Exception ex) + { + _logger.Fatal(ex, "Panic, exception while loading cache data."); + throw; + } + + // finally, cache is ready! + return true; } - - if (bootState != SyncBootState.ColdBoot && _localMediaDbExists) - { - okMedia = LockAndLoadMedia(scope => LoadMediaFromLocalDbLocked(true)); - if (!okMedia) - _logger.Warn("Loading media from local db raised warnings, will reload from database."); - } - - if (!okContent) - LockAndLoadContent(scope => LoadContentFromDatabaseLocked(scope, true)); - - if (!okMedia) - LockAndLoadMedia(scope => LoadMediaFromDatabaseLocked(scope, true)); - - LockAndLoadDomains(); - } - catch (Exception ex) - { - _logger.Fatal(ex, "Panic, exception while loading cache data."); - throw; - } - - // finally, cache is ready! - _isReady = true; - } + }); private void InitializeRepositoryEvents() { @@ -1146,9 +1157,13 @@ namespace Umbraco.Web.PublishedCache.NuCache public override IPublishedSnapshot CreatePublishedSnapshot(string previewToken) { + EnsureCaches(); + // no cache, no joy - if (_isReady == false) + if (Volatile.Read(ref _isReady) == false) + { throw new InvalidOperationException("The published snapshot service has not properly initialized."); + } var preview = previewToken.IsNullOrWhiteSpace() == false; return new PublishedSnapshot(this, preview); @@ -1159,6 +1174,8 @@ namespace Umbraco.Web.PublishedCache.NuCache // even though the underlying elements may not change (store snapshots) public PublishedSnapshot.PublishedSnapshotElements GetElements(bool previewDefault) { + EnsureCaches(); + // note: using ObjectCacheAppCache for elements and snapshot caches // is not recommended because it creates an inner MemoryCache which is a heavy // thing - better use a dictionary-based cache which "just" creates a concurrent @@ -1805,6 +1822,8 @@ AND cmsContentNu.nodeId IS NULL public void Collect() { + EnsureCaches(); + var contentCollect = _contentStore.CollectAsync(); var mediaCollect = _mediaStore.CollectAsync(); System.Threading.Tasks.Task.WaitAll(contentCollect, mediaCollect); @@ -1814,8 +1833,17 @@ AND cmsContentNu.nodeId IS NULL #region Internals/Testing - internal ContentStore GetContentStore() => _contentStore; - internal ContentStore GetMediaStore() => _mediaStore; + internal ContentStore GetContentStore() + { + EnsureCaches(); + return _contentStore; + } + + internal ContentStore GetMediaStore() + { + EnsureCaches(); + return _mediaStore; + } #endregion } diff --git a/src/Umbraco.Web/Search/ExamineFinalComponent.cs b/src/Umbraco.Web/Search/ExamineFinalComponent.cs index 335d19baf0..6dd775ab64 100644 --- a/src/Umbraco.Web/Search/ExamineFinalComponent.cs +++ b/src/Umbraco.Web/Search/ExamineFinalComponent.cs @@ -29,26 +29,10 @@ namespace Umbraco.Web.Search _indexRebuilder = indexRebuilder; _mainDom = mainDom; _syncBootStateAccessor = syncBootStateAccessor; - - // must add the handler in the ctor because it will be too late in Initialize - // TODO: All of this boot synchronization for cold boot logic needs should be fixed in netcore - _syncBootStateAccessor.Booting += _syncBootStateAccessor_Booting; - } - - /// - /// Once the boot state is known we can see if we require rebuilds - /// - /// - /// - private void _syncBootStateAccessor_Booting(object sender, SyncBootState e) - { - UmbracoModule.RouteAttempt += UmbracoModule_RouteAttempt; } private void UmbracoModule_RouteAttempt(object sender, RoutableAttemptEventArgs e) { - UmbracoModule.RouteAttempt -= UmbracoModule_RouteAttempt; - if (!_initialized) { lock (_locker) @@ -58,6 +42,8 @@ namespace Umbraco.Web.Search { _initialized = true; + UmbracoModule.RouteAttempt -= UmbracoModule_RouteAttempt; + if (!_mainDom.IsMainDom) return; var bootState = _syncBootStateAccessor.GetSyncBootState(); @@ -74,12 +60,12 @@ namespace Umbraco.Web.Search } public void Initialize() - { + { + UmbracoModule.RouteAttempt += UmbracoModule_RouteAttempt; } public void Terminate() { - _syncBootStateAccessor.Booting -= _syncBootStateAccessor_Booting; } } } From eba3c82a86ceea384a4a063d7b5bb50edf5ad6ed Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 24 May 2021 12:51:40 -0700 Subject: [PATCH 134/182] Changes startup logic, no more "Boot" method, the component just handles events --- .../Sync/DatabaseServerMessenger.cs | 16 +++------ .../BatchedDatabaseServerMessenger.cs | 33 ++++++++----------- ...aseServerRegistrarAndMessengerComponent.cs | 12 ++++--- 3 files changed, 26 insertions(+), 35 deletions(-) diff --git a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs index 2b52a0948e..75ccf5e4f9 100644 --- a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs +++ b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs @@ -111,17 +111,11 @@ namespace Umbraco.Core.Sync #region Sync - /// - /// Boots the messenger. - /// - /// - /// Thread safety: this is NOT thread safe. Because it is NOT meant to run multi-threaded. - /// Callers MUST ensure thread-safety. - /// + [Obsolete("This is no longer used and will be removed in future versions")] protected void Boot() { - var bootState = GetSyncBootState(); - Booting?.Invoke(this, bootState); + // if called, just forces the boot logic + _ = GetSyncBootState(); } private SyncBootState BootInternal() @@ -545,8 +539,6 @@ namespace Umbraco.Core.Sync + "/D" + AppDomain.CurrentDomain.Id // eg 22 + "] " + Guid.NewGuid().ToString("N").ToUpper(); // make it truly unique - public event EventHandler Booting; - private string GetDistCacheFilePath(IGlobalSettings globalSettings) { var fileName = HttpRuntime.AppDomainAppId.ReplaceNonAlphanumericChars(string.Empty) + "-lastsynced.txt"; @@ -565,7 +557,7 @@ namespace Umbraco.Core.Sync #endregion - public SyncBootState GetSyncBootState() => _getSyncBootState.Value; + public virtual SyncBootState GetSyncBootState() => _getSyncBootState.Value; #region Notify refreshers diff --git a/src/Umbraco.Web/BatchedDatabaseServerMessenger.cs b/src/Umbraco.Web/BatchedDatabaseServerMessenger.cs index 6596fee245..f89f62eb3d 100644 --- a/src/Umbraco.Web/BatchedDatabaseServerMessenger.cs +++ b/src/Umbraco.Web/BatchedDatabaseServerMessenger.cs @@ -26,6 +26,7 @@ namespace Umbraco.Web public class BatchedDatabaseServerMessenger : DatabaseServerMessenger { private readonly IUmbracoDatabaseFactory _databaseFactory; + private readonly Lazy _syncBootState; [Obsolete("This overload should not be used, enableDistCalls has no effect")] [EditorBrowsable(EditorBrowsableState.Never)] @@ -39,28 +40,22 @@ namespace Umbraco.Web : base(runtime, scopeProvider, sqlContext, proflog, globalSettings, true, options) { _databaseFactory = databaseFactory; - } - - // invoked by DatabaseServerRegistrarAndMessengerComponent - internal void Startup() - { - UmbracoModule.EndRequest += UmbracoModule_EndRequest; - - if (_databaseFactory.CanConnect == false) + _syncBootState = new Lazy(() => { - Logger.Warn("Cannot connect to the database, distributed calls will not be enabled for this server."); - } - else - { - Boot(); - } + if (_databaseFactory.CanConnect == false) + { + Logger.Warn("Cannot connect to the database, distributed calls will not be enabled for this server."); + return SyncBootState.Unknown; + } + else + { + return base.GetSyncBootState(); + } + }); } - private void UmbracoModule_EndRequest(object sender, UmbracoRequestEventArgs e) - { - // will clear the batch - will remain in HttpContext though - that's ok - FlushBatch(); - } + // override to deal with database connectivity + public override SyncBootState GetSyncBootState() => _syncBootState.Value; protected override void DeliverRemote(ICacheRefresher refresher, MessageType messageType, IEnumerable ids = null, string json = null) { diff --git a/src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs b/src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs index ef13e166a5..d696ae5527 100644 --- a/src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs +++ b/src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs @@ -59,15 +59,19 @@ namespace Umbraco.Web.Compose if (_registrar != null || _messenger != null) { UmbracoModule.RouteAttempt += RegisterBackgroundTasksOnce; - } - - // must come last, as it references some _variables - _messenger?.Startup(); + UmbracoModule.EndRequest += UmbracoModule_EndRequest; + } } public void Terminate() { } + private void UmbracoModule_EndRequest(object sender, UmbracoRequestEventArgs e) + { + // will clear the batch - will remain in HttpContext though - that's ok + _messenger?.FlushBatch(); + } + /// /// Handle when a request is made /// From 3b8f9d5e33f8e150c9276ab57a9a7cf97e8ce6c5 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 25 May 2021 08:53:13 +0200 Subject: [PATCH 135/182] https://github.com/umbraco/Umbraco-CMS/issues/10295 - Use FileUploadConfigurationEditor in FileUploadPropertyEditor --- .../PropertyEditors/FileUploadPropertyEditor.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs index 536f3cbeb2..3511a4ce0e 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs @@ -33,30 +33,30 @@ namespace Umbraco.Cms.Core.PropertyEditors private readonly MediaFileManager _mediaFileManager; private readonly ContentSettings _contentSettings; private readonly UploadAutoFillProperties _uploadAutoFillProperties; - private readonly IDataTypeService _dataTypeService; - private readonly ILocalizationService _localizationService; private readonly ILocalizedTextService _localizedTextService; private readonly IContentService _contentService; + private readonly IIOHelper _ioHelper; public FileUploadPropertyEditor( IDataValueEditorFactory dataValueEditorFactory, MediaFileManager mediaFileManager, IOptions contentSettings, - IDataTypeService dataTypeService, - ILocalizationService localizationService, ILocalizedTextService localizedTextService, UploadAutoFillProperties uploadAutoFillProperties, - IContentService contentService) + IContentService contentService, + IIOHelper ioHelper) : base(dataValueEditorFactory) { _mediaFileManager = mediaFileManager ?? throw new ArgumentNullException(nameof(mediaFileManager)); _contentSettings = contentSettings.Value; - _dataTypeService = dataTypeService; - _localizationService = localizationService; _localizedTextService = localizedTextService; _uploadAutoFillProperties = uploadAutoFillProperties; _contentService = contentService; + _ioHelper = ioHelper; } + /// + protected override IConfigurationEditor CreateConfigurationEditor() => new FileUploadConfigurationEditor(_ioHelper); + /// /// Creates the corresponding property value editor. From ecdc687e39846908f44552d3c65de2dbc6cbafa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 25 May 2021 15:02:55 +0200 Subject: [PATCH 136/182] use label --- .../infiniteeditors/mediaentryeditor/mediaentryeditor.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediaentryeditor/mediaentryeditor.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediaentryeditor/mediaentryeditor.html index afa3451899..f4302551ab 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediaentryeditor/mediaentryeditor.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediaentryeditor/mediaentryeditor.html @@ -35,7 +35,7 @@ width="{{crop.width}}" max-size="75"> - {{crop.alias}} + {{crop.label}} From a68a6b2ac29ea4eab94b92101c301ce60af979d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 25 May 2021 14:58:07 +0200 Subject: [PATCH 137/182] added windowResizeListener --- .../directives/components/imaging/umbimagecrop.directive.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagecrop.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagecrop.directive.js index 744e4280db..60ba71d7a5 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagecrop.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagecrop.directive.js @@ -26,7 +26,7 @@ angular.module("umbraco.directives") forceUpdate: '@?' }, - link: function (scope, element, attrs) { + link: function (scope, element, attrs, windowResizeListener) { var unsubscribe = []; let sliderRef = null; From 630fa6a98906fa005d8eb796255594e97e6d2058 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 25 May 2021 14:57:19 +0200 Subject: [PATCH 138/182] dont remove outline --- .../src/less/components/umb-range-slider.less | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-range-slider.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-range-slider.less index cc5c17ba70..6c1980a6e4 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-range-slider.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-range-slider.less @@ -19,7 +19,6 @@ padding: 2px 6px; } .umb-range-slider .noUi-handle { - outline: none; cursor: grab; border-radius: 100px; border: none; From b07f6519e7a1c890b534502982612ce6b3fea293 Mon Sep 17 00:00:00 2001 From: Callum Whyte Date: Wed, 26 May 2021 07:36:21 +0100 Subject: [PATCH 139/182] Rename models builder modes (#10272) * Renaming AppData Models Builder mode * Renaming PureLive Models Builder mode to Runtime * ModelsBuilderAssembly attribute flags if models are "Live" or not * Updated ModelsMode names * Only add tag if it is not a pull request. Apparently this is not allowed when it is from a fork. * Revert "Only add tag if it is not a pull request. Apparently this is not allowed when it is from a fork." This reverts commit 92b33f3c Co-authored-by: Bjarke Berg --- .../Models/ModelsBuilderSettings.cs | 4 +- src/Umbraco.Core/Configuration/ModelsMode.cs | 10 +- .../Configuration/ModelsModeExtensions.cs | 16 +- .../PublishedModelFactoryExtensions.cs | 8 +- ...ctory.cs => IAutoPublishedModelFactory.cs} | 2 +- ...er.cs => AutoModelsNotificationHandler.cs} | 24 +- .../ModelsBuilder/Building/Builder.cs | 10 +- .../ModelsBuilder/Building/PropertyModel.cs | 3 +- .../ModelsBuilderAssemblyAttribute.cs | 6 +- .../ModelsBuilder/UmbracoServices.cs | 2 +- .../PublishedSnapshotService.cs | 10 +- .../ModelsBuilder/DashboardReport.cs | 6 +- .../ModelsBuilderDashboardController.cs | 1 + .../Filters/ModelBindingExceptionAttribute.cs | 5 +- ...acoBuilderDependencyInjectionExtensions.cs | 25 +- ...odelFactory.cs => InMemoryModelFactory.cs} | 30 +- .../ModelsBuilderNotificationHandler.cs | 12 +- .../RefreshingRazorViewEngine.cs | 20 +- src/Umbraco.Web.UI.Client/package-lock.json | 902 +++++++++--------- src/Umbraco.Web.UI.NetCore/appsettings.json | 2 +- src/Umbraco.Web.UI/web.Template.config | 2 +- 21 files changed, 554 insertions(+), 546 deletions(-) rename src/Umbraco.Core/Models/PublishedContent/{ILivePublishedModelFactory.cs => IAutoPublishedModelFactory.cs} (90%) rename src/Umbraco.Infrastructure/ModelsBuilder/{LiveModelsProvider.cs => AutoModelsNotificationHandler.cs} (83%) rename src/Umbraco.Web.Common/ModelsBuilder/{PureLiveModelFactory.cs => InMemoryModelFactory.cs} (97%) diff --git a/src/Umbraco.Core/Configuration/Models/ModelsBuilderSettings.cs b/src/Umbraco.Core/Configuration/Models/ModelsBuilderSettings.cs index 349d9f01da..76e7adc4b4 100644 --- a/src/Umbraco.Core/Configuration/Models/ModelsBuilderSettings.cs +++ b/src/Umbraco.Core/Configuration/Models/ModelsBuilderSettings.cs @@ -17,7 +17,7 @@ namespace Umbraco.Cms.Core.Configuration.Models /// /// Gets or sets a value for the models mode. /// - public ModelsMode ModelsMode { get; set; } = ModelsMode.PureLive; + public ModelsMode ModelsMode { get; set; } = ModelsMode.InMemoryAuto; /// /// Gets or sets a value for models namespace. @@ -39,7 +39,7 @@ namespace Umbraco.Cms.Core.Configuration.Models set { - if (!ModelsMode.IsLive()) + if (!ModelsMode.IsAuto()) { _flagOutOfDateModels = false; } diff --git a/src/Umbraco.Core/Configuration/ModelsMode.cs b/src/Umbraco.Core/Configuration/ModelsMode.cs index 1917f2b4cd..064e035892 100644 --- a/src/Umbraco.Core/Configuration/ModelsMode.cs +++ b/src/Umbraco.Core/Configuration/ModelsMode.cs @@ -18,22 +18,22 @@ namespace Umbraco.Cms.Core.Configuration /// When: a content type change occurs. /// /// The app does not restart. Models are available in views exclusively. - PureLive, + InMemoryAuto, /// - /// Generate models in AppData. + /// Generate models as *.cs files. /// When: generation is triggered. /// /// Generation can be triggered from the dashboard. The app does not restart. /// Models are not compiled and thus are not available to the project. - AppData, + SourceCodeManual, /// - /// Generate models in AppData. + /// Generate models as *.cs files. /// When: a content type change occurs, or generation is triggered. /// /// Generation can be triggered from the dashboard. The app does not restart. /// Models are not compiled and thus are not available to the project. - LiveAppData + SourceCodeAuto } } diff --git a/src/Umbraco.Core/Configuration/ModelsModeExtensions.cs b/src/Umbraco.Core/Configuration/ModelsModeExtensions.cs index f5f4e9e09c..f27d54b55d 100644 --- a/src/Umbraco.Core/Configuration/ModelsModeExtensions.cs +++ b/src/Umbraco.Core/Configuration/ModelsModeExtensions.cs @@ -8,21 +8,21 @@ namespace Umbraco.Extensions public static class ModelsModeExtensions { /// - /// Gets a value indicating whether the mode is LiveAnything or PureLive. + /// Gets a value indicating whether the mode is *Auto. /// - public static bool IsLive(this ModelsMode modelsMode) - => modelsMode == ModelsMode.PureLive || modelsMode == ModelsMode.LiveAppData; + public static bool IsAuto(this ModelsMode modelsMode) + => modelsMode == ModelsMode.InMemoryAuto || modelsMode == ModelsMode.SourceCodeAuto; /// - /// Gets a value indicating whether the mode is LiveAnything but not PureLive. + /// Gets a value indicating whether the mode is *Auto but not InMemory. /// - public static bool IsLiveNotPure(this ModelsMode modelsMode) - => modelsMode == ModelsMode.LiveAppData; + public static bool IsAutoNotInMemory(this ModelsMode modelsMode) + => modelsMode == ModelsMode.SourceCodeAuto; /// - /// Gets a value indicating whether the mode supports explicit generation (as opposed to pure live). + /// Gets a value indicating whether the mode supports explicit manual generation. /// public static bool SupportsExplicitGeneration(this ModelsMode modelsMode) - => modelsMode == ModelsMode.AppData || modelsMode == ModelsMode.LiveAppData; + => modelsMode == ModelsMode.SourceCodeManual || modelsMode == ModelsMode.SourceCodeAuto; } } diff --git a/src/Umbraco.Core/Extensions/PublishedModelFactoryExtensions.cs b/src/Umbraco.Core/Extensions/PublishedModelFactoryExtensions.cs index f10c22ef08..b714caf744 100644 --- a/src/Umbraco.Core/Extensions/PublishedModelFactoryExtensions.cs +++ b/src/Umbraco.Core/Extensions/PublishedModelFactoryExtensions.cs @@ -16,7 +16,7 @@ namespace Umbraco.Extensions /// public static bool IsLiveFactoryEnabled(this IPublishedModelFactory factory) { - if (factory is ILivePublishedModelFactory liveFactory) + if (factory is IAutoPublishedModelFactory liveFactory) { return liveFactory.Enabled; } @@ -26,14 +26,14 @@ namespace Umbraco.Extensions } /// - /// Sets a flag to reset the ModelsBuilder models if the is + /// Sets a flag to reset the ModelsBuilder models if the is /// /// - /// This does not recompile the pure live models, only sets a flag to tell models builder to recompile when they are requested. + /// This does not recompile the InMemory models, only sets a flag to tell models builder to recompile when they are requested. /// internal static void WithSafeLiveFactoryReset(this IPublishedModelFactory factory, Action action) { - if (factory is ILivePublishedModelFactory liveFactory) + if (factory is IAutoPublishedModelFactory liveFactory) { lock (liveFactory.SyncRoot) { diff --git a/src/Umbraco.Core/Models/PublishedContent/ILivePublishedModelFactory.cs b/src/Umbraco.Core/Models/PublishedContent/IAutoPublishedModelFactory.cs similarity index 90% rename from src/Umbraco.Core/Models/PublishedContent/ILivePublishedModelFactory.cs rename to src/Umbraco.Core/Models/PublishedContent/IAutoPublishedModelFactory.cs index f16fe0d3f5..2838297a8e 100644 --- a/src/Umbraco.Core/Models/PublishedContent/ILivePublishedModelFactory.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IAutoPublishedModelFactory.cs @@ -4,7 +4,7 @@ namespace Umbraco.Cms.Core.Models.PublishedContent /// /// Provides a live published model creation service. /// - public interface ILivePublishedModelFactory : IPublishedModelFactory + public interface IAutoPublishedModelFactory : IPublishedModelFactory { /// /// Gets an object that can be used to synchronize access to the factory. diff --git a/src/Umbraco.Infrastructure/ModelsBuilder/LiveModelsProvider.cs b/src/Umbraco.Infrastructure/ModelsBuilder/AutoModelsNotificationHandler.cs similarity index 83% rename from src/Umbraco.Infrastructure/ModelsBuilder/LiveModelsProvider.cs rename to src/Umbraco.Infrastructure/ModelsBuilder/AutoModelsNotificationHandler.cs index 000f4ab1b2..8017cfbf8e 100644 --- a/src/Umbraco.Infrastructure/ModelsBuilder/LiveModelsProvider.cs +++ b/src/Umbraco.Infrastructure/ModelsBuilder/AutoModelsNotificationHandler.cs @@ -2,34 +2,39 @@ using System; using System.Threading; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Runtime; using Umbraco.Cms.Infrastructure.ModelsBuilder.Building; using Umbraco.Extensions; +using Umbraco.Cms.Core.Configuration; namespace Umbraco.Cms.Infrastructure.ModelsBuilder { - // supports LiveAppData - but not PureLive - public sealed class LiveModelsProvider : INotificationHandler, + /// + /// Notification handlers used by . + /// + /// + /// supports mode but not mode. + /// + public sealed class AutoModelsNotificationHandler : INotificationHandler, INotificationHandler, INotificationHandler, INotificationHandler { private static int s_req; - private readonly ILogger _logger; + private readonly ILogger _logger; private readonly ModelsBuilderSettings _config; private readonly ModelsGenerator _modelGenerator; private readonly ModelsGenerationError _mbErrors; private readonly IMainDom _mainDom; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public LiveModelsProvider( - ILogger logger, + public AutoModelsNotificationHandler( + ILogger logger, IOptions config, ModelsGenerator modelGenerator, ModelsGenerationError mbErrors, @@ -42,8 +47,8 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder _mainDom = mainDom; } - // we do not manage pure live here - internal bool IsEnabled => _config.ModelsMode.IsLiveNotPure(); + // we do not manage InMemory models here + internal bool IsEnabled => _config.ModelsMode.IsAutoNotInMemory(); /// /// Handles the notification @@ -74,6 +79,7 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder } _logger.LogDebug("Requested to generate models."); + Interlocked.Exchange(ref s_req, 1); } diff --git a/src/Umbraco.Infrastructure/ModelsBuilder/Building/Builder.cs b/src/Umbraco.Infrastructure/ModelsBuilder/Building/Builder.cs index e5d1b9d51f..bb157e5c7a 100644 --- a/src/Umbraco.Infrastructure/ModelsBuilder/Building/Builder.cs +++ b/src/Umbraco.Infrastructure/ModelsBuilder/Building/Builder.cs @@ -93,12 +93,12 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder.Building { TypeModel.MapModelTypes(_typeModels, ModelsNamespace); - var pureLive = Config.ModelsMode == ModelsMode.PureLive; + var isInMemoryMode = Config.ModelsMode == ModelsMode.InMemoryAuto; // for the first two of these two tests, - // always throw, even in purelive: cannot happen unless ppl start fidling with attributes to rename + // always throw, even in InMemory mode: cannot happen unless ppl start fidling with attributes to rename // things, and then they should pay attention to the generation error log - there's no magic here - // for the last one, don't throw in purelive, see comment + // for the last one, don't throw in InMemory mode, see comment // ensure we have no duplicates type names foreach (var xx in _typeModels.GroupBy(x => x.ClrName).Where(x => x.Count() > 1)) @@ -118,12 +118,12 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder.Building { foreach (var xx in typeModel.Properties.Where(x => x.ClrName == typeModel.ClrName)) { - if (!pureLive) + if (!isInMemoryMode) throw new InvalidOperationException($"The model class for content type with alias \"{typeModel.Alias}\" is named \"{xx.ClrName}\"." + $" CSharp does not support using the same name for the property with alias \"{xx.Alias}\"." + " Consider using an attribute to assign a different name to the property."); - // for purelive, will we generate a commented out properties with an error message, + // in InMemory mode we generate commented out properties with an error message, // instead of throwing, because then it kills the sites and ppl don't understand why xx.AddError($"The class {typeModel.ClrName} cannot implement this property, because" + $" CSharp does not support naming the property with alias \"{xx.Alias}\" with the same name as content type with alias \"{typeModel.Alias}\"." diff --git a/src/Umbraco.Infrastructure/ModelsBuilder/Building/PropertyModel.cs b/src/Umbraco.Infrastructure/ModelsBuilder/Building/PropertyModel.cs index fbb29e3ebd..30f5ecd945 100644 --- a/src/Umbraco.Infrastructure/ModelsBuilder/Building/PropertyModel.cs +++ b/src/Umbraco.Infrastructure/ModelsBuilder/Building/PropertyModel.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Umbraco.Cms.Core.Configuration; namespace Umbraco.Cms.Infrastructure.ModelsBuilder.Building { @@ -46,7 +47,7 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder.Building /// /// This should be null, unless something prevents the property from being /// generated, and then the value should explain what. This can be used to generate - /// commented out code eg in PureLive. + /// commented out code eg in mode. public List Errors; /// diff --git a/src/Umbraco.Infrastructure/ModelsBuilder/ModelsBuilderAssemblyAttribute.cs b/src/Umbraco.Infrastructure/ModelsBuilder/ModelsBuilderAssemblyAttribute.cs index 6754060d6f..37199c0990 100644 --- a/src/Umbraco.Infrastructure/ModelsBuilder/ModelsBuilderAssemblyAttribute.cs +++ b/src/Umbraco.Infrastructure/ModelsBuilder/ModelsBuilderAssemblyAttribute.cs @@ -9,10 +9,10 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder public sealed class ModelsBuilderAssemblyAttribute : Attribute { /// - /// Gets or sets a value indicating whether the assembly is a PureLive assembly. + /// Gets or sets a value indicating whether the assembly is a InMemory assembly. /// - /// A Models Builder assembly can be either PureLive or normal Dll. - public bool PureLive { get; set; } + /// A Models Builder assembly can be either InMemory or a normal Dll. + public bool IsInMemory { get; set; } /// /// Gets or sets a hash value representing the state of the custom source code files diff --git a/src/Umbraco.Infrastructure/ModelsBuilder/UmbracoServices.cs b/src/Umbraco.Infrastructure/ModelsBuilder/UmbracoServices.cs index b4963639c4..a02d974b1c 100644 --- a/src/Umbraco.Infrastructure/ModelsBuilder/UmbracoServices.cs +++ b/src/Umbraco.Infrastructure/ModelsBuilder/UmbracoServices.cs @@ -43,7 +43,7 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder { var types = new List(); - // TODO: this will require 3 rather large SQL queries on startup in PureLive. I know that these will be cached after lookup but it will slow + // TODO: this will require 3 rather large SQL queries on startup in ModelsMode.InMemoryAuto mode. I know that these will be cached after lookup but it will slow // down startup time ... BUT these queries are also used in NuCache on startup so we can't really avoid them. Maybe one day we can // load all of these in in one query and still have them cached per service, and/or somehow improve the perf of these since they are used on startup // in more than one place. diff --git a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs index 9c08a2fc5a..b236f8ccd0 100644 --- a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs @@ -501,7 +501,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // be processed as soon as we are configured and the messenger processes instructions. // note: notifications for content type and data type changes should be invoked with the - // pure live model factory, if any, locked and refreshed - see ContentTypeCacheRefresher and + // InMemoryModelFactory, if any, locked and refreshed - see ContentTypeCacheRefresher and // DataTypeCacheRefresher public void Notify(ContentCacheRefresher.JsonPayload[] payloads, out bool draftChanged, out bool publishedChanged) @@ -721,17 +721,17 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache if (_publishedModelFactory.IsLiveFactoryEnabled()) { - // In the case of Pure Live - we actually need to refresh all of the content and the media + // In the case of ModelsMode.InMemoryAuto generated models - we actually need to refresh all of the content and the media // see https://github.com/umbraco/Umbraco-CMS/issues/5671 - // The underlying issue is that in Pure Live the ILivePublishedModelFactory will re-compile all of the classes/models + // The underlying issue is that in ModelsMode.InMemoryAuto mode the IAutoPublishedModelFactory will re-compile all of the classes/models // into a new DLL for the application which includes both content types and media types. // Since the models in the cache are based on these actual classes, all of the objects in the cache need to be updated // to use the newest version of the class. // NOTE: Ideally this can be run on background threads here which would prevent blocking the UI - // as is the case when saving a content type. Intially one would think that it won't be any different + // as is the case when saving a content type. Initially one would think that it won't be any different // between running this here or in another background thread immediately after with regards to how the - // UI will respond because we already know between calling `WithSafeLiveFactoryReset` to reset the PureLive models + // UI will respond because we already know between calling `WithSafeLiveFactoryReset` to reset the generated models // and this code here, that many front-end requests could be attempted to be processed. If that is the case, those pages are going to get a // model binding error and our ModelBindingExceptionFilter is going to to its magic to reload those pages so the end user is none the wiser. // So whether or not this executes 'here' or on a background thread immediately wouldn't seem to make any difference except that we can return diff --git a/src/Umbraco.Web.BackOffice/ModelsBuilder/DashboardReport.cs b/src/Umbraco.Web.BackOffice/ModelsBuilder/DashboardReport.cs index 9620e7c08f..cb0eacbcae 100644 --- a/src/Umbraco.Web.BackOffice/ModelsBuilder/DashboardReport.cs +++ b/src/Umbraco.Web.BackOffice/ModelsBuilder/DashboardReport.cs @@ -48,13 +48,13 @@ namespace Umbraco.Cms.Web.BackOffice.ModelsBuilder case ModelsMode.Nothing: sb.Append("Strongly typed models are not generated. All content and cache will operate from instance of IPublishedContent only."); break; - case ModelsMode.PureLive: + case ModelsMode.InMemoryAuto: sb.Append("Strongly typed models are re-generated on startup and anytime schema changes (i.e. Content Type) are made. No recompilation necessary but the generated models are not available to code outside of Razor."); break; - case ModelsMode.AppData: + case ModelsMode.SourceCodeManual: sb.Append("Strongly typed models are generated on demand. Recompilation is necessary and models are available to all CSharp code."); break; - case ModelsMode.LiveAppData: + case ModelsMode.SourceCodeAuto: sb.Append("Strong typed models are generated on demand and anytime schema changes (i.e. Content Type) are made. Recompilation is necessary and models are available to all CSharp code."); break; } diff --git a/src/Umbraco.Web.BackOffice/ModelsBuilder/ModelsBuilderDashboardController.cs b/src/Umbraco.Web.BackOffice/ModelsBuilder/ModelsBuilderDashboardController.cs index 07000c069c..5e33fa1835 100644 --- a/src/Umbraco.Web.BackOffice/ModelsBuilder/ModelsBuilderDashboardController.cs +++ b/src/Umbraco.Web.BackOffice/ModelsBuilder/ModelsBuilderDashboardController.cs @@ -50,6 +50,7 @@ namespace Umbraco.Cms.Web.BackOffice.ModelsBuilder if (!_config.ModelsMode.SupportsExplicitGeneration()) { var result2 = new BuildResult { Success = false, Message = "Models generation is not enabled." }; + return Ok(result2); } diff --git a/src/Umbraco.Web.Common/Filters/ModelBindingExceptionAttribute.cs b/src/Umbraco.Web.Common/Filters/ModelBindingExceptionAttribute.cs index 87d611947e..5b8421b9cb 100644 --- a/src/Umbraco.Web.Common/Filters/ModelBindingExceptionAttribute.cs +++ b/src/Umbraco.Web.Common/Filters/ModelBindingExceptionAttribute.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Configuration; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Web.Common.ModelBinders; @@ -17,7 +18,7 @@ namespace Umbraco.Cms.Web.Common.Filters /// In which case it returns a redirect to the same page after 1 sec if not in debug mode. /// /// - /// This is only enabled when running PureLive + /// This is only enabled when using mode /// public sealed class ModelBindingExceptionAttribute : TypeFilterAttribute { @@ -69,7 +70,7 @@ namespace Umbraco.Cms.Web.Common.Filters /// /// /// ModelBindingException: - /// Cannot bind source content type Umbraco.Web.PublishedModels.Home to model type Umbraco.Web.PublishedModels.Home. Both view and content models are PureLive, with different versions. The application is in an unstable state and is going to be restarted. The application is restarting now. + /// Cannot bind source content type Umbraco.Web.PublishedModels.Home to model type Umbraco.Web.PublishedModels.Home. Both view and content models are generated models, with different versions. The application is in an unstable state and is going to be restarted. The application is restarting now. /// /// private bool IsMessageAboutTheSameModelType(string exceptionMessage) diff --git a/src/Umbraco.Web.Common/ModelsBuilder/DependencyInjection/UmbracoBuilderDependencyInjectionExtensions.cs b/src/Umbraco.Web.Common/ModelsBuilder/DependencyInjection/UmbracoBuilderDependencyInjectionExtensions.cs index 93534f6da4..a1aa5b24d8 100644 --- a/src/Umbraco.Web.Common/ModelsBuilder/DependencyInjection/UmbracoBuilderDependencyInjectionExtensions.cs +++ b/src/Umbraco.Web.Common/ModelsBuilder/DependencyInjection/UmbracoBuilderDependencyInjectionExtensions.cs @@ -31,7 +31,7 @@ using Umbraco.Cms.Web.Common.ModelsBuilder; * * The way that RazorReferenceManager works is by resolving references from the ApplicationPartsManager - either by * an application part that is specifically an ICompilationReferencesProvider or an AssemblyPart. So to fulfill this - * requirement, we add the MB assembly to the assembly parts manager within the PureLiveModelFactory when the assembly + * requirement, we add the MB assembly to the assembly parts manager within the InMemoryModelFactory when the assembly * is (re)generated. But due to the above restrictions, when re-generating, this will have no effect since the references * have already been resolved with the LazyInitializer in the RazorReferenceManager. There is a known public API * where you can add reference paths to the runtime razor compiler via it's IOptions: MvcRazorRuntimeCompilationOptions @@ -69,7 +69,7 @@ using Umbraco.Cms.Web.Common.ModelsBuilder; * with razor defaults before ours are added. * - We replace the default implementation of IRazorViewEngine with our own. This is a wrapping service that wraps the default RazorViewEngine instance. * The ctor for this service takes in a Factory method to re-construct the default RazorViewEngine and all of it's dependency graph. - * - When the PureLive models change, the Factory is invoked and the default razor services are all re-created, thus clearing their caches and the newly + * - When the models change, the Factory is invoked and the default razor services are all re-created, thus clearing their caches and the newly * created instance is wrapped. The RazorViewEngine is the only service that needs to be replaced and wrapped for this to work because it's dependency * graph includes all of the above mentioned services, all the way up to the RazorProjectEngine and it's LazyMetadataReferenceFeature. */ @@ -95,35 +95,34 @@ namespace Umbraco.Extensions builder.Services.Add(umbServices); - builder.AddPureLiveRazorEngine(); + builder.AddInMemoryModelsRazorEngine(); // TODO: I feel like we could just do builder.AddNotificationHandler() and it // would automatically just register for all implemented INotificationHandler{T}? builder.AddNotificationHandler(); builder.AddNotificationHandler(); builder.AddNotificationHandler(); - builder.AddNotificationHandler(); - builder.AddNotificationHandler(); - builder.AddNotificationHandler(); - builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + builder.AddNotificationHandler(); builder.Services.AddSingleton(); - builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.AddNotificationHandler(); builder.AddNotificationHandler(); builder.Services.AddSingleton(); - builder.Services.AddSingleton(); + builder.Services.AddSingleton(); // This is what the community MB would replace, all of the above services are fine to be registered // even if the community MB is in place. builder.Services.AddSingleton(factory => { ModelsBuilderSettings config = factory.GetRequiredService>().Value; - if (config.ModelsMode == ModelsMode.PureLive) + if (config.ModelsMode == ModelsMode.InMemoryAuto) { - return factory.GetRequiredService(); + return factory.GetRequiredService(); } else { @@ -140,7 +139,7 @@ namespace Umbraco.Extensions return builder; } - private static IUmbracoBuilder AddPureLiveRazorEngine(this IUmbracoBuilder builder) + private static IUmbracoBuilder AddInMemoryModelsRazorEngine(this IUmbracoBuilder builder) { // See notes in RefreshingRazorViewEngine for information on what this is doing. @@ -160,7 +159,7 @@ namespace Umbraco.Extensions // is produced, if we don't re-create the container then it will just return the same instance. ServiceProvider recreatedServices = initialCollection.BuildServiceProvider(); return recreatedServices.GetRequiredService(); - }, s.GetRequiredService())); + }, s.GetRequiredService())); return builder; } diff --git a/src/Umbraco.Web.Common/ModelsBuilder/PureLiveModelFactory.cs b/src/Umbraco.Web.Common/ModelsBuilder/InMemoryModelFactory.cs similarity index 97% rename from src/Umbraco.Web.Common/ModelsBuilder/PureLiveModelFactory.cs rename to src/Umbraco.Web.Common/ModelsBuilder/InMemoryModelFactory.cs index f2e7a4f973..5958316729 100644 --- a/src/Umbraco.Web.Common/ModelsBuilder/PureLiveModelFactory.cs +++ b/src/Umbraco.Web.Common/ModelsBuilder/InMemoryModelFactory.cs @@ -25,14 +25,14 @@ using File = System.IO.File; namespace Umbraco.Cms.Web.Common.ModelsBuilder { - internal class PureLiveModelFactory : ILivePublishedModelFactory, IRegisteredObject + internal class InMemoryModelFactory : IAutoPublishedModelFactory, IRegisteredObject { private Infos _infos = new Infos { ModelInfos = null, ModelTypeMap = new Dictionary() }; private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(); private volatile bool _hasModels; // volatile 'cos reading outside lock private bool _pendingRebuild; private readonly IProfilingLogger _profilingLogger; - private readonly ILogger _logger; + private readonly ILogger _logger; private readonly FileSystemWatcher _watcher; private int _ver; private int _skipver; @@ -54,10 +54,10 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder - public PureLiveModelFactory( + public InMemoryModelFactory( Lazy umbracoServices, IProfilingLogger profilingLogger, - ILogger logger, + ILogger logger, IOptions config, IHostingEnvironment hostingEnvironment, IApplicationShutdownRegistry hostingLifetime, @@ -106,7 +106,7 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder private UmbracoServices UmbracoServices => _umbracoServices.Value; /// - /// Gets the currently loaded pure live models assembly + /// Gets the currently loaded Live models assembly /// /// /// Can be null @@ -134,7 +134,7 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder } /// - public bool Enabled => _config.ModelsMode == ModelsMode.PureLive; + public bool Enabled => _config.ModelsMode == ModelsMode.InMemoryAuto; /// /// Handle the event when a reference cannot be resolved from the default context and return our custom MB assembly reference if we have one @@ -291,7 +291,7 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder // we don't have models, // either they haven't been loaded from the cache yet // or they have been reseted and are pending a rebuild - using (_profilingLogger.DebugDuration("Get models.", "Got models.")) + using (_profilingLogger.DebugDuration("Get models.", "Got models.")) { try { @@ -316,7 +316,7 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder { _logger.LogError(e, "Failed to build models."); _logger.LogWarning("Running without models."); // be explicit - _errors.Report("Failed to build PureLive models.", e); + _errors.Report("Failed to build InMemory models.", e); } finally { @@ -453,7 +453,7 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder assembly = ReloadAssembly(dllPath); ModelsBuilderAssemblyAttribute attr = assembly.GetCustomAttribute(); - if (attr != null && attr.PureLive && attr.SourceHash == currentHash) + if (attr != null && attr.IsInMemory && attr.SourceHash == currentHash) { // if we were to resume at that revision, then _ver would keep increasing // and that is probably a bad idea - so, we'll always rebuild starting at @@ -517,11 +517,11 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder // generate code, save var code = GenerateModelsCode(typeModels); // add extra attributes, - // PureLiveAssembly helps identifying Assemblies that contain PureLive models + // IsLive=true helps identifying Assemblies that contain live models // AssemblyVersion is so that we have a different version for each rebuild var ver = _ver == _skipver ? ++_ver : _ver; _ver++; - string mbAssemblyDirective = $@"[assembly:ModelsBuilderAssembly(PureLive = true, SourceHash = ""{currentHash}"")] + string mbAssemblyDirective = $@"[assembly:ModelsBuilderAssembly(IsInMemory = true, SourceHash = ""{currentHash}"")] [assembly:System.Reflection.AssemblyVersion(""0.0.0.{ver}"")]"; code = code.Replace("//ASSATTR", mbAssemblyDirective); File.WriteAllText(modelsSrcFile, code); @@ -587,7 +587,7 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder return Path.Combine(dirInfo.FullName, $"generated.cs{currentHash}.dll"); } - + private void ClearOnFailingToCompile(string dllPathFile, string modelsHashFile, string projFile) { _logger.LogDebug("Failed to compile."); @@ -676,7 +676,7 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder private string GenerateModelsCode(IList typeModels) { - + if (!Directory.Exists(_pureLiveDirectory.Value)) { Directory.CreateDirectory(_pureLiveDirectory.Value); @@ -696,7 +696,7 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder return code; } - + private static string GenerateModelsProj(IDictionary files) { @@ -767,7 +767,7 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder //if (_building && OurFiles.Contains(changed)) //{ - // //_logger.LogInformation("Ignoring files self-changes."); + // //_logger.LogInformation("Ignoring files self-changes."); // return; //} diff --git a/src/Umbraco.Web.Common/ModelsBuilder/ModelsBuilderNotificationHandler.cs b/src/Umbraco.Web.Common/ModelsBuilder/ModelsBuilderNotificationHandler.cs index 100dff7352..90a48c4017 100644 --- a/src/Umbraco.Web.Common/ModelsBuilder/ModelsBuilderNotificationHandler.cs +++ b/src/Umbraco.Web.Common/ModelsBuilder/ModelsBuilderNotificationHandler.cs @@ -161,17 +161,17 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder } // both are ModelsBuilder types - var pureSource = sourceAttr.PureLive; - var pureModel = modelAttr.PureLive; + var pureSource = sourceAttr.IsInMemory; + var pureModel = modelAttr.IsInMemory; - if (sourceAttr.PureLive || modelAttr.PureLive) + if (sourceAttr.IsInMemory || modelAttr.IsInMemory) { if (pureSource == false || pureModel == false) { // only one is pure - report, but better not restart (loops?) notification.Message.Append(pureSource - ? " The content model is PureLive, but the view model is not." - : " The view model is PureLive, but the content model is not."); + ? " The content model is in memory generated, but the view model is not." + : " The view model is in memory generated, but the content model is not."); notification.Message.Append(" The application is in an unstable state and should be restarted."); } else @@ -180,7 +180,7 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder // if same version... makes no sense... and better not restart (loops?) Version sourceVersion = notification.SourceType.Assembly.GetName().Version; Version modelVersion = notification.ModelType.Assembly.GetName().Version; - notification.Message.Append(" Both view and content models are PureLive, with "); + notification.Message.Append(" Both view and content models are in memory generated, with "); notification.Message.Append(sourceVersion == modelVersion ? "same version. The application is in an unstable state and should be restarted." : "different versions. The application is in an unstable state and should be restarted."); diff --git a/src/Umbraco.Web.Common/ModelsBuilder/RefreshingRazorViewEngine.cs b/src/Umbraco.Web.Common/ModelsBuilder/RefreshingRazorViewEngine.cs index c49668451a..a541d61368 100644 --- a/src/Umbraco.Web.Common/ModelsBuilder/RefreshingRazorViewEngine.cs +++ b/src/Umbraco.Web.Common/ModelsBuilder/RefreshingRazorViewEngine.cs @@ -19,7 +19,7 @@ using Microsoft.AspNetCore.Mvc.ViewEngines; * * The way that RazorReferenceManager works is by resolving references from the ApplicationPartsManager - either by * an application part that is specifically an ICompilationReferencesProvider or an AssemblyPart. So to fulfill this - * requirement, we add the MB assembly to the assembly parts manager within the PureLiveModelFactory when the assembly + * requirement, we add the MB assembly to the assembly parts manager within the InMemoryModelFactory when the assembly * is (re)generated. But due to the above restrictions, when re-generating, this will have no effect since the references * have already been resolved with the LazyInitializer in the RazorReferenceManager. * @@ -55,7 +55,7 @@ using Microsoft.AspNetCore.Mvc.ViewEngines; * with razor defaults before ours are added. * - We replace the default implementation of IRazorViewEngine with our own. This is a wrapping service that wraps the default RazorViewEngine instance. * The ctor for this service takes in a Factory method to re-construct the default RazorViewEngine and all of it's dependency graph. - * - When the PureLive models change, the Factory is invoked and the default razor services are all re-created, thus clearing their caches and the newly + * - When the models change, the Factory is invoked and the default razor services are all re-created, thus clearing their caches and the newly * created instance is wrapped. The RazorViewEngine is the only service that needs to be replaced and wrapped for this to work because it's dependency * graph includes all of the above mentioned services, all the way up to the RazorProjectEngine and it's LazyMetadataReferenceFeature. */ @@ -66,14 +66,14 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder /// Custom that wraps aspnetcore's default implementation /// /// - /// This is used so that when new PureLive models are built, the entire razor stack is re-constructed so all razor + /// This is used so that when new models are built, the entire razor stack is re-constructed so all razor /// caches and assembly references, etc... are cleared. /// internal class RefreshingRazorViewEngine : IRazorViewEngine, IDisposable { private IRazorViewEngine _current; private bool _disposedValue; - private readonly PureLiveModelFactory _pureLiveModelFactory; + private readonly InMemoryModelFactory _inMemoryModelFactory; private readonly Func _defaultRazorViewEngineFactory; private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(); @@ -83,19 +83,19 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder /// /// A factory method used to re-construct the default aspnetcore /// - /// The - public RefreshingRazorViewEngine(Func defaultRazorViewEngineFactory, PureLiveModelFactory pureLiveModelFactory) + /// The + public RefreshingRazorViewEngine(Func defaultRazorViewEngineFactory, InMemoryModelFactory inMemoryModelFactory) { - _pureLiveModelFactory = pureLiveModelFactory; + _inMemoryModelFactory = inMemoryModelFactory; _defaultRazorViewEngineFactory = defaultRazorViewEngineFactory; _current = _defaultRazorViewEngineFactory(); - _pureLiveModelFactory.ModelsChanged += PureLiveModelFactory_ModelsChanged; + _inMemoryModelFactory.ModelsChanged += InMemoryModelFactoryModelsChanged; } /// - /// When the pure live models change, re-construct the razor stack + /// When the models change, re-construct the razor stack /// - private void PureLiveModelFactory_ModelsChanged(object sender, EventArgs e) + private void InMemoryModelFactoryModelsChanged(object sender, EventArgs e) { _locker.EnterWriteLock(); try diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 19b9b62503..f19063fecd 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -14,7 +14,7 @@ "@babel/core": { "version": "7.6.4", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.6.4.tgz", - "integrity": "sha512-Rm0HGw101GY8FTzpWSyRbki/jzq+/PkNQJ+nSulrdY6gFGOsNseCqD6KHRYe2E+EdzuBdr2pxCp6s4Uk6eJ+XQ==", + "integrity": "sha1-br2f4Akl9sPhd7tyahiLX1eAiP8=", "dev": true, "requires": { "@babel/code-frame": "^7.5.5", @@ -739,7 +739,7 @@ "@babel/preset-env": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.6.3.tgz", - "integrity": "sha512-CWQkn7EVnwzlOdR5NOm2+pfgSNEZmvGjOhlCHBDq0J8/EStr+G+FvPEiz9B56dR6MoiUFjXhfE4hjLoAKKJtIQ==", + "integrity": "sha1-nhvwWi4taHA20kxA5GOdxGzvInE=", "dev": true, "requires": { "@babel/helper-module-imports": "^7.0.0", @@ -865,7 +865,7 @@ "@gulp-sourcemaps/identity-map": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-1.0.2.tgz", - "integrity": "sha512-ciiioYMLdo16ShmfHBXJBOFm3xPC4AuwO4xeRpFeHz7WK9PYsWCmigagG2XyzZpubK4a3qNKoUBDhbzHfa50LQ==", + "integrity": "sha1-Hm/l2AJ7HyhdwNMXYvVmvM1z1ak=", "dev": true, "requires": { "acorn": "^5.0.3", @@ -884,7 +884,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true } } @@ -948,7 +948,7 @@ "@sindresorhus/is": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz", - "integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==", + "integrity": "sha1-mgb08TfuhNffBGDB/bETX/psUP0=", "dev": true, "optional": true }, @@ -970,7 +970,7 @@ "@types/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "integrity": "sha1-PcoOPzOyAPx9ETnAzZbBJoyt/Z0=", "dev": true }, "@types/node": { @@ -994,7 +994,7 @@ "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "integrity": "sha1-UxvHJlF6OytB+FACHGzBXqq1B80=", "dev": true, "requires": { "mime-types": "~2.1.24", @@ -1004,7 +1004,7 @@ "accord": { "version": "0.29.0", "resolved": "https://registry.npmjs.org/accord/-/accord-0.29.0.tgz", - "integrity": "sha512-3OOR92FTc2p5/EcOzPcXp+Cbo+3C15nV9RXHlOUBCBpHhcB+0frbSNR9ehED/o7sTcyGVtqGJpguToEdlXhD0w==", + "integrity": "sha1-t0HBdtAENcWSnUZt/oz2vukzseQ=", "dev": true, "requires": { "convert-source-map": "^1.5.0", @@ -1051,7 +1051,7 @@ "ace-builds": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.4.2.tgz", - "integrity": "sha512-M1JtZctO2Zg+1qeGUFZXtYKsyaRptqQtqpVzlj80I0NzGW9MF3um0DBuizIvQlrPYUlTdm+wcOPZpZoerkxQdA==" + "integrity": "sha1-avwuQ6e17/3ETYQHQ2EShSVo6A0=" }, "acorn": { "version": "7.1.0", @@ -1148,12 +1148,12 @@ "angular-animate": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-animate/-/angular-animate-1.7.5.tgz", - "integrity": "sha512-kU/fHIGf2a4a3bH7E1tzALTHk+QfoUSCK9fEcMFisd6ZWvNDwPzXWAilItqOC3EDiAXPmGHaNc9/aXiD9xrAxQ==" + "integrity": "sha1-H/xsKpze4ieiunnMbNj3HsRNtdw=" }, "angular-aria": { "version": "1.7.9", "resolved": "https://registry.npmjs.org/angular-aria/-/angular-aria-1.7.9.tgz", - "integrity": "sha512-luI3Jemd1AbOQW0krdzfEG3fM0IFtLY0bSSqIDEx3POE0XjKIC1MkrO8Csyq9PPgueLphyAPofzUwZ8YeZ88SA==" + "integrity": "sha1-kMYYlf+9h26VkVIisyp70AcK9+M=" }, "angular-chart.js": { "version": "1.1.1", @@ -1178,12 +1178,12 @@ "angular-cookies": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-cookies/-/angular-cookies-1.7.5.tgz", - "integrity": "sha512-/8xvvSl/Z9Vwu8ChRm+OQE3vmli8Icwl8uTYkHqD7j7cknJP9kNaf7SgsENlsLVtOqLE/I7TCFYrSx3bmSeNQA==" + "integrity": "sha1-HFqzwFzcQ/F3e+lQbmRYfLNUNjQ=" }, "angular-dynamic-locale": { "version": "0.1.37", "resolved": "https://registry.npmjs.org/angular-dynamic-locale/-/angular-dynamic-locale-0.1.37.tgz", - "integrity": "sha512-m5Kyk8W8/mOZSqRxuByOwHBjv8labLBAgvl0Z3iQx2xT/tWCqb94imKUPwumudszdPDjxeopwyucQvm8Sw7ogw==", + "integrity": "sha1-fon70uxFvdaryJ82zaiJODjkk1Q=", "requires": { "@types/angular": "^1.6.25" } @@ -1191,7 +1191,7 @@ "angular-i18n": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-i18n/-/angular-i18n-1.7.5.tgz", - "integrity": "sha512-52+Jpt8HRJV2bqSbSU6fWkwOvGzj/DxbNpKXxnTuCS9heuJrlm77BS/lhrF4BA8+Uudnh7npr5/yRELobP+8Yw==" + "integrity": "sha1-Lie2Thl3qMa2sFHFHQF1xtTcglI=" }, "angular-local-storage": { "version": "0.7.1", @@ -1201,32 +1201,32 @@ "angular-messages": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-messages/-/angular-messages-1.7.5.tgz", - "integrity": "sha512-YDpJpFLyrIgZjE/sIAjgww1y6r3QqXBJbNDI0QjftD37vHXLkwvAOo3A4bxPw8BikyGLcJrFrgf6hRAzntJIWA==" + "integrity": "sha1-fC/XgTFaQ6GYOLEX2gFCqYhFThQ=" }, "angular-mocks": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-mocks/-/angular-mocks-1.7.5.tgz", - "integrity": "sha512-I+Ue2Bkx6R9W5178DYrNvzjIdGh4wKKoCWsgz8dc7ysH4mA70Q3M9v5xRF0RUu7r+2CZj+nDeUecvh2paxcYvg==" + "integrity": "sha1-yLq6WgbtYLk0aXAmtJIWliavOEs=" }, "angular-route": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-route/-/angular-route-1.7.5.tgz", - "integrity": "sha512-7KfyEVVOWTI+jTY/j5rUNCIHGRyeCOx7YqZI/Ci3IbDK7GIsy6xH+hS5ai0Xi0sLjzDZ0PUDO4gBn+K0dVtlOg==" + "integrity": "sha1-NKNkjEB6FKAw0HXPSFMY4zuiPw4=" }, "angular-sanitize": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-sanitize/-/angular-sanitize-1.7.5.tgz", - "integrity": "sha512-wjKCJOIwrkEvfD0keTnKGi6We13gtoCAQIHcdoqyoo3gwvcgNfYymVQIS3+iCGVcjfWz0jHuS3KgB4ysRWsTTA==" + "integrity": "sha1-ddSeFQccqccFgedtIJQPJjcuJNI=" }, "angular-touch": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-touch/-/angular-touch-1.7.5.tgz", - "integrity": "sha512-XNAZNG0RA1mtdwBJheViCF1H/7wOygp4MLIfs5y1K+rne6AeaYKZcV6EJs9fvgfLKLO6ecm1+3J8hoCkdhhxQw==" + "integrity": "sha1-7SYyKmhfApmyPLauqYNMEZQk2kY=" }, "angular-ui-sortable": { "version": "0.19.0", "resolved": "https://registry.npmjs.org/angular-ui-sortable/-/angular-ui-sortable-0.19.0.tgz", - "integrity": "sha512-u/uc981Nzg4XN1bMU9qKleMTSt7F1XjMWnyGw6gxPLIeQeLZm8jWNy7tj8y2r2HmvzXFbQVq2z6rObznFKAekQ==", + "integrity": "sha1-SsQ5H8TU3lcRDbS10xp8GY0xT9A=", "requires": { "angular": ">=1.2.x", "jquery": ">=3.1.x", @@ -1241,7 +1241,7 @@ "ansi-colors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", - "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "integrity": "sha1-Y3S03V1HGP884npnGjscrQdxMqk=", "dev": true, "requires": { "ansi-wrap": "^0.1.0" @@ -1289,7 +1289,7 @@ "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -1469,7 +1469,7 @@ "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=", "dev": true, "requires": { "sprintf-js": "~1.0.2" @@ -1493,7 +1493,7 @@ "arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=", "dev": true }, "arr-map": { @@ -1543,7 +1543,7 @@ "is-number": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "integrity": "sha1-ACbjf1RU1z41bf5lZGmYZ8an8P8=", "dev": true } } @@ -1551,7 +1551,7 @@ "array-last": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz", - "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==", + "integrity": "sha1-eqdwc/7FZd2rJJP1+IGF9ASp0zY=", "dev": true, "requires": { "is-number": "^4.0.0" @@ -1560,7 +1560,7 @@ "is-number": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "integrity": "sha1-ACbjf1RU1z41bf5lZGmYZ8an8P8=", "dev": true } } @@ -1568,13 +1568,13 @@ "array-slice": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", - "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", + "integrity": "sha1-42jqFfibxwaff/uJrsOmx9SsItQ=", "dev": true }, "array-sort": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz", - "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==", + "integrity": "sha1-5MBTVkU/VvU1EqfR1hI/LFTAqIo=", "dev": true, "requires": { "default-compare": "^1.0.0", @@ -1585,7 +1585,7 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", "dev": true } } @@ -1593,7 +1593,7 @@ "array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "integrity": "sha1-t5hCCtvrHego2ErNii4j0+/oXo0=", "dev": true }, "array-uniq": { @@ -1611,7 +1611,7 @@ "arraybuffer.slice": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", - "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", + "integrity": "sha1-O7xCdd1YTMGxCAm4nU6LY6aednU=", "dev": true }, "asap": { @@ -1624,7 +1624,7 @@ "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "integrity": "sha1-jSR136tVO7M+d7VOWeiAu4ziMTY=", "requires": { "safer-buffer": "~2.1.0" } @@ -1643,13 +1643,13 @@ "astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "integrity": "sha1-bIw/uCfdQ+45GPJ7gngqt2WKb9k=", "dev": true }, "async": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "integrity": "sha1-1yYl4jRKNlbjo61Pp0n6gymdgv8=", "dev": true, "requires": { "lodash": "^4.17.14" @@ -1666,7 +1666,7 @@ "async-done": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", - "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==", + "integrity": "sha1-XhWqcplipLB0FPUoqIzfGOCykKI=", "dev": true, "requires": { "end-of-stream": "^1.1.0", @@ -1678,13 +1678,13 @@ "async-each": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "integrity": "sha1-tyfb+H12UWAvBvTUrDh/R9kbDL8=", "dev": true }, "async-limiter": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + "integrity": "sha1-3TeelPDbgxCwgpH51kwyCXZmF/0=" }, "async-settle": { "version": "1.0.0", @@ -1703,13 +1703,13 @@ "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "integrity": "sha1-bZUX654DDSQ2ZmZR6GvZ9vE1M8k=", "dev": true }, "autoprefixer": { "version": "9.6.5", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.6.5.tgz", - "integrity": "sha512-rGd50YV8LgwFQ2WQp4XzOTG69u1qQsXn0amww7tjqV5jJuNazgFKYEVItEBngyyvVITKOg20zr2V+9VsrXJQ2g==", + "integrity": "sha1-mPSv5+k8zPMjKHUV1CYBlhl3Xl4=", "dev": true, "requires": { "browserslist": "^4.7.0", @@ -1742,7 +1742,7 @@ "babel-plugin-dynamic-import-node": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", - "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==", + "integrity": "sha1-8A9Qe9qjw+P/bn5emNkKesq5b38=", "dev": true, "requires": { "object.assign": "^4.1.0" @@ -1780,7 +1780,7 @@ "base": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "integrity": "sha1-e95c7RRbbVUakNuH+DxVi060io8=", "dev": true, "requires": { "cache-base": "^1.0.1", @@ -1804,7 +1804,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -1813,7 +1813,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -1822,7 +1822,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -1877,7 +1877,7 @@ "bin-build": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bin-build/-/bin-build-3.0.0.tgz", - "integrity": "sha512-jcUOof71/TNAI2uM5uoUaDq2ePcVBQ3R/qhxAz1rX7UfvduAL/RXD3jXzvn8cVcDJdGVkiR1shal3OH0ImpuhA==", + "integrity": "sha1-xXgKJaip+WbYJEIX5sH1CCoUOGE=", "dev": true, "optional": true, "requires": { @@ -1928,7 +1928,7 @@ "bin-check": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bin-check/-/bin-check-4.1.0.tgz", - "integrity": "sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA==", + "integrity": "sha1-/ElZcL3Ii7HVo1/BfmXEoUn8Skk=", "dev": true, "optional": true, "requires": { @@ -1976,7 +1976,7 @@ "bin-version": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bin-version/-/bin-version-3.1.0.tgz", - "integrity": "sha512-Mkfm4iE1VFt4xd4vH+gx+0/71esbfus2LsnCGe8Pi4mndSPyT+NGES/Eg99jx8/lUGWfu3z2yuB/bt5UB+iVbQ==", + "integrity": "sha1-WwnrKAdSsb0o8MnbP5by9DtsCDk=", "dev": true, "optional": true, "requires": { @@ -1987,7 +1987,7 @@ "bin-version-check": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/bin-version-check/-/bin-version-check-4.0.0.tgz", - "integrity": "sha512-sR631OrhC+1f8Cvs8WyVWOA33Y8tgwjETNPyyD/myRBXLkfS/vl74FmH/lFcRl9KY3zwGh7jFhvyk9vV3/3ilQ==", + "integrity": "sha1-fYGcYklpkfgNiT5uAqMDI2Fgj3E=", "dev": true, "optional": true, "requires": { @@ -1999,7 +1999,7 @@ "bin-wrapper": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bin-wrapper/-/bin-wrapper-4.1.0.tgz", - "integrity": "sha512-hfRmo7hWIXPkbpi0ZltboCMVrU+0ClXR/JgbCKKjlDjQf6igXa7OwdqNcFWQZPZTgiY7ZpzE3+LjjkLiTN2T7Q==", + "integrity": "sha1-mTSPLPhQMePvfvzn5TAK6q6WBgU=", "dev": true, "optional": true, "requires": { @@ -2014,7 +2014,7 @@ "download": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/download/-/download-7.1.0.tgz", - "integrity": "sha512-xqnBTVd/E+GxJVrX5/eUJiLYjCGPwMpdL+jGhGU57BvtcA7wwhtHVbXBeUk51kOpW3S7Jn3BQbN9Q1R1Km2qDQ==", + "integrity": "sha1-kFmqnXC1A+52oTKJe+beyOVYcjM=", "dev": true, "optional": true, "requires": { @@ -2044,7 +2044,7 @@ "file-type": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-8.1.0.tgz", - "integrity": "sha512-qyQ0pzAy78gVoJsmYeNgl8uH8yKhr1lVhW7JbzJmnlRi0I4R2eEDEJZVKG8agpDnLpacwNbDhLNG/LMdxHD2YQ==", + "integrity": "sha1-JE87fvZBu+DMoZbHJ25LMyOZ9ow=", "dev": true, "optional": true }, @@ -2058,7 +2058,7 @@ "got": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/got/-/got-8.3.2.tgz", - "integrity": "sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw==", + "integrity": "sha1-HSP2Q5Dpf3dsrFLluTbl9RTS6Tc=", "dev": true, "optional": true, "requires": { @@ -2093,7 +2093,7 @@ "make-dir": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "integrity": "sha1-ecEDO4BRW9bSTsmTPoYMp17ifww=", "dev": true, "optional": true, "requires": { @@ -2112,14 +2112,14 @@ "p-cancelable": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz", - "integrity": "sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==", + "integrity": "sha1-NfNj1n1SCByNlYXje8zrfgu8sqA=", "dev": true, "optional": true }, "p-event": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/p-event/-/p-event-2.3.1.tgz", - "integrity": "sha512-NQCqOFhbpVTMX4qMe8PF8lbGtzZ+LCiN7pcNrb/413Na7+TRoe1xkKUzuWa/YEJdGQ0FvKtj35EEbDoVPO2kbA==", + "integrity": "sha1-WWJ57xaassPgyuiMHPuwgHmZPvY=", "dev": true, "optional": true, "requires": { @@ -2129,7 +2129,7 @@ "p-timeout": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz", - "integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==", + "integrity": "sha1-2N0ZeVldLcATnh/ka4tkbLPN8Dg=", "dev": true, "optional": true, "requires": { @@ -2139,7 +2139,7 @@ "pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "integrity": "sha1-SyzSXFDVmHNcUCkiJP2MbfQeMjE=", "dev": true, "optional": true }, @@ -2165,7 +2165,7 @@ "binary-extensions": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "integrity": "sha1-WYr+VHVbKGilMw0q/51Ou1Mgm2U=", "dev": true }, "bl": { @@ -2217,7 +2217,7 @@ "blob": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", - "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==", + "integrity": "sha1-1oDu7yX4zZGtUz9bAe7UjmTK9oM=", "dev": true }, "bluebird": { @@ -2229,7 +2229,7 @@ "body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "integrity": "sha1-lrJwnlfJxOCab9Zqj9l5hE9p8Io=", "dev": true, "requires": { "bytes": "3.1.0", @@ -2247,7 +2247,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", "dev": true, "requires": { "ms": "2.0.0" @@ -2262,7 +2262,7 @@ "qs": { "version": "6.7.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "integrity": "sha1-QdwaAV49WB8WIXdr4xr7KHapsbw=", "dev": true } } @@ -2276,7 +2276,7 @@ "bootstrap": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-3.4.1.tgz", - "integrity": "sha512-yN5oZVmRCwe5aKwzRj6736nSmKDX7pLYwsXiCj/EYmo16hODaBiT4En5btW/jhBF/seV+XMx3aYwukYC3A49DA==" + "integrity": "sha1-w6NH1Bniia0R9AM+PEEyuHwIHXI=" }, "bootstrap-social": { "version": "5.1.1", @@ -2290,7 +2290,7 @@ "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", "dev": true, "requires": { "balanced-match": "^1.0.0", @@ -2300,7 +2300,7 @@ "braces": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "integrity": "sha1-WXn9PxTNUxVl5fot8av/8d+u5yk=", "dev": true, "requires": { "arr-flatten": "^1.1.0", @@ -2357,7 +2357,7 @@ "buffer-alloc": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", - "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "integrity": "sha1-iQ3ZDZI6hz4I4Q5f1RpX5bfM4Ow=", "dev": true, "requires": { "buffer-alloc-unsafe": "^1.1.0", @@ -2367,7 +2367,7 @@ "buffer-alloc-unsafe": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "integrity": "sha1-vX3CauKXLQ7aJTvgYdupkjScGfA=", "dev": true }, "buffer-crc32": { @@ -2392,7 +2392,7 @@ "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "integrity": "sha1-MnE7wCj3XAL9txDXx7zsHyxgcO8=", "dev": true }, "bufferstreams": { @@ -2407,13 +2407,13 @@ "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "integrity": "sha1-9s95M6Ng4FiPqf3oVlHNx/gF0fY=", "dev": true }, "cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "integrity": "sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=", "dev": true, "requires": { "collection-visit": "^1.0.0", @@ -2460,7 +2460,7 @@ "normalize-url": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz", - "integrity": "sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==", + "integrity": "sha1-g1qdoVUfom9w6SMpBpojqmV01+Y=", "dev": true, "optional": true, "requires": { @@ -2514,7 +2514,7 @@ }, "callsites": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "resolved": "http://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", "dev": true }, @@ -2547,7 +2547,7 @@ "caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "integrity": "sha1-Xk2Q4idJYdRikZl99Znj7QCO5MA=", "dev": true, "requires": { "browserslist": "^4.0.0", @@ -2570,7 +2570,7 @@ "caw": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/caw/-/caw-2.0.1.tgz", - "integrity": "sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA==", + "integrity": "sha1-bDygcfwZRyCIPC3F2psHS/x+npU=", "dev": true, "optional": true, "requires": { @@ -2593,7 +2593,7 @@ "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "integrity": "sha1-zUJUFnelQzPPVBpJEIwUMrRMlCQ=", "dev": true, "requires": { "ansi-styles": "^3.2.1", @@ -2604,7 +2604,7 @@ "chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "integrity": "sha1-kAlISfCTfy7twkJdDSip5fDLrZ4=", "dev": true }, "chart.js": { @@ -2628,7 +2628,7 @@ "chartjs-color-string": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz", - "integrity": "sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==", + "integrity": "sha1-HfCWYhwOcHIKZPQTXqFx0FFAL3E=", "requires": { "color-name": "^1.0.0" } @@ -2636,7 +2636,7 @@ "chokidar": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "integrity": "sha1-gEs6e2qZNYw8XGHnHYco8EHP+Rc=", "dev": true, "requires": { "anymatch": "^2.0.0", @@ -2686,7 +2686,7 @@ "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "integrity": "sha1-Dc1p/yOhybEf0JeDFmRKA4ghamU=", "dev": true } } @@ -2694,7 +2694,7 @@ "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "integrity": "sha1-+TNprouafOAv1B+q0MqDAzGQxGM=", "dev": true, "requires": { "arr-union": "^3.1.0", @@ -2717,7 +2717,7 @@ "clean-css": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", - "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", + "integrity": "sha1-LUEe92uFabbQyEBo2r6FsKpeXBc=", "dev": true, "requires": { "source-map": "~0.6.0" @@ -2726,7 +2726,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true } } @@ -2734,7 +2734,7 @@ "cli-color": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-1.4.0.tgz", - "integrity": "sha512-xu6RvQqqrWEo6MPR1eixqGPywhYBHRs653F9jfXB2Hx4jdM/3WxiNE1vppRmxtMIfl16SFYTpYlrnqH/HsK/2w==", + "integrity": "sha1-fRBzj0hSaCT4/n2lGFfLD1cv4B8=", "dev": true, "requires": { "ansi-regex": "^2.1.1", @@ -2763,7 +2763,7 @@ "clipboard": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.4.tgz", - "integrity": "sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ==", + "integrity": "sha1-g22v1mzw/qXXHOXVsL9ulYAJES0=", "requires": { "good-listener": "^1.2.2", "select": "^1.1.2", @@ -2812,7 +2812,7 @@ "cloneable-readable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", - "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", + "integrity": "sha1-EgoAywU7+2OiIucJ+Wg+ouEdjOw=", "dev": true, "requires": { "inherits": "^2.0.1", @@ -2855,7 +2855,7 @@ "coa": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", - "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "integrity": "sha1-Q/bCEVG07yv1cYfbDXPeIp4+fsM=", "dev": true, "requires": { "@types/q": "^1.5.1", @@ -2893,7 +2893,7 @@ "color": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/color/-/color-3.1.2.tgz", - "integrity": "sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==", + "integrity": "sha1-aBSOf4XUGtdknF+oyBBvCY0inhA=", "dev": true, "requires": { "color-convert": "^1.9.1", @@ -2925,12 +2925,12 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=" }, "color-string": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", - "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", + "integrity": "sha1-ybvF8BtYtUkvPWhXRZy2WQziBMw=", "dev": true, "requires": { "color-name": "^1.0.0", @@ -2940,7 +2940,7 @@ "color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "integrity": "sha1-k4NDeaHMmgxh+C9S8NBDIiUb1aI=", "dev": true }, "colornames": { @@ -2952,13 +2952,13 @@ "colors": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "integrity": "sha1-xQSRR51MG9rtLJztMs98fcI2D3g=", "dev": true }, "colorspace": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", - "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", + "integrity": "sha1-4BKJUNCCuGohaFgHlqCqXWxo2MU=", "dev": true, "requires": { "color": "3.0.x", @@ -2968,7 +2968,7 @@ "color": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", - "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", + "integrity": "sha1-2SC0Mo1TSjrIKV1o971LpsQnvpo=", "dev": true, "requires": { "color-convert": "^1.9.1", @@ -2995,7 +2995,7 @@ "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "integrity": "sha1-w9RaizT9cwYxoRCoolIGgrMdWn8=", "requires": { "delayed-stream": "~1.0.0" } @@ -3015,7 +3015,7 @@ "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "integrity": "sha1-FuQHD7qK4ptnnyIVhT7hgasuq8A=", "dev": true }, "component-inherit": { @@ -3033,7 +3033,7 @@ "concat-stream": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "integrity": "sha1-kEvfGUzTEi/Gdcd/xKw9T/D9GjQ=", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -3066,7 +3066,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -3077,7 +3077,7 @@ "concat-with-sourcemaps": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz", - "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==", + "integrity": "sha1-1OqT8FriV5CVG5nns7CeOQikCC4=", "dev": true, "requires": { "source-map": "^0.6.1" @@ -3086,7 +3086,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true } } @@ -3094,7 +3094,7 @@ "config-chain": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", - "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", + "integrity": "sha1-D96NCRIA616AjK8l/mGMAvSOTvo=", "dev": true, "optional": true, "requires": { @@ -3105,7 +3105,7 @@ "connect": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", - "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "integrity": "sha1-XUk0iRDKpeB6AYALAw0MNfIEhPg=", "dev": true, "requires": { "debug": "2.6.9", @@ -3117,7 +3117,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", "dev": true, "requires": { "ms": "2.0.0" @@ -3141,7 +3141,7 @@ "consolidate": { "version": "0.15.1", "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz", - "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==", + "integrity": "sha1-IasEMjXHGgfUXZqtmFk7DbpWurc=", "dev": true, "requires": { "bluebird": "^3.1.1" @@ -3150,7 +3150,7 @@ "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "integrity": "sha1-4TDK9+cnkIfFYWwgB9BIVpiYT70=", "dev": true, "optional": true, "requires": { @@ -3160,7 +3160,7 @@ "content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=", "dev": true }, "convert-source-map": { @@ -3187,7 +3187,7 @@ "copy-props": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.4.tgz", - "integrity": "sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A==", + "integrity": "sha1-k7scrfr9MdpbuKnUtB9HHsOnLf4=", "dev": true, "requires": { "each-props": "^1.3.0", @@ -3246,7 +3246,7 @@ "cosmiconfig": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", - "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "integrity": "sha1-BA9yaAnFked6F8CjYmykW08Wixo=", "dev": true, "requires": { "import-fresh": "^2.0.0", @@ -3258,7 +3258,7 @@ "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "integrity": "sha1-Sl7Hxk364iw6FBJNus3uhG2Ay8Q=", "dev": true, "requires": { "nice-try": "^1.0.4", @@ -3271,7 +3271,7 @@ "css": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", - "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "integrity": "sha1-xkZ1XHOXHyu6amAeLPL9cbEpiSk=", "dev": true, "requires": { "inherits": "^2.0.3", @@ -3283,7 +3283,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true } } @@ -3297,7 +3297,7 @@ "css-declaration-sorter": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", - "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", + "integrity": "sha1-wZiUD2OnbX42wecQGLABchBUyyI=", "dev": true, "requires": { "postcss": "^7.0.1", @@ -3319,7 +3319,7 @@ "css-select-base-adapter": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", - "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", + "integrity": "sha1-Oy/0lyzDYquIVhUHqVQIoUMhNdc=", "dev": true }, "css-tree": { @@ -3353,7 +3353,7 @@ "cssnano": { "version": "4.1.10", "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.10.tgz", - "integrity": "sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==", + "integrity": "sha1-CsQfCxPRPUZUh+ERt3jULaYxuLI=", "dev": true, "requires": { "cosmiconfig": "^5.0.0", @@ -3365,7 +3365,7 @@ "cssnano-preset-default": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz", - "integrity": "sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA==", + "integrity": "sha1-UexmLM/KD4izltzZZ5zbkxvhf3Y=", "dev": true, "requires": { "css-declaration-sorter": "^4.0.1", @@ -3415,7 +3415,7 @@ "cssnano-util-raw-cache": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", - "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", + "integrity": "sha1-sm1f1fcqEd/np4RvtMZyYPlr8oI=", "dev": true, "requires": { "postcss": "^7.0.0" @@ -3424,7 +3424,7 @@ "cssnano-util-same-parent": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", - "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==", + "integrity": "sha1-V0CC+yhZ0ttDOFWDXZqEVuoYu/M=", "dev": true }, "csso": { @@ -3496,7 +3496,7 @@ "d": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "integrity": "sha1-hpgJU3LVjb7jRv/Qxwk/mfj561o=", "dev": true, "requires": { "es5-ext": "^0.10.50", @@ -3537,7 +3537,7 @@ "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "integrity": "sha1-O3ImAlUQnGtYnO4FDx1RYTlmR5E=", "dev": true, "requires": { "ms": "^2.1.1" @@ -3546,7 +3546,7 @@ "debug-fabulous": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-1.1.0.tgz", - "integrity": "sha512-GZqvGIgKNlUnHUPQhepnUZFIMoi3dgZKQBzKDeL2g7oJF9SNAji/AAu36dusFUas0O+pae74lNeoIPHqXWDkLg==", + "integrity": "sha1-r4oIYyRlIk70F0qfBjCMPCoevI4=", "dev": true, "requires": { "debug": "3.X", @@ -3609,7 +3609,7 @@ "make-dir": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "integrity": "sha1-ecEDO4BRW9bSTsmTPoYMp17ifww=", "dev": true, "optional": true, "requires": { @@ -3640,7 +3640,7 @@ "decompress-tar": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", - "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", + "integrity": "sha1-cYy9P8sWIJcW5womuE57pFkuWvE=", "dev": true, "optional": true, "requires": { @@ -3661,7 +3661,7 @@ "decompress-tarbz2": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", - "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", + "integrity": "sha1-MIKluIDqQEOBY0nzeLVsUWvho5s=", "dev": true, "optional": true, "requires": { @@ -3675,7 +3675,7 @@ "file-type": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", - "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==", + "integrity": "sha1-5QzXXTVv/tTjBtxPW89Sp5kDqRk=", "dev": true, "optional": true } @@ -3684,7 +3684,7 @@ "decompress-targz": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", - "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", + "integrity": "sha1-wJvDXE0R894J8tLaU+neI+fOHu4=", "dev": true, "optional": true, "requires": { @@ -3751,7 +3751,7 @@ "default-compare": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", - "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", + "integrity": "sha1-y2ETGESthNhHiPto/QFoHKd4Gi8=", "dev": true, "requires": { "kind-of": "^5.0.2" @@ -3760,7 +3760,7 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", "dev": true } } @@ -3774,7 +3774,7 @@ "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "integrity": "sha1-z4jabL7ib+bbcJT2HYcMvYTO6fE=", "dev": true, "requires": { "object-keys": "^1.0.12" @@ -3783,7 +3783,7 @@ "define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "integrity": "sha1-1Flono1lS6d+AqgX+HENcCyxbp0=", "dev": true, "requires": { "is-descriptor": "^1.0.2", @@ -3793,7 +3793,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -3802,7 +3802,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -3811,7 +3811,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -3829,7 +3829,7 @@ "delegate": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", - "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" + "integrity": "sha1-tmtxwxWFIuirV0T3INjKDCr1kWY=" }, "depd": { "version": "1.1.2", @@ -3858,7 +3858,7 @@ "diagnostics": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz", - "integrity": "sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ==", + "integrity": "sha1-yrasM99wydmnJ0kK5DrJladpsio=", "dev": true, "requires": { "colorspace": "1.1.x", @@ -3869,12 +3869,12 @@ "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==" + "integrity": "sha1-gAwN0eCov7yVg1wgKtIg/jF+WhI=" }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "integrity": "sha1-Vtv3PZkqSpO6FYT0U0Bj/S5BcX8=", "dev": true, "requires": { "path-type": "^4.0.0" @@ -3883,7 +3883,7 @@ "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "integrity": "sha1-hO0BwKe6OAr+CdkKjBgNzZ0DBDs=", "dev": true } } @@ -3891,7 +3891,7 @@ "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "integrity": "sha1-rd6+rXKmV023g2OdyHoSF3OXOWE=", "dev": true, "requires": { "esutils": "^2.0.2" @@ -3922,7 +3922,7 @@ "domelementtype": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", - "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==", + "integrity": "sha1-H4vf6R9aeAYydOgDtL3O326U+U0=", "dev": true } } @@ -3930,7 +3930,7 @@ "domelementtype": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "integrity": "sha1-0EjESzew0Qp/Kj1f7j9DM9eQSB8=", "dev": true }, "domexception": { @@ -3953,7 +3953,7 @@ "domhandler": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "integrity": "sha1-iAUJfpM9ZehVRvcm1g9euItE+AM=", "dev": true, "requires": { "domelementtype": "1" @@ -3962,7 +3962,7 @@ "domutils": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "integrity": "sha1-Vuo0HoNOBuZ0ivehyyXaZ+qfjCo=", "dev": true, "requires": { "dom-serializer": "0", @@ -3981,7 +3981,7 @@ "download": { "version": "6.2.5", "resolved": "https://registry.npmjs.org/download/-/download-6.2.5.tgz", - "integrity": "sha512-DpO9K1sXAST8Cpzb7kmEhogJxymyVUd5qz/vCOSyvwtp2Klj2XcDt5YUuasgxka44SxF0q5RriKIwJmQHG2AuA==", + "integrity": "sha1-rNalQuTNC7Qspwz8mMnkOwcDlxQ=", "dev": true, "optional": true, "requires": { @@ -4015,7 +4015,7 @@ "make-dir": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "integrity": "sha1-ecEDO4BRW9bSTsmTPoYMp17ifww=", "dev": true, "optional": true, "requires": { @@ -4050,7 +4050,7 @@ "duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "integrity": "sha1-Kk31MX9sz9kfhtb9JdjYoQO4gwk=", "dev": true, "requires": { "end-of-stream": "^1.0.0", @@ -4094,7 +4094,7 @@ "each-props": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz", - "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==", + "integrity": "sha1-6kWkFNFt1c+kGbGoFyDVygaJIzM=", "dev": true, "requires": { "is-plain-object": "^2.0.1", @@ -4152,7 +4152,7 @@ "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "integrity": "sha1-WuZKX0UFe682JuwU2gyl5LJDHrA=", "dev": true, "requires": { "once": "^1.4.0" @@ -4264,7 +4264,7 @@ "entities": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", - "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==", + "integrity": "sha1-aNYITKsbB5dnVA2A5Wo5tCPkq/Q=", "dev": true }, "env-variable": { @@ -4276,7 +4276,7 @@ "errno": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "integrity": "sha1-RoTXF3mtOa8Xfj8AeZb3xnyFJhg=", "dev": true, "optional": true, "requires": { @@ -4286,7 +4286,7 @@ "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "integrity": "sha1-tKxAZIEH/c3PriQvQovqihTU8b8=", "dev": true, "requires": { "is-arrayish": "^0.2.1" @@ -4346,7 +4346,7 @@ "es6-promise": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "integrity": "sha1-TrIVlMlyvEBVPSduUQU5FD21Pgo=", "dev": true }, "es6-symbol": { @@ -4362,7 +4362,7 @@ "es6-weak-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", - "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "integrity": "sha1-ttofFswswNm+Q+a9v8Xn383zHVM=", "dev": true, "requires": { "d": "1", @@ -4405,7 +4405,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true, "optional": true } @@ -4459,7 +4459,7 @@ "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "integrity": "sha1-i5+PCM8ay4Q3Vqg5yox+MWjFGZc=", "dev": true }, "glob-parent": { @@ -4493,19 +4493,19 @@ "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "integrity": "sha1-SrzYUq0y3Xuqv+m0DgCjbbXzkuY=", "dev": true }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "integrity": "sha1-7gpkyK9ejO6mdoexM3YeG+y9HT0=", "dev": true }, "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "integrity": "sha1-jJpTb+tq/JYr36WxBKUJHBrZwK4=", "dev": true, "requires": { "ansi-regex": "^4.1.0" @@ -4526,7 +4526,7 @@ "eslint-utils": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", - "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "integrity": "sha1-dP7HxU0Hdrb2fgJRBAtYBlZOmB8=", "dev": true, "requires": { "eslint-visitor-keys": "^1.1.0" @@ -4552,7 +4552,7 @@ "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "integrity": "sha1-E7BM2z5sXRnfkatph6hpVhmwqnE=", "dev": true }, "esquery": { @@ -4594,13 +4594,13 @@ "estraverse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "integrity": "sha1-OYrT88WiSUi+dyXoPRGn3ijNvR0=", "dev": true }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "integrity": "sha1-dNLrTeC42hKTcRkQ1Qd1ubcQ72Q=", "dev": true }, "event-emitter": { @@ -4630,7 +4630,7 @@ "exec-buffer": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/exec-buffer/-/exec-buffer-3.2.0.tgz", - "integrity": "sha512-wsiD+2Tp6BWHoVv3B+5Dcx6E7u5zky+hUwOHjuH2hKSLR3dvRmX8fk8UD8uqQixHs4Wk6eDmiegVrMPjKj7wpA==", + "integrity": "sha1-sWhtvZBMfPmC5lLB9aebHlVzCCs=", "dev": true, "optional": true, "requires": { @@ -4704,7 +4704,7 @@ "executable": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", - "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", + "integrity": "sha1-QVMr/zYdPlevTXY7cFgtsY9dEzw=", "dev": true, "optional": true, "requires": { @@ -4729,7 +4729,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", "dev": true, "requires": { "ms": "2.0.0" @@ -4773,7 +4773,7 @@ "fill-range": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "integrity": "sha1-6x53OrsFbc2N8r/favWbizqTZWU=", "dev": true, "requires": { "is-number": "^2.1.0", @@ -4830,7 +4830,7 @@ "ext-list": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", - "integrity": "sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==", + "integrity": "sha1-C5jmTtgvWs8PKTG6v2khLvUt3Tc=", "dev": true, "optional": true, "requires": { @@ -4840,7 +4840,7 @@ "ext-name": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz", - "integrity": "sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==", + "integrity": "sha1-cHgZgdGD7hXROZPIgiBFxQbI8KY=", "dev": true, "optional": true, "requires": { @@ -4851,7 +4851,7 @@ "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + "integrity": "sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo=" }, "extend-shallow": { "version": "3.0.2", @@ -4866,7 +4866,7 @@ "is-extendable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", "dev": true, "requires": { "is-plain-object": "^2.0.4" @@ -4877,7 +4877,7 @@ "external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "integrity": "sha1-ywP3QL764D6k0oPK7SdBqD8zVJU=", "dev": true, "requires": { "chardet": "^0.7.0", @@ -4888,7 +4888,7 @@ "extglob": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "integrity": "sha1-rQD+TcYSqSMuhxhxHcXLWrAoVUM=", "dev": true, "requires": { "array-unique": "^0.3.2", @@ -4922,7 +4922,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -4931,7 +4931,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -4940,7 +4940,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -4958,7 +4958,7 @@ "fancy-log": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", - "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "integrity": "sha1-28GRVPVYaQFQojlToK29A1vkX8c=", "dev": true, "requires": { "ansi-gray": "^0.1.1", @@ -4989,7 +4989,7 @@ "braces": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "integrity": "sha1-NFThpGLujVmeI23zNs2epPiv4Qc=", "dev": true, "requires": { "fill-range": "^7.0.1" @@ -4998,7 +4998,7 @@ "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "integrity": "sha1-GRmmp8df44ssfHflGYU12prN2kA=", "dev": true, "requires": { "to-regex-range": "^5.0.1" @@ -5025,13 +5025,13 @@ "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "integrity": "sha1-dTU0W4lnNNX4DE0GxQlVUnoU8Ss=", "dev": true }, "micromatch": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "integrity": "sha1-T8sJmb+fvC/L3SEvbWKbmlbDklk=", "dev": true, "requires": { "braces": "^3.0.1", @@ -5047,7 +5047,7 @@ "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "integrity": "sha1-FkjESq58jZiKMmAY7XL1tN0DkuQ=", "dev": true, "requires": { "is-number": "^7.0.0" @@ -5097,7 +5097,7 @@ "file-entry-cache": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "integrity": "sha1-yg9u+m3T1WEzP7FFFQZcL6/fQ5w=", "dev": true, "requires": { "flat-cache": "^2.0.1" @@ -5125,7 +5125,7 @@ "filenamify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-2.1.0.tgz", - "integrity": "sha512-ICw7NTT6RsDp2rnYKVd8Fu4cr6ITzGy3+u4vUujPkabyaz+03F24NWEX7fs5fp+kBonlaqPH8fAO2NM+SXt/JA==", + "integrity": "sha1-iPr0lfsbR6v9YSMAACoWIoxnfuk=", "dev": true, "optional": true, "requires": { @@ -5160,7 +5160,7 @@ "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "integrity": "sha1-t+fQAP/RGTjQ/bBTUG9uur6fWH0=", "dev": true, "requires": { "debug": "2.6.9", @@ -5175,7 +5175,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", "dev": true, "requires": { "ms": "2.0.0" @@ -5235,7 +5235,7 @@ "fined": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", - "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", + "integrity": "sha1-0AvszxqitHXRbUI7Aji3E6LEo3s=", "dev": true, "requires": { "expand-tilde": "^2.0.2", @@ -5289,13 +5289,13 @@ "flagged-respawn": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", - "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", + "integrity": "sha1-595vEnnd2cqarIpZcdYYYGs6q0E=", "dev": true }, "flat-cache": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "integrity": "sha1-XSltbwS9pEpGMKMBQTvbwuwIXsA=", "dev": true, "requires": { "flatted": "^2.0.0", @@ -5306,18 +5306,18 @@ "flatpickr": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.5.2.tgz", - "integrity": "sha512-jDy4QYGpmiy7+Qk8QvKJ4spjDdxcx9cxMydmq1x427HkKWBw0qizLYeYM2F6tMcvvqGjU5VpJS55j4LnsaBblA==" + "integrity": "sha1-R8itRyoJbl+350uAmwcDU1OD8g0=" }, "flatted": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", - "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", + "integrity": "sha1-aeV8qo8OrLwoHS4stFjUb9tEngg=", "dev": true }, "flush-write-stream": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", - "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "integrity": "sha1-jdfYc6G6vCB9lOrQwuDkQnbr8ug=", "dev": true, "requires": { "inherits": "^2.0.3", @@ -5404,7 +5404,7 @@ "form-data": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "integrity": "sha1-3M5SwF9kTymManq5Nr1yTO/786Y=", "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", @@ -5475,7 +5475,7 @@ "fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "integrity": "sha1-a+Dem+mYzhavivwkSXue6bfM2a0=", "dev": true, "optional": true }, @@ -5492,7 +5492,7 @@ "fs-readfile-promise": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/fs-readfile-promise/-/fs-readfile-promise-3.0.1.tgz", - "integrity": "sha512-LsSxMeaJdYH27XrW7Dmq0Gx63mioULCRel63B5VeELYLavi1wF5s0XfsIdKDFdCL9hsfQ2qBvXJszQtQJ9h17A==", + "integrity": "sha1-0NMHt/au38kgwx+m5XEu+qKXyVg=", "dev": true, "requires": { "graceful-fs": "^4.1.11" @@ -6055,7 +6055,7 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=", "dev": true }, "functional-red-black-tree": { @@ -6067,13 +6067,13 @@ "get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "integrity": "sha1-+Xj6TJDR3+f/LWvtoqUV5xO9z0o=", "dev": true }, "get-proxy": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/get-proxy/-/get-proxy-2.1.0.tgz", - "integrity": "sha512-zmZIaQTWnNQb4R4fJUEp/FC51eZsc6EkErspy3xtIYStaq8EB/hDIWipxsal+E8rz0qD7f2sL/NA9Xee4RInJw==", + "integrity": "sha1-NJ8rTZHUTE1NTpy6KtkBQ/rF75M=", "dev": true, "optional": true, "requires": { @@ -6221,7 +6221,7 @@ "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "integrity": "sha1-tKIRaBW94vTh6mAjVOjHVWUQemQ=", "dev": true, "optional": true, "requires": { @@ -6371,7 +6371,7 @@ "glob-watcher": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.3.tgz", - "integrity": "sha512-8tWsULNEPHKQ2MR4zXuzSmqbdyV5PtwwCaWSGQ1WwHsJ07ilNeN1JB8ntxhckbnpSHaf9dXFUHzIWvm1I13dsg==", + "integrity": "sha1-iKir8cTRMeuTkomUvEpZPC5d1iY=", "dev": true, "requires": { "anymatch": "^2.0.0", @@ -6397,7 +6397,7 @@ "global-modules": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "integrity": "sha1-bXcPDrUjrHgWTXK15xqIdyZcw+o=", "dev": true, "requires": { "global-prefix": "^1.0.1", @@ -6421,7 +6421,7 @@ "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "integrity": "sha1-q4eVM4hooLq9hSV1gBjCp+uVxC4=", "dev": true }, "globby": { @@ -6457,7 +6457,7 @@ "glogg": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", - "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", + "integrity": "sha1-LX3XAr7aIus7/634gGltpthGMT8=", "dev": true, "requires": { "sparkles": "^1.0.0" @@ -6474,7 +6474,7 @@ "got": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", - "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", + "integrity": "sha1-BUUP2ECU5rvqVvRRpDqcKJFmOFo=", "dev": true, "optional": true, "requires": { @@ -6518,7 +6518,7 @@ "gulp": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", - "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==", + "integrity": "sha1-VDZRBw/Q9qsKBlDGo+b/WnywnKo=", "dev": true, "requires": { "glob-watcher": "^5.0.3", @@ -6551,7 +6551,7 @@ "gulp-babel": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/gulp-babel/-/gulp-babel-8.0.0.tgz", - "integrity": "sha512-oomaIqDXxFkg7lbpBou/gnUkX51/Y/M2ZfSjL2hdqXTAlSWZcgZtd2o0cOH0r/eE8LWD0+Q/PsLsr2DKOoqToQ==", + "integrity": "sha1-4NqW9PLsSojdOjAw9HbjirISbYc=", "dev": true, "requires": { "plugin-error": "^1.0.1", @@ -6571,7 +6571,7 @@ "gulp-clean-css": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/gulp-clean-css/-/gulp-clean-css-4.2.0.tgz", - "integrity": "sha512-r4zQsSOAK2UYUL/ipkAVCTRg/2CLZ2A+oPVORopBximRksJ6qy3EX1KGrIWT4ZrHxz3Hlobb1yyJtqiut7DNjA==", + "integrity": "sha1-kV7CWNxtPmpQBD9hAGbVwurE9U4=", "dev": true, "requires": { "clean-css": "4.2.1", @@ -6609,7 +6609,7 @@ "through2": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", - "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "integrity": "sha1-OSducTwzAu3544jdnIEt07glvVo=", "dev": true, "requires": { "readable-stream": "2 || 3" @@ -6691,7 +6691,7 @@ "gulp-eslint": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/gulp-eslint/-/gulp-eslint-6.0.0.tgz", - "integrity": "sha512-dCVPSh1sA+UVhn7JSQt7KEb4An2sQNbOdB3PA8UCfxsoPlAKjJHxYHGXdXC7eb+V1FAnilSFFqslPrq037l1ig==", + "integrity": "sha1-fUArtF+KZ2UrhoJ3ARgSBXNwqDI=", "dev": true, "requires": { "eslint": "^6.0.0", @@ -6766,7 +6766,7 @@ "gulp-less": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/gulp-less/-/gulp-less-4.0.1.tgz", - "integrity": "sha512-hmM2k0FfQp7Ptm3ZaqO2CkMX3hqpiIOn4OHtuSsCeFym63F7oWlEua5v6u1cIjVUKYsVIs9zPg9vbqTEb/udpA==", + "integrity": "sha1-NIwzpd3nogfFdxsdgmHRrBAhzu0=", "dev": true, "requires": { "accord": "^0.29.0", @@ -6811,7 +6811,7 @@ }, "kind-of": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", "dev": true }, @@ -6943,7 +6943,7 @@ "gulp-notify": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/gulp-notify/-/gulp-notify-3.2.0.tgz", - "integrity": "sha512-qEocs1UVoDKKUjfsxJNMNwkRla0PbsyJwsqNNXpzYWsLQ29LhxRMY3wnTGZcc4hMHtalnvah/Dwlwb4NijH/0A==", + "integrity": "sha1-KugiUAnfiB7vWb5d1aLxM3OHdk4=", "dev": true, "requires": { "ansi-colors": "^1.0.1", @@ -6995,7 +6995,7 @@ "lodash.template": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", - "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", + "integrity": "sha1-+XYZXPPzR9DV9SSDVp/oAxzM6Ks=", "dev": true, "requires": { "lodash._reinterpolate": "^3.0.0", @@ -7005,7 +7005,7 @@ "lodash.templatesettings": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", - "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "integrity": "sha1-5IExDwSdPPbUfpEq0JMTsVTw+zM=", "dev": true, "requires": { "lodash._reinterpolate": "^3.0.0" @@ -7029,7 +7029,7 @@ "gulp-postcss": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/gulp-postcss/-/gulp-postcss-8.0.0.tgz", - "integrity": "sha512-Wtl6vH7a+8IS/fU5W9IbOpcaLqKxd5L1DUOzaPmlnCbX1CrG0aWdwVnC3Spn8th0m8D59YbysV5zPUe1n/GJYg==", + "integrity": "sha1-jTdyzU0nvKVeyMtMjlduO95NxVA=", "dev": true, "requires": { "fancy-log": "^1.3.2", @@ -7042,7 +7042,7 @@ "gulp-rename": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-1.4.0.tgz", - "integrity": "sha512-swzbIGb/arEoFK89tPY58vg3Ok1bw+d35PfUNwWqdo7KM4jkmuGA78JiDNqR+JeZFaeeHnRg9N7aihX3YPmsyg==", + "integrity": "sha1-3hxxjnxAla6GH3KW708ySGSCQL0=", "dev": true }, "gulp-sort": { @@ -7057,7 +7057,7 @@ "gulp-sourcemaps": { "version": "2.6.5", "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-2.6.5.tgz", - "integrity": "sha512-SYLBRzPTew8T5Suh2U8jCSDKY+4NARua4aqjj8HOysBh2tSgT9u4jc1FYirAdPx1akUxxDeK++fqw6Jg0LkQRg==", + "integrity": "sha1-o/AC2HNG0sDzrsNq9+uHPyPeiuY=", "dev": true, "requires": { "@gulp-sourcemaps/identity-map": "1.X", @@ -7082,7 +7082,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true } } @@ -7121,7 +7121,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { @@ -7143,7 +7143,7 @@ "gulp-watch": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/gulp-watch/-/gulp-watch-5.0.1.tgz", - "integrity": "sha512-HnTSBdzAOFIT4wmXYPDUn783TaYAq9bpaN05vuZNP5eni3z3aRx0NAKbjhhMYtcq76x4R1wf4oORDGdlrEjuog==", + "integrity": "sha1-g9N4dS9b+0baAj5zwX7R2nBmIV0=", "dev": true, "requires": { "ansi-colors": "1.1.0", @@ -7244,7 +7244,7 @@ "gulp-wrap": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/gulp-wrap/-/gulp-wrap-0.15.0.tgz", - "integrity": "sha512-f17zkGObA+hE/FThlg55gfA0nsXbdmHK1WqzjjB2Ytq1TuhLR7JiCBJ3K4AlMzCyoFaCjfowos+VkToUNE0WTQ==", + "integrity": "sha1-6QFMm7hkOrMQ6TjURpuFaFUaVS8=", "dev": true, "requires": { "consolidate": "^0.15.1", @@ -7288,7 +7288,7 @@ "through2": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", - "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "integrity": "sha1-OSducTwzAu3544jdnIEt07glvVo=", "dev": true, "requires": { "readable-stream": "2 || 3" @@ -7353,7 +7353,7 @@ "har-validator": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "integrity": "sha1-HvievT5JllV2de7ZiTEQ3DUPoIA=", "requires": { "ajv": "^6.5.5", "har-schema": "^2.0.0" @@ -7362,7 +7362,7 @@ "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "integrity": "sha1-ci18v8H2qoJB8W3YFOAR4fQeh5Y=", "dev": true, "requires": { "function-bind": "^1.1.1" @@ -7380,7 +7380,7 @@ "has-binary2": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", - "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", + "integrity": "sha1-d3asYn8+p3JQz8My2rfd9eT10R0=", "dev": true, "requires": { "isarray": "2.0.1" @@ -7418,7 +7418,7 @@ "has-symbol-support-x": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", - "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==", + "integrity": "sha1-FAn5i8ACR9pF2mfO4KNvKC/yZFU=", "dev": true, "optional": true }, @@ -7431,7 +7431,7 @@ "has-to-string-tag-x": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", - "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", + "integrity": "sha1-oEWrOD17SyASoAFIqwql8pAETU0=", "dev": true, "optional": true, "requires": { @@ -7473,13 +7473,13 @@ "hex-color-regex": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", - "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", + "integrity": "sha1-TAb8y0YC/iYCs8k9+C1+fb8aio4=", "dev": true }, "homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "integrity": "sha1-dDKYzvTlrz4ZQWH7rcwhUdOgWOg=", "dev": true, "requires": { "parse-passwd": "^1.0.0" @@ -7506,7 +7506,7 @@ "html-comment-regex": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", - "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", + "integrity": "sha1-l9RoiutcgYhqNk+qDK0d2hTUM6c=", "dev": true }, "html-encoding-sniffer": { @@ -7535,7 +7535,7 @@ "entities": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "integrity": "sha1-vfpzUplmTfr9NFKe1PhSKidf6lY=", "dev": true }, "isarray": { @@ -7573,14 +7573,14 @@ "http-cache-semantics": { "version": "3.8.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", - "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", + "integrity": "sha1-ObDhat2bYFvwqe89nar0hDtMrNI=", "dev": true, "optional": true }, "http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "integrity": "sha1-T1ApzxMjnzEDblsuVSkrz7zIXI8=", "dev": true, "requires": { "depd": "~1.1.2", @@ -7629,7 +7629,7 @@ "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "integrity": "sha1-ICK0sl+93CHS9SSXSkdKr+czkIs=", "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" @@ -7645,7 +7645,7 @@ "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "integrity": "sha1-dQ49tYYgh7RzfrrIIH/9HvJ7Jfw=", "dev": true }, "image-size": { @@ -7705,7 +7705,7 @@ "imagemin-optipng": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/imagemin-optipng/-/imagemin-optipng-7.1.0.tgz", - "integrity": "sha512-JNORTZ6j6untH7e5gF4aWdhDCxe3ODsSLKs/f7Grewy3ebZpl1ZsU+VUTPY4rzeHgaFA8GSWOoA8V2M3OixWZQ==", + "integrity": "sha1-IiXILDXlwpt/qY1Pns7hFhpo6Ig=", "dev": true, "optional": true, "requires": { @@ -7838,7 +7838,7 @@ "import-lazy": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-3.1.0.tgz", - "integrity": "sha512-8/gvXvX2JMn0F+CDlSC4l6kOmVaLOO3XLkksI7CI3Ud95KDYJuYur2b9P/PUt/i/pDAMd/DulQsNbbbmRRsDIQ==", + "integrity": "sha1-iRJ5ICyKIoD9vWZ029jaGh38Z8w=", "dev": true, "optional": true }, @@ -7889,13 +7889,13 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "integrity": "sha1-D6LGT5MpF8NDOg3tVTY6rjdBa3w=", "dev": true }, "ini": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "integrity": "sha1-7uJfVtscnsYIXgwid4CD9Zar+Sc=", "dev": true }, "inquirer": { @@ -7991,7 +7991,7 @@ "invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "integrity": "sha1-YQ88ksk1nOHbYW5TgAjSP/NRWOY=", "dev": true, "requires": { "loose-envify": "^1.0.0" @@ -8012,19 +8012,19 @@ "irregular-plurals": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-2.0.0.tgz", - "integrity": "sha512-Y75zBYLkh0lJ9qxeHlMjQ7bSbyiSqNW/UOPWDmzC7cXskL1hekSITh1Oc6JV0XCWWZ9DE8VYSB71xocLk3gmGw==", + "integrity": "sha1-OdQPBbAPZW0Lf6RxIw3TtxSvKHI=", "dev": true }, "is": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz", - "integrity": "sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg==", + "integrity": "sha1-Yc/23TxBk9uUo9YlggcrROVkXXk=", "dev": true }, "is-absolute": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", - "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "integrity": "sha1-OV4a6EsR8mrReV5zwXN45IowFXY=", "dev": true, "requires": { "is-relative": "^1.0.0", @@ -8075,13 +8075,13 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=", "dev": true }, "is-callable": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha1-HhrfIZ4e62hNaR+dagX/DTCiTXU=", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", "dev": true }, "is-color-stop": { @@ -8127,7 +8127,7 @@ "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", "dev": true, "requires": { "is-accessor-descriptor": "^0.1.6", @@ -8138,7 +8138,7 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", "dev": true } } @@ -8195,7 +8195,7 @@ "is-gif": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-gif/-/is-gif-3.0.0.tgz", - "integrity": "sha512-IqJ/jlbw5WJSNfwQ/lHEDXF8rxhRgF6ythk2oiEvhpG29F704eX9NO6TvPfMiq9DrbwgcEDnETYNcZDPewQoVw==", + "integrity": "sha1-xL5gsmowHWlbuDOyDZtdZsbPg7E=", "dev": true, "optional": true, "requires": { @@ -8205,7 +8205,7 @@ "file-type": { "version": "10.11.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-10.11.0.tgz", - "integrity": "sha512-uzk64HRpUZyTGZtVuvrjP0FYxzQrBf4rojot6J65YMEbwBLB0CWm0CLojVpwpmFmxcE/lkvYICgfcGozbBq6rw==", + "integrity": "sha1-KWHQnkZ1ufuaPua2npzSP0P9GJA=", "dev": true, "optional": true } @@ -8283,7 +8283,7 @@ "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "integrity": "sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=", "dev": true, "requires": { "isobject": "^3.0.1" @@ -8292,7 +8292,7 @@ "is-png": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-png/-/is-png-2.0.0.tgz", - "integrity": "sha512-4KPGizaVGj2LK7xwJIz8o5B2ubu1D/vcQsgOGFEDlpcvgZHto4gBnyd0ig7Ws+67ixmwKoNmu0hYnpo6AaKb5g==", + "integrity": "sha1-7oy8npsFBCXO3utKb7dKZJsKSo0=", "dev": true, "optional": true }, @@ -8332,7 +8332,7 @@ "is-relative": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", - "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "integrity": "sha1-obtpNc6MXboei5dUubLcwCDiJg0=", "dev": true, "requires": { "is-unc-path": "^1.0.0" @@ -8341,13 +8341,13 @@ "is-resolvable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "integrity": "sha1-+xj4fOH+uSUWnJpAfBkxijIG7Yg=", "dev": true }, "is-retry-allowed": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", - "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", + "integrity": "sha1-13hIi9CkZmo76KFIK58rqv7eqLQ=", "dev": true, "optional": true }, @@ -8361,7 +8361,7 @@ "is-svg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz", - "integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==", + "integrity": "sha1-kyHb0pwhLlypnE+peUxxS8r6L3U=", "dev": true, "requires": { "html-comment-regex": "^1.1.0" @@ -8384,7 +8384,7 @@ "is-unc-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", - "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "integrity": "sha1-1zHoiY7QkKEsNSrS6u1Qla0yLJ0=", "dev": true, "requires": { "unc-path-regex": "^0.1.2" @@ -8405,7 +8405,7 @@ "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "integrity": "sha1-0YUOuXkezRjmGCzhKjDzlmNLsZ0=", "dev": true }, "is-wsl": { @@ -8449,7 +8449,7 @@ "isurl": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", - "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", + "integrity": "sha1-sn9PSfPNqj6kSgpbfzRi5u3DnWc=", "dev": true, "optional": true, "requires": { @@ -8460,7 +8460,7 @@ "jasmine-core": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.5.0.tgz", - "integrity": "sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA==", + "integrity": "sha1-Eywj5kWvlthci8oTyHWLGEKfweQ=", "dev": true }, "jquery": { @@ -8481,19 +8481,19 @@ "js-levenshtein": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", - "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", + "integrity": "sha1-xs7ljrNVA3LfjeuF+tXOZs4B1Z0=", "dev": true }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "integrity": "sha1-GSA/tZmR35jjoocFDUZHzerzJJk=", "dev": true }, "js-yaml": { "version": "3.13.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "integrity": "sha1-r/FRswv9+o5J4F2iLnQV6d+jeEc=", "dev": true, "requires": { "argparse": "^1.0.7", @@ -8627,7 +8627,7 @@ "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "integrity": "sha1-gFZNLkg9rPbo7yCWUKZ98/DCg6Q=", "dev": true }, "json-buffer": { @@ -8640,7 +8640,7 @@ "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "integrity": "sha1-u4Z8+zRQ5pEHwTHRxRS6s9yLyqk=", "dev": true }, "json-schema": { @@ -8651,7 +8651,7 @@ "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=" }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -8667,7 +8667,7 @@ "json5": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz", - "integrity": "sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==", + "integrity": "sha1-gbbLBOm6SW8ccAXQe0NoomOPkLY=", "dev": true, "requires": { "minimist": "^1.2.0" @@ -8687,7 +8687,7 @@ "junk": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz", - "integrity": "sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==", + "integrity": "sha1-MUmQmNkCt+mMXZucgPQ0V6iKv6E=", "dev": true }, "just-debounce": { @@ -8733,7 +8733,7 @@ "anymatch": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "integrity": "sha1-xV7PAhheJGklk5kxDBc84xIzsUI=", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -8749,7 +8749,7 @@ "braces": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "integrity": "sha1-NFThpGLujVmeI23zNs2epPiv4Qc=", "dev": true, "requires": { "fill-range": "^7.0.1" @@ -8774,7 +8774,7 @@ "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "integrity": "sha1-GRmmp8df44ssfHflGYU12prN2kA=", "dev": true, "requires": { "to-regex-range": "^5.0.1" @@ -8799,7 +8799,7 @@ "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "integrity": "sha1-6h9/O4DwZCNug0cPhsCcJU+0Wwk=", "dev": true, "requires": { "binary-extensions": "^2.0.0" @@ -8817,7 +8817,7 @@ "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "integrity": "sha1-dTU0W4lnNNX4DE0GxQlVUnoU8Ss=", "dev": true }, "mime": { @@ -8829,7 +8829,7 @@ "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "integrity": "sha1-Dc1p/yOhybEf0JeDFmRKA4ghamU=", "dev": true }, "readdirp": { @@ -8850,7 +8850,7 @@ "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "integrity": "sha1-FkjESq58jZiKMmAY7XL1tN0DkuQ=", "dev": true, "requires": { "is-number": "^7.0.0" @@ -8861,7 +8861,7 @@ "karma-jasmine": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-2.0.1.tgz", - "integrity": "sha512-iuC0hmr9b+SNn1DaUD2QEYtUxkS1J+bSJSn7ejdEexs7P8EYvA1CWkEdrDQ+8jVH3AgWlCNwjYsT1chjcNW9lA==", + "integrity": "sha1-JuPjHy+vJy3YDrsOGJiRTMOhl2M=", "dev": true, "requires": { "jasmine-core": "^3.3" @@ -8876,7 +8876,7 @@ "karma-junit-reporter": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/karma-junit-reporter/-/karma-junit-reporter-2.0.1.tgz", - "integrity": "sha512-VtcGfE0JE4OE1wn0LK8xxDKaTP7slN8DO3I+4xg6gAi1IoAHAXOJ1V9G/y45Xg6sxdxPOR3THCFtDlAfBo9Afw==", + "integrity": "sha1-007vfwsv0GTgiWlU6IUakM8UyPM=", "dev": true, "requires": { "path-is-absolute": "^1.0.0", @@ -8895,7 +8895,7 @@ "keyv": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz", - "integrity": "sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA==", + "integrity": "sha1-RJI7o55osSp87H32wyaMAx8u83M=", "dev": true, "optional": true, "requires": { @@ -8905,13 +8905,13 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true }, "kuler": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz", - "integrity": "sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ==", + "integrity": "sha1-73x4TzbJ+24W3TFQ0VJneysCKKY=", "dev": true, "requires": { "colornames": "^1.1.1" @@ -9000,7 +9000,7 @@ "less": { "version": "3.10.3", "resolved": "https://registry.npmjs.org/less/-/less-3.10.3.tgz", - "integrity": "sha512-vz32vqfgmoxF1h3K4J+yKCtajH0PWmjkIFgbs5d78E/c/e+UQTnI+lWK+1eQRE95PXM2mC3rJlLSSP9VQHnaow==", + "integrity": "sha1-QXoJddXu7MUs/0vPo8CdNXgeZ5I=", "dev": true, "requires": { "clone": "^2.1.2", @@ -9023,7 +9023,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true, "optional": true } @@ -9213,7 +9213,7 @@ "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "integrity": "sha1-VYqlO0O2YeGSWgr9+japoQhf5Xo=", "dev": true }, "lodash.partialright": { @@ -9326,7 +9326,7 @@ "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "integrity": "sha1-ce5R+nvkyuwaY4OffmgtgTLTDK8=", "dev": true, "requires": { "js-tokens": "^3.0.0 || ^4.0.0" @@ -9346,7 +9346,7 @@ "lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "integrity": "sha1-b54wtHCE2XGnyCD/FabFFnt0wm8=", "dev": true, "optional": true }, @@ -9366,7 +9366,7 @@ "lru-cache": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "integrity": "sha1-i75Q6oW+1ZvJ4z3KuCNe6bz0Q80=", "dev": true, "requires": { "pseudomap": "^1.0.2", @@ -9394,7 +9394,7 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "integrity": "sha1-7gpkyK9ejO6mdoexM3YeG+y9HT0=", "dev": true } } @@ -9402,7 +9402,7 @@ "make-iterator": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", - "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", + "integrity": "sha1-KbM/MSqo9UfEpeSQ9Wr87JkTOtY=", "dev": true, "requires": { "kind-of": "^6.0.2" @@ -9433,7 +9433,7 @@ "marked": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz", - "integrity": "sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==", + "integrity": "sha1-tkIB8FHScbHtwQoE0a6bdLuOXA4=", "dev": true }, "matchdep": { @@ -9465,13 +9465,13 @@ "math-random": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", - "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==", + "integrity": "sha1-XdaUPJOFSCZwFtTjTwV1gwgMUUw=", "dev": true }, "mdn-data": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", - "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", + "integrity": "sha1-aZs8OKxvHXKAkaZGULZdOIUC/Vs=", "dev": true }, "media-typer": { @@ -9483,7 +9483,7 @@ "memoizee": { "version": "0.4.14", "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.14.tgz", - "integrity": "sha512-/SWFvWegAIYAO4NQMpcX+gcra0yEZu4OntmUdrBaWrJncxOqAziGFlHxc7yjKVK2uu3lpPW27P27wkR82wA8mg==", + "integrity": "sha1-B6APIEaZ+alcLZ53IYJxx81hDVc=", "dev": true, "requires": { "d": "1", @@ -9527,7 +9527,7 @@ "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "integrity": "sha1-UoI2KaFN0AyXcPtq1H3GMQ8sH2A=", "dev": true }, "merge2": { @@ -9539,7 +9539,7 @@ "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "integrity": "sha1-cIWbyVyYQJUvNZoGij/En57PrCM=", "dev": true, "requires": { "arr-diff": "^4.0.0", @@ -9560,7 +9560,7 @@ "mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "integrity": "sha1-Ms2eXGRVO9WNGaVor0Uqz/BJgbE=", "dev": true, "optional": true }, @@ -9586,14 +9586,14 @@ "mimic-response": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "integrity": "sha1-SSNTiHju9CBjy4o+OweYeBSHqxs=", "dev": true, "optional": true }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -9601,14 +9601,14 @@ }, "minimist": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, "minimize": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/minimize/-/minimize-2.2.0.tgz", - "integrity": "sha512-IxR2XMbw9pXCxApkdD9BTcH2U4XlXhbeySUrv71rmMS9XDA8BVXEsIuFu24LtwCfBgfbL7Fuh8/ZzkO5DaTLlQ==", + "integrity": "sha1-ixZ28wBR2FmNdDZGvRJpCwdNpMM=", "dev": true, "requires": { "argh": "^0.1.4", @@ -9623,7 +9623,7 @@ "mixin-deep": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "integrity": "sha1-ESC0PcNZp4Xc5ltVuC4lfM9HlWY=", "dev": true, "requires": { "for-in": "^1.0.2", @@ -9633,7 +9633,7 @@ "is-extendable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", "dev": true, "requires": { "is-plain-object": "^2.0.4" @@ -9652,7 +9652,7 @@ "dependencies": { "minimist": { "version": "0.0.8", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true } @@ -9678,7 +9678,7 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=", "dev": true }, "multipipe": { @@ -9693,7 +9693,7 @@ "mute-stdout": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", - "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==", + "integrity": "sha1-rLAwDrTeI6fd7sAU4+lgRLNHIzE=", "dev": true }, "mute-stream": { @@ -9712,7 +9712,7 @@ "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "integrity": "sha1-uHqKpPwN6P5r6IiVs4mD/yZb0Rk=", "dev": true, "requires": { "arr-diff": "^4.0.0", @@ -9737,12 +9737,12 @@ "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "integrity": "sha1-/qz3zPUlp3rpY0Q2pkiD/+yjRvs=", "dev": true }, "next-tick": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, @@ -9754,13 +9754,13 @@ "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "integrity": "sha1-ozeKdpbOfSI+iPybdkvX7xCJ42Y=", "dev": true }, "node-notifier": { "version": "5.4.3", "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.3.tgz", - "integrity": "sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q==", + "integrity": "sha1-y3La+UyTkECY4oucWQ/YZuRkvVA=", "dev": true, "requires": { "growly": "^1.3.0", @@ -9782,7 +9782,7 @@ "node.extend": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/node.extend/-/node.extend-2.0.2.tgz", - "integrity": "sha512-pDT4Dchl94/+kkgdwyS2PauDFjZG0Hk0IcHIB+LkW27HLDtdoeMxHTxZh39DYbPP8UflWXWj9JcdDozF+YDOpQ==", + "integrity": "sha1-tEBFJUlKzJl0DzcDxJa31Rgsxsw=", "dev": true, "requires": { "has": "^1.0.3", @@ -9792,7 +9792,7 @@ "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "integrity": "sha1-5m2xg4sgDB38IzIl0SyzZSDiNKg=", "dev": true, "requires": { "hosted-git-info": "^2.1.4", @@ -9819,7 +9819,7 @@ "normalize-url": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", - "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", + "integrity": "sha1-suHE3E98bVd0PfczpPWXjRhlBVk=", "dev": true }, "nouislider": { @@ -9830,7 +9830,7 @@ "now-and-later": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz", - "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==", + "integrity": "sha1-jlechoV2SnzALLaAOA6U9DzLH3w=", "dev": true, "requires": { "once": "^1.3.2" @@ -12933,7 +12933,7 @@ "npm-conf": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", - "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", + "integrity": "sha1-JWzEe9DiGMJZxOlVC/QTvCGSr/k=", "dev": true, "optional": true, "requires": { @@ -12963,7 +12963,7 @@ "nth-check": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "integrity": "sha1-sr0pXDfj3VijvwcAN2Zjuk2c8Fw=", "dev": true, "requires": { "boolbase": "~1.0.0" @@ -12990,7 +12990,7 @@ "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + "integrity": "sha1-R6ewFrqmi1+g7PPe4IqFxnmsZFU=" }, "object-assign": { "version": "3.0.0", @@ -13044,7 +13044,7 @@ "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "integrity": "sha1-HEfyct8nfzsdrwYWd9nILiMixg4=", "dev": true }, "object-visit": { @@ -13059,7 +13059,7 @@ "object.assign": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "integrity": "sha1-lovxEA15Vrs8oIbwBvhGs7xACNo=", "dev": true, "requires": { "define-properties": "^1.1.2", @@ -13220,7 +13220,7 @@ "optipng-bin": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/optipng-bin/-/optipng-bin-6.0.0.tgz", - "integrity": "sha512-95bB4y8IaTsa/8x6QH4bLUuyvyOoGBCLDA7wOgDL8UFqJpSUh1Hob8JRJhit+wC1ZLN3tQ7mFt7KuBj0x8F2Wg==", + "integrity": "sha1-N2Eg+nnV5x7uL1JBdu/dOl6r0xY=", "dev": true, "optional": true, "requires": { @@ -13281,7 +13281,7 @@ "os-filter-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/os-filter-obj/-/os-filter-obj-2.0.0.tgz", - "integrity": "sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg==", + "integrity": "sha1-HAti1fOiRCdJotE55t3e5ugdjRY=", "dev": true, "optional": true, "requires": { @@ -13290,7 +13290,7 @@ }, "os-locale": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "dev": true, "requires": { @@ -13306,7 +13306,7 @@ "p-cancelable": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", - "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==", + "integrity": "sha1-ueEjgAvOu3rBOkeb4ZW1B7mNMPo=", "dev": true, "optional": true }, @@ -13370,7 +13370,7 @@ "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "integrity": "sha1-aR0nCeeMefrjoVZiJFLQB2LKqqI=", "dev": true, "requires": { "callsites": "^3.0.0" @@ -13379,7 +13379,7 @@ "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "integrity": "sha1-s2MKvYlDQy9Us/BRkjjjPNffL3M=", "dev": true } } @@ -13437,7 +13437,7 @@ "parse-node-version": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", - "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "integrity": "sha1-4rXb7eAOf6m8NjYH9TMn6LBzGJs=", "dev": true }, "parse-passwd": { @@ -13473,7 +13473,7 @@ "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "integrity": "sha1-naGee+6NEt/wUT7Vt2lXeTvC6NQ=", "dev": true }, "pascalcase": { @@ -13512,7 +13512,7 @@ "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "integrity": "sha1-1i27VnlAXXLEc37FhgDp3c8G0kw=", "dev": true }, "path-root": { @@ -13569,7 +13569,7 @@ }, "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true }, @@ -13591,7 +13591,7 @@ "plugin-error": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", - "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "integrity": "sha1-dwFr2JGdCsN3/c3QMiMolTyleBw=", "dev": true, "requires": { "ansi-colors": "^1.0.1", @@ -13603,7 +13603,7 @@ "plur": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/plur/-/plur-3.1.1.tgz", - "integrity": "sha512-t1Ax8KUvV3FFII8ltczPn2tJdjqbd1sIzu6t4JL7nQ3EyeL/lTrj5PWKb06ic5/6XYDr65rQ4uzQEGN70/6X5w==", + "integrity": "sha1-YCZ5Z4ZqjYEVBP5Y8vqrojdUals=", "dev": true, "requires": { "irregular-plurals": "^2.0.0" @@ -13629,13 +13629,13 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true }, "supports-color": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "integrity": "sha1-B2Srxpxj1ayELdSGfo0CXogN+PM=", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -13658,7 +13658,7 @@ "postcss-colormin": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", - "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", + "integrity": "sha1-rgYLzpPteUrHEmTwgTLVUJVr04E=", "dev": true, "requires": { "browserslist": "^4.0.0", @@ -13671,7 +13671,7 @@ "postcss-convert-values": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", - "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", + "integrity": "sha1-yjgT7U2g+BL51DcDWE5Enr4Ymn8=", "dev": true, "requires": { "postcss": "^7.0.0", @@ -13681,7 +13681,7 @@ "postcss-discard-comments": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", - "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", + "integrity": "sha1-H7q9LCRr/2qq15l7KwkY9NevQDM=", "dev": true, "requires": { "postcss": "^7.0.0" @@ -13690,7 +13690,7 @@ "postcss-discard-duplicates": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", - "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", + "integrity": "sha1-P+EzzTyCKC5VD8myORdqkge3hOs=", "dev": true, "requires": { "postcss": "^7.0.0" @@ -13699,7 +13699,7 @@ "postcss-discard-empty": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", - "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", + "integrity": "sha1-yMlR6fc+2UKAGUWERKAq2Qu592U=", "dev": true, "requires": { "postcss": "^7.0.0" @@ -13708,7 +13708,7 @@ "postcss-discard-overridden": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", - "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", + "integrity": "sha1-ZSrvipZybwKfXj4AFG7npOdV/1c=", "dev": true, "requires": { "postcss": "^7.0.0" @@ -13717,7 +13717,7 @@ "postcss-load-config": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.0.tgz", - "integrity": "sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q==", + "integrity": "sha1-yE1pK3u3tB3c7ZTuYuirMbQXsAM=", "dev": true, "requires": { "cosmiconfig": "^5.0.0", @@ -13727,7 +13727,7 @@ "postcss-merge-longhand": { "version": "4.0.11", "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", - "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", + "integrity": "sha1-YvSaE+Sg7gTnuY9CuxYGLKJUniQ=", "dev": true, "requires": { "css-color-names": "0.0.4", @@ -13739,7 +13739,7 @@ "postcss-merge-rules": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", - "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", + "integrity": "sha1-NivqT/Wh+Y5AdacTxsslrv75plA=", "dev": true, "requires": { "browserslist": "^4.0.0", @@ -13766,7 +13766,7 @@ "postcss-minify-font-values": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", - "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", + "integrity": "sha1-zUw0TM5HQ0P6xdgiBqssvLiv1aY=", "dev": true, "requires": { "postcss": "^7.0.0", @@ -13776,7 +13776,7 @@ "postcss-minify-gradients": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", - "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", + "integrity": "sha1-k7KcL/UJnFNe7NpWxKpuZlpmNHE=", "dev": true, "requires": { "cssnano-util-get-arguments": "^4.0.0", @@ -13788,7 +13788,7 @@ "postcss-minify-params": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", - "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", + "integrity": "sha1-a5zvAwwR41Jh+V9hjJADbWgNuHQ=", "dev": true, "requires": { "alphanum-sort": "^1.0.0", @@ -13802,7 +13802,7 @@ "postcss-minify-selectors": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", - "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", + "integrity": "sha1-4uXrQL/uUA0M2SQ1APX46kJi+9g=", "dev": true, "requires": { "alphanum-sort": "^1.0.0", @@ -13827,7 +13827,7 @@ "postcss-normalize-charset": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", - "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", + "integrity": "sha1-izWt067oOhNrBHHg1ZvlilAoXdQ=", "dev": true, "requires": { "postcss": "^7.0.0" @@ -13836,7 +13836,7 @@ "postcss-normalize-display-values": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", - "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", + "integrity": "sha1-Db4EpM6QY9RmftK+R2u4MMglk1o=", "dev": true, "requires": { "cssnano-util-get-match": "^4.0.0", @@ -13847,7 +13847,7 @@ "postcss-normalize-positions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", - "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", + "integrity": "sha1-BfdX+E8mBDc3g2ipH4ky1LECkX8=", "dev": true, "requires": { "cssnano-util-get-arguments": "^4.0.0", @@ -13859,7 +13859,7 @@ "postcss-normalize-repeat-style": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", - "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", + "integrity": "sha1-xOu8KJ85kaAo1EdRy90RkYsXkQw=", "dev": true, "requires": { "cssnano-util-get-arguments": "^4.0.0", @@ -13871,7 +13871,7 @@ "postcss-normalize-string": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", - "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", + "integrity": "sha1-zUTECrB6DHo23F6Zqs4eyk7CaQw=", "dev": true, "requires": { "has": "^1.0.0", @@ -13882,7 +13882,7 @@ "postcss-normalize-timing-functions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", - "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", + "integrity": "sha1-jgCcoqOUnNr4rSPmtquZy159KNk=", "dev": true, "requires": { "cssnano-util-get-match": "^4.0.0", @@ -13893,7 +13893,7 @@ "postcss-normalize-unicode": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", - "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", + "integrity": "sha1-hBvUj9zzAZrUuqdJOj02O1KuHPs=", "dev": true, "requires": { "browserslist": "^4.0.0", @@ -13904,7 +13904,7 @@ "postcss-normalize-url": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", - "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", + "integrity": "sha1-EOQ3+GvHx+WPe5ZS7YeNqqlfquE=", "dev": true, "requires": { "is-absolute-url": "^2.0.0", @@ -13916,7 +13916,7 @@ "postcss-normalize-whitespace": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", - "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", + "integrity": "sha1-vx1AcP5Pzqh9E0joJdjMDF+qfYI=", "dev": true, "requires": { "postcss": "^7.0.0", @@ -13926,7 +13926,7 @@ "postcss-ordered-values": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", - "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", + "integrity": "sha1-DPdcgg7H1cTSgBiVWeC1ceusDu4=", "dev": true, "requires": { "cssnano-util-get-arguments": "^4.0.0", @@ -13937,7 +13937,7 @@ "postcss-reduce-initial": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", - "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", + "integrity": "sha1-f9QuvqXpyBRgljniwuhK4nC6SN8=", "dev": true, "requires": { "browserslist": "^4.0.0", @@ -13949,7 +13949,7 @@ "postcss-reduce-transforms": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", - "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", + "integrity": "sha1-F++kBerMbge+NBSlyi0QdGgdTik=", "dev": true, "requires": { "cssnano-util-get-match": "^4.0.0", @@ -13972,7 +13972,7 @@ "postcss-svgo": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.2.tgz", - "integrity": "sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw==", + "integrity": "sha1-F7mXvHEbMzurFDqu07jT1uPTglg=", "dev": true, "requires": { "is-svg": "^3.0.0", @@ -13984,7 +13984,7 @@ "postcss-unique-selectors": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", - "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", + "integrity": "sha1-lEaRHzKJv9ZMbWgPBzwDsfnuS6w=", "dev": true, "requires": { "alphanum-sort": "^1.0.0", @@ -14025,32 +14025,32 @@ }, "pretty-hrtime": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "resolved": "http://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", "dev": true }, "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "integrity": "sha1-I4Hts2ifelPWUxkAYPz4ItLzaP8=", "dev": true }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "integrity": "sha1-eCDZsWEgzFXKmud5JoCufbptf+I=", "dev": true }, "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "integrity": "sha1-foz42PW48jnBvGi+tOt4Vn1XLvg=", "dev": true }, "promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "integrity": "sha1-BktyYCsY+Q8pGSuLG8QY/9Hr078=", "dev": true, "optional": true, "requires": { @@ -14085,7 +14085,7 @@ "pump": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "integrity": "sha1-Ejma3W5M91Jtlzy8i1zi4pCLOQk=", "dev": true, "requires": { "end-of-stream": "^1.1.0", @@ -14095,7 +14095,7 @@ "pumpify": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "integrity": "sha1-NlE74karJ1cLGjdKXOJ4v9dDcM4=", "dev": true, "requires": { "duplexify": "^3.6.0", @@ -14106,7 +14106,7 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=" }, "q": { "version": "1.5.1", @@ -14117,18 +14117,18 @@ "qjobs": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", - "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "integrity": "sha1-xF6cYYAL0IfviNfiVkI73Unl0HE=", "dev": true }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + "integrity": "sha1-yzroBuh0BERYTvFUzo7pjUA/PjY=" }, "query-string": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", - "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", + "integrity": "sha1-p4wBK3HBfgXy4/ojGd0zBoLvs8s=", "dev": true, "optional": true, "requires": { @@ -14154,7 +14154,7 @@ "randomatic": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", - "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", + "integrity": "sha1-t3bvxZN1mE42xTey9RofCv8Noe0=", "dev": true, "requires": { "is-number": "^4.0.0", @@ -14165,7 +14165,7 @@ "is-number": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "integrity": "sha1-ACbjf1RU1z41bf5lZGmYZ8an8P8=", "dev": true } } @@ -14173,13 +14173,13 @@ "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "integrity": "sha1-PPNwI9GZ4cJNGlW4SADC8+ZGgDE=", "dev": true }, "raw-body": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "integrity": "sha1-oc5vucm8NWylLoklarWQWeE9AzI=", "dev": true, "requires": { "bytes": "3.1.0", @@ -14224,7 +14224,7 @@ "readdirp": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "integrity": "sha1-DodiKjMlqjPokihcr4tOhGUppSU=", "dev": true, "requires": { "graceful-fs": "^4.1.11", @@ -14262,7 +14262,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -14293,7 +14293,7 @@ "regenerate": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", - "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", + "integrity": "sha1-SoVuxLVuQHfFV1icroXnpMiGmhE=", "dev": true }, "regenerate-unicode-properties": { @@ -14317,7 +14317,7 @@ "regex-cache": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "integrity": "sha1-db3FiioUls7EihKDW8VMjVYjNt0=", "dev": true, "requires": { "is-equal-shallow": "^0.1.3" @@ -14326,7 +14326,7 @@ "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "integrity": "sha1-H07OJ+ALC2XgJHpoEOaoXYOldSw=", "dev": true, "requires": { "extend-shallow": "^3.0.2", @@ -14336,7 +14336,7 @@ "regexpp": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "integrity": "sha1-jRnTHPYySCtYkEn4KB+T28uk0H8=", "dev": true }, "regexpu-core": { @@ -14356,7 +14356,7 @@ "regjsgen": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz", - "integrity": "sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==", + "integrity": "sha1-SPC/Gl6iBRlpKcDZeYtC0e2YRDw=", "dev": true }, "regjsparser": { @@ -14379,7 +14379,7 @@ "remove-bom-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", - "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", + "integrity": "sha1-wr8eN3Ug0yT2I4kuM8EMrCwlK1M=", "dev": true, "requires": { "is-buffer": "^1.1.5", @@ -14406,7 +14406,7 @@ "repeat-element": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "integrity": "sha1-eC4NglwMWjuzlzH4Tv7mt0Lmsc4=", "dev": true }, "repeat-string": { @@ -14579,19 +14579,19 @@ "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "integrity": "sha1-uKSCXVvbH8P29Twrwz+BOIaBx7w=", "dev": true }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "integrity": "sha1-kNo4Kx4SbvwCFG6QhFqI2xKSXXY=", "dev": true }, "rfdc": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.4.tgz", - "integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug==", + "integrity": "sha1-unLME2egzNnPgahws7WL060H+MI=", "dev": true }, "rgb-regex": { @@ -14618,7 +14618,7 @@ "rimraf": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "integrity": "sha1-stEE/g2Psnz54KHNqCYt04M8bKs=", "dev": true, "requires": { "glob": "^7.1.3" @@ -14642,7 +14642,7 @@ "run-sequence": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/run-sequence/-/run-sequence-2.2.1.tgz", - "integrity": "sha512-qkzZnQWMZjcKbh3CNly2srtrkaO/2H/SI5f2eliMCapdRD3UhMrwjfOAZJAnZ2H8Ju4aBzFZkBGXUqFs9V0yxw==", + "integrity": "sha1-HOZD2jb9jH6n4akynaM/wriJhJU=", "dev": true, "requires": { "chalk": "^1.1.3", @@ -14680,7 +14680,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { @@ -14702,7 +14702,7 @@ }, "kind-of": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", "dev": true }, @@ -14739,7 +14739,7 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=" }, "safe-regex": { "version": "1.1.0", @@ -14753,12 +14753,12 @@ "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=" }, "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "integrity": "sha1-KBYjTiN4vdxOU1T6tcqold9xANk=", "dev": true }, "saxes": { @@ -14788,7 +14788,7 @@ "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "integrity": "sha1-qVT5Ma66UI0we78Gnv8MAclhFvc=", "dev": true }, "semver-greatest-satisfied-range": { @@ -14803,7 +14803,7 @@ "semver-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz", - "integrity": "sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==", + "integrity": "sha1-qTwsWERTmncCMzeRB7OMe0rJ0zg=", "dev": true, "optional": true }, @@ -14826,7 +14826,7 @@ "set-value": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "integrity": "sha1-oY1AUw5vB95CKMfe/kInr4ytAFs=", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -14849,7 +14849,7 @@ "setprototypeof": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "integrity": "sha1-fpWsskqpL1iF4KvvW6ExMw1K5oM=", "dev": true }, "shebang-command": { @@ -14870,7 +14870,7 @@ "shellwords": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", - "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "integrity": "sha1-1rkYHBpI05cyTISHHvvPxz/AZUs=", "dev": true }, "signal-exit": { @@ -14891,7 +14891,7 @@ "is-arrayish": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "integrity": "sha1-RXSirlb3qyBolvtDHq7tBm/fjwM=", "dev": true } } @@ -14905,7 +14905,7 @@ "slice-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "integrity": "sha1-ys12k0YaY3pXiNkqfdT7oGjoFjY=", "dev": true, "requires": { "ansi-styles": "^3.2.0", @@ -14924,7 +14924,7 @@ "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "integrity": "sha1-ZJIufFZbDhQgS6GqfWlkJ40lGC0=", "dev": true, "requires": { "base": "^0.11.1", @@ -14940,7 +14940,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", "dev": true, "requires": { "ms": "2.0.0" @@ -14975,7 +14975,7 @@ "snapdragon-node": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "integrity": "sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=", "dev": true, "requires": { "define-property": "^1.0.0", @@ -14995,7 +14995,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -15004,7 +15004,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -15013,7 +15013,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -15026,7 +15026,7 @@ "snapdragon-util": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "integrity": "sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=", "dev": true, "requires": { "kind-of": "^3.2.0" @@ -15231,13 +15231,13 @@ "sparkles": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", - "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==", + "integrity": "sha1-AI22XtzmxQ7sDF4ijhlFBh3QQ3w=", "dev": true }, "spdx-correct": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "integrity": "sha1-+4PlBERSaPFUsHTiGMh8ADzTHfQ=", "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", @@ -15247,7 +15247,7 @@ "spdx-exceptions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "integrity": "sha1-LqRQrudPKom/uUUZwH/Nb0EyKXc=", "dev": true }, "spdx-expression-parse": { @@ -15263,7 +15263,7 @@ "spdx-license-ids": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "integrity": "sha1-NpS1gEVnpFjTyARYQqY1hjL2JlQ=", "dev": true }, "spectrum-colorpicker2": { @@ -15274,7 +15274,7 @@ "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "integrity": "sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=", "dev": true, "requires": { "extend-shallow": "^3.0.0" @@ -15307,7 +15307,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "optional": true, @@ -15331,7 +15331,7 @@ "sshpk": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "integrity": "sha1-+2YcC+8ps520B2nuOfpwCT1vaHc=", "requires": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -15347,7 +15347,7 @@ "stable": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "integrity": "sha1-g26zyDgv4pNv6vVEYxAXzn1Ho88=", "dev": true }, "stack-trace": { @@ -15392,7 +15392,7 @@ "stream-exhaust": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", - "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==", + "integrity": "sha1-rNrI2lnvK8HheiwMz2wyDRIOVV0=", "dev": true }, "stream-shift": { @@ -15526,7 +15526,7 @@ "strip-dirs": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", - "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", + "integrity": "sha1-SYdzYmT8NEzyD2w0rKnRPR1O1sU=", "dev": true, "optional": true, "requires": { @@ -15566,7 +15566,7 @@ "strip-outer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", - "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", + "integrity": "sha1-sv0qv2YEudHmATBXGV34Nrip1jE=", "dev": true, "optional": true, "requires": { @@ -15576,7 +15576,7 @@ "stylehacks": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", - "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==", + "integrity": "sha1-Zxj8r00eB9ihMYaQiB6NlnJqcdU=", "dev": true, "requires": { "browserslist": "^4.0.0", @@ -15600,7 +15600,7 @@ "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -15646,7 +15646,7 @@ "table": { "version": "5.4.6", "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "integrity": "sha1-EpLRlQDOP4YFOwXw6Ofko7shB54=", "dev": true, "requires": { "ajv": "^6.10.2", @@ -15658,7 +15658,7 @@ "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "integrity": "sha1-i5+PCM8ay4Q3Vqg5yox+MWjFGZc=", "dev": true }, "is-fullwidth-code-point": { @@ -15670,7 +15670,7 @@ "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "integrity": "sha1-InZ74htirxCBV0MG9prFG2IgOWE=", "dev": true, "requires": { "emoji-regex": "^7.0.1", @@ -15681,7 +15681,7 @@ "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "integrity": "sha1-jJpTb+tq/JYr36WxBKUJHBrZwK4=", "dev": true, "requires": { "ansi-regex": "^4.1.0" @@ -15692,7 +15692,7 @@ "tar-stream": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", - "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", + "integrity": "sha1-jqVdqzeXIlPZqa+Q/c1VmuQ1xVU=", "dev": true, "optional": true, "requires": { @@ -15780,7 +15780,7 @@ "text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "integrity": "sha1-adycGxdEbueakr9biEu0uRJ1BvU=", "dev": true }, "text-table": { @@ -15798,7 +15798,7 @@ "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "integrity": "sha1-AcHjnrMdB8t9A6lqcIIyYLIxMs0=", "dev": true, "requires": { "readable-stream": "~2.3.6", @@ -15840,7 +15840,7 @@ "through2-concurrent": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/through2-concurrent/-/through2-concurrent-2.0.0.tgz", - "integrity": "sha512-R5/jLkfMvdmDD+seLwN7vB+mhbqzWop5fAjx5IX8/yQq7VhBhzDmhXgaHAOnhnWkCpRMM7gToYHycB0CS/pd+A==", + "integrity": "sha1-yd0sFGUE7Jli28hqUWi2PWYmafo=", "dev": true, "requires": { "through2": "^2.0.0" @@ -15849,7 +15849,7 @@ "through2-filter": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", - "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", + "integrity": "sha1-cA54bfI2fCyIzYqlvkz5weeDElQ=", "dev": true, "requires": { "through2": "~2.0.0", @@ -15872,7 +15872,7 @@ "timers-ext": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", - "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", + "integrity": "sha1-b1ethXjgej+5+R2Th9ZWR1VeJcY=", "dev": true, "requires": { "es5-ext": "~0.10.46", @@ -15888,7 +15888,7 @@ "tiny-emitter": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", - "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" + "integrity": "sha1-HRpW7fxRxD6GPLtTgqcjMONVVCM=" }, "tinymce": { "version": "4.9.11", @@ -15898,7 +15898,7 @@ "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "integrity": "sha1-bTQzWIl2jSGyvNoKonfO07G/rfk=", "dev": true, "requires": { "os-tmpdir": "~1.0.2" @@ -15923,7 +15923,7 @@ "to-buffer": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", - "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", + "integrity": "sha1-STvUj2LXxD/N7TE6A9ytsuEhOoA=", "dev": true, "optional": true }, @@ -15956,7 +15956,7 @@ "to-regex": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "integrity": "sha1-E8/dmzNlUvMLUfM6iuG0Knp1mc4=", "dev": true, "requires": { "define-property": "^2.0.2", @@ -15987,7 +15987,7 @@ "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "integrity": "sha1-fhvjRw8ed5SLxD2Uo8j013UrpVM=", "dev": true }, "tough-cookie": { @@ -16061,7 +16061,7 @@ "type": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "integrity": "sha1-hI3XaY2vo+VKbEeedZxLw/GIR6A=", "dev": true }, "type-check": { @@ -16076,7 +16076,7 @@ "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "integrity": "sha1-TlUs0F3wlGfcvE73Od6J8s83wTE=", "dev": true, "requires": { "media-typer": "0.3.0", @@ -16183,7 +16183,7 @@ "undertaker": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.2.1.tgz", - "integrity": "sha512-71WxIzDkgYk9ZS+spIB8iZXchFhAdEo2YU8xYqBYJ39DIUIqziK78ftm26eecoIY49X0J2MLhG4hr18Yp6/CMA==", + "integrity": "sha1-cBZi/4zjWHFTJN/UkqTwNgVd/ks=", "dev": true, "requires": { "arr-flatten": "^1.0.1", @@ -16206,13 +16206,13 @@ "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", - "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", + "integrity": "sha1-JhmADEyCWADv3YNDr33Zkzy+KBg=", "dev": true }, "unicode-match-property-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", - "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "integrity": "sha1-jtKjJWmWG86SJ9Cc0/+7j+1fAgw=", "dev": true, "requires": { "unicode-canonical-property-names-ecmascript": "^1.0.4", @@ -16234,7 +16234,7 @@ "union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "integrity": "sha1-C2/nuDWuzaYcbqTU8CwUIh4QmEc=", "dev": true, "requires": { "arr-union": "^3.1.0", @@ -16258,7 +16258,7 @@ "unique-stream": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", - "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", + "integrity": "sha1-xl0RDppK35psWUiygFPZqNBMvqw=", "dev": true, "requires": { "json-stable-stringify-without-jsonify": "^1.0.1", @@ -16268,7 +16268,7 @@ "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "integrity": "sha1-tkb2m+OULavOzJ1mOcgNwQXvqmY=", "dev": true }, "unpipe": { @@ -16332,13 +16332,13 @@ "upath": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "integrity": "sha1-j2bbzVWog6za5ECK+LA1pQRMGJQ=", "dev": true }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "integrity": "sha1-lMVA4f93KVbiKZUHwBCupsiDjrA=", "requires": { "punycode": "^2.1.0" } @@ -16378,7 +16378,7 @@ "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "integrity": "sha1-1QyMrHmhn7wg8pEfVuuXP04QBw8=", "dev": true }, "useragent": { @@ -16400,7 +16400,7 @@ "util.promisify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", - "integrity": "sha1-RA9xZaRZyaFtwUXrjnLzVocJcDA=", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", "dev": true, "requires": { "define-properties": "^1.1.2", @@ -16436,7 +16436,7 @@ "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "integrity": "sha1-/JH2uce6FchX9MssXe/uw51PQQo=", "dev": true, "requires": { "spdx-correct": "^3.0.0", @@ -16515,7 +16515,7 @@ "vinyl-fs": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz", - "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==", + "integrity": "sha1-yFhJQF9nQo/qu71cXb3WT0fTG8c=", "dev": true, "requires": { "fs-mkdirp-stream": "^1.0.0", @@ -16724,7 +16724,7 @@ "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "integrity": "sha1-pFBD1U9YBTFtqNYvn1CRjT2nCwo=", "dev": true, "requires": { "isexe": "^2.0.0" @@ -16755,7 +16755,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { @@ -16772,7 +16772,7 @@ "write": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "integrity": "sha1-CADhRSO5I6OH5BUSPIZWFqrg9cM=", "dev": true, "requires": { "mkdirp": "^0.5.1" @@ -16798,7 +16798,7 @@ "xmlbuilder": { "version": "12.0.0", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-12.0.0.tgz", - "integrity": "sha512-lMo8DJ8u6JRWp0/Y4XLa/atVDr75H9litKlb2E5j3V3MesoL50EBgZDWoLT3F/LztVnG67GjPXLZpqcky/UMnQ==", + "integrity": "sha1-4u1nXgaDSgid37hNuW4sKwP3jBo=", "dev": true }, "xmlchars": { @@ -16816,7 +16816,7 @@ "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "integrity": "sha1-u3J3n1+kZRhrH0OPZ0+jR/2121Q=", "dev": true }, "y18n": { diff --git a/src/Umbraco.Web.UI.NetCore/appsettings.json b/src/Umbraco.Web.UI.NetCore/appsettings.json index a44b576dd6..aaa5e72f9b 100644 --- a/src/Umbraco.Web.UI.NetCore/appsettings.json +++ b/src/Umbraco.Web.UI.NetCore/appsettings.json @@ -66,7 +66,7 @@ "EnableTours": true }, "ModelsBuilder": { - "ModelsMode": "PureLive", + "ModelsMode": "InMemoryAuto", "Enable": true } } diff --git a/src/Umbraco.Web.UI/web.Template.config b/src/Umbraco.Web.UI/web.Template.config index 78420bc1fb..437e6c60a5 100644 --- a/src/Umbraco.Web.UI/web.Template.config +++ b/src/Umbraco.Web.UI/web.Template.config @@ -47,7 +47,7 @@ - + - + From 0b3eeafb47a35b794d88194777ab1735ff2e8ea3 Mon Sep 17 00:00:00 2001 From: Jason Elkin Date: Fri, 11 Jun 2021 13:09:41 +0100 Subject: [PATCH 170/182] fixed a typo in the UmbracoProject template (#10432) * fixed a typo in the UmbracoProject template * Update build/templates/UmbracoProject/UmbracoProject.csproj Co-authored-by: Bjarke Berg --- build/templates/UmbracoProject/UmbracoProject.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/templates/UmbracoProject/UmbracoProject.csproj b/build/templates/UmbracoProject/UmbracoProject.csproj index 9798a40ff2..6850940edd 100644 --- a/build/templates/UmbracoProject/UmbracoProject.csproj +++ b/build/templates/UmbracoProject/UmbracoProject.csproj @@ -44,7 +44,7 @@ - + false From 20f3b223c5ccf04790dfef9b4f225c8eda2b1531 Mon Sep 17 00:00:00 2001 From: Jose Marcenaro <923289+JoseMarcenaro@users.noreply.github.com> Date: Fri, 11 Jun 2021 09:17:12 -0300 Subject: [PATCH 171/182] Use default( ) instead of null for model value (#10435) --- src/Umbraco.Web.Common/Views/UmbracoViewPage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.Common/Views/UmbracoViewPage.cs b/src/Umbraco.Web.Common/Views/UmbracoViewPage.cs index c2608efc41..97ba933838 100644 --- a/src/Umbraco.Web.Common/Views/UmbracoViewPage.cs +++ b/src/Umbraco.Web.Common/Views/UmbracoViewPage.cs @@ -228,7 +228,7 @@ namespace Umbraco.Cms.Web.Common.Views // if not possible or it is not generic then we need to create a new ViewDataDictionary Type nViewDataType = typeof(ViewDataDictionary<>).MakeGenericType(modelType); - var tViewData = new ViewDataDictionary(viewData) { Model = null }; // temp view data to copy values + var tViewData = new ViewDataDictionary(viewData) { Model = default(TModel) }; // temp view data to copy values var nViewData = (ViewDataDictionary)Activator.CreateInstance(nViewDataType, tViewData); return nViewData; } From e70c3628513ca720c4b4098f60a0a3a43df399f2 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Mon, 14 Jun 2021 12:20:06 +0100 Subject: [PATCH 172/182] Run `npm update caniuse-lite` --- src/Umbraco.Web.UI.Client/package-lock.json | 205 +++++--------------- src/Umbraco.Web.UI.Client/package.json | 4 +- 2 files changed, 55 insertions(+), 154 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 3f53638fc6..b2d811df89 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -1836,8 +1836,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", - "dev": true, - "optional": true + "dev": true }, "base64id": { "version": "1.0.0", @@ -2084,7 +2083,6 @@ "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz", "integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==", "dev": true, - "optional": true, "requires": { "p-finally": "^1.0.0" } @@ -2126,7 +2124,6 @@ "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", "integrity": "sha1-oWCRFxcQPAdBDO9j71Gzl8Alr5w=", "dev": true, - "optional": true, "requires": { "readable-stream": "^2.3.5", "safe-buffer": "^5.1.1" @@ -2136,15 +2133,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true, - "optional": true + "dev": true }, "readable-stream": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", "dev": true, - "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -2160,7 +2155,6 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "dev": true, - "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -2301,7 +2295,6 @@ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.4.3.tgz", "integrity": "sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A==", "dev": true, - "optional": true, "requires": { "base64-js": "^1.0.2", "ieee754": "^1.1.4" @@ -2327,8 +2320,7 @@ "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", - "dev": true, - "optional": true + "dev": true }, "buffer-equal": { "version": "1.0.0", @@ -2503,9 +2495,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001168", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001168.tgz", - "integrity": "sha512-P2zmX7swIXKu+GMMR01TWa4csIKELTNnZKc+f1CjebmZJQtTAEXmpQSoKVJVVcvPGAA0TEYTOUp3VehavZSFPQ==", + "version": "1.0.30001237", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001237.tgz", + "integrity": "sha512-pDHgRndit6p1NR2GhzMbQ6CkRrp4VKuSsqbcLeOQppYPKOYkKT/6ZvZDvKJUqcmtyWIAHuZq3SVS2vc1egCZzw==", "dev": true }, "caseless": { @@ -2519,7 +2511,6 @@ "resolved": "https://registry.npmjs.org/caw/-/caw-2.0.1.tgz", "integrity": "sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA==", "dev": true, - "optional": true, "requires": { "get-proxy": "^2.0.0", "isurl": "^1.0.0-alpha5", @@ -2953,7 +2944,6 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", "dev": true, - "optional": true, "requires": { "graceful-readlink": ">= 1.0.0" } @@ -3048,7 +3038,6 @@ "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", "dev": true, - "optional": true, "requires": { "ini": "^1.3.4", "proto-list": "~1.2.1" @@ -3104,7 +3093,6 @@ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", "dev": true, - "optional": true, "requires": { "safe-buffer": "5.1.2" } @@ -3548,7 +3536,6 @@ "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.0.tgz", "integrity": "sha1-eu3YVCflqS2s/lVnSnxQXpbQH50=", "dev": true, - "optional": true, "requires": { "decompress-tar": "^4.0.0", "decompress-tarbz2": "^4.0.0", @@ -3565,7 +3552,6 @@ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", "dev": true, - "optional": true, "requires": { "pify": "^3.0.0" }, @@ -3574,8 +3560,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true, - "optional": true + "dev": true } } } @@ -3586,7 +3571,6 @@ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", "dev": true, - "optional": true, "requires": { "mimic-response": "^1.0.0" } @@ -3596,7 +3580,6 @@ "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", "dev": true, - "optional": true, "requires": { "file-type": "^5.2.0", "is-stream": "^1.1.0", @@ -3607,8 +3590,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=", - "dev": true, - "optional": true + "dev": true } } }, @@ -3617,7 +3599,6 @@ "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", "dev": true, - "optional": true, "requires": { "decompress-tar": "^4.1.0", "file-type": "^6.1.0", @@ -3630,8 +3611,7 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==", - "dev": true, - "optional": true + "dev": true } } }, @@ -3640,7 +3620,6 @@ "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", "dev": true, - "optional": true, "requires": { "decompress-tar": "^4.1.1", "file-type": "^5.2.0", @@ -3651,8 +3630,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=", - "dev": true, - "optional": true + "dev": true } } }, @@ -3661,7 +3639,6 @@ "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", "integrity": "sha1-3qrM39FK6vhVePczroIQ+bSEj2k=", "dev": true, - "optional": true, "requires": { "file-type": "^3.8.0", "get-stream": "^2.2.0", @@ -3673,15 +3650,13 @@ "version": "3.9.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=", - "dev": true, - "optional": true + "dev": true }, "get-stream": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=", "dev": true, - "optional": true, "requires": { "object-assign": "^4.0.1", "pinkie-promise": "^2.0.0" @@ -3691,8 +3666,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, - "optional": true + "dev": true } } }, @@ -3974,8 +3948,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true, - "optional": true + "dev": true } } }, @@ -3992,8 +3965,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "dev": true, - "optional": true + "dev": true }, "duplexify": { "version": "3.7.1", @@ -4596,7 +4568,6 @@ "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "dev": true, - "optional": true, "requires": { "cross-spawn": "^5.0.1", "get-stream": "^3.0.0", @@ -4612,7 +4583,6 @@ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, - "optional": true, "requires": { "lru-cache": "^4.0.1", "shebang-command": "^1.2.0", @@ -4752,7 +4722,6 @@ "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", "integrity": "sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==", "dev": true, - "optional": true, "requires": { "mime-db": "^1.28.0" } @@ -4762,7 +4731,6 @@ "resolved": "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz", "integrity": "sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==", "dev": true, - "optional": true, "requires": { "ext-list": "^2.0.0", "sort-keys-length": "^1.0.0" @@ -4997,7 +4965,6 @@ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", "dev": true, - "optional": true, "requires": { "pend": "~1.2.0" } @@ -5036,15 +5003,13 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", "integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=", - "dev": true, - "optional": true + "dev": true }, "filenamify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-2.1.0.tgz", "integrity": "sha512-ICw7NTT6RsDp2rnYKVd8Fu4cr6ITzGy3+u4vUujPkabyaz+03F24NWEX7fs5fp+kBonlaqPH8fAO2NM+SXt/JA==", "dev": true, - "optional": true, "requires": { "filename-reserved-regex": "^2.0.0", "strip-outer": "^1.0.0", @@ -5405,8 +5370,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha1-a+Dem+mYzhavivwkSXue6bfM2a0=", - "dev": true, - "optional": true + "dev": true }, "fs-mkdirp-stream": { "version": "1.0.0", @@ -5453,8 +5417,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -5475,14 +5438,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5497,20 +5458,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -5627,8 +5585,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -5640,7 +5597,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -5655,7 +5611,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5663,14 +5618,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -5689,7 +5642,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -5770,8 +5722,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -5783,7 +5734,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -5869,8 +5819,7 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -5906,7 +5855,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -5926,7 +5874,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -5970,14 +5917,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -6004,7 +5949,6 @@ "resolved": "https://registry.npmjs.org/get-proxy/-/get-proxy-2.1.0.tgz", "integrity": "sha512-zmZIaQTWnNQb4R4fJUEp/FC51eZsc6EkErspy3xtIYStaq8EB/hDIWipxsal+E8rz0qD7f2sL/NA9Xee4RInJw==", "dev": true, - "optional": true, "requires": { "npm-conf": "^1.1.0" } @@ -6013,15 +5957,13 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true, - "optional": true + "dev": true }, "get-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true, - "optional": true + "dev": true }, "get-value": { "version": "2.0.6", @@ -6336,8 +6278,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", - "dev": true, - "optional": true + "dev": true }, "growly": { "version": "1.3.0", @@ -7108,8 +7049,7 @@ "version": "1.4.2", "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==", - "dev": true, - "optional": true + "dev": true }, "has-symbols": { "version": "1.0.0", @@ -7122,7 +7062,6 @@ "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", "dev": true, - "optional": true, "requires": { "has-symbol-support-x": "^1.4.1" } @@ -7322,8 +7261,7 @@ "version": "1.1.13", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "dev": true, - "optional": true + "dev": true }, "ignore": { "version": "4.0.6", @@ -7453,7 +7391,6 @@ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", "dev": true, - "optional": true, "requires": { "repeating": "^2.0.0" } @@ -7781,7 +7718,6 @@ "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -7834,8 +7770,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", "integrity": "sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=", - "dev": true, - "optional": true + "dev": true }, "is-negated-glob": { "version": "1.0.0", @@ -7873,15 +7808,13 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", - "dev": true, - "optional": true + "dev": true }, "is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true, - "optional": true + "dev": true }, "is-plain-object": { "version": "2.0.4", @@ -7951,15 +7884,13 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", - "dev": true, - "optional": true + "dev": true }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true, - "optional": true + "dev": true }, "is-svg": { "version": "3.0.0", @@ -8056,7 +7987,6 @@ "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", "dev": true, - "optional": true, "requires": { "has-to-string-tag-x": "^1.2.0", "is-object": "^1.0.1" @@ -8969,8 +8899,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", "integrity": "sha1-b54wtHCE2XGnyCD/FabFFnt0wm8=", - "dev": true, - "optional": true + "dev": true }, "lpad-align": { "version": "1.1.2", @@ -9040,8 +8969,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true, - "optional": true + "dev": true }, "map-visit": { "version": "1.0.0", @@ -9211,8 +9139,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true, - "optional": true + "dev": true }, "minimatch": { "version": "3.0.4", @@ -9435,9 +9362,9 @@ "dev": true }, "nouislider": { - "version": "14.6.3", - "resolved": "https://registry.npmjs.org/nouislider/-/nouislider-14.6.3.tgz", - "integrity": "sha512-/3tAqsWY2JYW9vd7bC14bFRA1P9A+pRHOtKmoMsyfnB0fQcd1UFx2pdY1Ey5wAUzTnXTesmYaEo/ecLVETijIQ==" + "version": "14.6.4", + "resolved": "https://registry.npmjs.org/nouislider/-/nouislider-14.6.4.tgz", + "integrity": "sha512-PVCGYl+aC7/nVEbW61ypJWfuW3UCpvctz/luxpt4byxxli1FFyjBX9NIiy4Yak9AaO6a5BkPGfFYMCW4eg3eeQ==" }, "now-and-later": { "version": "2.0.1", @@ -12547,7 +12474,6 @@ "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", "dev": true, - "optional": true, "requires": { "config-chain": "^1.1.11", "pify": "^3.0.0" @@ -12557,8 +12483,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true, - "optional": true + "dev": true } } }, @@ -12567,7 +12492,6 @@ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, - "optional": true, "requires": { "path-key": "^2.0.0" } @@ -12929,8 +12853,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true, - "optional": true + "dev": true }, "p-is-promise": { "version": "1.1.0", @@ -12967,7 +12890,6 @@ "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", "dev": true, - "optional": true, "requires": { "p-finally": "^1.0.0" } @@ -13158,8 +13080,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", - "dev": true, - "optional": true + "dev": true }, "performance-now": { "version": "2.1.0", @@ -13667,8 +13588,7 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", - "dev": true, - "optional": true + "dev": true }, "prr": { "version": "1.0.1", @@ -14024,7 +13944,6 @@ "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "dev": true, - "optional": true, "requires": { "is-finite": "^1.0.0" } @@ -14386,7 +14305,6 @@ "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz", "integrity": "sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w=", "dev": true, - "optional": true, "requires": { "commander": "~2.8.1" } @@ -14789,7 +14707,6 @@ "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", "dev": true, - "optional": true, "requires": { "is-plain-obj": "^1.0.0" } @@ -14799,7 +14716,6 @@ "resolved": "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz", "integrity": "sha1-nLb09OnkgVWmqgZx7dM2/xR5oYg=", "dev": true, - "optional": true, "requires": { "sort-keys": "^1.0.0" } @@ -15130,7 +15046,6 @@ "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", "dev": true, - "optional": true, "requires": { "is-natural-number": "^4.0.1" } @@ -15139,8 +15054,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true, - "optional": true + "dev": true }, "strip-indent": { "version": "1.0.1", @@ -15163,7 +15077,6 @@ "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", "integrity": "sha1-sv0qv2YEudHmATBXGV34Nrip1jE=", "dev": true, - "optional": true, "requires": { "escape-string-regexp": "^1.0.2" } @@ -15289,7 +15202,6 @@ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", "integrity": "sha1-jqVdqzeXIlPZqa+Q/c1VmuQ1xVU=", "dev": true, - "optional": true, "requires": { "bl": "^1.0.0", "buffer-alloc": "^1.2.0", @@ -15304,15 +15216,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true, - "optional": true + "dev": true }, "readable-stream": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", "dev": true, - "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -15328,7 +15238,6 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "dev": true, - "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -15339,15 +15248,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", "integrity": "sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=", - "dev": true, - "optional": true + "dev": true }, "tempfile": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-2.0.0.tgz", "integrity": "sha1-awRGhWqbERTRhW/8vlCczLCXcmU=", "dev": true, - "optional": true, "requires": { "temp-dir": "^1.0.0", "uuid": "^3.0.1" @@ -15442,8 +15349,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", - "dev": true, - "optional": true + "dev": true }, "timers-ext": { "version": "0.1.7", @@ -15500,8 +15406,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", "integrity": "sha1-STvUj2LXxD/N7TE6A9ytsuEhOoA=", - "dev": true, - "optional": true + "dev": true }, "to-fast-properties": { "version": "2.0.0", @@ -15605,7 +15510,6 @@ "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", "dev": true, - "optional": true, "requires": { "escape-string-regexp": "^1.0.2" } @@ -15742,7 +15646,6 @@ "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz", "integrity": "sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg==", "dev": true, - "optional": true, "requires": { "buffer": "^5.2.1", "through": "^2.3.8" @@ -15943,8 +15846,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=", - "dev": true, - "optional": true + "dev": true }, "use": { "version": "3.1.1", @@ -16440,7 +16342,6 @@ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", "dev": true, - "optional": true, "requires": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 6514f2f217..d0f1c1cb99 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -54,7 +54,7 @@ "@babel/core": "7.6.4", "@babel/preset-env": "7.6.3", "autoprefixer": "9.6.5", - "caniuse-lite": "^1.0.30001037", + "caniuse-lite": "^1.0.30001237", "cssnano": "4.1.10", "fs": "0.0.2", "gulp": "4.0.2", @@ -77,8 +77,8 @@ "jasmine-core": "3.5.0", "jsdom": "16.4.0", "karma": "4.4.1", - "karma-jsdom-launcher": "^8.0.2", "karma-jasmine": "2.0.1", + "karma-jsdom-launcher": "^8.0.2", "karma-junit-reporter": "2.0.1", "karma-spec-reporter": "0.0.32", "less": "3.10.3", From 6f3a83c50746f5ce1d26790b4c730699d18e8e1a Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Tue, 15 Jun 2021 15:12:39 +1000 Subject: [PATCH 173/182] Fix interface inheritance (#10470) --- .../ApplicationBuilder/IUmbracoApplicationBuilder.cs | 2 +- .../ApplicationBuilder/UmbracoApplicationBuilder.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.Common/ApplicationBuilder/IUmbracoApplicationBuilder.cs b/src/Umbraco.Web.Common/ApplicationBuilder/IUmbracoApplicationBuilder.cs index 68ba148f49..090ef52790 100644 --- a/src/Umbraco.Web.Common/ApplicationBuilder/IUmbracoApplicationBuilder.cs +++ b/src/Umbraco.Web.Common/ApplicationBuilder/IUmbracoApplicationBuilder.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Services; namespace Umbraco.Cms.Web.Common.ApplicationBuilder { - public interface IUmbracoApplicationBuilder : IUmbracoMiddlewareBuilder + public interface IUmbracoApplicationBuilder { /// /// Called to include umbraco middleware diff --git a/src/Umbraco.Web.Common/ApplicationBuilder/UmbracoApplicationBuilder.cs b/src/Umbraco.Web.Common/ApplicationBuilder/UmbracoApplicationBuilder.cs index 05fc38cc71..b7acc45d22 100644 --- a/src/Umbraco.Web.Common/ApplicationBuilder/UmbracoApplicationBuilder.cs +++ b/src/Umbraco.Web.Common/ApplicationBuilder/UmbracoApplicationBuilder.cs @@ -9,7 +9,7 @@ namespace Umbraco.Cms.Web.Common.ApplicationBuilder /// /// A builder to allow encapsulating the enabled endpoints in Umbraco /// - internal class UmbracoApplicationBuilder : IUmbracoApplicationBuilder + internal class UmbracoApplicationBuilder : IUmbracoApplicationBuilder, IUmbracoMiddlewareBuilder { public UmbracoApplicationBuilder(IServiceProvider services, IRuntimeState runtimeState, IApplicationBuilder appBuilder) { From 8d3434d7d8a2314410920b60aaf63985c869f25b Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Tue, 15 Jun 2021 08:42:52 +0100 Subject: [PATCH 174/182] dotnet new template - Escape JSON special characters for password & connection string (#10467) * Fix sourceName to match UmbracoProject and use a generated symbol to replace the Umbraco.Cms.Web.UI.NetCore in Program and Startup files * Update template.json * Run `npm update caniuse-lite` * Regex replacement for Password & Connection strings that gets placed into AppSettings.Development.json So escaping \ and " for JSON strings * Escaping input and handle namespace replacing. + Updated to beta004 Co-authored-by: Elitsa Marinovska Co-authored-by: Elitsa Marinovska <21998037+elit0451@users.noreply.github.com> Co-authored-by: Bjarke Berg --- build/build.ps1 | 25 ---- .../.template.config/template.json | 141 +++++++++++++++++- src/Directory.Build.props | 2 +- src/Umbraco.Web.UI.Client/package.json | 6 +- 4 files changed, 138 insertions(+), 36 deletions(-) diff --git a/build/build.ps1 b/build/build.ps1 index ba99e1e660..fb869738f3 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -351,29 +351,6 @@ $this.RemoveDirectory("$tmp\Templates\UmbracoProject\bin") }) - $ubuild.DefineMethod("PackageZip", - { - - Write-Host "Create Zip packages" - - $src = "$($this.SolutionRoot)\src" - $tmp = $this.BuildTemp - $out = $this.BuildOutput - - Write-Host "Zip all binaries" - &$this.BuildEnv.Zip a -r "$out\UmbracoCms.AllBinaries.$($this.Version.Semver).zip" ` - "$tmp\bin\*" ` - "-x!dotless.Core.*" ` - > $null - if (-not $?) { throw "Failed to zip UmbracoCms.AllBinaries." } - - Write-Host "Zip cms" - &$this.BuildEnv.Zip a -r "$out\UmbracoCms.$($this.Version.Semver).zip" ` - "$tmp\WebApp\*" ` - "-x!dotless.Core.*" "-x!Content_Types.xml" "-x!*.pdb" ` - > $null - if (-not $?) { throw "Failed to zip UmbracoCms." } - }) $ubuild.DefineMethod("PrepareBuild", { @@ -541,8 +518,6 @@ # not running tests $this.PreparePackages() if ($this.OnError()) { return } - $this.PackageZip() - if ($this.OnError()) { return } $this.VerifyNuGet() if ($this.OnError()) { return } $this.PackageNuGet() diff --git a/build/templates/UmbracoProject/.template.config/template.json b/build/templates/UmbracoProject/.template.config/template.json index 4f2454706d..fa652af767 100644 --- a/build/templates/UmbracoProject/.template.config/template.json +++ b/build/templates/UmbracoProject/.template.config/template.json @@ -15,7 +15,7 @@ }, "primaryOutputs": [ { - "path": "UmbracoProject.csproj" + "path": "UmbracoProject.csproj" } ], "postActions": [ @@ -29,12 +29,35 @@ "continueOnError": true } ], - "sourceName": "Umbraco.Cms.Web.UI.NetCore", + "sourceName": "UmbracoProject", "symbols": { + "namespaceReplacer": { + "type": "generated", + "generator": "regex", + "dataType": "string", + "replaces": "Umbraco.Cms.Web.UI.NetCore", + "parameters": { + "source": "name", + "steps": [ + { + "regex": "\\s", // whitespaces are not valid. Replace with _ + "replacement": "_" + }, + { + "regex": "-", // - are not valid. Replace with _ + "replacement": "_" + }, + { + "regex": "^[^a-zA-Z_]+", // should start with a-z, A-Z or _ to be a valid namespace + "replacement": "_" + } + ] + } + }, "version": { "type": "parameter", "datatype": "string", - "defaultValue": "9.0.0-beta003", + "defaultValue": "9.0.0-beta004", "description": "The version of Umbraco to load using NuGet", "replaces": "UMBRACO_VERSION_FROM_TEMPLATE" }, @@ -98,30 +121,134 @@ "type": "parameter", "datatype":"text", "description": "The friendly name of the user for Umbraco login when using Unattended install (Without installer wizard UI)", - "replaces": "FRIENDLY_NAME_FROM_TEMPLATE", "defaultValue": "" }, + "FriendlyNameReplaced":{ + "type": "generated", + "generator": "regex", + "dataType": "string", + "replaces": "FRIENDLY_NAME_FROM_TEMPLATE", + "parameters": { + "source": "FriendlyName", + "steps": [ + { + "regex": "\\\\", + "replacement": "\\\\" + }, + { + "regex": "\\\"", + "replacement": "\\\"" + }, + { + "regex": "\\\n", + "replacement": "\\\n" + }, + { + "regex": "\\\t", + "replacement": "\\\t" + } + ] + } + }, "Email":{ "type": "parameter", "datatype":"text", "description": "Email to use for Umbraco login when using Unattended install (Without installer wizard UI)", - "replaces": "EMAIL_FROM_TEMPLATE", "defaultValue": "" }, + "EmailReplaced":{ + "type": "generated", + "generator": "regex", + "dataType": "string", + "replaces": "EMAIL_FROM_TEMPLATE", + "parameters": { + "source": "Email", + "steps": [ + { + "regex": "\\\\", + "replacement": "\\\\" + }, + { + "regex": "\\\"", + "replacement": "\\\"" + }, + { + "regex": "\\\n", + "replacement": "\\\n" + }, + { + "regex": "\\\t", + "replacement": "\\\t" + } + ] + } + }, "Password":{ "type": "parameter", "datatype":"text", "description": "Password to use for Umbraco login when using Unattended install (Without installer wizard UI)", - "replaces": "PASSWORD_FROM_TEMPLATE", "defaultValue": "" }, + "PasswordReplaced":{ + "type": "generated", + "generator": "regex", + "dataType": "string", + "replaces": "PASSWORD_FROM_TEMPLATE", + "parameters": { + "source": "Password", + "steps": [ + { + "regex": "\\\\", + "replacement": "\\\\" + }, + { + "regex": "\\\"", + "replacement": "\\\"" + }, + { + "regex": "\\\n", + "replacement": "\\\n" + }, + { + "regex": "\\\t", + "replacement": "\\\t" + } + ] + } + }, "ConnectionString":{ "type": "parameter", "datatype":"text", "description": "Database connection string when using Unattended install (Without installer wizard UI)", - "replaces": "CONNECTION_FROM_TEMPLATE", "defaultValue": "" }, + "ConnectionStringReplaced":{ + "type": "generated", + "generator": "regex", + "dataType": "string", + "replaces": "CONNECTION_FROM_TEMPLATE", + "parameters": { + "source": "ConnectionString", + "steps": [ + { + "regex": "\\\\", + "replacement": "\\\\" + }, + { + "regex": "\\\"", + "replacement": "\\\"" + }, + { + "regex": "\\\n", + "replacement": "\\\n" + }, + { + "regex": "\\\t", + "replacement": "\\\t" + } + ] + } + }, "UsingUnattenedInstall":{ "type": "computed", "value": "(FriendlyName != \"\" && Email != \"\" && Password != \"\" && ConnectionString != \"\")" diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 66b7cb4bb5..a1220f9b3f 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -2,7 +2,7 @@ 9.0.0 9.0.0 - 9.0.0-beta003 + 9.0.0-beta004 9.0.0 9.0 en-US diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 9474cdf3d4..813ba75095 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -52,8 +52,8 @@ }, "devDependencies": { "@babel/core": "7.6.4", - "@babel/preset-env": "7.6.3", "@babel/plugin-proposal-object-rest-spread": "7.13.8", + "@babel/preset-env": "7.6.3", "autoprefixer": "9.6.5", "caniuse-lite": "^1.0.30001237", "cssnano": "4.1.10", @@ -67,6 +67,7 @@ "gulp-eslint": "6.0.0", "gulp-imagemin": "7.1.0", "gulp-less": "4.0.1", + "gulp-minify": "3.1.0", "gulp-notify": "^3.0.0", "gulp-postcss": "8.0.0", "gulp-rename": "1.4.0", @@ -75,12 +76,11 @@ "gulp-watch": "5.0.1", "gulp-wrap": "0.15.0", "gulp-wrap-js": "0.4.1", - "gulp-minify": "3.1.0", "jasmine-core": "3.5.0", "jsdom": "16.4.0", "karma": "4.4.1", - "karma-jsdom-launcher": "^8.0.2", "karma-jasmine": "2.0.1", + "karma-jsdom-launcher": "^8.0.2", "karma-junit-reporter": "2.0.1", "karma-spec-reporter": "0.0.32", "less": "3.10.3", From ceb4db4f4539a4d23ea7520689d4e718db3217be Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Tue, 15 Jun 2021 08:54:46 +0100 Subject: [PATCH 175/182] Adds dotnetRunMessages flag to launch settings for Kestrel (#10472) https://github.com/dotnet/sdk/issues/12227 https://github.com/dotnet/sdk/pull/12581 https://github.com/dotnet/aspnetcore/blob/v6.0.0-preview.5.21301.17/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Properties/launchSettings.json#L22 --- .../templates/UmbracoProject/Properties/launchSettings.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/build/templates/UmbracoProject/Properties/launchSettings.json b/build/templates/UmbracoProject/Properties/launchSettings.json index 5f9252d553..f40916f4d1 100644 --- a/build/templates/UmbracoProject/Properties/launchSettings.json +++ b/build/templates/UmbracoProject/Properties/launchSettings.json @@ -18,11 +18,12 @@ }, "Umbraco.Web.UI.NetCore": { "commandName": "Project", + "dotnetRunMessages": true, "launchBrowser": true, + "applicationUrl": "https://localhost:HTTPS_PORT_FROM_TEMPLATE;http://localhost:HTTP_PORT_FROM_TEMPLATE", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "https://localhost:HTTPS_PORT_FROM_TEMPLATE;http://localhost:HTTP_PORT_FROM_TEMPLATE" + } } } } From 418ba569f11ae1afacb8b091d3ab73f517645dad Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 15 Jun 2021 10:47:04 +0200 Subject: [PATCH 176/182] remove comments as build script cannot use these in json --- .../templates/UmbracoProject/.template.config/template.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/templates/UmbracoProject/.template.config/template.json b/build/templates/UmbracoProject/.template.config/template.json index fa652af767..8a8c396dcb 100644 --- a/build/templates/UmbracoProject/.template.config/template.json +++ b/build/templates/UmbracoProject/.template.config/template.json @@ -40,15 +40,15 @@ "source": "name", "steps": [ { - "regex": "\\s", // whitespaces are not valid. Replace with _ + "regex": "\\s", "replacement": "_" }, { - "regex": "-", // - are not valid. Replace with _ + "regex": "-", "replacement": "_" }, { - "regex": "^[^a-zA-Z_]+", // should start with a-z, A-Z or _ to be a valid namespace + "regex": "^[^a-zA-Z_]+", "replacement": "_" } ] From e0a9397d92bcf475f35ea402dc885949a68b6ea2 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 15 Jun 2021 11:02:55 +0200 Subject: [PATCH 177/182] V9: Fix issue with recurring services that executes too often (#10473) * Fix exception in ReportSiteTask.cs, when running multiple times.. Also fixes issue with how often the tasks are executed * Fix timeout --- .../RecurringHostedServiceBase.cs | 5 +++-- .../HostedServices/ReportSiteTask.cs | 22 +++++++++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Infrastructure/HostedServices/RecurringHostedServiceBase.cs b/src/Umbraco.Infrastructure/HostedServices/RecurringHostedServiceBase.cs index 131b81322a..70dcb3a04e 100644 --- a/src/Umbraco.Infrastructure/HostedServices/RecurringHostedServiceBase.cs +++ b/src/Umbraco.Infrastructure/HostedServices/RecurringHostedServiceBase.cs @@ -62,8 +62,9 @@ namespace Umbraco.Cms.Infrastructure.HostedServices } finally { - // Resume now that the task is complete - _timer?.Change((int)_delay.TotalMilliseconds, (int)_period.TotalMilliseconds); + // Resume now that the task is complete - Note we use period in both because we don't want to execute again after the delay. + // So first execution is after _delay, and the we wait _period between each + _timer?.Change((int)_period.TotalMilliseconds, (int)_period.TotalMilliseconds); } } diff --git a/src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs b/src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs index b1411270c3..6eab3a60bc 100644 --- a/src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs +++ b/src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs @@ -52,13 +52,24 @@ namespace Umbraco.Cms.Infrastructure.HostedServices try { - // Send data to LIVE telemetry - s_httpClient.BaseAddress = new Uri("https://telemetry.umbraco.com/"); + + if (s_httpClient.BaseAddress is null) + { + // Send data to LIVE telemetry + s_httpClient.BaseAddress = new Uri("https://telemetry.umbraco.com/"); + + // Set a low timeout - no need to use a larger default timeout for this POST request + s_httpClient.Timeout = new TimeSpan(0, 0, 1); #if DEBUG - // Send data to DEBUG telemetry service - s_httpClient.BaseAddress = new Uri("https://telemetry.rainbowsrock.net/"); + // Send data to DEBUG telemetry service + s_httpClient.BaseAddress = new Uri("https://telemetry.rainbowsrock.net/"); + + + #endif + } + s_httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json"); @@ -67,9 +78,6 @@ namespace Umbraco.Cms.Infrastructure.HostedServices var postData = new TelemetryReportData { Id = telemetrySiteIdentifier, Version = _umbracoVersion.SemanticVersion.ToSemanticStringWithoutBuild() }; request.Content = new StringContent(JsonConvert.SerializeObject(postData), Encoding.UTF8, "application/json"); //CONTENT-TYPE header - // Set a low timeout - no need to use a larger default timeout for this POST request - s_httpClient.Timeout = new TimeSpan(0, 0, 1); - // Make a HTTP Post to telemetry service // https://telemetry.umbraco.com/installs/ // Fire & Forget, do not need to know if its a 200, 500 etc From 0c347706959148197b3d9a33d5442698d64aa769 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 15 Jun 2021 11:06:07 +0200 Subject: [PATCH 178/182] https://github.com/umbraco/Umbraco-CMS/issues/10438 - Adds missing `DataContract` to `FileExtensionConfigItem` --- src/Umbraco.Core/PropertyEditors/FileExtensionConfigItem.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Core/PropertyEditors/FileExtensionConfigItem.cs b/src/Umbraco.Core/PropertyEditors/FileExtensionConfigItem.cs index c777aedd89..d653a831cb 100644 --- a/src/Umbraco.Core/PropertyEditors/FileExtensionConfigItem.cs +++ b/src/Umbraco.Core/PropertyEditors/FileExtensionConfigItem.cs @@ -2,6 +2,7 @@ namespace Umbraco.Cms.Core.PropertyEditors { + [DataContract] public class FileExtensionConfigItem : IFileExtensionConfigItem { [DataMember(Name = "id")] From f2c1c30e7696f2ac531f0985dd0730feeacd729c Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 15 Jun 2021 12:07:11 +0200 Subject: [PATCH 179/182] Remove zips from azure build --- build/azure-pipelines.yml | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/build/azure-pipelines.yml b/build/azure-pipelines.yml index 893c62509a..b2824d8237 100644 --- a/build/azure-pipelines.yml +++ b/build/azure-pipelines.yml @@ -401,7 +401,7 @@ stages: publishJUnitResults: true testResultsFiles: '**\TESTS-*.xml' - task: PowerShell@1 - displayName: Prepare Packages & Zip + displayName: Prepare Packages inputs: scriptType: inlineScript inlineScript: | @@ -410,19 +410,6 @@ stages: $ubuild.CompileUmbraco() $ubuild.PreparePackages() - $ubuild.PackageZip() - - task: CopyFiles@2 - displayName: Copy Zip Files to Staging - inputs: - SourceFolder: build.out - Contents: '*.zip' - TargetFolder: $(build.artifactstagingdirectory) - CleanTargetFolder: true - - task: PublishBuildArtifacts@1 - displayName: Publish Zip Files - inputs: - PathtoPublish: $(build.artifactstagingdirectory) - ArtifactName: zips - task: PowerShell@1 displayName: Verify & Package NuGet inputs: From 22de16a691c46506d9162e8d639335de64794740 Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Tue, 15 Jun 2021 12:43:03 +0200 Subject: [PATCH 180/182] Added two further notifications to the boot process for custom functionality that may need to run at different stages. (#10471) --- ...icationComponentsInstallingNotification.cs | 22 +++++++++++++++++++ ...oApplicationMainDomAcquiredNotification.cs | 19 ++++++++++++++++ .../Runtime/CoreRuntime.cs | 12 +++++----- 3 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 src/Umbraco.Core/Notifications/UmbracoApplicationComponentsInstallingNotification.cs create mode 100644 src/Umbraco.Core/Notifications/UmbracoApplicationMainDomAcquiredNotification.cs diff --git a/src/Umbraco.Core/Notifications/UmbracoApplicationComponentsInstallingNotification.cs b/src/Umbraco.Core/Notifications/UmbracoApplicationComponentsInstallingNotification.cs new file mode 100644 index 0000000000..c29df4e85f --- /dev/null +++ b/src/Umbraco.Core/Notifications/UmbracoApplicationComponentsInstallingNotification.cs @@ -0,0 +1,22 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +namespace Umbraco.Cms.Core.Notifications +{ + /// + /// Notification that occurs during the Umbraco boot process, before instances of initialize. + /// + public class UmbracoApplicationComponentsInstallingNotification : INotification + { + /// + /// Initializes a new instance of the class. + /// + /// The runtime level + public UmbracoApplicationComponentsInstallingNotification(RuntimeLevel runtimeLevel) => RuntimeLevel = runtimeLevel; + + /// + /// Gets the runtime level of execution. + /// + public RuntimeLevel RuntimeLevel { get; } + } +} diff --git a/src/Umbraco.Core/Notifications/UmbracoApplicationMainDomAcquiredNotification.cs b/src/Umbraco.Core/Notifications/UmbracoApplicationMainDomAcquiredNotification.cs new file mode 100644 index 0000000000..6a66e2413f --- /dev/null +++ b/src/Umbraco.Core/Notifications/UmbracoApplicationMainDomAcquiredNotification.cs @@ -0,0 +1,19 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +namespace Umbraco.Cms.Core.Notifications +{ + /// + /// Notification that occurs during Umbraco boot after the MainDom has been acquired. + /// + public class UmbracoApplicationMainDomAcquiredNotification : INotification + { + /// + /// Initializes a new instance of the class. + /// + /// The runtime level + public UmbracoApplicationMainDomAcquiredNotification() + { + } + } +} diff --git a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs index f7065fe0b5..0a262b58c5 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs @@ -103,6 +103,8 @@ namespace Umbraco.Cms.Infrastructure.Runtime // acquire the main domain - if this fails then anything that should be registered with MainDom will not operate AcquireMainDom(); + await _eventAggregator.PublishAsync(new UmbracoApplicationMainDomAcquiredNotification(), cancellationToken); + DoUnattendedInstall(); DetermineRuntimeLevel(); @@ -117,8 +119,6 @@ namespace Umbraco.Cms.Infrastructure.Runtime 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)}"); } - - // if level is Run and reason is UpgradeMigrations, that means we need to perform an unattended upgrade if (State.Reason == RuntimeLevelReason.UpgradeMigrations && State.Level == RuntimeLevel.Run) { @@ -127,9 +127,10 @@ namespace Umbraco.Cms.Infrastructure.Runtime // upgrade is done, set reason to Run DetermineRuntimeLevel(); - } + await _eventAggregator.PublishAsync(new UmbracoApplicationComponentsInstallingNotification(State.Level), cancellationToken); + // create & initialize the components _components.Initialize(); @@ -148,10 +149,7 @@ namespace Umbraco.Cms.Infrastructure.Runtime } - private void DoUnattendedInstall() - { - State.DoUnattendedInstall(); - } + private void DoUnattendedInstall() => State.DoUnattendedInstall(); public async Task StopAsync(CancellationToken cancellationToken) { From 0c3db013d54b9f8256e2088fc5557e462db833a7 Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska <21998037+elit0451@users.noreply.github.com> Date: Tue, 15 Jun 2021 13:51:39 +0200 Subject: [PATCH 181/182] Approve member when created with built-in macros (#10428) * Allow members created with built-in macros to be approved * Cleanup * Newly created members are always approved like in v8. Co-authored-by: Bjarke Berg --- src/Umbraco.Infrastructure/Security/MemberIdentityUser.cs | 3 ++- src/Umbraco.Web.BackOffice/Controllers/MemberController.cs | 1 + src/Umbraco.Web.Website/Controllers/UmbRegisterController.cs | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Infrastructure/Security/MemberIdentityUser.cs b/src/Umbraco.Infrastructure/Security/MemberIdentityUser.cs index 459417b289..724eb77030 100644 --- a/src/Umbraco.Infrastructure/Security/MemberIdentityUser.cs +++ b/src/Umbraco.Infrastructure/Security/MemberIdentityUser.cs @@ -34,7 +34,7 @@ namespace Umbraco.Cms.Core.Security /// /// Used to construct a new instance without an identity /// - public static MemberIdentityUser CreateNew(string username, string email, string memberTypeAlias, string name = null) + public static MemberIdentityUser CreateNew(string username, string email, string memberTypeAlias, bool isApproved, string name = null) { if (string.IsNullOrWhiteSpace(username)) { @@ -46,6 +46,7 @@ namespace Umbraco.Cms.Core.Security user.UserName = username; user.Email = email; user.MemberTypeAlias = memberTypeAlias; + user.IsApproved = isApproved; user.Id = null; user.HasIdentity = false; user.Name = name; diff --git a/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs b/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs index 3d435322b8..6045cec8f9 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs @@ -366,6 +366,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers contentItem.Username, contentItem.Email, memberType.Alias, + contentItem.IsApproved, contentItem.Name); IdentityResult created = await _memberManager.CreateAsync(identityMember, contentItem.Password.NewPassword); diff --git a/src/Umbraco.Web.Website/Controllers/UmbRegisterController.cs b/src/Umbraco.Web.Website/Controllers/UmbRegisterController.cs index 2a8dd6a71a..1e6c417ed3 100644 --- a/src/Umbraco.Web.Website/Controllers/UmbRegisterController.cs +++ b/src/Umbraco.Web.Website/Controllers/UmbRegisterController.cs @@ -127,7 +127,7 @@ namespace Umbraco.Cms.Web.Website.Controllers model.Username = (model.UsernameIsEmail || model.Username == null) ? model.Email : model.Username; - var identityUser = MemberIdentityUser.CreateNew(model.Username, model.Email, model.MemberTypeAlias, model.Name); + var identityUser = MemberIdentityUser.CreateNew(model.Username, model.Email, model.MemberTypeAlias, true, model.Name); IdentityResult identityResult = await _memberManager.CreateAsync( identityUser, model.Password); @@ -142,6 +142,7 @@ namespace Umbraco.Cms.Web.Website.Controllers // should never happen throw new InvalidOperationException($"Could not find a member with key: {member.Key}."); } + if (model.MemberProperties != null) { foreach (MemberPropertyModel property in model.MemberProperties.Where(p => p.Value != null) @@ -159,7 +160,6 @@ namespace Umbraco.Cms.Web.Website.Controllers } return identityResult; - } } } From 067395b0ff3715a0a2c3fcba8b101c4d48077ebf Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Tue, 15 Jun 2021 12:57:14 +0100 Subject: [PATCH 182/182] Fix for #10401 to allow you to chaneg your own password if using user section & fixes notification errors to be displayed in the UI (#10422) --- src/Umbraco.Web.BackOffice/Controllers/UsersController.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs index cdd00913e7..19def88456 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs @@ -680,15 +680,15 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers IUser currentUser = _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser; - // if it's the current user, the current user cannot reset their own password - if (currentUser.Username == found.Username) + // if it's the current user, the current user cannot reset their own password without providing their old password + if (currentUser.Username == found.Username && string.IsNullOrEmpty(changingPasswordModel.OldPassword)) { - return new ValidationErrorResult("Password reset is not allowed"); + return ValidationErrorResult.CreateNotificationValidationErrorResult("Password reset is not allowed without providing old password"); } if (!currentUser.IsAdmin() && found.IsAdmin()) { - return new ValidationErrorResult("The current user cannot change the password for the specified user"); + return ValidationErrorResult.CreateNotificationValidationErrorResult("The current user cannot change the password for the specified user"); } Attempt passwordChangeResult = await _passwordChanger.ChangePasswordWithIdentityAsync(changingPasswordModel, _userManager);