From 8a60c95a36242ee6cbae36232599bed38e902ec9 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 12 Sep 2019 17:04:29 +1000 Subject: [PATCH 01/69] Fixes default weighting for Umbraco.Web handlers and adds unit test --- .../ApplicationEventsResolver.cs | 14 +++++--- .../ApplicationEventsResolverTests.cs | 32 +++++++++++++++++++ .../Resolvers/LazyManyObjectResolverTests.cs | 6 ++-- src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + 4 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 src/Umbraco.Tests/Resolvers/ApplicationEventsResolverTests.cs diff --git a/src/Umbraco.Core/ObjectResolution/ApplicationEventsResolver.cs b/src/Umbraco.Core/ObjectResolution/ApplicationEventsResolver.cs index eda1b94e37..20c49de588 100644 --- a/src/Umbraco.Core/ObjectResolution/ApplicationEventsResolver.cs +++ b/src/Umbraco.Core/ObjectResolution/ApplicationEventsResolver.cs @@ -89,16 +89,20 @@ namespace Umbraco.Core.ObjectResolution } } - protected override int GetObjectWeight(object o) - { + protected override int GetObjectWeight(object o) => GetObjectWeightInternal(o, DefaultPluginWeight); + + internal static int GetObjectWeightInternal(object o, int defaultPluginWeight) + { var type = o.GetType(); var attr = type.GetCustomAttribute(true); if (attr != null) return attr.Weight; var name = type.Assembly.FullName; // we should really attribute all our Core handlers, so this is temp - var core = name.InvariantStartsWith("Umbraco.") || name.InvariantStartsWith("Concorde."); - return core ? -DefaultPluginWeight : DefaultPluginWeight; + var core = name.InvariantStartsWith("umbraco,") // This handles the umbraco.dll (Umbraco.Web) project + || name.InvariantStartsWith("Umbraco.") // This handles all other Umbraco.* assemblies - in the case of v7, this is ONLY Umbraco.Core + || name.InvariantStartsWith("Concorde."); // Special case for Cloud assemblies + return core ? -defaultPluginWeight : defaultPluginWeight; } /// @@ -202,4 +206,4 @@ namespace Umbraco.Core.ObjectResolution _orderedAndFiltered = null; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Tests/Resolvers/ApplicationEventsResolverTests.cs b/src/Umbraco.Tests/Resolvers/ApplicationEventsResolverTests.cs new file mode 100644 index 0000000000..3fba16fde3 --- /dev/null +++ b/src/Umbraco.Tests/Resolvers/ApplicationEventsResolverTests.cs @@ -0,0 +1,32 @@ +using NUnit.Framework; +using umbraco.BusinessLogic; +using Umbraco.Core; +using Umbraco.Core.Models.Identity; +using Umbraco.Core.ObjectResolution; +using Umbraco.Web.PropertyEditors; + +namespace Umbraco.Tests.Resolvers +{ + [TestFixture] + public class ApplicationEventsResolverTests + { + [Test] + public void Core_Event_Handler_Weight_Test() + { + //from the 'umbraco' (Umbraco.Web) assembly + Assert.AreEqual(-100, ApplicationEventsResolver.GetObjectWeightInternal(new GridPropertyEditor(), 100)); + //from the 'Umbraco.Core' assembly + Assert.AreEqual(-100, ApplicationEventsResolver.GetObjectWeightInternal(new IdentityModelMappings(), 100)); + //from the 'Umbraco.Test' assembly + Assert.AreEqual(-100, ApplicationEventsResolver.GetObjectWeightInternal(new MyTestEventHandler(), 100)); + + //from the 'umbraco.BusinessLogic' assembly - which we are not checking for and not setting as the negative of the default + Assert.AreEqual(100, ApplicationEventsResolver.GetObjectWeightInternal(new ApplicationRegistrar(), 100)); + } + + private class MyTestEventHandler : ApplicationEventHandler + { + + } + } +} diff --git a/src/Umbraco.Tests/Resolvers/LazyManyObjectResolverTests.cs b/src/Umbraco.Tests/Resolvers/LazyManyObjectResolverTests.cs index 33bb1ab34c..841c302e52 100644 --- a/src/Umbraco.Tests/Resolvers/LazyManyObjectResolverTests.cs +++ b/src/Umbraco.Tests/Resolvers/LazyManyObjectResolverTests.cs @@ -10,7 +10,9 @@ using Umbraco.Core.ObjectResolution; namespace Umbraco.Tests.Resolvers { - [TestFixture] + + + [TestFixture] public class LazyManyObjectResolverTests { @@ -191,4 +193,4 @@ namespace Umbraco.Tests.Resolvers #endregion } -} \ No newline at end of file +} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index d45c556d37..4a67223b3a 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -197,6 +197,7 @@ + From 905b2414263975c0eeb79066281a0479b5988fd6 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Thu, 26 Sep 2019 13:39:53 +0200 Subject: [PATCH 02/69] #2996 added GlobalSettings.DebugMode switch for JSON formatting indentation. (cherry picked from commits d3c4aace160b78739a8eeb673cb561e96cc04101 / 16837d018a44c324e620d7c72b63be015a87895c) --- src/Umbraco.Core/Serialization/JsonNetSerializer.cs | 6 ++++-- src/Umbraco.Web/Editors/BackOfficeController.cs | 12 ++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Core/Serialization/JsonNetSerializer.cs b/src/Umbraco.Core/Serialization/JsonNetSerializer.cs index 800278abf0..52f39c6109 100644 --- a/src/Umbraco.Core/Serialization/JsonNetSerializer.cs +++ b/src/Umbraco.Core/Serialization/JsonNetSerializer.cs @@ -4,6 +4,7 @@ using System.Reflection; using System.Text; using Newtonsoft.Json; using Newtonsoft.Json.Converters; +using Umbraco.Core.Configuration; namespace Umbraco.Core.Serialization { @@ -60,7 +61,8 @@ namespace Umbraco.Core.Serialization /// public IStreamedResult ToStream(object input) { - string s = JsonConvert.SerializeObject(input, Formatting.Indented, _settings); + var formatting = GlobalSettings.DebugMode ? Formatting.Indented : Formatting.None; + string s = JsonConvert.SerializeObject(input, formatting, _settings); byte[] bytes = Encoding.UTF8.GetBytes(s); MemoryStream ms = new MemoryStream(bytes); @@ -69,4 +71,4 @@ namespace Umbraco.Core.Serialization #endregion } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs index 093c095b5d..4a79038fc0 100644 --- a/src/Umbraco.Web/Editors/BackOfficeController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeController.cs @@ -177,8 +177,8 @@ namespace Umbraco.Web.Editors //the dictionary returned is fine but the delimiter between an 'area' and a 'value' is a '/' but the javascript // in the back office requres the delimiter to be a '_' so we'll just replace it .ToDictionary(key => key.Key.Replace("/", "_"), val => val.Value); - - return new JsonNetResult { Data = textForCulture, Formatting = Formatting.Indented }; + var formatting = GlobalSettings.DebugMode ? Formatting.Indented : Formatting.None; + return new JsonNetResult { Data = textForCulture, Formatting = formatting }; } /// @@ -230,8 +230,8 @@ namespace Umbraco.Web.Editors typeof(BackOfficeController) + "GetManifestAssetList", () => getResult(), new TimeSpan(0, 10, 0)); - - return new JsonNetResult { Data = result, Formatting = Formatting.Indented }; + var formatting = GlobalSettings.DebugMode ? Formatting.Indented : Formatting.None; + return new JsonNetResult { Data = result, Formatting = formatting }; } [UmbracoAuthorize(Order = 0)] @@ -244,8 +244,8 @@ namespace Umbraco.Web.Editors new DirectoryInfo(Server.MapPath(SystemDirectories.AppPlugins)), new DirectoryInfo(Server.MapPath(SystemDirectories.Config)), HttpContext.IsDebuggingEnabled); - - return new JsonNetResult { Data = gridConfig.EditorsConfig.Editors, Formatting = Formatting.Indented }; + var formatting = GlobalSettings.DebugMode ? Formatting.Indented : Formatting.None; + return new JsonNetResult { Data = gridConfig.EditorsConfig.Editors, Formatting = formatting }; } From a37b1075a100b3b50b32382bb6af68477f6179a5 Mon Sep 17 00:00:00 2001 From: Benjamin Howarth <322383+benjaminhowarth1@users.noreply.github.com> Date: Mon, 30 Sep 2019 16:54:28 +0100 Subject: [PATCH 03/69] #2996 resubmitting ContentExtensions and ObjectExtensions fixes (#6473) (cherry picked from commit 79bf9b753caf97a55d474c5c6db8821a33ca398f) --- src/Umbraco.Core/ObjectExtensions.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Umbraco.Core/ObjectExtensions.cs b/src/Umbraco.Core/ObjectExtensions.cs index 479e425c99..5c1c5fdeac 100644 --- a/src/Umbraco.Core/ObjectExtensions.cs +++ b/src/Umbraco.Core/ObjectExtensions.cs @@ -8,7 +8,10 @@ using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using System.Xml; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using Umbraco.Core.Collections; +using Formatting = Newtonsoft.Json.Formatting; namespace Umbraco.Core { @@ -125,6 +128,11 @@ namespace Umbraco.Core return Attempt.Succeed(input); } + if (target == typeof(string) && inputType == typeof(JObject)) + { + return Attempt.Succeed(JsonConvert.SerializeObject(input, Formatting.None)); + } + // Check for string so that overloaders of ToString() can take advantage of the conversion. if (target == typeof(string)) { From f967f2111938d8954b5a61b7db54f23c43c45b49 Mon Sep 17 00:00:00 2001 From: Dirk De Grave Date: Tue, 1 Oct 2019 13:39:32 +0200 Subject: [PATCH 04/69] Fixes #6499 by reloading the content in list view after bulk copy/move --- .../listview/listview.controller.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js index 9b4c01f4f9..0812bcff3e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js @@ -472,7 +472,7 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie // a specific value from one of the methods, so we'll have to try this way. Even though the first method // will fire once per every node moved, the destination path will be the same and we need to use that to sync. var newPath = null; - applySelected( + var attempt = applySelected( function(selected, index) { return contentResource.move({ parentId: target.id, id: getIdCallback(selected[index]) }) .then(function(path) { @@ -509,6 +509,11 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie }); } }); + if (attempt) { + attempt.then(function () { + $scope.getContent(); + }); + } } $scope.copy = function () { @@ -536,7 +541,7 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie }; function performCopy(target, relateToOriginal) { - applySelected( + var attempt = applySelected( function (selected, index) { return contentResource.copy({ parentId: target.id, id: getIdCallback(selected[index]), relateToOriginal: relateToOriginal }); }, function (count, total) { var key = (total === 1 ? "bulk_copiedItemOfItem" : "bulk_copiedItemOfItems"); @@ -546,6 +551,11 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie var key = (total === 1 ? "bulk_copiedItem" : "bulk_copiedItems"); return localizationService.localize(key, [total]); }); + if (attempt) { + attempt.then(function () { + $scope.getContent(); + }); + } } function getCustomPropertyValue(alias, properties) { From b05ef082fdb76e923b50461cad42d668b4ed5448 Mon Sep 17 00:00:00 2001 From: Dirk De Grave Date: Tue, 1 Oct 2019 13:39:32 +0200 Subject: [PATCH 05/69] Fixes #6499 by reloading the content in list view after bulk copy/move (cherry picked from commit f967f2111938d8954b5a61b7db54f23c43c45b49) --- .../listview/listview.controller.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js index 9b4c01f4f9..0812bcff3e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js @@ -472,7 +472,7 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie // a specific value from one of the methods, so we'll have to try this way. Even though the first method // will fire once per every node moved, the destination path will be the same and we need to use that to sync. var newPath = null; - applySelected( + var attempt = applySelected( function(selected, index) { return contentResource.move({ parentId: target.id, id: getIdCallback(selected[index]) }) .then(function(path) { @@ -509,6 +509,11 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie }); } }); + if (attempt) { + attempt.then(function () { + $scope.getContent(); + }); + } } $scope.copy = function () { @@ -536,7 +541,7 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie }; function performCopy(target, relateToOriginal) { - applySelected( + var attempt = applySelected( function (selected, index) { return contentResource.copy({ parentId: target.id, id: getIdCallback(selected[index]), relateToOriginal: relateToOriginal }); }, function (count, total) { var key = (total === 1 ? "bulk_copiedItemOfItem" : "bulk_copiedItemOfItems"); @@ -546,6 +551,11 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie var key = (total === 1 ? "bulk_copiedItem" : "bulk_copiedItems"); return localizationService.localize(key, [total]); }); + if (attempt) { + attempt.then(function () { + $scope.getContent(); + }); + } } function getCustomPropertyValue(alias, properties) { From 9c28a1ff808d4ca0059002cd78d90eab7676bf69 Mon Sep 17 00:00:00 2001 From: jmayntzhusen Date: Tue, 1 Oct 2019 20:40:46 +0200 Subject: [PATCH 06/69] Update styles of API docs to match new identity --- apidocs/umbracotemplate/styles/main.css | 274 ++++++++++++++++++------ 1 file changed, 208 insertions(+), 66 deletions(-) diff --git a/apidocs/umbracotemplate/styles/main.css b/apidocs/umbracotemplate/styles/main.css index d74d51b150..69dde09875 100644 --- a/apidocs/umbracotemplate/styles/main.css +++ b/apidocs/umbracotemplate/styles/main.css @@ -1,73 +1,215 @@ body { - color: rgba(0,0,0,.8); -} -.navbar-inverse { - background: #a3db78; -} -.navbar-inverse .navbar-nav>li>a, .navbar-inverse .navbar-text { - color: rgba(0,0,0,.8); -} - -.navbar-inverse { - border-color: transparent; -} - -.sidetoc { - background-color: #f5fbf1; -} -body .toc { - background-color: #f5fbf1; -} -.sidefilter { - background-color: #daf0c9; -} -.subnav { - background-color: #f5fbf1; -} - -.navbar-inverse .navbar-nav>.active>a { - color: rgba(0,0,0,.8); - background-color: #daf0c9; -} - -.navbar-inverse .navbar-nav>.active>a:focus, .navbar-inverse .navbar-nav>.active>a:hover { - color: rgba(0,0,0,.8); - background-color: #daf0c9; -} - -.btn-primary { - color: rgba(0,0,0,.8); - background-color: #fff; - border-color: rgba(0,0,0,.8); -} -.btn-primary:hover { - background-color: #daf0c9; - color: rgba(0,0,0,.8); - border-color: rgba(0,0,0,.8); -} - -.toc .nav > li > a { - color: rgba(0,0,0,.8); -} - -button, a { - color: #f36f21; -} - -button:hover, -button:focus, -a:hover, -a:focus { - color: #143653; - text-decoration: none; + color: #34393e; + font-family: 'Roboto', sans-serif; + line-height: 1.5; + font-size: 16px; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; + word-wrap: break-word } .navbar-header .navbar-brand { - background: url(https://our.umbraco.com/assets/images/logo.svg) left center no-repeat; - background-size: 40px auto; - width:50px; + background: url(https://our.umbraco.com/assets/images/logo.svg) left center no-repeat; + background-size: 40px auto; + width:50px; } -.toc .nav > li.active > a { - color: #f36f21; +#_content>a { + margin-top: 5px; } +/* HEADINGS */ + +h1 { + font-weight: 600; + font-size: 32px; +} + +h2 { + font-weight: 600; + font-size: 24px; + line-height: 1.8; +} + +h3 { + font-weight: 600; + font-size: 20px; + line-height: 1.8; +} + +h5 { + font-size: 14px; + padding: 10px 0px; +} + +article h1, +article h2, +article h3, +article h4 { + margin-top: 35px; + margin-bottom: 15px; +} + +article h4 { + padding-bottom: 8px; + border-bottom: 2px solid #ddd; +} + +/* NAVBAR */ + +.navbar-brand>img { + color: #fff; +} + +.navbar { + border: none; + /* Both navbars use box-shadow */ + -webkit-box-shadow: 0px 1px 3px 0px rgba(100, 100, 100, 0.5); + -moz-box-shadow: 0px 1px 3px 0px rgba(100, 100, 100, 0.5); + box-shadow: 0px 1px 3px 0px rgba(100, 100, 100, 0.5); +} + +.subnav { + border-top: 1px solid #ddd; + background-color: #fff; +} + +.navbar-inverse { + background-color: #303ea1; + z-index: 100; +} + +.navbar-inverse .navbar-nav>li>a, +.navbar-inverse .navbar-text { + color: #fff; + background-color: #303ea1; + border-bottom: 3px solid transparent; + padding-bottom: 12px; +} + +.navbar-inverse .navbar-nav>li>a:focus, +.navbar-inverse .navbar-nav>li>a:hover { + color: #fff; + background-color: #303ea1; + border-bottom: 3px solid white; +} + +.navbar-inverse .navbar-nav>.active>a, +.navbar-inverse .navbar-nav>.active>a:focus, +.navbar-inverse .navbar-nav>.active>a:hover { + color: #fff; + background-color: #303ea1; + border-bottom: 3px solid white; +} + +.navbar-form .form-control { + border: none; + border-radius: 20px; +} + +/* SIDEBAR */ + +.toc .level1>li { + font-weight: 400; +} + +.toc .nav>li>a { + color: #34393e; +} + +.sidefilter { + background-color: #fff; + border-left: none; + border-right: none; +} + +.sidefilter { + background-color: #fff; + border-left: none; + border-right: none; +} + +.toc-filter { + padding: 10px; + margin: 0; +} + +.toc-filter>input { + border: 2px solid #ddd; + border-radius: 20px; +} + +.toc-filter>.filter-icon { + display: none; +} + +.sidetoc>.toc { + background-color: #fff; + overflow-x: hidden; +} + +.sidetoc { + background-color: #fff; + border: none; +} + +/* ALERTS */ + +.alert { + padding: 0px 0px 5px 0px; + color: inherit; + background-color: inherit; + border: none; + box-shadow: 0px 2px 2px 0px rgba(100, 100, 100, 0.4); +} + +.alert>p { + margin-bottom: 0; + padding: 5px 10px; +} + +.alert>ul { + margin-bottom: 0; + padding: 5px 40px; +} + +.alert>h5 { + padding: 10px 15px; + margin-top: 0; + text-transform: uppercase; + font-weight: bold; + border-radius: 4px 4px 0 0; +} + +.alert-info>h5 { + color: #1976d2; + border-bottom: 4px solid #1976d2; + background-color: #e3f2fd; +} + +.alert-warning>h5 { + color: #f57f17; + border-bottom: 4px solid #f57f17; + background-color: #fff3e0; +} + +.alert-danger>h5 { + color: #d32f2f; + border-bottom: 4px solid #d32f2f; + background-color: #ffebee; +} + +/* CODE HIGHLIGHT */ +pre { +padding: 9.5px; +margin: 0 0 10px; +font-size: 13px; +word-break: break-all; +word-wrap: break-word; +background-color: #fffaef; +border-radius: 4px; +box-shadow: 0px 1px 4px 1px rgba(100, 100, 100, 0.4); +} + +.sideaffix { + overflow: visible; +} \ No newline at end of file From e0ea4ef36ffb6ff5289c34541f006cdbba9394e2 Mon Sep 17 00:00:00 2001 From: Benjamin Carleski Date: Wed, 2 Oct 2019 14:16:03 -0700 Subject: [PATCH 07/69] Add null check in allowed template check --- src/Umbraco.Web/PublishedContentExtensions.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index c80f41d9fc..d1bc7dffff 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -135,6 +135,9 @@ namespace Umbraco.Web public static bool IsAllowedTemplate(this IPublishedContent content, int templateId) { + if (content == null) + return false; + if (UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates == true) return content.TemplateId == templateId; From 96a2af2653da49ebdc793c9aa53acd54cc173110 Mon Sep 17 00:00:00 2001 From: Rasmus Olofsson Date: Thu, 24 Oct 2019 18:43:37 +0200 Subject: [PATCH 08/69] Add Swedish translations for prompt --- src/Umbraco.Web.UI/umbraco/config/lang/sv.xml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/sv.xml b/src/Umbraco.Web.UI/umbraco/config/lang/sv.xml index 982f31e383..0a2f37ce02 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/sv.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/sv.xml @@ -182,10 +182,11 @@ Besök - Stay - Discard changes - You have unsaved changes - Are you sure you want to navigate away from this page? - you have unsaved changes + Stanna + Avfärda ändringar + Du har inte sparat dina ändringar + Är du säker på att du vill navigera bort från denna sida? - du har inte sparat dina ändringar + Avpublicering kommer att ta bort denna sida och alla dess ättlingar från hemsidan. Done From 8a02b4f58723d2c9746e7a59d417449ff5e2e933 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 30 Oct 2019 11:24:08 +0100 Subject: [PATCH 09/69] Editors should always be allowed to delete their own content (#6799) * Users should always be allowed to delete their own content * Changed comment to be more precise --- src/Umbraco.Web/Editors/ContentController.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 8bb7af0077..bfc9ae071f 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -28,6 +28,7 @@ using Constants = Umbraco.Core.Constants; using umbraco.cms.businesslogic; using System.Collections; using umbraco; +using umbraco.BusinessLogic.Actions; namespace Umbraco.Web.Editors { @@ -1171,6 +1172,12 @@ namespace Umbraco.Web.Editors var path = contentItem != null ? contentItem.Path : nodeId.ToString(); var permission = userService.GetPermissionsForPath(user, path); + // users are allowed to delete their own content - see ContentTreeControllerBase.GetAllowedUserMenuItemsForNode() + if(contentItem != null && contentItem.CreatorId == user.Id) + { + permission.PermissionsSet.Add(new EntityPermission(0, contentItem.Id, new [] { ActionDelete.Instance.Letter.ToString() } )); + } + var allowed = true; foreach (var p in permissionsToCheck) { From f172760f961218760ce4f9e8dc0e9b7908ad90b0 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 30 Oct 2019 11:24:08 +0100 Subject: [PATCH 10/69] Editors should always be allowed to delete their own content (#6799) * Users should always be allowed to delete their own content * Changed comment to be more precise (cherry picked from commit 8a02b4f58723d2c9746e7a59d417449ff5e2e933) --- src/Umbraco.Web/Editors/ContentController.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 8bb7af0077..bfc9ae071f 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -28,6 +28,7 @@ using Constants = Umbraco.Core.Constants; using umbraco.cms.businesslogic; using System.Collections; using umbraco; +using umbraco.BusinessLogic.Actions; namespace Umbraco.Web.Editors { @@ -1171,6 +1172,12 @@ namespace Umbraco.Web.Editors var path = contentItem != null ? contentItem.Path : nodeId.ToString(); var permission = userService.GetPermissionsForPath(user, path); + // users are allowed to delete their own content - see ContentTreeControllerBase.GetAllowedUserMenuItemsForNode() + if(contentItem != null && contentItem.CreatorId == user.Id) + { + permission.PermissionsSet.Add(new EntityPermission(0, contentItem.Id, new [] { ActionDelete.Instance.Letter.ToString() } )); + } + var allowed = true; foreach (var p in permissionsToCheck) { From d38f275467a84a031e89276557b1e81769d362c4 Mon Sep 17 00:00:00 2001 From: Steve Megson Date: Wed, 30 Oct 2019 12:15:32 +0000 Subject: [PATCH 11/69] Media uploader in RTE doesn't select uploaded image --- .../common/overlays/mediaPicker/mediapicker.controller.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js index fc62c0ce67..136e035b85 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js @@ -251,7 +251,12 @@ angular.module("umbraco") if (files.length === 1 && $scope.model.selectedImages.length === 0) { var image = $scope.images[$scope.images.length - 1]; $scope.target = image; - $scope.target.url = mediaHelper.resolveFile(image); + // handle both entity and full media object + if (image.image) { + $scope.target.url = image.image; + } else { + $scope.target.url = mediaHelper.resolveFile(image); + } selectImage(image); } }); From 38d241952047f00b8c11ef0662d2b60093dc41f0 Mon Sep 17 00:00:00 2001 From: Steve Megson Date: Wed, 30 Oct 2019 12:15:32 +0000 Subject: [PATCH 12/69] Media uploader in RTE doesn't select uploaded image (cherry picked from commit d38f275467a84a031e89276557b1e81769d362c4) --- .../common/overlays/mediaPicker/mediapicker.controller.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js index fc62c0ce67..136e035b85 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js @@ -251,7 +251,12 @@ angular.module("umbraco") if (files.length === 1 && $scope.model.selectedImages.length === 0) { var image = $scope.images[$scope.images.length - 1]; $scope.target = image; - $scope.target.url = mediaHelper.resolveFile(image); + // handle both entity and full media object + if (image.image) { + $scope.target.url = image.image; + } else { + $scope.target.url = mediaHelper.resolveFile(image); + } selectImage(image); } }); From b0e2f2d5113d36049a32c9a9be2523f9964b88a4 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 8 Nov 2019 11:51:02 +1100 Subject: [PATCH 13/69] bumps cdf requirements --- build/NuSpecs/UmbracoCms.Core.nuspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index 9f507b4915..7f180b4ca0 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -29,8 +29,8 @@ - - + + From 09273b6d6fa37a298941eeb06be195276c2a8af2 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 12 Nov 2019 08:53:35 +0100 Subject: [PATCH 14/69] Remove JS error in console when editing document types with Nested Content properties --- .../propertyeditors/nestedcontent/nestedcontent.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js index f2a2fc0cc2..6f2b918642 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js @@ -690,7 +690,7 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop } function updatePropertyActionStates() { - copyAllEntriesAction.isDisabled = $scope.model.value.length === 0; + copyAllEntriesAction.isDisabled = !$scope.model.value || !$scope.model.value.length; } $scope.$watch("currentNode", function (newVal) { From 091b3844aded43fe5847977b7ea88c2c04cb8df8 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 12 Nov 2019 20:34:30 +0100 Subject: [PATCH 15/69] Make sure the media picker can survive a logout and subsequent login --- .../infiniteeditors/mediapicker/mediapicker.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ba103a2761..a27fc757f7 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 @@ -311,7 +311,7 @@ angular.module("umbraco") // also make sure the node is not trashed if (nodePath.indexOf($scope.startNodeId.toString()) !== -1 && node.trashed === false) { - gotoFolder({ id: $scope.lastOpenedNode, name: "Media", icon: "icon-folder", path: node.path }); + gotoFolder({ id: $scope.lastOpenedNode || $scope.startNodeId, name: "Media", icon: "icon-folder", path: node.path }); return true; } else { gotoFolder({ id: $scope.startNodeId, name: "Media", icon: "icon-folder" }); From 6f67105645e616a8b36dcddb1db7cabd8c9a3c2d Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Wed, 11 Dec 2019 09:25:30 +0100 Subject: [PATCH 16/69] Undo removing the MiniProfiler routes if solution is not in debug mode --- .../Profiling/WebProfilerProvider.cs | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/src/Umbraco.Web/Profiling/WebProfilerProvider.cs b/src/Umbraco.Web/Profiling/WebProfilerProvider.cs index e0dcfcf9b1..ffd1871ecc 100644 --- a/src/Umbraco.Web/Profiling/WebProfilerProvider.cs +++ b/src/Umbraco.Web/Profiling/WebProfilerProvider.cs @@ -1,10 +1,7 @@ using System; -using System.Linq; using System.Threading; using System.Web; -using System.Web.Routing; using StackExchange.Profiling; -using Umbraco.Core.Configuration; namespace Umbraco.Web.Profiling { @@ -27,20 +24,6 @@ namespace Umbraco.Web.Profiling { // booting... _bootPhase = BootPhase.Boot; - - // Remove Mini Profiler routes when not in debug mode - if (GlobalSettings.DebugMode == false) - { - //NOTE: Keep the global fully qualified name, for some reason without it I was getting null refs - var prefix = global::StackExchange.Profiling.MiniProfiler.Settings.RouteBasePath.Replace("~/", string.Empty); - - using (RouteTable.Routes.GetWriteLock()) - { - var routes = RouteTable.Routes.Where(x => x is Route r && r.Url.StartsWith(prefix)).ToList(); - foreach(var r in routes) - RouteTable.Routes.Remove(r); - } - } } /// @@ -135,4 +118,4 @@ namespace Umbraco.Web.Profiling } } } -} +} \ No newline at end of file From c845f5d8c578e6bdf12bc20000b485dd9933bf10 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Wed, 11 Dec 2019 09:26:31 +0100 Subject: [PATCH 17/69] Discard MiniProfiler results when not in debug mode --- src/Umbraco.Web/Profiling/WebProfiler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Profiling/WebProfiler.cs b/src/Umbraco.Web/Profiling/WebProfiler.cs index fd980db2d1..f44d4876e2 100644 --- a/src/Umbraco.Web/Profiling/WebProfiler.cs +++ b/src/Umbraco.Web/Profiling/WebProfiler.cs @@ -82,7 +82,7 @@ namespace Umbraco.Web.Profiling if (isBootRequest) _provider.EndBootRequest(); if (isBootRequest || ShouldProfile(sender)) - Stop(); + Stop(!GlobalSettings.DebugMode); } private bool ShouldProfile(object sender) From 262a4cba36fee93cff531ab84ad26b4f88d2ea9c Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Wed, 11 Dec 2019 09:30:25 +0100 Subject: [PATCH 18/69] Explicitly set MiniProfiler storage to HttpRuntimeCacheStorage --- src/Umbraco.Web/Profiling/WebProfiler.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Umbraco.Web/Profiling/WebProfiler.cs b/src/Umbraco.Web/Profiling/WebProfiler.cs index f44d4876e2..2fa5639fa7 100644 --- a/src/Umbraco.Web/Profiling/WebProfiler.cs +++ b/src/Umbraco.Web/Profiling/WebProfiler.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Web; using StackExchange.Profiling; using StackExchange.Profiling.SqlFormatters; +using StackExchange.Profiling.Storage; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; @@ -32,6 +33,7 @@ namespace Umbraco.Web.Profiling MiniProfiler.Settings.SqlFormatter = new SqlServerFormatter(); MiniProfiler.Settings.StackMaxLength = 5000; MiniProfiler.Settings.ProfilerProvider = _provider; + MiniProfiler.Settings.Storage = new HttpRuntimeCacheStorage(TimeSpan.FromMinutes(30)); //Binds to application events to enable the MiniProfiler with a real HttpRequest UmbracoApplicationBase.ApplicationInit += UmbracoApplicationApplicationInit; From 492fb01ad9c3c56d7e1fcdd2cb22f977fdecd835 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Thu, 12 Dec 2019 13:41:40 +0100 Subject: [PATCH 19/69] Don't load languages in treepicker unless they're needed --- .../treepicker/treepicker.controller.js | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.controller.js index 0ff6403761..edb344b0a5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.controller.js @@ -75,18 +75,20 @@ angular.module("umbraco").controller("Umbraco.Editors.TreePickerController", */ function onInit () { - // load languages - languageResource.getAll().then(function (languages) { - vm.languages = languages; + if (vm.showLanguageSelector) { + // load languages + languageResource.getAll().then(function (languages) { + vm.languages = languages; - // set the default language - vm.languages.forEach(function (language) { - if (language.isDefault) { - vm.selectedLanguage = language; - vm.languageSelectorIsOpen = false; - } + // set the default language + vm.languages.forEach(function (language) { + if (language.isDefault) { + vm.selectedLanguage = language; + vm.languageSelectorIsOpen = false; + } + }); }); - }); + } if (vm.treeAlias === "content") { vm.entityType = "Document"; @@ -211,7 +213,7 @@ angular.module("umbraco").controller("Umbraco.Editors.TreePickerController", if (vm.dataTypeKey) { queryParams["dataTypeKey"] = vm.dataTypeKey; } - + var queryString = $.param(queryParams); //create the query string from the params object if (!queryString) { From d4e6eb2b6b052b79d34d095f2405b21f15618ede Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Thu, 16 Jan 2020 17:04:38 +0100 Subject: [PATCH 20/69] AB4084 - Backported/reimplemented fix for contentservice returning outdated results --- .../Cache/DistributedCacheExtensions.cs | 930 ++++---- src/Umbraco.Web/Cache/MediaCacheRefresher.cs | 390 +-- .../Cache/UnpublishedPageCacheRefresher.cs | 46 +- .../XmlPublishedCache/PublishedMediaCache.cs | 2086 ++++++++--------- src/Umbraco.Web/Search/ExamineEvents.cs | 1465 ++++++------ 5 files changed, 2482 insertions(+), 2435 deletions(-) diff --git a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs b/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs index 849cd6c81a..321e710d8e 100644 --- a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs +++ b/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs @@ -1,465 +1,465 @@ -using System; -using Umbraco.Core; -using Umbraco.Core.Configuration; -using Umbraco.Core.Events; -using Umbraco.Core.Models; -using umbraco; -using umbraco.cms.businesslogic.web; -using Umbraco.Core.Persistence.Repositories; - -namespace Umbraco.Web.Cache -{ - /// - /// Extension methods for - /// - internal static class DistributedCacheExtensions - { - #region Public access - - public static void RefreshPublicAccess(this DistributedCache dc) - { - dc.RefreshAll(DistributedCache.PublicAccessCacheRefresherGuid); - } - - #endregion - - #region Application tree cache - - public static void RefreshAllApplicationTreeCache(this DistributedCache dc) - { - dc.RefreshAll(DistributedCache.ApplicationTreeCacheRefresherGuid); - } - - #endregion - - #region Application cache - - public static void RefreshAllApplicationCache(this DistributedCache dc) - { - dc.RefreshAll(DistributedCache.ApplicationCacheRefresherGuid); - } - - #endregion - - #region User cache - - public static void RemoveUserCache(this DistributedCache dc, int userId) - { - dc.Remove(DistributedCache.UserCacheRefresherGuid, userId); - } - - public static void RefreshUserCache(this DistributedCache dc, int userId) - { - dc.Refresh(DistributedCache.UserCacheRefresherGuid, userId); - } - - public static void RefreshAllUserCache(this DistributedCache dc) - { - dc.RefreshAll(DistributedCache.UserCacheRefresherGuid); - } - - #endregion - - #region User group cache - - public static void RemoveUserGroupCache(this DistributedCache dc, int userId) - { - dc.Remove(DistributedCache.UserGroupCacheRefresherGuid, userId); - } - - public static void RefreshUserGroupCache(this DistributedCache dc, int userId) - { - dc.Refresh(DistributedCache.UserGroupCacheRefresherGuid, userId); - } - - public static void RefreshAllUserGroupCache(this DistributedCache dc) - { - dc.RefreshAll(DistributedCache.UserGroupCacheRefresherGuid); - } - - #endregion - - #region User group permissions cache - - public static void RemoveUserGroupPermissionsCache(this DistributedCache dc, int groupId) - { - dc.Remove(DistributedCache.UserGroupPermissionsCacheRefresherGuid, groupId); - } - - public static void RefreshUserGroupPermissionsCache(this DistributedCache dc, int groupId) - { - //TODO: Not sure if we need this yet depends if we start caching permissions - //dc.Refresh(DistributedCache.UserGroupPermissionsCacheRefresherGuid, groupId); - } - - public static void RefreshAllUserGroupPermissionsCache(this DistributedCache dc) - { - dc.RefreshAll(DistributedCache.UserGroupPermissionsCacheRefresherGuid); - } - - #endregion - - #region Template cache - - public static void RefreshTemplateCache(this DistributedCache dc, int templateId) - { - dc.Refresh(DistributedCache.TemplateRefresherGuid, templateId); - } - - public static void RemoveTemplateCache(this DistributedCache dc, int templateId) - { - dc.Remove(DistributedCache.TemplateRefresherGuid, templateId); - } - - #endregion - - #region Dictionary cache - - public static void RefreshDictionaryCache(this DistributedCache dc, int dictionaryItemId) - { - dc.Refresh(DistributedCache.DictionaryCacheRefresherGuid, dictionaryItemId); - } - - public static void RemoveDictionaryCache(this DistributedCache dc, int dictionaryItemId) - { - dc.Remove(DistributedCache.DictionaryCacheRefresherGuid, dictionaryItemId); - } - - #endregion - - #region Data type cache - - public static void RefreshDataTypeCache(this DistributedCache dc, IDataTypeDefinition dataType) - { - if (dataType == null) return; - dc.RefreshByJson(DistributedCache.DataTypeCacheRefresherGuid, DataTypeCacheRefresher.SerializeToJsonPayload(dataType)); - } - - public static void RemoveDataTypeCache(this DistributedCache dc, IDataTypeDefinition dataType) - { - if (dataType == null) return; - dc.RefreshByJson(DistributedCache.DataTypeCacheRefresherGuid, DataTypeCacheRefresher.SerializeToJsonPayload(dataType)); - } - - #endregion - - #region Page cache - - public static void RefreshAllPageCache(this DistributedCache dc) - { - dc.RefreshAll(DistributedCache.PageCacheRefresherGuid); - } - - public static void RefreshPageCache(this DistributedCache dc, int documentId) - { - dc.Refresh(DistributedCache.PageCacheRefresherGuid, documentId); - } - - public static void RefreshPageCache(this DistributedCache dc, params IContent[] content) - { - dc.Refresh(DistributedCache.PageCacheRefresherGuid, x => x.Id, content); - } - - public static void RemovePageCache(this DistributedCache dc, params IContent[] content) - { - dc.Remove(DistributedCache.PageCacheRefresherGuid, x => x.Id, content); - } - - public static void RemovePageCache(this DistributedCache dc, int documentId) - { - dc.Remove(DistributedCache.PageCacheRefresherGuid, documentId); - } - - public static void RefreshUnpublishedPageCache(this DistributedCache dc, params IContent[] content) - { - dc.Refresh(DistributedCache.UnpublishedPageCacheRefresherGuid, x => x.Id, content); - } - - public static void RemoveUnpublishedPageCache(this DistributedCache dc, params IContent[] content) - { - dc.Remove(DistributedCache.UnpublishedPageCacheRefresherGuid, x => x.Id, content); - } - - public static void RemoveUnpublishedCachePermanently(this DistributedCache dc, params int[] contentIds) - { - dc.RefreshByJson(DistributedCache.UnpublishedPageCacheRefresherGuid, UnpublishedPageCacheRefresher.SerializeToJsonPayloadForPermanentDeletion(contentIds)); - } - - #endregion - - #region Member cache - - public static void RefreshMemberCache(this DistributedCache dc, params IMember[] members) - { - dc.Refresh(DistributedCache.MemberCacheRefresherGuid, x => x.Id, members); - } - - public static void RemoveMemberCache(this DistributedCache dc, params IMember[] members) - { - dc.Remove(DistributedCache.MemberCacheRefresherGuid, x => x.Id, members); - } - - [Obsolete("Use the RefreshMemberCache with strongly typed IMember objects instead")] - public static void RefreshMemberCache(this DistributedCache dc, int memberId) - { - dc.Refresh(DistributedCache.MemberCacheRefresherGuid, memberId); - } - - [Obsolete("Use the RemoveMemberCache with strongly typed IMember objects instead")] - public static void RemoveMemberCache(this DistributedCache dc, int memberId) - { - dc.Remove(DistributedCache.MemberCacheRefresherGuid, memberId); - } - - #endregion - - #region Member group cache - - public static void RefreshMemberGroupCache(this DistributedCache dc, int memberGroupId) - { - dc.Refresh(DistributedCache.MemberGroupCacheRefresherGuid, memberGroupId); - } - - public static void RemoveMemberGroupCache(this DistributedCache dc, int memberGroupId) - { - dc.Remove(DistributedCache.MemberGroupCacheRefresherGuid, memberGroupId); - } - - #endregion - - #region Media Cache - - public static void RefreshMediaCache(this DistributedCache dc, params IMedia[] media) - { - dc.RefreshByJson(DistributedCache.MediaCacheRefresherGuid, MediaCacheRefresher.SerializeToJsonPayload(MediaCacheRefresher.OperationType.Saved, media)); - } - - public static void RefreshMediaCacheAfterMoving(this DistributedCache dc, params MoveEventInfo[] media) - { - dc.RefreshByJson(DistributedCache.MediaCacheRefresherGuid, MediaCacheRefresher.SerializeToJsonPayloadForMoving(MediaCacheRefresher.OperationType.Saved, media)); - } - - // clearing by Id will never work for load balanced scenarios for media since we require a Path - // to clear all of the cache but the media item will be removed before the other servers can - // look it up. Only here for legacy purposes. - [Obsolete("Ensure to clear with other RemoveMediaCache overload")] - public static void RemoveMediaCache(this DistributedCache dc, int mediaId) - { - dc.Remove(new Guid(DistributedCache.MediaCacheRefresherId), mediaId); - } - - public static void RemoveMediaCacheAfterRecycling(this DistributedCache dc, params MoveEventInfo[] media) - { - dc.RefreshByJson(DistributedCache.MediaCacheRefresherGuid, MediaCacheRefresher.SerializeToJsonPayloadForMoving(MediaCacheRefresher.OperationType.Trashed, media)); - } - - public static void RemoveMediaCachePermanently(this DistributedCache dc, params int[] mediaIds) - { - dc.RefreshByJson(DistributedCache.MediaCacheRefresherGuid, MediaCacheRefresher.SerializeToJsonPayloadForPermanentDeletion(mediaIds)); - } - - #endregion - - #region Macro Cache - - public static void ClearAllMacroCacheOnCurrentServer(this DistributedCache dc) - { - var macroRefresher = CacheRefreshersResolver.Current.GetById(DistributedCache.MacroCacheRefresherGuid); - macroRefresher.RefreshAll(); - } - - public static void RefreshMacroCache(this DistributedCache dc, IMacro macro) - { - if (macro == null) return; - dc.RefreshByJson(DistributedCache.MacroCacheRefresherGuid, MacroCacheRefresher.SerializeToJsonPayload(macro)); - } - - public static void RemoveMacroCache(this DistributedCache dc, IMacro macro) - { - if (macro == null) return; - dc.RefreshByJson(DistributedCache.MacroCacheRefresherGuid, MacroCacheRefresher.SerializeToJsonPayload(macro)); - } - - public static void RefreshMacroCache(this DistributedCache dc, global::umbraco.cms.businesslogic.macro.Macro macro) - { - if (macro == null) return; - dc.RefreshByJson(DistributedCache.MacroCacheRefresherGuid, MacroCacheRefresher.SerializeToJsonPayload(macro)); - } - - public static void RemoveMacroCache(this DistributedCache dc, global::umbraco.cms.businesslogic.macro.Macro macro) - { - if (macro == null) return; - dc.RefreshByJson(DistributedCache.MacroCacheRefresherGuid, MacroCacheRefresher.SerializeToJsonPayload(macro)); - } - - public static void RemoveMacroCache(this DistributedCache dc, macro macro) - { - if (macro == null || macro.Model == null) return; - dc.RefreshByJson(DistributedCache.MacroCacheRefresherGuid, MacroCacheRefresher.SerializeToJsonPayload(macro)); - } - - #endregion - - #region Document type cache - - public static void RefreshContentTypeCache(this DistributedCache dc, IContentType contentType) - { - if (contentType == null) return; - dc.RefreshByJson(DistributedCache.ContentTypeCacheRefresherGuid, ContentTypeCacheRefresher.SerializeToJsonPayload(false, contentType)); - } - - public static void RemoveContentTypeCache(this DistributedCache dc, IContentType contentType) - { - if (contentType == null) return; - dc.RefreshByJson(DistributedCache.ContentTypeCacheRefresherGuid, ContentTypeCacheRefresher.SerializeToJsonPayload(true, contentType)); - } - - #endregion - - #region Media type cache - - public static void RefreshMediaTypeCache(this DistributedCache dc, IMediaType mediaType) - { - if (mediaType == null) return; - dc.RefreshByJson(DistributedCache.ContentTypeCacheRefresherGuid, ContentTypeCacheRefresher.SerializeToJsonPayload(false, mediaType)); - } - - public static void RemoveMediaTypeCache(this DistributedCache dc, IMediaType mediaType) - { - if (mediaType == null) return; - dc.RefreshByJson(DistributedCache.ContentTypeCacheRefresherGuid, ContentTypeCacheRefresher.SerializeToJsonPayload(true, mediaType)); - } - - #endregion - - #region Media type cache - - public static void RefreshMemberTypeCache(this DistributedCache dc, IMemberType memberType) - { - if (memberType == null) return; - dc.RefreshByJson(DistributedCache.ContentTypeCacheRefresherGuid, ContentTypeCacheRefresher.SerializeToJsonPayload(false, memberType)); - } - - public static void RemoveMemberTypeCache(this DistributedCache dc, IMemberType memberType) - { - if (memberType == null) return; - dc.RefreshByJson(DistributedCache.ContentTypeCacheRefresherGuid, ContentTypeCacheRefresher.SerializeToJsonPayload(true, memberType)); - } - - #endregion - - #region Stylesheet Cache - - public static void RefreshStylesheetPropertyCache(this DistributedCache dc, global::umbraco.cms.businesslogic.web.StylesheetProperty styleSheetProperty) - { - if (styleSheetProperty == null) return; - dc.Refresh(DistributedCache.StylesheetPropertyCacheRefresherGuid, styleSheetProperty.Id); - } - - public static void RemoveStylesheetPropertyCache(this DistributedCache dc, global::umbraco.cms.businesslogic.web.StylesheetProperty styleSheetProperty) - { - if (styleSheetProperty == null) return; - dc.Remove(DistributedCache.StylesheetPropertyCacheRefresherGuid, styleSheetProperty.Id); - } - - public static void RefreshStylesheetCache(this DistributedCache dc, StyleSheet styleSheet) - { - if (styleSheet == null) return; - dc.Refresh(DistributedCache.StylesheetCacheRefresherGuid, styleSheet.Id); - } - - public static void RemoveStylesheetCache(this DistributedCache dc, StyleSheet styleSheet) - { - if (styleSheet == null) return; - dc.Remove(DistributedCache.StylesheetCacheRefresherGuid, styleSheet.Id); - } - - public static void RefreshStylesheetCache(this DistributedCache dc, Umbraco.Core.Models.Stylesheet styleSheet) - { - if (styleSheet == null) return; - dc.Refresh(DistributedCache.StylesheetCacheRefresherGuid, styleSheet.Id); - } - - public static void RemoveStylesheetCache(this DistributedCache dc, Umbraco.Core.Models.Stylesheet styleSheet) - { - if (styleSheet == null) return; - dc.Remove(DistributedCache.StylesheetCacheRefresherGuid, styleSheet.Id); - } - - #endregion - - #region Domain Cache - - public static void RefreshDomainCache(this DistributedCache dc, IDomain domain) - { - if (domain == null) return; - dc.Refresh(DistributedCache.DomainCacheRefresherGuid, domain.Id); - } - - public static void RemoveDomainCache(this DistributedCache dc, IDomain domain) - { - if (domain == null) return; - dc.Remove(DistributedCache.DomainCacheRefresherGuid, domain.Id); - } - - public static void ClearDomainCacheOnCurrentServer(this DistributedCache dc) - { - var domainRefresher = CacheRefreshersResolver.Current.GetById(DistributedCache.DomainCacheRefresherGuid); - domainRefresher.RefreshAll(); - } - - #endregion - - #region Language Cache - - public static void RefreshLanguageCache(this DistributedCache dc, ILanguage language) - { - if (language == null) return; - dc.Refresh(DistributedCache.LanguageCacheRefresherGuid, language.Id); - } - - public static void RemoveLanguageCache(this DistributedCache dc, ILanguage language) - { - if (language == null) return; - dc.Remove(DistributedCache.LanguageCacheRefresherGuid, language.Id); - } - - public static void RefreshLanguageCache(this DistributedCache dc, global::umbraco.cms.businesslogic.language.Language language) - { - if (language == null) return; - dc.Refresh(DistributedCache.LanguageCacheRefresherGuid, language.id); - } - - public static void RemoveLanguageCache(this DistributedCache dc, global::umbraco.cms.businesslogic.language.Language language) - { - if (language == null) return; - dc.Remove(DistributedCache.LanguageCacheRefresherGuid, language.id); - } - - #endregion - - #region Xslt Cache - - public static void ClearXsltCacheOnCurrentServer(this DistributedCache dc) - { - if (UmbracoConfig.For.UmbracoSettings().Content.UmbracoLibraryCacheDuration <= 0) return; - ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheObjectTypes("MS.Internal.Xml.XPath.XPathSelectionIterator"); - } - - #endregion - - #region Relation type cache - - public static void RefreshRelationTypeCache(this DistributedCache dc, int id) - { - dc.Refresh(DistributedCache.RelationTypeCacheRefresherGuid, id); - } - - public static void RemoveRelationTypeCache(this DistributedCache dc, int id) - { - dc.Remove(DistributedCache.RelationTypeCacheRefresherGuid, id); - } - - #endregion - } -} \ No newline at end of file +using System; +using Umbraco.Core; +using Umbraco.Core.Configuration; +using Umbraco.Core.Events; +using Umbraco.Core.Models; +using umbraco; +using umbraco.cms.businesslogic.web; +using Umbraco.Core.Persistence.Repositories; + +namespace Umbraco.Web.Cache +{ + /// + /// Extension methods for + /// + internal static class DistributedCacheExtensions + { + #region Public access + + public static void RefreshPublicAccess(this DistributedCache dc) + { + dc.RefreshAll(DistributedCache.PublicAccessCacheRefresherGuid); + } + + #endregion + + #region Application tree cache + + public static void RefreshAllApplicationTreeCache(this DistributedCache dc) + { + dc.RefreshAll(DistributedCache.ApplicationTreeCacheRefresherGuid); + } + + #endregion + + #region Application cache + + public static void RefreshAllApplicationCache(this DistributedCache dc) + { + dc.RefreshAll(DistributedCache.ApplicationCacheRefresherGuid); + } + + #endregion + + #region User cache + + public static void RemoveUserCache(this DistributedCache dc, int userId) + { + dc.Remove(DistributedCache.UserCacheRefresherGuid, userId); + } + + public static void RefreshUserCache(this DistributedCache dc, int userId) + { + dc.Refresh(DistributedCache.UserCacheRefresherGuid, userId); + } + + public static void RefreshAllUserCache(this DistributedCache dc) + { + dc.RefreshAll(DistributedCache.UserCacheRefresherGuid); + } + + #endregion + + #region User group cache + + public static void RemoveUserGroupCache(this DistributedCache dc, int userId) + { + dc.Remove(DistributedCache.UserGroupCacheRefresherGuid, userId); + } + + public static void RefreshUserGroupCache(this DistributedCache dc, int userId) + { + dc.Refresh(DistributedCache.UserGroupCacheRefresherGuid, userId); + } + + public static void RefreshAllUserGroupCache(this DistributedCache dc) + { + dc.RefreshAll(DistributedCache.UserGroupCacheRefresherGuid); + } + + #endregion + + #region User group permissions cache + + public static void RemoveUserGroupPermissionsCache(this DistributedCache dc, int groupId) + { + dc.Remove(DistributedCache.UserGroupPermissionsCacheRefresherGuid, groupId); + } + + public static void RefreshUserGroupPermissionsCache(this DistributedCache dc, int groupId) + { + //TODO: Not sure if we need this yet depends if we start caching permissions + //dc.Refresh(DistributedCache.UserGroupPermissionsCacheRefresherGuid, groupId); + } + + public static void RefreshAllUserGroupPermissionsCache(this DistributedCache dc) + { + dc.RefreshAll(DistributedCache.UserGroupPermissionsCacheRefresherGuid); + } + + #endregion + + #region Template cache + + public static void RefreshTemplateCache(this DistributedCache dc, int templateId) + { + dc.Refresh(DistributedCache.TemplateRefresherGuid, templateId); + } + + public static void RemoveTemplateCache(this DistributedCache dc, int templateId) + { + dc.Remove(DistributedCache.TemplateRefresherGuid, templateId); + } + + #endregion + + #region Dictionary cache + + public static void RefreshDictionaryCache(this DistributedCache dc, int dictionaryItemId) + { + dc.Refresh(DistributedCache.DictionaryCacheRefresherGuid, dictionaryItemId); + } + + public static void RemoveDictionaryCache(this DistributedCache dc, int dictionaryItemId) + { + dc.Remove(DistributedCache.DictionaryCacheRefresherGuid, dictionaryItemId); + } + + #endregion + + #region Data type cache + + public static void RefreshDataTypeCache(this DistributedCache dc, IDataTypeDefinition dataType) + { + if (dataType == null) return; + dc.RefreshByJson(DistributedCache.DataTypeCacheRefresherGuid, DataTypeCacheRefresher.SerializeToJsonPayload(dataType)); + } + + public static void RemoveDataTypeCache(this DistributedCache dc, IDataTypeDefinition dataType) + { + if (dataType == null) return; + dc.RefreshByJson(DistributedCache.DataTypeCacheRefresherGuid, DataTypeCacheRefresher.SerializeToJsonPayload(dataType)); + } + + #endregion + + #region Page cache + + public static void RefreshAllPageCache(this DistributedCache dc) + { + dc.RefreshAll(DistributedCache.PageCacheRefresherGuid); + } + + public static void RefreshPageCache(this DistributedCache dc, int documentId) + { + dc.Refresh(DistributedCache.PageCacheRefresherGuid, documentId); + } + + public static void RefreshPageCache(this DistributedCache dc, params IContent[] content) + { + dc.Refresh(DistributedCache.PageCacheRefresherGuid, x => x.Id, content); + } + + public static void RemovePageCache(this DistributedCache dc, params IContent[] content) + { + dc.Remove(DistributedCache.PageCacheRefresherGuid, x => x.Id, content); + } + + public static void RemovePageCache(this DistributedCache dc, int documentId) + { + dc.Remove(DistributedCache.PageCacheRefresherGuid, documentId); + } + + public static void RefreshUnpublishedPageCache(this DistributedCache dc, params IContent[] content) + { + dc.RefreshByJson(DistributedCache.UnpublishedPageCacheRefresherGuid, UnpublishedPageCacheRefresher.SerializeToJsonPayload(UnpublishedPageCacheRefresher.OperationType.Refresh, content)); + } + + public static void RemoveUnpublishedPageCache(this DistributedCache dc, params IContent[] content) + { + dc.RefreshByJson(DistributedCache.UnpublishedPageCacheRefresherGuid, UnpublishedPageCacheRefresher.SerializeToJsonPayload(UnpublishedPageCacheRefresher.OperationType.Deleted, content)); + } + + public static void RemoveUnpublishedCachePermanently(this DistributedCache dc, params int[] contentIds) + { + dc.RefreshByJson(DistributedCache.UnpublishedPageCacheRefresherGuid, UnpublishedPageCacheRefresher.SerializeToJsonPayloadForPermanentDeletion(contentIds)); + } + + #endregion + + #region Member cache + + public static void RefreshMemberCache(this DistributedCache dc, params IMember[] members) + { + dc.Refresh(DistributedCache.MemberCacheRefresherGuid, x => x.Id, members); + } + + public static void RemoveMemberCache(this DistributedCache dc, params IMember[] members) + { + dc.Remove(DistributedCache.MemberCacheRefresherGuid, x => x.Id, members); + } + + [Obsolete("Use the RefreshMemberCache with strongly typed IMember objects instead")] + public static void RefreshMemberCache(this DistributedCache dc, int memberId) + { + dc.Refresh(DistributedCache.MemberCacheRefresherGuid, memberId); + } + + [Obsolete("Use the RemoveMemberCache with strongly typed IMember objects instead")] + public static void RemoveMemberCache(this DistributedCache dc, int memberId) + { + dc.Remove(DistributedCache.MemberCacheRefresherGuid, memberId); + } + + #endregion + + #region Member group cache + + public static void RefreshMemberGroupCache(this DistributedCache dc, int memberGroupId) + { + dc.Refresh(DistributedCache.MemberGroupCacheRefresherGuid, memberGroupId); + } + + public static void RemoveMemberGroupCache(this DistributedCache dc, int memberGroupId) + { + dc.Remove(DistributedCache.MemberGroupCacheRefresherGuid, memberGroupId); + } + + #endregion + + #region Media Cache + + public static void RefreshMediaCache(this DistributedCache dc, params IMedia[] media) + { + dc.RefreshByJson(DistributedCache.MediaCacheRefresherGuid, MediaCacheRefresher.SerializeToJsonPayload(MediaCacheRefresher.OperationType.Saved, media)); + } + + public static void RefreshMediaCacheAfterMoving(this DistributedCache dc, params MoveEventInfo[] media) + { + dc.RefreshByJson(DistributedCache.MediaCacheRefresherGuid, MediaCacheRefresher.SerializeToJsonPayloadForMoving(MediaCacheRefresher.OperationType.Saved, media)); + } + + // clearing by Id will never work for load balanced scenarios for media since we require a Path + // to clear all of the cache but the media item will be removed before the other servers can + // look it up. Only here for legacy purposes. + [Obsolete("Ensure to clear with other RemoveMediaCache overload")] + public static void RemoveMediaCache(this DistributedCache dc, int mediaId) + { + dc.Remove(new Guid(DistributedCache.MediaCacheRefresherId), mediaId); + } + + public static void RemoveMediaCacheAfterRecycling(this DistributedCache dc, params MoveEventInfo[] media) + { + dc.RefreshByJson(DistributedCache.MediaCacheRefresherGuid, MediaCacheRefresher.SerializeToJsonPayloadForMoving(MediaCacheRefresher.OperationType.Trashed, media)); + } + + public static void RemoveMediaCachePermanently(this DistributedCache dc, params int[] mediaIds) + { + dc.RefreshByJson(DistributedCache.MediaCacheRefresherGuid, MediaCacheRefresher.SerializeToJsonPayloadForPermanentDeletion(mediaIds)); + } + + #endregion + + #region Macro Cache + + public static void ClearAllMacroCacheOnCurrentServer(this DistributedCache dc) + { + var macroRefresher = CacheRefreshersResolver.Current.GetById(DistributedCache.MacroCacheRefresherGuid); + macroRefresher.RefreshAll(); + } + + public static void RefreshMacroCache(this DistributedCache dc, IMacro macro) + { + if (macro == null) return; + dc.RefreshByJson(DistributedCache.MacroCacheRefresherGuid, MacroCacheRefresher.SerializeToJsonPayload(macro)); + } + + public static void RemoveMacroCache(this DistributedCache dc, IMacro macro) + { + if (macro == null) return; + dc.RefreshByJson(DistributedCache.MacroCacheRefresherGuid, MacroCacheRefresher.SerializeToJsonPayload(macro)); + } + + public static void RefreshMacroCache(this DistributedCache dc, global::umbraco.cms.businesslogic.macro.Macro macro) + { + if (macro == null) return; + dc.RefreshByJson(DistributedCache.MacroCacheRefresherGuid, MacroCacheRefresher.SerializeToJsonPayload(macro)); + } + + public static void RemoveMacroCache(this DistributedCache dc, global::umbraco.cms.businesslogic.macro.Macro macro) + { + if (macro == null) return; + dc.RefreshByJson(DistributedCache.MacroCacheRefresherGuid, MacroCacheRefresher.SerializeToJsonPayload(macro)); + } + + public static void RemoveMacroCache(this DistributedCache dc, macro macro) + { + if (macro == null || macro.Model == null) return; + dc.RefreshByJson(DistributedCache.MacroCacheRefresherGuid, MacroCacheRefresher.SerializeToJsonPayload(macro)); + } + + #endregion + + #region Document type cache + + public static void RefreshContentTypeCache(this DistributedCache dc, IContentType contentType) + { + if (contentType == null) return; + dc.RefreshByJson(DistributedCache.ContentTypeCacheRefresherGuid, ContentTypeCacheRefresher.SerializeToJsonPayload(false, contentType)); + } + + public static void RemoveContentTypeCache(this DistributedCache dc, IContentType contentType) + { + if (contentType == null) return; + dc.RefreshByJson(DistributedCache.ContentTypeCacheRefresherGuid, ContentTypeCacheRefresher.SerializeToJsonPayload(true, contentType)); + } + + #endregion + + #region Media type cache + + public static void RefreshMediaTypeCache(this DistributedCache dc, IMediaType mediaType) + { + if (mediaType == null) return; + dc.RefreshByJson(DistributedCache.ContentTypeCacheRefresherGuid, ContentTypeCacheRefresher.SerializeToJsonPayload(false, mediaType)); + } + + public static void RemoveMediaTypeCache(this DistributedCache dc, IMediaType mediaType) + { + if (mediaType == null) return; + dc.RefreshByJson(DistributedCache.ContentTypeCacheRefresherGuid, ContentTypeCacheRefresher.SerializeToJsonPayload(true, mediaType)); + } + + #endregion + + #region Media type cache + + public static void RefreshMemberTypeCache(this DistributedCache dc, IMemberType memberType) + { + if (memberType == null) return; + dc.RefreshByJson(DistributedCache.ContentTypeCacheRefresherGuid, ContentTypeCacheRefresher.SerializeToJsonPayload(false, memberType)); + } + + public static void RemoveMemberTypeCache(this DistributedCache dc, IMemberType memberType) + { + if (memberType == null) return; + dc.RefreshByJson(DistributedCache.ContentTypeCacheRefresherGuid, ContentTypeCacheRefresher.SerializeToJsonPayload(true, memberType)); + } + + #endregion + + #region Stylesheet Cache + + public static void RefreshStylesheetPropertyCache(this DistributedCache dc, global::umbraco.cms.businesslogic.web.StylesheetProperty styleSheetProperty) + { + if (styleSheetProperty == null) return; + dc.Refresh(DistributedCache.StylesheetPropertyCacheRefresherGuid, styleSheetProperty.Id); + } + + public static void RemoveStylesheetPropertyCache(this DistributedCache dc, global::umbraco.cms.businesslogic.web.StylesheetProperty styleSheetProperty) + { + if (styleSheetProperty == null) return; + dc.Remove(DistributedCache.StylesheetPropertyCacheRefresherGuid, styleSheetProperty.Id); + } + + public static void RefreshStylesheetCache(this DistributedCache dc, StyleSheet styleSheet) + { + if (styleSheet == null) return; + dc.Refresh(DistributedCache.StylesheetCacheRefresherGuid, styleSheet.Id); + } + + public static void RemoveStylesheetCache(this DistributedCache dc, StyleSheet styleSheet) + { + if (styleSheet == null) return; + dc.Remove(DistributedCache.StylesheetCacheRefresherGuid, styleSheet.Id); + } + + public static void RefreshStylesheetCache(this DistributedCache dc, Umbraco.Core.Models.Stylesheet styleSheet) + { + if (styleSheet == null) return; + dc.Refresh(DistributedCache.StylesheetCacheRefresherGuid, styleSheet.Id); + } + + public static void RemoveStylesheetCache(this DistributedCache dc, Umbraco.Core.Models.Stylesheet styleSheet) + { + if (styleSheet == null) return; + dc.Remove(DistributedCache.StylesheetCacheRefresherGuid, styleSheet.Id); + } + + #endregion + + #region Domain Cache + + public static void RefreshDomainCache(this DistributedCache dc, IDomain domain) + { + if (domain == null) return; + dc.Refresh(DistributedCache.DomainCacheRefresherGuid, domain.Id); + } + + public static void RemoveDomainCache(this DistributedCache dc, IDomain domain) + { + if (domain == null) return; + dc.Remove(DistributedCache.DomainCacheRefresherGuid, domain.Id); + } + + public static void ClearDomainCacheOnCurrentServer(this DistributedCache dc) + { + var domainRefresher = CacheRefreshersResolver.Current.GetById(DistributedCache.DomainCacheRefresherGuid); + domainRefresher.RefreshAll(); + } + + #endregion + + #region Language Cache + + public static void RefreshLanguageCache(this DistributedCache dc, ILanguage language) + { + if (language == null) return; + dc.Refresh(DistributedCache.LanguageCacheRefresherGuid, language.Id); + } + + public static void RemoveLanguageCache(this DistributedCache dc, ILanguage language) + { + if (language == null) return; + dc.Remove(DistributedCache.LanguageCacheRefresherGuid, language.Id); + } + + public static void RefreshLanguageCache(this DistributedCache dc, global::umbraco.cms.businesslogic.language.Language language) + { + if (language == null) return; + dc.Refresh(DistributedCache.LanguageCacheRefresherGuid, language.id); + } + + public static void RemoveLanguageCache(this DistributedCache dc, global::umbraco.cms.businesslogic.language.Language language) + { + if (language == null) return; + dc.Remove(DistributedCache.LanguageCacheRefresherGuid, language.id); + } + + #endregion + + #region Xslt Cache + + public static void ClearXsltCacheOnCurrentServer(this DistributedCache dc) + { + if (UmbracoConfig.For.UmbracoSettings().Content.UmbracoLibraryCacheDuration <= 0) return; + ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheObjectTypes("MS.Internal.Xml.XPath.XPathSelectionIterator"); + } + + #endregion + + #region Relation type cache + + public static void RefreshRelationTypeCache(this DistributedCache dc, int id) + { + dc.Refresh(DistributedCache.RelationTypeCacheRefresherGuid, id); + } + + public static void RemoveRelationTypeCache(this DistributedCache dc, int id) + { + dc.Remove(DistributedCache.RelationTypeCacheRefresherGuid, id); + } + + #endregion + } +} diff --git a/src/Umbraco.Web/Cache/MediaCacheRefresher.cs b/src/Umbraco.Web/Cache/MediaCacheRefresher.cs index 783ca95841..bdaf9aa9e3 100644 --- a/src/Umbraco.Web/Cache/MediaCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/MediaCacheRefresher.cs @@ -1,194 +1,196 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Web.Script.Serialization; -using Umbraco.Core; -using Umbraco.Core.Cache; -using Umbraco.Core.Events; -using Umbraco.Core.IO; -using Umbraco.Core.Models; - -using Umbraco.Core.Persistence.Repositories; -using umbraco.interfaces; -using System.Linq; -using Newtonsoft.Json; -using Umbraco.Web.PublishedCache.XmlPublishedCache; - -namespace Umbraco.Web.Cache -{ - /// - /// A cache refresher to ensure media cache is updated - /// - /// - /// This is not intended to be used directly in your code and it should be sealed but due to legacy code we cannot seal it. - /// - public class MediaCacheRefresher : JsonCacheRefresherBase - { - #region Static helpers - - /// - /// Converts the json to a JsonPayload object - /// - /// - /// - public static JsonPayload[] DeserializeFromJsonPayload(string json) - { - var jsonObject = JsonConvert.DeserializeObject(json); - return jsonObject; - } - - /// - /// Creates the custom Json payload used to refresh cache amongst the servers - /// - /// - /// - /// - internal static string SerializeToJsonPayload(OperationType operation, params IMedia[] media) - { - var items = media.Select(x => FromMedia(x, operation)).ToArray(); - var json = JsonConvert.SerializeObject(items); - return json; - } - - internal static string SerializeToJsonPayloadForMoving(OperationType operation, MoveEventInfo[] media) - { - var items = media.Select(x => new JsonPayload - { - Id = x.Entity.Id, - Operation = operation, - Path = x.OriginalPath - }).ToArray(); - var json = JsonConvert.SerializeObject(items); - return json; - } - - internal static string SerializeToJsonPayloadForPermanentDeletion(params int[] mediaIds) - { - var items = mediaIds.Select(x => new JsonPayload - { - Id = x, - Operation = OperationType.Deleted - }).ToArray(); - var json = JsonConvert.SerializeObject(items); - return json; - } - - /// - /// Converts a macro to a jsonPayload object - /// - /// - /// - /// - internal static JsonPayload FromMedia(IMedia media, OperationType operation) - { - if (media == null) return null; - - var payload = new JsonPayload - { - Id = media.Id, - Path = media.Path, - Operation = operation - }; - return payload; - } - - #endregion - - #region Sub classes - - public enum OperationType - { - Saved, - Trashed, - Deleted - } - - public class JsonPayload - { - public string Path { get; set; } - public int Id { get; set; } - public OperationType Operation { get; set; } - } - - #endregion - - protected override MediaCacheRefresher Instance - { - get { return this; } - } - - public override Guid UniqueIdentifier - { - get { return new Guid(DistributedCache.MediaCacheRefresherId); } - } - - public override string Name - { - get { return "Clears Media Cache from umbraco.library"; } - } - - public override void Refresh(string jsonPayload) - { - ClearCache(DeserializeFromJsonPayload(jsonPayload)); - base.Refresh(jsonPayload); - } - - public override void Refresh(int id) - { - ClearCache(FromMedia(ApplicationContext.Current.Services.MediaService.GetById(id), OperationType.Saved)); - base.Refresh(id); - } - - public override void Remove(int id) - { - ClearCache(FromMedia(ApplicationContext.Current.Services.MediaService.GetById(id), - //NOTE: we'll just default to trashed for this one. - OperationType.Trashed)); - base.Remove(id); - } - - private static void ClearCache(params JsonPayload[] payloads) - { - if (payloads == null) return; - - ApplicationContext.Current.ApplicationCache.ClearPartialViewCache(); - - foreach (var payload in payloads) - { - if (payload.Operation == OperationType.Deleted) - ApplicationContext.Current.Services.IdkMap.ClearCache(payload.Id); - - var mediaCache = ApplicationContext.Current.ApplicationCache.IsolatedRuntimeCache.GetCache(); - - //if there's no path, then just use id (this will occur on permanent deletion like emptying recycle bin) - if (payload.Path.IsNullOrWhiteSpace()) - { - ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch( - string.Format("{0}_{1}", CacheKeys.MediaCacheKey, payload.Id)); - } - else - { - foreach (var idPart in payload.Path.Split(',')) - { - int idPartAsInt; - if (int.TryParse(idPart, out idPartAsInt) && mediaCache) - { - mediaCache.Result.ClearCacheItem(RepositoryBase.GetCacheIdKey(idPartAsInt)); - } - - ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch( - string.Format("{0}_{1}_True", CacheKeys.MediaCacheKey, idPart)); - - // Also clear calls that only query this specific item! - if (idPart == payload.Id.ToString(CultureInfo.InvariantCulture)) - ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch( - string.Format("{0}_{1}", CacheKeys.MediaCacheKey, payload.Id)); - } - } - - // published cache... - PublishedMediaCache.ClearCache(payload.Id); - } - } - } -} +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Web.Script.Serialization; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Events; +using Umbraco.Core.IO; +using Umbraco.Core.Models; + +using Umbraco.Core.Persistence.Repositories; +using umbraco.interfaces; +using System.Linq; +using Newtonsoft.Json; +using Umbraco.Web.PublishedCache.XmlPublishedCache; + +namespace Umbraco.Web.Cache +{ + /// + /// A cache refresher to ensure media cache is updated + /// + /// + /// This is not intended to be used directly in your code and it should be sealed but due to legacy code we cannot seal it. + /// + public class MediaCacheRefresher : JsonCacheRefresherBase + { + #region Static helpers + + /// + /// Converts the json to a JsonPayload object + /// + /// + /// + public static JsonPayload[] DeserializeFromJsonPayload(string json) + { + var jsonObject = JsonConvert.DeserializeObject(json); + return jsonObject; + } + + /// + /// Creates the custom Json payload used to refresh cache amongst the servers + /// + /// + /// + /// + internal static string SerializeToJsonPayload(OperationType operation, params IMedia[] media) + { + var items = media.Select(x => FromMedia(x, operation)).ToArray(); + var json = JsonConvert.SerializeObject(items); + return json; + } + + internal static string SerializeToJsonPayloadForMoving(OperationType operation, MoveEventInfo[] media) + { + var items = media.Select(x => new JsonPayload + { + Id = x.Entity.Id, + Operation = operation, + Path = x.OriginalPath + }).ToArray(); + var json = JsonConvert.SerializeObject(items); + return json; + } + + internal static string SerializeToJsonPayloadForPermanentDeletion(params int[] mediaIds) + { + var items = mediaIds.Select(x => new JsonPayload + { + Id = x, + Operation = OperationType.Deleted + }).ToArray(); + var json = JsonConvert.SerializeObject(items); + return json; + } + + /// + /// Converts a macro to a jsonPayload object + /// + /// + /// + /// + internal static JsonPayload FromMedia(IMedia media, OperationType operation) + { + if (media == null) return null; + + var payload = new JsonPayload + { + Id = media.Id, + Key = media.Key, + Path = media.Path, + Operation = operation + }; + return payload; + } + + #endregion + + #region Sub classes + + public enum OperationType + { + Saved, + Trashed, + Deleted + } + + public class JsonPayload + { + public string Path { get; set; } + public int Id { get; set; } + public OperationType Operation { get; set; } + public Guid? Key { get; set; } + } + + #endregion + + protected override MediaCacheRefresher Instance + { + get { return this; } + } + + public override Guid UniqueIdentifier + { + get { return new Guid(DistributedCache.MediaCacheRefresherId); } + } + + public override string Name + { + get { return "Clears Media Cache from umbraco.library"; } + } + + public override void Refresh(string jsonPayload) + { + ClearCache(DeserializeFromJsonPayload(jsonPayload)); + base.Refresh(jsonPayload); + } + + public override void Refresh(int id) + { + ClearCache(FromMedia(ApplicationContext.Current.Services.MediaService.GetById(id), OperationType.Saved)); + base.Refresh(id); + } + + public override void Remove(int id) + { + ClearCache(FromMedia(ApplicationContext.Current.Services.MediaService.GetById(id), + //NOTE: we'll just default to trashed for this one. + OperationType.Trashed)); + base.Remove(id); + } + + private static void ClearCache(params JsonPayload[] payloads) + { + if (payloads == null) return; + + ApplicationContext.Current.ApplicationCache.ClearPartialViewCache(); + + foreach (var payload in payloads) + { + if (payload.Operation == OperationType.Deleted) + ApplicationContext.Current.Services.IdkMap.ClearCache(payload.Id); + + var mediaCache = ApplicationContext.Current.ApplicationCache.IsolatedRuntimeCache.GetCache(); + mediaCache.Result.ClearCacheItem(RepositoryBase.GetCacheIdKey(payload.Key)); + //if there's no path, then just use id (this will occur on permanent deletion like emptying recycle bin) + if (payload.Path.IsNullOrWhiteSpace()) + { + ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch( + string.Format("{0}_{1}", CacheKeys.MediaCacheKey, payload.Id)); + } + else + { + foreach (var idPart in payload.Path.Split(',')) + { + int idPartAsInt; + if (int.TryParse(idPart, out idPartAsInt) && mediaCache) + { + mediaCache.Result.ClearCacheItem(RepositoryBase.GetCacheIdKey(idPartAsInt)); + } + + ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch( + string.Format("{0}_{1}_True", CacheKeys.MediaCacheKey, idPart)); + + // Also clear calls that only query this specific item! + if (idPart == payload.Id.ToString(CultureInfo.InvariantCulture)) + ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch( + string.Format("{0}_{1}", CacheKeys.MediaCacheKey, payload.Id)); + } + } + + // published cache... + PublishedMediaCache.ClearCache(payload.Id); + } + } + } +} diff --git a/src/Umbraco.Web/Cache/UnpublishedPageCacheRefresher.cs b/src/Umbraco.Web/Cache/UnpublishedPageCacheRefresher.cs index bf58454bfc..4fc38a27a4 100644 --- a/src/Umbraco.Web/Cache/UnpublishedPageCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/UnpublishedPageCacheRefresher.cs @@ -56,6 +56,17 @@ namespace Umbraco.Web.Cache var json = JsonConvert.SerializeObject(items); return json; } + internal static string SerializeToJsonPayload(OperationType operationType, params IContent[] contents) + { + var items = contents.Select(x => new JsonPayload + { + Id = x.Id, + Key = x.Key, + Operation = operationType + }).ToArray(); + var json = JsonConvert.SerializeObject(items); + return json; + } #endregion @@ -63,12 +74,14 @@ namespace Umbraco.Web.Cache internal enum OperationType { - Deleted + Deleted, + Refresh } internal class JsonPayload { public int Id { get; set; } + public Guid? Key { get; set; } public OperationType Operation { get; set; } } @@ -139,10 +152,26 @@ namespace Umbraco.Web.Cache foreach (var payload in DeserializeFromJsonPayload(jsonPayload)) { - ApplicationContext.Current.Services.IdkMap.ClearCache(payload.Id); ClearRepositoryCacheItemById(payload.Id); - content.Instance.UpdateSortOrder(payload.Id); - content.Instance.ClearPreviewXmlContent(payload.Id); + ClearRepositoryCacheItemById(payload.Key); + ClearAllIsolatedCacheByEntityType(); + + if (payload.Operation == OperationType.Deleted) + { + ApplicationContext.Current.Services.IdkMap.ClearCache(payload.Id); + content.Instance.ClearPreviewXmlContent(payload.Id); + base.Remove(payload.Id); + } + + if (payload.Operation == OperationType.Refresh) + { + content.Instance.UpdateSortOrder(payload.Id); + var d = new Document(payload.Id); + content.Instance.UpdateDocumentCache(d); + content.Instance.UpdatePreviewXmlContent(d); + + base.Refresh(payload.Id); + } } DistributedCache.Instance.ClearDomainCacheOnCurrentServer(); @@ -158,5 +187,14 @@ namespace Umbraco.Web.Cache contentCache.Result.ClearCacheItem(RepositoryBase.GetCacheIdKey(id)); } } + + private void ClearRepositoryCacheItemById(Guid? key) + { + var contentCache = ApplicationContext.Current.ApplicationCache.IsolatedRuntimeCache.GetCache(); + if (contentCache) + { + contentCache.Result.ClearCacheItem(RepositoryBase.GetCacheIdKey(key)); + } + } } } diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs index 3bc0ecb7cd..14ee38031d 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs @@ -1,1047 +1,1047 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Configuration; -using System.IO; -using System.Linq; -using System.Threading; -using System.Xml.XPath; -using Examine; -using Examine.LuceneEngine.SearchCriteria; -using Examine.Providers; -using Lucene.Net.Documents; -using Lucene.Net.Store; -using Umbraco.Core; -using Umbraco.Core.Configuration; -using Umbraco.Core.Dynamics; -using Umbraco.Core.Logging; -using Umbraco.Core.Models; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.Xml; -using Umbraco.Web.Models; -using UmbracoExamine; -using umbraco; -using Umbraco.Core.Cache; -using Umbraco.Core.Sync; -using Umbraco.Web.Cache; - -namespace Umbraco.Web.PublishedCache.XmlPublishedCache -{ - /// - /// An IPublishedMediaStore that first checks for the media in Examine, and then reverts to the database - /// - /// - /// NOTE: In the future if we want to properly cache all media this class can be extended or replaced when these classes/interfaces are exposed publicly. - /// - internal class PublishedMediaCache : IPublishedMediaCache - { - public PublishedMediaCache(ApplicationContext applicationContext) - { - if (applicationContext == null) throw new ArgumentNullException("applicationContext"); - _applicationContext = applicationContext; - } - - /// - /// Generally used for unit testing to use an explicit examine searcher - /// - /// - /// - /// - internal PublishedMediaCache(ApplicationContext applicationContext, BaseSearchProvider searchProvider, BaseIndexProvider indexProvider) - { - if (applicationContext == null) throw new ArgumentNullException("applicationContext"); - if (searchProvider == null) throw new ArgumentNullException("searchProvider"); - if (indexProvider == null) throw new ArgumentNullException("indexProvider"); - - _applicationContext = applicationContext; - _searchProvider = searchProvider; - _indexProvider = indexProvider; - } - - static PublishedMediaCache() - { - InitializeCacheConfig(); - } - - private readonly ApplicationContext _applicationContext; - private readonly BaseSearchProvider _searchProvider; - private readonly BaseIndexProvider _indexProvider; - - public virtual IPublishedContent GetById(UmbracoContext umbracoContext, bool preview, int nodeId) - { - return GetUmbracoMedia(nodeId); - } - - public virtual IPublishedContent GetById(UmbracoContext umbracoContext, bool preview, Guid nodeKey) - { - // TODO optimize with Examine? - var mapAttempt = ApplicationContext.Current.Services.IdkMap.GetIdForKey(nodeKey, UmbracoObjectTypes.Media); - return mapAttempt ? GetById(umbracoContext, preview, mapAttempt.Result) : null; - } - - public virtual IEnumerable GetAtRoot(UmbracoContext umbracoContext, bool preview) - { - var searchProvider = GetSearchProviderSafe(); - - if (searchProvider != null) - { - try - { - // first check in Examine for the cache values - // +(+parentID:-1) +__IndexType:media - - var criteria = searchProvider.CreateSearchCriteria("media"); - var filter = criteria.ParentId(-1).Not().Field(UmbracoContentIndexer.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()); - - var result = searchProvider.Search(filter.Compile()); - if (result != null) - return result.Select(x => CreateFromCacheValues(ConvertFromSearchResult(x))); - } - catch (Exception ex) - { - if (ex is FileNotFoundException) - { - //Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco - //See this thread: http://examine.cdodeplex.com/discussions/264341 - //Catch the exception here for the time being, and just fallback to GetMedia - //TODO: Need to fix examine in LB scenarios! - LogHelper.Error("Could not load data from Examine index for media", ex); - } - else if (ex is AlreadyClosedException) - { - //If the app domain is shutting down and the site is under heavy load the index reader will be closed and it really cannot - //be re-opened since the app domain is shutting down. In this case we have no option but to try to load the data from the db. - LogHelper.Error("Could not load data from Examine index for media, the app domain is most likely in a shutdown state", ex); - } - else throw; - } +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Configuration; +using System.IO; +using System.Linq; +using System.Threading; +using System.Xml.XPath; +using Examine; +using Examine.LuceneEngine.SearchCriteria; +using Examine.Providers; +using Lucene.Net.Documents; +using Lucene.Net.Store; +using Umbraco.Core; +using Umbraco.Core.Configuration; +using Umbraco.Core.Dynamics; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Xml; +using Umbraco.Web.Models; +using UmbracoExamine; +using umbraco; +using Umbraco.Core.Cache; +using Umbraco.Core.Sync; +using Umbraco.Web.Cache; + +namespace Umbraco.Web.PublishedCache.XmlPublishedCache +{ + /// + /// An IPublishedMediaStore that first checks for the media in Examine, and then reverts to the database + /// + /// + /// NOTE: In the future if we want to properly cache all media this class can be extended or replaced when these classes/interfaces are exposed publicly. + /// + internal class PublishedMediaCache : IPublishedMediaCache + { + public PublishedMediaCache(ApplicationContext applicationContext) + { + if (applicationContext == null) throw new ArgumentNullException("applicationContext"); + _applicationContext = applicationContext; + } + + /// + /// Generally used for unit testing to use an explicit examine searcher + /// + /// + /// + /// + internal PublishedMediaCache(ApplicationContext applicationContext, BaseSearchProvider searchProvider, BaseIndexProvider indexProvider) + { + if (applicationContext == null) throw new ArgumentNullException("applicationContext"); + if (searchProvider == null) throw new ArgumentNullException("searchProvider"); + if (indexProvider == null) throw new ArgumentNullException("indexProvider"); + + _applicationContext = applicationContext; + _searchProvider = searchProvider; + _indexProvider = indexProvider; + } + + static PublishedMediaCache() + { + InitializeCacheConfig(); + } + + private readonly ApplicationContext _applicationContext; + private readonly BaseSearchProvider _searchProvider; + private readonly BaseIndexProvider _indexProvider; + + public virtual IPublishedContent GetById(UmbracoContext umbracoContext, bool preview, int nodeId) + { + return GetUmbracoMedia(nodeId); + } + + public virtual IPublishedContent GetById(UmbracoContext umbracoContext, bool preview, Guid nodeKey) + { + // TODO optimize with Examine? + var mapAttempt = ApplicationContext.Current.Services.IdkMap.GetIdForKey(nodeKey, UmbracoObjectTypes.Media); + return mapAttempt ? GetById(umbracoContext, preview, mapAttempt.Result) : null; + } + + public virtual IEnumerable GetAtRoot(UmbracoContext umbracoContext, bool preview) + { + var searchProvider = GetSearchProviderSafe(); + + if (searchProvider != null) + { + try + { + // first check in Examine for the cache values + // +(+parentID:-1) +__IndexType:media + + var criteria = searchProvider.CreateSearchCriteria("media"); + var filter = criteria.ParentId(-1).Not().Field(UmbracoContentIndexer.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()); + + var result = searchProvider.Search(filter.Compile()); + if (result != null) + return result.Select(x => CreateFromCacheValues(ConvertFromSearchResult(x))); + } + catch (Exception ex) + { + if (ex is FileNotFoundException) + { + //Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco + //See this thread: http://examine.cdodeplex.com/discussions/264341 + //Catch the exception here for the time being, and just fallback to GetMedia + //TODO: Need to fix examine in LB scenarios! + LogHelper.Error("Could not load data from Examine index for media", ex); + } + else if (ex is AlreadyClosedException) + { + //If the app domain is shutting down and the site is under heavy load the index reader will be closed and it really cannot + //be re-opened since the app domain is shutting down. In this case we have no option but to try to load the data from the db. + LogHelper.Error("Could not load data from Examine index for media, the app domain is most likely in a shutdown state", ex); + } + else throw; + } } //something went wrong, fetch from the db - - var rootMedia = _applicationContext.Services.MediaService.GetRootMedia(); - return rootMedia.Select(m => CreateFromCacheValues(ConvertFromIMedia(m))); + + var rootMedia = _applicationContext.Services.MediaService.GetRootMedia(); + return rootMedia.Select(m => CreateFromCacheValues(ConvertFromIMedia(m))); } - - public virtual IPublishedContent GetSingleByXPath(UmbracoContext umbracoContext, bool preview, string xpath, XPathVariable[] vars) - { - throw new NotImplementedException("PublishedMediaCache does not support XPath."); - } - - public virtual IPublishedContent GetSingleByXPath(UmbracoContext umbracoContext, bool preview, XPathExpression xpath, XPathVariable[] vars) - { - throw new NotImplementedException("PublishedMediaCache does not support XPath."); - } - - public virtual IEnumerable GetByXPath(UmbracoContext umbracoContext, bool preview, string xpath, XPathVariable[] vars) - { - throw new NotImplementedException("PublishedMediaCache does not support XPath."); - } - - public virtual IEnumerable GetByXPath(UmbracoContext umbracoContext, bool preview, XPathExpression xpath, XPathVariable[] vars) - { - throw new NotImplementedException("PublishedMediaCache does not support XPath."); - } - - public virtual XPathNavigator GetXPathNavigator(UmbracoContext umbracoContext, bool preview) - { - throw new NotImplementedException("PublishedMediaCache does not support XPath."); - } - - public bool XPathNavigatorIsNavigable { get { return false; } } - - public virtual bool HasContent(UmbracoContext context, bool preview) { throw new NotImplementedException(); } - - private ExamineManager GetExamineManagerSafe() - { - try - { - return ExamineManager.Instance; - } - catch (TypeInitializationException) - { - return null; - } - } - - private BaseIndexProvider GetIndexProviderSafe() - { - if (_indexProvider != null) - return _indexProvider; - - var eMgr = GetExamineManagerSafe(); - if (eMgr != null) - { - try - { - //by default use the InternalSearcher - var indexer = eMgr.IndexProviderCollection[Constants.Examine.InternalIndexer]; - if (indexer.IndexerData.IncludeNodeTypes.Any() || indexer.IndexerData.ExcludeNodeTypes.Any()) - { - LogHelper.Warn("The InternalIndexer for examine is configured incorrectly, it should not list any include/exclude node types or field names, it should simply be configured as: " + ""); - } - return indexer; - } - catch (Exception ex) - { - LogHelper.Error("Could not retrieve the InternalIndexer", ex); - //something didn't work, continue returning null. - } - } - return null; - } - - private BaseSearchProvider GetSearchProviderSafe() - { - if (_searchProvider != null) - return _searchProvider; - - var eMgr = GetExamineManagerSafe(); - if (eMgr != null) - { - try - { - //by default use the InternalSearcher - return eMgr.SearchProviderCollection[Constants.Examine.InternalSearcher]; - } - catch (FileNotFoundException) - { - //Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco - //See this thread: http://examine.cdodeplex.com/discussions/264341 - //Catch the exception here for the time being, and just fallback to GetMedia - //TODO: Need to fix examine in LB scenarios! - } - catch (NullReferenceException) - { - //This will occur when the search provider cannot be initialized. In newer examine versions the initialization is lazy and therefore - // the manager will return the singleton without throwing initialization errors, however if examine isn't configured correctly a null - // reference error will occur because the examine settings are null. - } - catch (AlreadyClosedException) - { - //If the app domain is shutting down and the site is under heavy load the index reader will be closed and it really cannot - //be re-opened since the app domain is shutting down. In this case we have no option but to try to load the data from the db. - } - } - return null; - } - - private IPublishedContent GetUmbracoMedia(int id) - { - // this recreates an IPublishedContent and model each time - // it is called, but at least it should NOT hit the database - // nor Lucene each time, relying on the memory cache instead - - if (id <= 0) return null; // fail fast - - var cacheValues = GetCacheValues(id, GetUmbracoMediaCacheValues); - - return cacheValues == null ? null : CreateFromCacheValues(cacheValues); - } - - private CacheValues GetUmbracoMediaCacheValues(int id) - { - var searchProvider = GetSearchProviderSafe(); - - if (searchProvider != null) - { - try - { - // first check in Examine as this is WAY faster - // - // the filter will create a query like this: - // +(+__NodeId:3113 -__Path:-1,-21,*) +__IndexType:media - // - // note that since the use of the wildcard, it automatically escapes it in Lucene. - - var criteria = searchProvider.CreateSearchCriteria("media"); - var filter = criteria.Id(id).Not().Field(UmbracoContentIndexer.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()); - - var result = searchProvider.Search(filter.Compile()).FirstOrDefault(); - if (result != null) return ConvertFromSearchResult(result); - } - catch (Exception ex) - { - if (ex is FileNotFoundException) - { - //Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco - //See this thread: http://examine.cdodeplex.com/discussions/264341 - //Catch the exception here for the time being, and just fallback to GetMedia - //TODO: Need to fix examine in LB scenarios! - LogHelper.Error("Could not load data from Examine index for media", ex); - } - else if (ex is AlreadyClosedException) - { - //If the app domain is shutting down and the site is under heavy load the index reader will be closed and it really cannot - //be re-opened since the app domain is shutting down. In this case we have no option but to try to load the data from the db. - LogHelper.Error("Could not load data from Examine index for media, the app domain is most likely in a shutdown state", ex); - } - else throw; - } - } - - // don't log a warning here, as it can flood the log in case of eg a media picker referencing a media - // that has been deleted, hence is not in the Examine index anymore (for a good reason). try to get - // the media from the service, first - - var media = ApplicationContext.Current.Services.MediaService.GetById(id); - if (media == null || media.Trashed) return null; // not found, ok - - // so, the media was not found in Examine's index *yet* it exists, which probably indicates that - // the index is corrupted. Or not up-to-date. Log a warning, but only once, and only if seeing the - // error more that a number of times. - - var miss = Interlocked.CompareExchange(ref _examineIndexMiss, 0, 0); // volatile read - if (miss < ExamineIndexMissMax && Interlocked.Increment(ref _examineIndexMiss) == ExamineIndexMissMax) - LogHelper.Warn("Failed ({0} times) to retrieve medias from Examine index and had to load" - + " them from DB. This may indicate that the Examine index is corrupted.", - () => ExamineIndexMissMax); - - return ConvertFromIMedia(media); - } - - private const int ExamineIndexMissMax = 10; - private int _examineIndexMiss; - - internal CacheValues ConvertFromXPathNodeIterator(XPathNodeIterator media, int id) - { - if (media != null && media.Current != null) - { - return media.Current.Name.InvariantEquals("error") - ? null - : ConvertFromXPathNavigator(media.Current); - } - - LogHelper.Warn( - "Could not retrieve media {0} from Examine index or from legacy library.GetMedia method", - () => id); - - return null; - } - - internal CacheValues ConvertFromSearchResult(SearchResult searchResult) - { - //NOTE: Some fields will not be included if the config section for the internal index has been - //mucked around with. It should index everything and so the index definition should simply be: - // - - - var values = new Dictionary(searchResult.Fields); - //we need to ensure some fields exist, because of the above issue - if (!new[] { "template", "templateId" }.Any(values.ContainsKey)) - values.Add("template", 0.ToString()); - if (!new[] { "sortOrder" }.Any(values.ContainsKey)) - values.Add("sortOrder", 0.ToString()); - if (!new[] { "urlName" }.Any(values.ContainsKey)) - values.Add("urlName", ""); - if (!new[] { "nodeType" }.Any(values.ContainsKey)) - values.Add("nodeType", 0.ToString()); - if (!new[] { "creatorName" }.Any(values.ContainsKey)) - values.Add("creatorName", ""); - if (!new[] { "writerID" }.Any(values.ContainsKey)) - values.Add("writerID", 0.ToString()); - if (!new[] { "creatorID" }.Any(values.ContainsKey)) - values.Add("creatorID", 0.ToString()); - if (!new[] { "createDate" }.Any(values.ContainsKey)) - values.Add("createDate", default(DateTime).ToString("yyyy-MM-dd HH:mm:ss")); - if (!new[] { "level" }.Any(values.ContainsKey)) - { - values.Add("level", values["__Path"].Split(',').Length.ToString()); - } - - // because, migration - if (values.ContainsKey("key") == false) - values["key"] = Guid.Empty.ToString(); - - return new CacheValues - { - Values = values, - FromExamine = true - }; - - //var content = new DictionaryPublishedContent(values, - // d => d.ParentId != -1 //parent should be null if -1 - // ? GetUmbracoMedia(d.ParentId) - // : null, - // //callback to return the children of the current node - // d => GetChildrenMedia(d.Id), - // GetProperty, - // true); - //return content.CreateModel(); - } - - internal CacheValues ConvertFromXPathNavigator(XPathNavigator xpath, bool forceNav = false) - { - if (xpath == null) throw new ArgumentNullException("xpath"); - - var values = new Dictionary { { "nodeName", xpath.GetAttribute("nodeName", "") } }; - if (!UmbracoConfig.For.UmbracoSettings().Content.UseLegacyXmlSchema) - { - values["nodeTypeAlias"] = xpath.Name; - } - - var result = xpath.SelectChildren(XPathNodeType.Element); - //add the attributes e.g. id, parentId etc - if (result.Current != null && result.Current.HasAttributes) - { - if (result.Current.MoveToFirstAttribute()) - { - //checking for duplicate keys because of the 'nodeTypeAlias' might already be added above. - if (!values.ContainsKey(result.Current.Name)) - { - values[result.Current.Name] = result.Current.Value; - } - while (result.Current.MoveToNextAttribute()) - { - if (!values.ContainsKey(result.Current.Name)) - { - values[result.Current.Name] = result.Current.Value; - } - } - result.Current.MoveToParent(); - } - } - // because, migration - if (values.ContainsKey("key") == false) - values["key"] = Guid.Empty.ToString(); - //add the user props - while (result.MoveNext()) - { - if (result.Current != null && !result.Current.HasAttributes) - { - string value = result.Current.Value; - if (string.IsNullOrEmpty(value)) - { - if (result.Current.HasAttributes || result.Current.SelectChildren(XPathNodeType.Element).Count > 0) - { - value = result.Current.OuterXml; - } - } - values[result.Current.Name] = value; - } - } - - return new CacheValues - { - Values = values, - XPath = forceNav ? xpath : null // outside of tests we do NOT want to cache the navigator! - }; - - //var content = new DictionaryPublishedContent(values, - // d => d.ParentId != -1 //parent should be null if -1 - // ? GetUmbracoMedia(d.ParentId) - // : null, - // //callback to return the children of the current node based on the xml structure already found - // d => GetChildrenMedia(d.Id, xpath), - // GetProperty, - // false); - //return content.CreateModel(); - } - - internal CacheValues ConvertFromIMedia(IMedia media) - { - var values = new Dictionary(); - - var creator = _applicationContext.Services.UserService.GetProfileById(media.CreatorId); - var creatorName = creator == null ? "" : creator.Name; - - values["id"] = media.Id.ToString(); - values["key"] = media.Key.ToString(); - values["parentID"] = media.ParentId.ToString(); - values["level"] = media.Level.ToString(); - values["creatorID"] = media.CreatorId.ToString(); - values["creatorName"] = creatorName; - values["writerID"] = media.CreatorId.ToString(); - values["writerName"] = creatorName; - values["template"] = "0"; - values["urlName"] = ""; - values["sortOrder"] = media.SortOrder.ToString(); - values["createDate"] = media.CreateDate.ToString("yyyy-MM-dd HH:mm:ss"); - values["updateDate"] = media.UpdateDate.ToString("yyyy-MM-dd HH:mm:ss"); - values["nodeName"] = media.Name; - values["path"] = media.Path; - values["nodeType"] = media.ContentType.Id.ToString(); - values["nodeTypeAlias"] = media.ContentType.Alias; - - // add the user props - foreach (var prop in media.Properties) - values[prop.Alias] = prop.Value == null ? null : prop.Value.ToString(); - - return new CacheValues - { - Values = values - }; - } - - /// - /// We will need to first check if the document was loaded by Examine, if so we'll need to check if this property exists - /// in the results, if it does not, then we'll have to revert to looking up in the db. - /// - /// - /// - /// - private IPublishedProperty GetProperty(DictionaryPublishedContent dd, string alias) - { - //lets check if the alias does not exist on the document. - //NOTE: Examine will not index empty values and we do not output empty XML Elements to the cache - either of these situations - // would mean that the property is missing from the collection whether we are getting the value from Examine or from the library media cache. - if (dd.Properties.All(x => x.PropertyTypeAlias.InvariantEquals(alias) == false)) - { - return null; - } - - if (dd.LoadedFromExamine) - { - //We are going to check for a special field however, that is because in some cases we store a 'Raw' - //value in the index such as for xml/html. - var rawValue = dd.Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(UmbracoContentIndexer.RawFieldPrefix + alias)); - return rawValue - ?? dd.Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(alias)); - } - - //if its not loaded from examine, then just return the property - return dd.Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(alias)); - } - - /// - /// A Helper methods to return the children for media whther it is based on examine or xml - /// - /// - /// - /// - private IEnumerable GetChildrenMedia(int parentId, XPathNavigator xpath = null) - { - - //if there is no navigator, try examine first, then re-look it up - if (xpath == null) - { - var searchProvider = GetSearchProviderSafe(); - - if (searchProvider != null) - { - try - { - //first check in Examine as this is WAY faster - var criteria = searchProvider.CreateSearchCriteria("media"); - - var filter = criteria.ParentId(parentId).Not().Field(UmbracoContentIndexer.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()); - //the above filter will create a query like this, NOTE: That since the use of the wildcard, it automatically escapes it in Lucene. - //+(+parentId:3113 -__Path:-1,-21,*) +__IndexType:media - - ISearchResults results; - - //we want to check if the indexer for this searcher has "sortOrder" flagged as sortable. - //if so, we'll use Lucene to do the sorting, if not we'll have to manually sort it (slower). - var indexer = GetIndexProviderSafe(); - var useLuceneSort = indexer != null && indexer.IndexerData.StandardFields.Any(x => x.Name.InvariantEquals("sortOrder") && x.EnableSorting); - if (useLuceneSort) - { - //we have a sortOrder field declared to be sorted, so we'll use Examine - results = searchProvider.Search( - filter.And().OrderBy(new SortableField("sortOrder", SortType.Int)).Compile()); - } - else - { - results = searchProvider.Search(filter.Compile()); - } - - if (results.Any()) - { - // var medias = results.Select(ConvertFromSearchResult); - var medias = results.Select(x => - { - int nid; - if (int.TryParse(x["__NodeId"], out nid) == false && int.TryParse(x["NodeId"], out nid) == false) - throw new Exception("Failed to extract NodeId from search result."); - var cacheValues = GetCacheValues(nid, id => ConvertFromSearchResult(x)); - return CreateFromCacheValues(cacheValues); - }); - - return useLuceneSort ? medias : medias.OrderBy(x => x.SortOrder); - } - else - { - //if there's no result then return null. Previously we defaulted back to library.GetMedia below - //but this will always get called for when we are getting descendents since many items won't have - //children and then we are hitting the database again! - //So instead we're going to rely on Examine to have the correct results like it should. - return Enumerable.Empty(); - } - } - catch (FileNotFoundException) - { - //Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco - //See this thread: http://examine.cdodeplex.com/discussions/264341 - //Catch the exception here for the time being, and just fallback to GetMedia - } - } - - //falling back to get media - - var media = library.GetMedia(parentId, true); - if (media != null && media.Current != null) - { - xpath = media.Current; - } - else - { - return Enumerable.Empty(); - } - } - - var mediaList = new List(); - - // this is so bad, really - var item = xpath.Select("//*[@id='" + parentId + "']"); - if (item.Current == null) - return Enumerable.Empty(); - var items = item.Current.SelectChildren(XPathNodeType.Element); - - // and this does not work, because... meh - //var q = "//* [@id='" + parentId + "']/* [@id]"; - //var items = xpath.Select(q); - - foreach (XPathNavigator itemm in items) - { - int id; - if (int.TryParse(itemm.GetAttribute("id", ""), out id) == false) - continue; // wtf? - var captured = itemm; - var cacheValues = GetCacheValues(id, idd => ConvertFromXPathNavigator(captured)); - mediaList.Add(CreateFromCacheValues(cacheValues)); - } - - ////The xpath might be the whole xpath including the current ones ancestors so we need to select the current node - //var item = xpath.Select("//*[@id='" + parentId + "']"); - //if (item.Current == null) - //{ - // return Enumerable.Empty(); - //} - //var children = item.Current.SelectChildren(XPathNodeType.Element); - - //foreach(XPathNavigator x in children) - //{ - // //NOTE: I'm not sure why this is here, it is from legacy code of ExamineBackedMedia, but - // // will leave it here as it must have done something! - // if (x.Name != "contents") - // { - // //make sure it's actually a node, not a property - // if (!string.IsNullOrEmpty(x.GetAttribute("path", "")) && - // !string.IsNullOrEmpty(x.GetAttribute("id", ""))) - // { - // mediaList.Add(ConvertFromXPathNavigator(x)); - // } - // } - //} - - return mediaList; - } - - /// - /// An IPublishedContent that is represented all by a dictionary. - /// - /// - /// This is a helper class and definitely not intended for public use, it expects that all of the values required - /// to create an IPublishedContent exist in the dictionary by specific aliases. - /// - internal class DictionaryPublishedContent : PublishedContentWithKeyBase - { - // note: I'm not sure this class fully complies with IPublishedContent rules especially - // I'm not sure that _properties contains all properties including those without a value, - // neither that GetProperty will return a property without a value vs. null... @zpqrtbnk - - // List of properties that will appear in the XML and do not match - // anything in the ContentType, so they must be ignored. - private static readonly string[] IgnoredKeys = { "version", "isDoc" }; - - public DictionaryPublishedContent( - IDictionary valueDictionary, - Func getParent, - Func> getChildren, - Func getProperty, - XPathNavigator nav, - bool fromExamine) - { - if (valueDictionary == null) throw new ArgumentNullException("valueDictionary"); - if (getParent == null) throw new ArgumentNullException("getParent"); - if (getProperty == null) throw new ArgumentNullException("getProperty"); - - _getParent = new Lazy(() => getParent(ParentId)); - _getChildren = new Lazy>(() => getChildren(Id, nav)); - _getProperty = getProperty; - - LoadedFromExamine = fromExamine; - - ValidateAndSetProperty(valueDictionary, val => _id = int.Parse(val), "id", "nodeId", "__NodeId"); //should validate the int! - ValidateAndSetProperty(valueDictionary, val => _key = Guid.Parse(val), "key"); - // wtf are we dealing with templates for medias?! - ValidateAndSetProperty(valueDictionary, val => _templateId = int.Parse(val), "template", "templateId"); - ValidateAndSetProperty(valueDictionary, val => _sortOrder = int.Parse(val), "sortOrder"); - ValidateAndSetProperty(valueDictionary, val => _name = val, "nodeName", "__nodeName"); - ValidateAndSetProperty(valueDictionary, val => _urlName = val, "urlName"); - ValidateAndSetProperty(valueDictionary, val => _documentTypeAlias = val, "nodeTypeAlias", UmbracoContentIndexer.NodeTypeAliasFieldName); - ValidateAndSetProperty(valueDictionary, val => _documentTypeId = int.Parse(val), "nodeType"); - ValidateAndSetProperty(valueDictionary, val => _writerName = val, "writerName"); - ValidateAndSetProperty(valueDictionary, val => _creatorName = val, "creatorName", "writerName"); //this is a bit of a hack fix for: U4-1132 - ValidateAndSetProperty(valueDictionary, val => _writerId = int.Parse(val), "writerID"); - ValidateAndSetProperty(valueDictionary, val => _creatorId = int.Parse(val), "creatorID", "writerID"); //this is a bit of a hack fix for: U4-1132 - ValidateAndSetProperty(valueDictionary, val => _path = val, "path", "__Path"); - ValidateAndSetProperty(valueDictionary, val => _createDate = ParseDateTimeValue(val), "createDate"); - ValidateAndSetProperty(valueDictionary, val => _updateDate = ParseDateTimeValue(val), "updateDate"); - ValidateAndSetProperty(valueDictionary, val => _level = int.Parse(val), "level"); - ValidateAndSetProperty(valueDictionary, val => - { - int pId; - ParentId = -1; - if (int.TryParse(val, out pId)) - { - ParentId = pId; - } - }, "parentID"); - - _contentType = PublishedContentType.Get(PublishedItemType.Media, _documentTypeAlias); - _properties = new Collection(); - - //handle content type properties - //make sure we create them even if there's no value - foreach (var propertyType in _contentType.PropertyTypes) - { - var alias = propertyType.PropertyTypeAlias; - _keysAdded.Add(alias); - string value; - const bool isPreviewing = false; // false :: never preview a media - var property = valueDictionary.TryGetValue(alias, out value) == false || value == null - ? new XmlPublishedProperty(propertyType, isPreviewing) - : new XmlPublishedProperty(propertyType, isPreviewing, value); - _properties.Add(property); - } - - //loop through remaining values that haven't been applied - foreach (var i in valueDictionary.Where(x => - _keysAdded.Contains(x.Key) == false // not already processed - && IgnoredKeys.Contains(x.Key) == false)) // not ignorable - { - if (i.Key.InvariantStartsWith("__")) - { - // no type for that one, dunno how to convert - IPublishedProperty property = new PropertyResult(i.Key, i.Value, PropertyResultType.CustomProperty); - _properties.Add(property); - } - else - { - // this is a property that does not correspond to anything, ignore and log - LogHelper.Warn("Dropping property \"" + i.Key + "\" because it does not belong to the content type."); - } - } - } - - private DateTime ParseDateTimeValue(string val) - { - if (LoadedFromExamine) - { - try - { - //we might need to parse the date time using Lucene converters - return DateTools.StringToDate(val); - } - catch (FormatException) - { - //swallow exception, its not formatted correctly so revert to just trying to parse - } - } - - return DateTime.Parse(val); - } - - /// - /// Flag to get/set if this was laoded from examine cache - /// - internal bool LoadedFromExamine { get; private set; } - - //private readonly Func _getParent; - private readonly Lazy _getParent; - //private readonly Func> _getChildren; - private readonly Lazy> _getChildren; - private readonly Func _getProperty; - - /// - /// Returns 'Media' as the item type - /// - public override PublishedItemType ItemType - { - get { return PublishedItemType.Media; } - } - - public override IPublishedContent Parent - { - get { return _getParent.Value; } - } - - public int ParentId { get; private set; } - public override int Id - { - get { return _id; } - } - - public override Guid Key { get { return _key; } } - - public override int TemplateId - { - get - { - //TODO: should probably throw a not supported exception since media doesn't actually support this. - return _templateId; - } - } - - public override int SortOrder - { - get { return _sortOrder; } - } - - public override string Name - { - get { return _name; } - } - - public override string UrlName - { - get { return _urlName; } - } - - public override string DocumentTypeAlias - { - get { return _documentTypeAlias; } - } - - public override int DocumentTypeId - { - get { return _documentTypeId; } - } - - public override string WriterName - { - get { return _writerName; } - } - - public override string CreatorName - { - get { return _creatorName; } - } - - public override int WriterId - { - get { return _writerId; } - } - - public override int CreatorId - { - get { return _creatorId; } - } - - public override string Path - { - get { return _path; } - } - - public override DateTime CreateDate - { - get { return _createDate; } - } - - public override DateTime UpdateDate - { - get { return _updateDate; } - } - - public override Guid Version - { - get { return _version; } - } - - public override int Level - { - get { return _level; } - } - - public override bool IsDraft - { - get { return false; } - } - - public override ICollection Properties - { - get { return _properties; } - } - - public override IEnumerable Children - { - get { return _getChildren.Value; } - } - - public override IPublishedProperty GetProperty(string alias) - { - return _getProperty(this, alias); - } - - public override PublishedContentType ContentType - { - get { return _contentType; } - } - - // override to implement cache - // cache at context level, ie once for the whole request - // but cache is not shared by requests because we wouldn't know how to clear it - public override IPublishedProperty GetProperty(string alias, bool recurse) - { - if (recurse == false) return GetProperty(alias); - - IPublishedProperty property; - string key = null; - var cache = UmbracoContextCache.Current; - - if (cache != null) - { - key = string.Format("RECURSIVE_PROPERTY::{0}::{1}", Id, alias.ToLowerInvariant()); - object o; - if (cache.TryGetValue(key, out o)) - { - property = o as IPublishedProperty; - if (property == null) - throw new InvalidOperationException("Corrupted cache."); - return property; - } - } - - // else get it for real, no cache - property = base.GetProperty(alias, true); - - if (cache != null) - cache[key] = property; - - return property; - } - - private readonly List _keysAdded = new List(); - private int _id; - private Guid _key; - private int _templateId; - private int _sortOrder; - private string _name; - private string _urlName; - private string _documentTypeAlias; - private int _documentTypeId; - private string _writerName; - private string _creatorName; - private int _writerId; - private int _creatorId; - private string _path; - private DateTime _createDate; - private DateTime _updateDate; - private Guid _version; - private int _level; - private readonly ICollection _properties; - private readonly PublishedContentType _contentType; - - private void ValidateAndSetProperty(IDictionary valueDictionary, Action setProperty, params string[] potentialKeys) - { - var key = potentialKeys.FirstOrDefault(x => valueDictionary.ContainsKey(x) && valueDictionary[x] != null); - if (key == null) - { - throw new FormatException("The valueDictionary is not formatted correctly and is missing any of the '" + string.Join(",", potentialKeys) + "' elements"); - } - - setProperty(valueDictionary[key]); - _keysAdded.Add(key); - } - } - - // REFACTORING - - // caching the basic atomic values - and the parent id - // but NOT caching actual parent nor children and NOT even - // the list of children ids - BUT caching the path - - internal class CacheValues - { - public IDictionary Values { get; set; } - public XPathNavigator XPath { get; set; } - public bool FromExamine { get; set; } - } - - public const string PublishedMediaCacheKey = "MediaCacheMeh."; - private const int PublishedMediaCacheTimespanSeconds = 4 * 60; // 4 mins - private static TimeSpan _publishedMediaCacheTimespan; - private static bool _publishedMediaCacheEnabled; - - private static void InitializeCacheConfig() - { - var value = ConfigurationManager.AppSettings["Umbraco.PublishedMediaCache.Seconds"]; - int seconds; - if (int.TryParse(value, out seconds) == false) - seconds = PublishedMediaCacheTimespanSeconds; - if (seconds > 0) - { - _publishedMediaCacheEnabled = true; - _publishedMediaCacheTimespan = TimeSpan.FromSeconds(seconds); - } - else - { - _publishedMediaCacheEnabled = false; - } - } - - internal IPublishedContent CreateFromCacheValues(CacheValues cacheValues) - { - var content = new DictionaryPublishedContent( - cacheValues.Values, - parentId => parentId < 0 ? null : GetUmbracoMedia(parentId), - GetChildrenMedia, - GetProperty, - cacheValues.XPath, // though, outside of tests, that should be null - cacheValues.FromExamine - ); - return content.CreateModel(); - } - - private static CacheValues GetCacheValues(int id, Func func) - { - if (_publishedMediaCacheEnabled == false) - return func(id); - - var cache = ApplicationContext.Current.ApplicationCache.RuntimeCache; - var key = PublishedMediaCacheKey + id; - return (CacheValues)cache.GetCacheItem(key, () => func(id), _publishedMediaCacheTimespan); - } - - internal static void ClearCache(int id) - { - var cache = ApplicationContext.Current.ApplicationCache.RuntimeCache; - var sid = id.ToString(); - var key = PublishedMediaCacheKey + sid; - - // we do clear a lot of things... but the cache refresher is somewhat - // convoluted and it's hard to tell what to clear exactly ;-( - - // clear the parent - NOT (why?) - //var exist = (CacheValues) cache.GetCacheItem(key); - //if (exist != null) - // cache.ClearCacheItem(PublishedMediaCacheKey + GetValuesValue(exist.Values, "parentID")); - - // clear the item - cache.ClearCacheItem(key); - - // clear all children - in case we moved and their path has changed - var fid = "/" + sid + "/"; - cache.ClearCacheObjectTypes((k, v) => - GetValuesValue(v.Values, "path", "__Path").Contains(fid)); - } - - private static string GetValuesValue(IDictionary d, params string[] keys) - { - string value = null; - var ignored = keys.Any(x => d.TryGetValue(x, out value)); - return value ?? ""; - } - } -} + + public virtual IPublishedContent GetSingleByXPath(UmbracoContext umbracoContext, bool preview, string xpath, XPathVariable[] vars) + { + throw new NotImplementedException("PublishedMediaCache does not support XPath."); + } + + public virtual IPublishedContent GetSingleByXPath(UmbracoContext umbracoContext, bool preview, XPathExpression xpath, XPathVariable[] vars) + { + throw new NotImplementedException("PublishedMediaCache does not support XPath."); + } + + public virtual IEnumerable GetByXPath(UmbracoContext umbracoContext, bool preview, string xpath, XPathVariable[] vars) + { + throw new NotImplementedException("PublishedMediaCache does not support XPath."); + } + + public virtual IEnumerable GetByXPath(UmbracoContext umbracoContext, bool preview, XPathExpression xpath, XPathVariable[] vars) + { + throw new NotImplementedException("PublishedMediaCache does not support XPath."); + } + + public virtual XPathNavigator GetXPathNavigator(UmbracoContext umbracoContext, bool preview) + { + throw new NotImplementedException("PublishedMediaCache does not support XPath."); + } + + public bool XPathNavigatorIsNavigable { get { return false; } } + + public virtual bool HasContent(UmbracoContext context, bool preview) { throw new NotImplementedException(); } + + private ExamineManager GetExamineManagerSafe() + { + try + { + return ExamineManager.Instance; + } + catch (TypeInitializationException) + { + return null; + } + } + + private BaseIndexProvider GetIndexProviderSafe() + { + if (_indexProvider != null) + return _indexProvider; + + var eMgr = GetExamineManagerSafe(); + if (eMgr != null) + { + try + { + //by default use the InternalSearcher + var indexer = eMgr.IndexProviderCollection[Constants.Examine.InternalIndexer]; + if (indexer.IndexerData.IncludeNodeTypes.Any() || indexer.IndexerData.ExcludeNodeTypes.Any()) + { + LogHelper.Warn("The InternalIndexer for examine is configured incorrectly, it should not list any include/exclude node types or field names, it should simply be configured as: " + ""); + } + return indexer; + } + catch (Exception ex) + { + LogHelper.Error("Could not retrieve the InternalIndexer", ex); + //something didn't work, continue returning null. + } + } + return null; + } + + private BaseSearchProvider GetSearchProviderSafe() + { + if (_searchProvider != null) + return _searchProvider; + + var eMgr = GetExamineManagerSafe(); + if (eMgr != null) + { + try + { + //by default use the InternalSearcher + return eMgr.SearchProviderCollection[Constants.Examine.InternalSearcher]; + } + catch (FileNotFoundException) + { + //Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco + //See this thread: http://examine.cdodeplex.com/discussions/264341 + //Catch the exception here for the time being, and just fallback to GetMedia + //TODO: Need to fix examine in LB scenarios! + } + catch (NullReferenceException) + { + //This will occur when the search provider cannot be initialized. In newer examine versions the initialization is lazy and therefore + // the manager will return the singleton without throwing initialization errors, however if examine isn't configured correctly a null + // reference error will occur because the examine settings are null. + } + catch (AlreadyClosedException) + { + //If the app domain is shutting down and the site is under heavy load the index reader will be closed and it really cannot + //be re-opened since the app domain is shutting down. In this case we have no option but to try to load the data from the db. + } + } + return null; + } + + private IPublishedContent GetUmbracoMedia(int id) + { + // this recreates an IPublishedContent and model each time + // it is called, but at least it should NOT hit the database + // nor Lucene each time, relying on the memory cache instead + + if (id <= 0) return null; // fail fast + + var cacheValues = GetCacheValues(id, GetUmbracoMediaCacheValues); + + return cacheValues == null ? null : CreateFromCacheValues(cacheValues); + } + + private CacheValues GetUmbracoMediaCacheValues(int id) + { + var searchProvider = GetSearchProviderSafe(); + + if (searchProvider != null) + { + try + { + // first check in Examine as this is WAY faster + // + // the filter will create a query like this: + // +(+__NodeId:3113 -__Path:-1,-21,*) +__IndexType:media + // + // note that since the use of the wildcard, it automatically escapes it in Lucene. + + var criteria = searchProvider.CreateSearchCriteria("media"); + var filter = criteria.Id(id).Not().Field(UmbracoContentIndexer.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()); + + var result = searchProvider.Search(filter.Compile()).FirstOrDefault(); + if (result != null) return ConvertFromSearchResult(result); + } + catch (Exception ex) + { + if (ex is FileNotFoundException) + { + //Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco + //See this thread: http://examine.cdodeplex.com/discussions/264341 + //Catch the exception here for the time being, and just fallback to GetMedia + //TODO: Need to fix examine in LB scenarios! + LogHelper.Error("Could not load data from Examine index for media", ex); + } + else if (ex is AlreadyClosedException) + { + //If the app domain is shutting down and the site is under heavy load the index reader will be closed and it really cannot + //be re-opened since the app domain is shutting down. In this case we have no option but to try to load the data from the db. + LogHelper.Error("Could not load data from Examine index for media, the app domain is most likely in a shutdown state", ex); + } + else throw; + } + } + + // don't log a warning here, as it can flood the log in case of eg a media picker referencing a media + // that has been deleted, hence is not in the Examine index anymore (for a good reason). try to get + // the media from the service, first + + var media = ApplicationContext.Current.Services.MediaService.GetById(id); + if (media == null || media.Trashed) return null; // not found, ok + + // so, the media was not found in Examine's index *yet* it exists, which probably indicates that + // the index is corrupted. Or not up-to-date. Log a warning, but only once, and only if seeing the + // error more that a number of times. + + var miss = Interlocked.CompareExchange(ref _examineIndexMiss, 0, 0); // volatile read + if (miss < ExamineIndexMissMax && Interlocked.Increment(ref _examineIndexMiss) == ExamineIndexMissMax) + LogHelper.Warn("Failed ({0} times) to retrieve medias from Examine index and had to load" + + " them from DB. This may indicate that the Examine index is corrupted.", + () => ExamineIndexMissMax); + + return ConvertFromIMedia(media); + } + + private const int ExamineIndexMissMax = 10; + private int _examineIndexMiss; + + internal CacheValues ConvertFromXPathNodeIterator(XPathNodeIterator media, int id) + { + if (media != null && media.Current != null) + { + return media.Current.Name.InvariantEquals("error") + ? null + : ConvertFromXPathNavigator(media.Current); + } + + LogHelper.Warn( + "Could not retrieve media {0} from Examine index or from legacy library.GetMedia method", + () => id); + + return null; + } + + internal CacheValues ConvertFromSearchResult(SearchResult searchResult) + { + //NOTE: Some fields will not be included if the config section for the internal index has been + //mucked around with. It should index everything and so the index definition should simply be: + // + + + var values = new Dictionary(searchResult.Fields); + //we need to ensure some fields exist, because of the above issue + if (!new[] { "template", "templateId" }.Any(values.ContainsKey)) + values.Add("template", 0.ToString()); + if (!new[] { "sortOrder" }.Any(values.ContainsKey)) + values.Add("sortOrder", 0.ToString()); + if (!new[] { "urlName" }.Any(values.ContainsKey)) + values.Add("urlName", ""); + if (!new[] { "nodeType" }.Any(values.ContainsKey)) + values.Add("nodeType", 0.ToString()); + if (!new[] { "creatorName" }.Any(values.ContainsKey)) + values.Add("creatorName", ""); + if (!new[] { "writerID" }.Any(values.ContainsKey)) + values.Add("writerID", 0.ToString()); + if (!new[] { "creatorID" }.Any(values.ContainsKey)) + values.Add("creatorID", 0.ToString()); + if (!new[] { "createDate" }.Any(values.ContainsKey)) + values.Add("createDate", default(DateTime).ToString("yyyy-MM-dd HH:mm:ss")); + if (!new[] { "level" }.Any(values.ContainsKey)) + { + values.Add("level", values["__Path"].Split(',').Length.ToString()); + } + + // because, migration + if (values.ContainsKey("key") == false) + values["key"] = Guid.Empty.ToString(); + + return new CacheValues + { + Values = values, + FromExamine = true + }; + + //var content = new DictionaryPublishedContent(values, + // d => d.ParentId != -1 //parent should be null if -1 + // ? GetUmbracoMedia(d.ParentId) + // : null, + // //callback to return the children of the current node + // d => GetChildrenMedia(d.Id), + // GetProperty, + // true); + //return content.CreateModel(); + } + + internal CacheValues ConvertFromXPathNavigator(XPathNavigator xpath, bool forceNav = false) + { + if (xpath == null) throw new ArgumentNullException("xpath"); + + var values = new Dictionary { { "nodeName", xpath.GetAttribute("nodeName", "") } }; + if (!UmbracoConfig.For.UmbracoSettings().Content.UseLegacyXmlSchema) + { + values["nodeTypeAlias"] = xpath.Name; + } + + var result = xpath.SelectChildren(XPathNodeType.Element); + //add the attributes e.g. id, parentId etc + if (result.Current != null && result.Current.HasAttributes) + { + if (result.Current.MoveToFirstAttribute()) + { + //checking for duplicate keys because of the 'nodeTypeAlias' might already be added above. + if (!values.ContainsKey(result.Current.Name)) + { + values[result.Current.Name] = result.Current.Value; + } + while (result.Current.MoveToNextAttribute()) + { + if (!values.ContainsKey(result.Current.Name)) + { + values[result.Current.Name] = result.Current.Value; + } + } + result.Current.MoveToParent(); + } + } + // because, migration + if (values.ContainsKey("key") == false) + values["key"] = Guid.Empty.ToString(); + //add the user props + while (result.MoveNext()) + { + if (result.Current != null && !result.Current.HasAttributes) + { + string value = result.Current.Value; + if (string.IsNullOrEmpty(value)) + { + if (result.Current.HasAttributes || result.Current.SelectChildren(XPathNodeType.Element).Count > 0) + { + value = result.Current.OuterXml; + } + } + values[result.Current.Name] = value; + } + } + + return new CacheValues + { + Values = values, + XPath = forceNav ? xpath : null // outside of tests we do NOT want to cache the navigator! + }; + + //var content = new DictionaryPublishedContent(values, + // d => d.ParentId != -1 //parent should be null if -1 + // ? GetUmbracoMedia(d.ParentId) + // : null, + // //callback to return the children of the current node based on the xml structure already found + // d => GetChildrenMedia(d.Id, xpath), + // GetProperty, + // false); + //return content.CreateModel(); + } + + internal CacheValues ConvertFromIMedia(IMedia media) + { + var values = new Dictionary(); + + var creator = _applicationContext.Services.UserService.GetProfileById(media.CreatorId); + var creatorName = creator == null ? "" : creator.Name; + + values["id"] = media.Id.ToString(); + values["key"] = media.Key.ToString(); + values["parentID"] = media.ParentId.ToString(); + values["level"] = media.Level.ToString(); + values["creatorID"] = media.CreatorId.ToString(); + values["creatorName"] = creatorName; + values["writerID"] = media.CreatorId.ToString(); + values["writerName"] = creatorName; + values["template"] = "0"; + values["urlName"] = ""; + values["sortOrder"] = media.SortOrder.ToString(); + values["createDate"] = media.CreateDate.ToString("yyyy-MM-dd HH:mm:ss"); + values["updateDate"] = media.UpdateDate.ToString("yyyy-MM-dd HH:mm:ss"); + values["nodeName"] = media.Name; + values["path"] = media.Path; + values["nodeType"] = media.ContentType.Id.ToString(); + values["nodeTypeAlias"] = media.ContentType.Alias; + + // add the user props + foreach (var prop in media.Properties) + values[prop.Alias] = prop.Value == null ? null : prop.Value.ToString(); + + return new CacheValues + { + Values = values + }; + } + + /// + /// We will need to first check if the document was loaded by Examine, if so we'll need to check if this property exists + /// in the results, if it does not, then we'll have to revert to looking up in the db. + /// + /// + /// + /// + private IPublishedProperty GetProperty(DictionaryPublishedContent dd, string alias) + { + //lets check if the alias does not exist on the document. + //NOTE: Examine will not index empty values and we do not output empty XML Elements to the cache - either of these situations + // would mean that the property is missing from the collection whether we are getting the value from Examine or from the library media cache. + if (dd.Properties.All(x => x.PropertyTypeAlias.InvariantEquals(alias) == false)) + { + return null; + } + + if (dd.LoadedFromExamine) + { + //We are going to check for a special field however, that is because in some cases we store a 'Raw' + //value in the index such as for xml/html. + var rawValue = dd.Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(UmbracoContentIndexer.RawFieldPrefix + alias)); + return rawValue + ?? dd.Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(alias)); + } + + //if its not loaded from examine, then just return the property + return dd.Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(alias)); + } + + /// + /// A Helper methods to return the children for media whther it is based on examine or xml + /// + /// + /// + /// + private IEnumerable GetChildrenMedia(int parentId, XPathNavigator xpath = null) + { + + //if there is no navigator, try examine first, then re-look it up + if (xpath == null) + { + var searchProvider = GetSearchProviderSafe(); + + if (searchProvider != null) + { + try + { + //first check in Examine as this is WAY faster + var criteria = searchProvider.CreateSearchCriteria("media"); + + var filter = criteria.ParentId(parentId).Not().Field(UmbracoContentIndexer.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()); + //the above filter will create a query like this, NOTE: That since the use of the wildcard, it automatically escapes it in Lucene. + //+(+parentId:3113 -__Path:-1,-21,*) +__IndexType:media + + ISearchResults results; + + //we want to check if the indexer for this searcher has "sortOrder" flagged as sortable. + //if so, we'll use Lucene to do the sorting, if not we'll have to manually sort it (slower). + var indexer = GetIndexProviderSafe(); + var useLuceneSort = indexer != null && indexer.IndexerData.StandardFields.Any(x => x.Name.InvariantEquals("sortOrder") && x.EnableSorting); + if (useLuceneSort) + { + //we have a sortOrder field declared to be sorted, so we'll use Examine + results = searchProvider.Search( + filter.And().OrderBy(new SortableField("sortOrder", SortType.Int)).Compile()); + } + else + { + results = searchProvider.Search(filter.Compile()); + } + + if (results.Any()) + { + // var medias = results.Select(ConvertFromSearchResult); + var medias = results.Select(x => + { + int nid; + if (int.TryParse(x["__NodeId"], out nid) == false && int.TryParse(x["NodeId"], out nid) == false) + throw new Exception("Failed to extract NodeId from search result."); + var cacheValues = GetCacheValues(nid, id => ConvertFromSearchResult(x)); + return CreateFromCacheValues(cacheValues); + }); + + return useLuceneSort ? medias : medias.OrderBy(x => x.SortOrder); + } + else + { + //if there's no result then return null. Previously we defaulted back to library.GetMedia below + //but this will always get called for when we are getting descendents since many items won't have + //children and then we are hitting the database again! + //So instead we're going to rely on Examine to have the correct results like it should. + return Enumerable.Empty(); + } + } + catch (FileNotFoundException) + { + //Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco + //See this thread: http://examine.cdodeplex.com/discussions/264341 + //Catch the exception here for the time being, and just fallback to GetMedia + } + } + + //falling back to get media + + var media = library.GetMedia(parentId, true); + if (media != null && media.Current != null) + { + xpath = media.Current; + } + else + { + return Enumerable.Empty(); + } + } + + var mediaList = new List(); + + // this is so bad, really + var item = xpath.Select("//*[@id='" + parentId + "']"); + if (item.Current == null) + return Enumerable.Empty(); + var items = item.Current.SelectChildren(XPathNodeType.Element); + + // and this does not work, because... meh + //var q = "//* [@id='" + parentId + "']/* [@id]"; + //var items = xpath.Select(q); + + foreach (XPathNavigator itemm in items) + { + int id; + if (int.TryParse(itemm.GetAttribute("id", ""), out id) == false) + continue; // wtf? + var captured = itemm; + var cacheValues = GetCacheValues(id, idd => ConvertFromXPathNavigator(captured)); + mediaList.Add(CreateFromCacheValues(cacheValues)); + } + + ////The xpath might be the whole xpath including the current ones ancestors so we need to select the current node + //var item = xpath.Select("//*[@id='" + parentId + "']"); + //if (item.Current == null) + //{ + // return Enumerable.Empty(); + //} + //var children = item.Current.SelectChildren(XPathNodeType.Element); + + //foreach(XPathNavigator x in children) + //{ + // //NOTE: I'm not sure why this is here, it is from legacy code of ExamineBackedMedia, but + // // will leave it here as it must have done something! + // if (x.Name != "contents") + // { + // //make sure it's actually a node, not a property + // if (!string.IsNullOrEmpty(x.GetAttribute("path", "")) && + // !string.IsNullOrEmpty(x.GetAttribute("id", ""))) + // { + // mediaList.Add(ConvertFromXPathNavigator(x)); + // } + // } + //} + + return mediaList; + } + + /// + /// An IPublishedContent that is represented all by a dictionary. + /// + /// + /// This is a helper class and definitely not intended for public use, it expects that all of the values required + /// to create an IPublishedContent exist in the dictionary by specific aliases. + /// + internal class DictionaryPublishedContent : PublishedContentWithKeyBase + { + // note: I'm not sure this class fully complies with IPublishedContent rules especially + // I'm not sure that _properties contains all properties including those without a value, + // neither that GetProperty will return a property without a value vs. null... @zpqrtbnk + + // List of properties that will appear in the XML and do not match + // anything in the ContentType, so they must be ignored. + private static readonly string[] IgnoredKeys = { "version", "isDoc" }; + + public DictionaryPublishedContent( + IDictionary valueDictionary, + Func getParent, + Func> getChildren, + Func getProperty, + XPathNavigator nav, + bool fromExamine) + { + if (valueDictionary == null) throw new ArgumentNullException("valueDictionary"); + if (getParent == null) throw new ArgumentNullException("getParent"); + if (getProperty == null) throw new ArgumentNullException("getProperty"); + + _getParent = new Lazy(() => getParent(ParentId)); + _getChildren = new Lazy>(() => getChildren(Id, nav)); + _getProperty = getProperty; + + LoadedFromExamine = fromExamine; + + ValidateAndSetProperty(valueDictionary, val => _id = int.Parse(val), "id", "nodeId", "__NodeId"); //should validate the int! + ValidateAndSetProperty(valueDictionary, val => _key = Guid.Parse(val), "key"); + // wtf are we dealing with templates for medias?! + ValidateAndSetProperty(valueDictionary, val => _templateId = int.Parse(val), "template", "templateId"); + ValidateAndSetProperty(valueDictionary, val => _sortOrder = int.Parse(val), "sortOrder"); + ValidateAndSetProperty(valueDictionary, val => _name = val, "nodeName", "__nodeName"); + ValidateAndSetProperty(valueDictionary, val => _urlName = val, "urlName"); + ValidateAndSetProperty(valueDictionary, val => _documentTypeAlias = val, "nodeTypeAlias", UmbracoContentIndexer.NodeTypeAliasFieldName); + ValidateAndSetProperty(valueDictionary, val => _documentTypeId = int.Parse(val), "nodeType"); + ValidateAndSetProperty(valueDictionary, val => _writerName = val, "writerName"); + ValidateAndSetProperty(valueDictionary, val => _creatorName = val, "creatorName", "writerName"); //this is a bit of a hack fix for: U4-1132 + ValidateAndSetProperty(valueDictionary, val => _writerId = int.Parse(val), "writerID"); + ValidateAndSetProperty(valueDictionary, val => _creatorId = int.Parse(val), "creatorID", "writerID"); //this is a bit of a hack fix for: U4-1132 + ValidateAndSetProperty(valueDictionary, val => _path = val, "path", "__Path"); + ValidateAndSetProperty(valueDictionary, val => _createDate = ParseDateTimeValue(val), "createDate"); + ValidateAndSetProperty(valueDictionary, val => _updateDate = ParseDateTimeValue(val), "updateDate"); + ValidateAndSetProperty(valueDictionary, val => _level = int.Parse(val), "level"); + ValidateAndSetProperty(valueDictionary, val => + { + int pId; + ParentId = -1; + if (int.TryParse(val, out pId)) + { + ParentId = pId; + } + }, "parentID"); + + _contentType = PublishedContentType.Get(PublishedItemType.Media, _documentTypeAlias); + _properties = new Collection(); + + //handle content type properties + //make sure we create them even if there's no value + foreach (var propertyType in _contentType.PropertyTypes) + { + var alias = propertyType.PropertyTypeAlias; + _keysAdded.Add(alias); + string value; + const bool isPreviewing = false; // false :: never preview a media + var property = valueDictionary.TryGetValue(alias, out value) == false || value == null + ? new XmlPublishedProperty(propertyType, isPreviewing) + : new XmlPublishedProperty(propertyType, isPreviewing, value); + _properties.Add(property); + } + + //loop through remaining values that haven't been applied + foreach (var i in valueDictionary.Where(x => + _keysAdded.Contains(x.Key) == false // not already processed + && IgnoredKeys.Contains(x.Key) == false)) // not ignorable + { + if (i.Key.InvariantStartsWith("__")) + { + // no type for that one, dunno how to convert + IPublishedProperty property = new PropertyResult(i.Key, i.Value, PropertyResultType.CustomProperty); + _properties.Add(property); + } + else + { + // this is a property that does not correspond to anything, ignore and log + LogHelper.Warn("Dropping property \"" + i.Key + "\" because it does not belong to the content type."); + } + } + } + + private DateTime ParseDateTimeValue(string val) + { + if (LoadedFromExamine) + { + try + { + //we might need to parse the date time using Lucene converters + return DateTools.StringToDate(val); + } + catch (FormatException) + { + //swallow exception, its not formatted correctly so revert to just trying to parse + } + } + + return DateTime.Parse(val); + } + + /// + /// Flag to get/set if this was laoded from examine cache + /// + internal bool LoadedFromExamine { get; private set; } + + //private readonly Func _getParent; + private readonly Lazy _getParent; + //private readonly Func> _getChildren; + private readonly Lazy> _getChildren; + private readonly Func _getProperty; + + /// + /// Returns 'Media' as the item type + /// + public override PublishedItemType ItemType + { + get { return PublishedItemType.Media; } + } + + public override IPublishedContent Parent + { + get { return _getParent.Value; } + } + + public int ParentId { get; private set; } + public override int Id + { + get { return _id; } + } + + public override Guid Key { get { return _key; } } + + public override int TemplateId + { + get + { + //TODO: should probably throw a not supported exception since media doesn't actually support this. + return _templateId; + } + } + + public override int SortOrder + { + get { return _sortOrder; } + } + + public override string Name + { + get { return _name; } + } + + public override string UrlName + { + get { return _urlName; } + } + + public override string DocumentTypeAlias + { + get { return _documentTypeAlias; } + } + + public override int DocumentTypeId + { + get { return _documentTypeId; } + } + + public override string WriterName + { + get { return _writerName; } + } + + public override string CreatorName + { + get { return _creatorName; } + } + + public override int WriterId + { + get { return _writerId; } + } + + public override int CreatorId + { + get { return _creatorId; } + } + + public override string Path + { + get { return _path; } + } + + public override DateTime CreateDate + { + get { return _createDate; } + } + + public override DateTime UpdateDate + { + get { return _updateDate; } + } + + public override Guid Version + { + get { return _version; } + } + + public override int Level + { + get { return _level; } + } + + public override bool IsDraft + { + get { return false; } + } + + public override ICollection Properties + { + get { return _properties; } + } + + public override IEnumerable Children + { + get { return _getChildren.Value; } + } + + public override IPublishedProperty GetProperty(string alias) + { + return _getProperty(this, alias); + } + + public override PublishedContentType ContentType + { + get { return _contentType; } + } + + // override to implement cache + // cache at context level, ie once for the whole request + // but cache is not shared by requests because we wouldn't know how to clear it + public override IPublishedProperty GetProperty(string alias, bool recurse) + { + if (recurse == false) return GetProperty(alias); + + IPublishedProperty property; + string key = null; + var cache = UmbracoContextCache.Current; + + if (cache != null) + { + key = string.Format("RECURSIVE_PROPERTY::{0}::{1}", Id, alias.ToLowerInvariant()); + object o; + if (cache.TryGetValue(key, out o)) + { + property = o as IPublishedProperty; + if (property == null) + throw new InvalidOperationException("Corrupted cache."); + return property; + } + } + + // else get it for real, no cache + property = base.GetProperty(alias, true); + + if (cache != null) + cache[key] = property; + + return property; + } + + private readonly List _keysAdded = new List(); + private int _id; + private Guid _key; + private int _templateId; + private int _sortOrder; + private string _name; + private string _urlName; + private string _documentTypeAlias; + private int _documentTypeId; + private string _writerName; + private string _creatorName; + private int _writerId; + private int _creatorId; + private string _path; + private DateTime _createDate; + private DateTime _updateDate; + private Guid _version; + private int _level; + private readonly ICollection _properties; + private readonly PublishedContentType _contentType; + + private void ValidateAndSetProperty(IDictionary valueDictionary, Action setProperty, params string[] potentialKeys) + { + var key = potentialKeys.FirstOrDefault(x => valueDictionary.ContainsKey(x) && valueDictionary[x] != null); + if (key == null) + { + throw new FormatException("The valueDictionary is not formatted correctly and is missing any of the '" + string.Join(",", potentialKeys) + "' elements"); + } + + setProperty(valueDictionary[key]); + _keysAdded.Add(key); + } + } + + // REFACTORING + + // caching the basic atomic values - and the parent id + // but NOT caching actual parent nor children and NOT even + // the list of children ids - BUT caching the path + + internal class CacheValues + { + public IDictionary Values { get; set; } + public XPathNavigator XPath { get; set; } + public bool FromExamine { get; set; } + } + + public const string PublishedMediaCacheKey = "MediaCacheMeh."; + private const int PublishedMediaCacheTimespanSeconds = 4 * 60; // 4 mins + private static TimeSpan _publishedMediaCacheTimespan; + private static bool _publishedMediaCacheEnabled; + + private static void InitializeCacheConfig() + { + var value = ConfigurationManager.AppSettings["Umbraco.PublishedMediaCache.Seconds"]; + int seconds; + if (int.TryParse(value, out seconds) == false) + seconds = PublishedMediaCacheTimespanSeconds; + if (seconds > 0) + { + _publishedMediaCacheEnabled = true; + _publishedMediaCacheTimespan = TimeSpan.FromSeconds(seconds); + } + else + { + _publishedMediaCacheEnabled = false; + } + } + + internal IPublishedContent CreateFromCacheValues(CacheValues cacheValues) + { + var content = new DictionaryPublishedContent( + cacheValues.Values, + parentId => parentId < 0 ? null : GetUmbracoMedia(parentId), + GetChildrenMedia, + GetProperty, + cacheValues.XPath, // though, outside of tests, that should be null + cacheValues.FromExamine + ); + return content.CreateModel(); + } + + private static CacheValues GetCacheValues(int id, Func func) + { + if (_publishedMediaCacheEnabled == false) + return func(id); + + var cache = ApplicationContext.Current.ApplicationCache.RuntimeCache; + var key = PublishedMediaCacheKey + id; + return (CacheValues)cache.GetCacheItem(key, () => func(id), _publishedMediaCacheTimespan); + } + + internal static void ClearCache(int id) + { + var cache = ApplicationContext.Current.ApplicationCache.RuntimeCache; + var sid = id.ToString(); + var key = PublishedMediaCacheKey + sid; + + // we do clear a lot of things... but the cache refresher is somewhat + // convoluted and it's hard to tell what to clear exactly ;-( + + // clear the parent - NOT (why?) + //var exist = (CacheValues) cache.GetCacheItem(key); + //if (exist != null) + // cache.ClearCacheItem(PublishedMediaCacheKey + GetValuesValue(exist.Values, "parentID")); + + // clear the item + cache.ClearCacheItem(key); + + // clear all children - in case we moved and their path has changed + var fid = "/" + sid + "/"; + cache.ClearCacheObjectTypes((k, v) => + GetValuesValue(v.Values, "path", "__Path").Contains(fid)); + } + + private static string GetValuesValue(IDictionary d, params string[] keys) + { + string value = null; + var ignored = keys.Any(x => d.TryGetValue(x, out value)); + return value ?? ""; + } + } +} diff --git a/src/Umbraco.Web/Search/ExamineEvents.cs b/src/Umbraco.Web/Search/ExamineEvents.cs index cf4d6e33d4..0c8eda7ecb 100644 --- a/src/Umbraco.Web/Search/ExamineEvents.cs +++ b/src/Umbraco.Web/Search/ExamineEvents.cs @@ -1,729 +1,736 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Xml; -using System.Xml.Linq; -using Examine; -using Examine.LuceneEngine; -using Lucene.Net.Documents; -using Umbraco.Core; -using Umbraco.Core.Cache; -using Umbraco.Core.Logging; -using Umbraco.Core.Models; -using Umbraco.Core.Scoping; -using Umbraco.Core.Sync; -using Umbraco.Web.Cache; -using UmbracoExamine; -using Content = umbraco.cms.businesslogic.Content; -using Document = umbraco.cms.businesslogic.web.Document; - -namespace Umbraco.Web.Search -{ - /// - /// Used to wire up events for Examine - /// - public sealed class ExamineEvents : ApplicationEventHandler - { - // 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; - - /// - /// Once the application has started we should bind to all events and initialize the providers. - /// - /// - /// - /// - /// We need to do this on the Started event as to guarantee that all resolvers are setup properly. - /// - protected override void ApplicationStarted(UmbracoApplicationBase httpApplication, ApplicationContext applicationContext) - { - LogHelper.Info("Initializing Examine and binding to business logic events"); - - var registeredProviders = ExamineManager.Instance.IndexProviderCollection - .OfType().Count(x => x.EnableDefaultEventHandler); - - LogHelper.Info("Adding examine event handlers for index providers: {0}", () => registeredProviders); - - //don't bind event handlers if we're not suppose to listen - if (registeredProviders == 0) - return; - - //Bind to distributed cache events - this ensures that this logic occurs on ALL servers that are taking part - // in a load balanced environment. - CacheRefresherBase.CacheUpdated += UnpublishedPageCacheRefresherCacheUpdated; - CacheRefresherBase.CacheUpdated += PublishedPageCacheRefresherCacheUpdated; - CacheRefresherBase.CacheUpdated += MediaCacheRefresherCacheUpdated; - CacheRefresherBase.CacheUpdated += MemberCacheRefresherCacheUpdated; - CacheRefresherBase.CacheUpdated += ContentTypeCacheRefresherCacheUpdated; - - var contentIndexer = ExamineManager.Instance.IndexProviderCollection[Constants.Examine.InternalIndexer] as UmbracoContentIndexer; - if (contentIndexer != null) - { - contentIndexer.DocumentWriting += IndexerDocumentWriting; - } - var memberIndexer = ExamineManager.Instance.IndexProviderCollection[Constants.Examine.InternalMemberIndexer] as UmbracoMemberIndexer; - if (memberIndexer != null) - { - memberIndexer.DocumentWriting += IndexerDocumentWriting; - } - } - - /// - /// This is used to refresh content indexers IndexData based on the DataService whenever a content type is changed since - /// properties may have been added/removed, then we need to re-index any required data if aliases have been changed - /// - /// - /// - /// - /// See: http://issues.umbraco.org/issue/U4-4798, http://issues.umbraco.org/issue/U4-7833 - /// - static void ContentTypeCacheRefresherCacheUpdated(ContentTypeCacheRefresher sender, CacheRefresherEventArgs e) - { - if (Suspendable.ExamineEvents.CanIndex == false) - return; - - var indexersToUpdated = ExamineManager.Instance.IndexProviderCollection.OfType(); - foreach (var provider in indexersToUpdated) - { - provider.RefreshIndexerDataFromDataService(); - } - - if (e.MessageType == MessageType.RefreshByJson) - { - var contentTypesChanged = new HashSet(); - var mediaTypesChanged = new HashSet(); - var memberTypesChanged = new HashSet(); - - var payloads = ContentTypeCacheRefresher.DeserializeFromJsonPayload(e.MessageObject.ToString()); - foreach (var payload in payloads) - { - if (payload.IsNew == false - && (payload.WasDeleted || payload.AliasChanged || payload.PropertyRemoved || payload.PropertyTypeAliasChanged)) - { - //if we get here it means that some aliases have changed and the indexes for those particular doc types will need to be updated - if (payload.Type == typeof(IContentType).Name) - { - //if it is content - contentTypesChanged.Add(payload.Alias); - } - else if (payload.Type == typeof(IMediaType).Name) - { - //if it is media - mediaTypesChanged.Add(payload.Alias); - } - else if (payload.Type == typeof(IMemberType).Name) - { - //if it is members - memberTypesChanged.Add(payload.Alias); - } - } - } - - //TODO: We need to update Examine to support re-indexing multiple items at once instead of one by one which will speed up - // the re-indexing process, we don't want to revert to rebuilding the whole thing! - - if (contentTypesChanged.Count > 0) - { - foreach (var alias in contentTypesChanged) - { - var ctType = ApplicationContext.Current.Services.ContentTypeService.GetContentType(alias); - if (ctType != null) - { - var contentItems = ApplicationContext.Current.Services.ContentService.GetContentOfContentType(ctType.Id); - foreach (var contentItem in contentItems) - { - ReIndexForContent(contentItem, contentItem.HasPublishedVersion && contentItem.Trashed == false); - } - } - } - } - if (mediaTypesChanged.Count > 0) - { - foreach (var alias in mediaTypesChanged) - { - var ctType = ApplicationContext.Current.Services.ContentTypeService.GetMediaType(alias); - if (ctType != null) - { - var mediaItems = ApplicationContext.Current.Services.MediaService.GetMediaOfMediaType(ctType.Id); - foreach (var mediaItem in mediaItems) - { - ReIndexForMedia(mediaItem, mediaItem.Trashed == false); - } - } - } - } - if (memberTypesChanged.Count > 0) - { - foreach (var alias in memberTypesChanged) - { - var ctType = ApplicationContext.Current.Services.MemberTypeService.Get(alias); - if (ctType != null) - { - var memberItems = ApplicationContext.Current.Services.MemberService.GetMembersByMemberType(ctType.Id); - foreach (var memberItem in memberItems) - { - ReIndexForMember(memberItem); - } - } - } - } - } - - } - - static void MemberCacheRefresherCacheUpdated(MemberCacheRefresher sender, CacheRefresherEventArgs e) - { - if (Suspendable.ExamineEvents.CanIndex == false) - return; - - switch (e.MessageType) - { - case MessageType.RefreshById: - var c1 = ApplicationContext.Current.Services.MemberService.GetById((int)e.MessageObject); - if (c1 != null) - { - ReIndexForMember(c1); - } - break; - case MessageType.RemoveById: - - // This is triggered when the item is permanently deleted - - DeleteIndexForEntity((int)e.MessageObject, false); - break; - case MessageType.RefreshByInstance: - var c3 = e.MessageObject as IMember; - if (c3 != null) - { - ReIndexForMember(c3); - } - break; - case MessageType.RemoveByInstance: - - // This is triggered when the item is permanently deleted - - var c4 = e.MessageObject as IMember; - if (c4 != null) - { - DeleteIndexForEntity(c4.Id, false); - } - break; - case MessageType.RefreshAll: - case MessageType.RefreshByJson: - default: - //We don't support these, these message types will not fire for unpublished content - break; - } - } - - /// - /// Handles index management for all media events - basically handling saving/copying/trashing/deleting - /// - /// - /// - static void MediaCacheRefresherCacheUpdated(MediaCacheRefresher sender, CacheRefresherEventArgs e) - { - if (Suspendable.ExamineEvents.CanIndex == false) - return; - - switch (e.MessageType) - { - case MessageType.RefreshById: - var c1 = ApplicationContext.Current.Services.MediaService.GetById((int)e.MessageObject); - if (c1 != null) - { - ReIndexForMedia(c1, c1.Trashed == false); - } - break; - case MessageType.RemoveById: - var c2 = ApplicationContext.Current.Services.MediaService.GetById((int)e.MessageObject); - if (c2 != null) - { - //This is triggered when the item has trashed. - // So we need to delete the index from all indexes not supporting unpublished content. - - DeleteIndexForEntity(c2.Id, true); - - //We then need to re-index this item for all indexes supporting unpublished content - - ReIndexForMedia(c2, false); - } - break; - case MessageType.RefreshByJson: - - var jsonPayloads = MediaCacheRefresher.DeserializeFromJsonPayload((string)e.MessageObject); - if (jsonPayloads.Any()) - { - foreach (var payload in jsonPayloads) - { - switch (payload.Operation) - { - case MediaCacheRefresher.OperationType.Saved: - var media1 = ApplicationContext.Current.Services.MediaService.GetById(payload.Id); - if (media1 != null) - { - ReIndexForMedia(media1, media1.Trashed == false); - } - break; - case MediaCacheRefresher.OperationType.Trashed: - - //keep if trashed for indexes supporting unpublished - //(delete the index from all indexes not supporting unpublished content) - - DeleteIndexForEntity(payload.Id, true); - - //We then need to re-index this item for all indexes supporting unpublished content - var media2 = ApplicationContext.Current.Services.MediaService.GetById(payload.Id); - if (media2 != null) - { - ReIndexForMedia(media2, false); - } - - break; - case MediaCacheRefresher.OperationType.Deleted: - - //permanently remove from all indexes - - DeleteIndexForEntity(payload.Id, false); - - break; - default: - throw new ArgumentOutOfRangeException(); - } - } - } - - break; - case MessageType.RefreshByInstance: - case MessageType.RemoveByInstance: - case MessageType.RefreshAll: - default: - //We don't support these, these message types will not fire for media - break; - } - } - - /// - /// Handles index management for all published content events - basically handling published/unpublished - /// - /// - /// - /// - /// This will execute on all servers taking part in load balancing - /// - static void PublishedPageCacheRefresherCacheUpdated(PageCacheRefresher sender, CacheRefresherEventArgs e) - { - if (Suspendable.ExamineEvents.CanIndex == false) - return; - - switch (e.MessageType) - { - case MessageType.RefreshById: - var c1 = ApplicationContext.Current.Services.ContentService.GetById((int)e.MessageObject); - if (c1 != null) - { - ReIndexForContent(c1, true); - } - break; - case MessageType.RemoveById: - - //This is triggered when the item has been unpublished or trashed (which also performs an unpublish). - - var c2 = ApplicationContext.Current.Services.ContentService.GetById((int)e.MessageObject); - if (c2 != null) - { - // So we need to delete the index from all indexes not supporting unpublished content. - - DeleteIndexForEntity(c2.Id, true); - - // We then need to re-index this item for all indexes supporting unpublished content - - ReIndexForContent(c2, false); - } - break; - case MessageType.RefreshByInstance: - var c3 = e.MessageObject as IContent; - if (c3 != null) - { - ReIndexForContent(c3, true); - } - break; - case MessageType.RemoveByInstance: - - //This is triggered when the item has been unpublished or trashed (which also performs an unpublish). - - var c4 = e.MessageObject as IContent; - if (c4 != null) - { - // So we need to delete the index from all indexes not supporting unpublished content. - - DeleteIndexForEntity(c4.Id, true); - - // We then need to re-index this item for all indexes supporting unpublished content - - ReIndexForContent(c4, false); - } - break; - case MessageType.RefreshAll: - case MessageType.RefreshByJson: - default: - //We don't support these for examine indexing - break; - } - } - - /// - /// Handles index management for all unpublished content events - basically handling saving/copying/deleting - /// - /// - /// - /// - /// This will execute on all servers taking part in load balancing - /// - static void UnpublishedPageCacheRefresherCacheUpdated(UnpublishedPageCacheRefresher sender, CacheRefresherEventArgs e) - { - if (Suspendable.ExamineEvents.CanIndex == false) - return; - - switch (e.MessageType) - { - case MessageType.RefreshById: - var c1 = ApplicationContext.Current.Services.ContentService.GetById((int) e.MessageObject); - if (c1 != null) - { - ReIndexForContent(c1, false); - } - break; - case MessageType.RemoveById: - - // This is triggered when the item is permanently deleted - - DeleteIndexForEntity((int)e.MessageObject, false); - break; - case MessageType.RefreshByInstance: - var c3 = e.MessageObject as IContent; - if (c3 != null) - { - ReIndexForContent(c3, false); - } - break; - case MessageType.RemoveByInstance: - - // This is triggered when the item is permanently deleted - - var c4 = e.MessageObject as IContent; - if (c4 != null) - { - DeleteIndexForEntity(c4.Id, false); - } - break; - case MessageType.RefreshByJson: - - var jsonPayloads = UnpublishedPageCacheRefresher.DeserializeFromJsonPayload((string)e.MessageObject); - if (jsonPayloads.Any()) - { - foreach (var payload in jsonPayloads) - { - switch (payload.Operation) - { - case UnpublishedPageCacheRefresher.OperationType.Deleted: - - //permanently remove from all indexes - - DeleteIndexForEntity(payload.Id, false); - - break; - default: - throw new ArgumentOutOfRangeException(); - } - } - } - - break; - - case MessageType.RefreshAll: - default: - //We don't support these, these message types will not fire for unpublished content - break; - } - } - - private static void ReIndexForMember(IMember member) - { - var actions = DeferedActions.Get(ApplicationContext.Current.ScopeProvider); - if (actions != null) - actions.Add(new DeferedReIndexForMember(member)); - else - DeferedReIndexForMember.Execute(member); - } - - /// - /// Event handler to create a lower cased version of the node name, this is so we can support case-insensitive searching and still - /// use the Whitespace Analyzer - /// - /// - /// - - private static void IndexerDocumentWriting(object sender, DocumentWritingEventArgs e) - { - if (e.Fields.Keys.Contains("nodeName")) - { - //TODO: This logic should really be put into the content indexer instead of hidden here!! - - //add the lower cased version - e.Document.Add(new Field("__nodeName", - e.Fields["nodeName"].ToLower(), - Field.Store.YES, - Field.Index.ANALYZED, - Field.TermVector.NO - )); - } - } - - private static void ReIndexForMedia(IMedia sender, bool isMediaPublished) - { - var actions = DeferedActions.Get(ApplicationContext.Current.ScopeProvider); - if (actions != null) - actions.Add(new DeferedReIndexForMedia(sender, isMediaPublished)); - else - DeferedReIndexForMedia.Execute(sender, isMediaPublished); - } - - /// - /// Remove items from any index that doesn't support unpublished content - /// - /// - /// - /// 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 static void DeleteIndexForEntity(int entityId, bool keepIfUnpublished) - { - var actions = DeferedActions.Get(ApplicationContext.Current.ScopeProvider); - if (actions != null) - actions.Add(new DeferedDeleteIndex(entityId, keepIfUnpublished)); - else - DeferedDeleteIndex.Execute(entityId, keepIfUnpublished); - } - - /// - /// Re-indexes a content item whether published or not but only indexes them for indexes supporting unpublished content - /// - /// - /// - /// Value indicating whether the item is published or not - /// - private static void ReIndexForContent(IContent sender, bool isContentPublished) - { - var actions = DeferedActions.Get(ApplicationContext.Current.ScopeProvider); - if (actions != null) - actions.Add(new DeferedReIndexForContent(sender, isContentPublished)); - else - DeferedReIndexForContent.Execute(sender, isContentPublished); - } - - private class DeferedActions - { - private readonly List _actions = new List(); - - public static DeferedActions Get(IScopeProvider scopeProvider) - { - var scopeContext = scopeProvider.Context; - if (scopeContext == null) return null; - - 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 (var action in _actions) - action.Execute(); - } - } - - private abstract class DeferedAction - { - public virtual void Execute() - { } - } - - private class DeferedReIndexForContent : DeferedAction - { - private readonly IContent _content; - private readonly bool _isPublished; - - public DeferedReIndexForContent(IContent content, bool isPublished) - { - _content = content; - _isPublished = isPublished; - } - - public override void Execute() - { - Execute(_content, _isPublished); - } - - public static void Execute(IContent content, bool isPublished) - { - var xml = content.ToXml(); - //add an icon attribute to get indexed - xml.Add(new XAttribute("icon", content.ContentType.Icon)); - - ExamineManager.Instance.ReIndexNode( - xml, IndexTypes.Content, - ExamineManager.Instance.IndexProviderCollection.OfType() - - //Index this item for all indexers if the content is published, otherwise if the item is not published - // then only index this for indexers supporting unpublished content - - .Where(x => isPublished || (x.SupportUnpublishedContent)) - .Where(x => x.EnableDefaultEventHandler)); - } - } - - private class DeferedReIndexForMedia : DeferedAction - { - private readonly IMedia _media; - private readonly bool _isPublished; - - public DeferedReIndexForMedia(IMedia media, bool isPublished) - { - _media = media; - _isPublished = isPublished; - } - - public override void Execute() - { - Execute(_media, _isPublished); - } - - public static void Execute(IMedia media, bool isPublished) - { - var xml = media.ToXml(); - //add an icon attribute to get indexed - xml.Add(new XAttribute("icon", media.ContentType.Icon)); - - ExamineManager.Instance.ReIndexNode( - xml, IndexTypes.Media, - ExamineManager.Instance.IndexProviderCollection.OfType() - - //Index this item for all indexers if the media is not trashed, otherwise if the item is trashed - // then only index this for indexers supporting unpublished media - - .Where(x => isPublished || (x.SupportUnpublishedContent)) - .Where(x => x.EnableDefaultEventHandler)); - } - } - - private class DeferedReIndexForMember : DeferedAction - { - private readonly IMember _member; - - public DeferedReIndexForMember(IMember member) - { - _member = member; - } - - public override void Execute() - { - Execute(_member); - } - - public static void Execute(IMember member) - { - ExamineManager.Instance.ReIndexNode( - member.ToXml(), IndexTypes.Member, - ExamineManager.Instance.IndexProviderCollection.OfType() - //ensure that only the providers are flagged to listen execute - .Where(x => x.EnableDefaultEventHandler)); - } - } - - private class DeferedDeleteIndex : DeferedAction - { - private readonly int _id; - private readonly bool _keepIfUnpublished; - - public DeferedDeleteIndex(int id, bool keepIfUnpublished) - { - _id = id; - _keepIfUnpublished = keepIfUnpublished; - } - - public override void Execute() - { - Execute(_id, _keepIfUnpublished); - } - - public static void Execute(int id, bool keepIfUnpublished) - { - ExamineManager.Instance.DeleteFromIndex( - id.ToString(CultureInfo.InvariantCulture), - ExamineManager.Instance.IndexProviderCollection.OfType() - - //if keepIfUnpublished == true then only delete this item from indexes not supporting unpublished content, - // otherwise if keepIfUnpublished == false then remove from all indexes - - .Where(x => keepIfUnpublished == false || x.SupportUnpublishedContent == false) - .Where(x => x.EnableDefaultEventHandler)); - } - } - - /// - /// Converts a content node to XDocument - /// - /// - /// true if data is going to be returned from cache - /// - [Obsolete("This method is no longer used and will be removed from the core in future versions, the cacheOnly parameter has no effect. Use the other ToXDocument overload instead")] - public static XDocument ToXDocument(Content node, bool cacheOnly) - { - return ToXDocument(node); - } - - /// - /// Converts a content node to Xml - /// - /// - /// - private static XDocument ToXDocument(Content node) - { - if (TypeHelper.IsTypeAssignableFrom(node)) - { - return new XDocument(((Document) node).ContentEntity.ToXml()); - } - - if (TypeHelper.IsTypeAssignableFrom(node)) - { - return new XDocument(((global::umbraco.cms.businesslogic.media.Media) node).MediaItem.ToXml()); - } - - var xDoc = new XmlDocument(); - var xNode = xDoc.CreateNode(XmlNodeType.Element, "node", ""); - node.XmlPopulate(xDoc, ref xNode, false); - - if (xNode.Attributes["nodeTypeAlias"] == null) - { - //we'll add the nodeTypeAlias ourselves - XmlAttribute d = xDoc.CreateAttribute("nodeTypeAlias"); - d.Value = node.ContentType.Alias; - xNode.Attributes.Append(d); - } - - return new XDocument(ExamineXmlExtensions.ToXElement(xNode)); - } - } -} +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Xml; +using System.Xml.Linq; +using Examine; +using Examine.LuceneEngine; +using Lucene.Net.Documents; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Scoping; +using Umbraco.Core.Sync; +using Umbraco.Web.Cache; +using UmbracoExamine; +using Content = umbraco.cms.businesslogic.Content; +using Document = umbraco.cms.businesslogic.web.Document; + +namespace Umbraco.Web.Search +{ + /// + /// Used to wire up events for Examine + /// + public sealed class ExamineEvents : ApplicationEventHandler + { + // 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; + + /// + /// Once the application has started we should bind to all events and initialize the providers. + /// + /// + /// + /// + /// We need to do this on the Started event as to guarantee that all resolvers are setup properly. + /// + protected override void ApplicationStarted(UmbracoApplicationBase httpApplication, ApplicationContext applicationContext) + { + LogHelper.Info("Initializing Examine and binding to business logic events"); + + var registeredProviders = ExamineManager.Instance.IndexProviderCollection + .OfType().Count(x => x.EnableDefaultEventHandler); + + LogHelper.Info("Adding examine event handlers for index providers: {0}", () => registeredProviders); + + //don't bind event handlers if we're not suppose to listen + if (registeredProviders == 0) + return; + + //Bind to distributed cache events - this ensures that this logic occurs on ALL servers that are taking part + // in a load balanced environment. + CacheRefresherBase.CacheUpdated += UnpublishedPageCacheRefresherCacheUpdated; + CacheRefresherBase.CacheUpdated += PublishedPageCacheRefresherCacheUpdated; + CacheRefresherBase.CacheUpdated += MediaCacheRefresherCacheUpdated; + CacheRefresherBase.CacheUpdated += MemberCacheRefresherCacheUpdated; + CacheRefresherBase.CacheUpdated += ContentTypeCacheRefresherCacheUpdated; + + var contentIndexer = ExamineManager.Instance.IndexProviderCollection[Constants.Examine.InternalIndexer] as UmbracoContentIndexer; + if (contentIndexer != null) + { + contentIndexer.DocumentWriting += IndexerDocumentWriting; + } + var memberIndexer = ExamineManager.Instance.IndexProviderCollection[Constants.Examine.InternalMemberIndexer] as UmbracoMemberIndexer; + if (memberIndexer != null) + { + memberIndexer.DocumentWriting += IndexerDocumentWriting; + } + } + + /// + /// This is used to refresh content indexers IndexData based on the DataService whenever a content type is changed since + /// properties may have been added/removed, then we need to re-index any required data if aliases have been changed + /// + /// + /// + /// + /// See: http://issues.umbraco.org/issue/U4-4798, http://issues.umbraco.org/issue/U4-7833 + /// + static void ContentTypeCacheRefresherCacheUpdated(ContentTypeCacheRefresher sender, CacheRefresherEventArgs e) + { + if (Suspendable.ExamineEvents.CanIndex == false) + return; + + var indexersToUpdated = ExamineManager.Instance.IndexProviderCollection.OfType(); + foreach (var provider in indexersToUpdated) + { + provider.RefreshIndexerDataFromDataService(); + } + + if (e.MessageType == MessageType.RefreshByJson) + { + var contentTypesChanged = new HashSet(); + var mediaTypesChanged = new HashSet(); + var memberTypesChanged = new HashSet(); + + var payloads = ContentTypeCacheRefresher.DeserializeFromJsonPayload(e.MessageObject.ToString()); + foreach (var payload in payloads) + { + if (payload.IsNew == false + && (payload.WasDeleted || payload.AliasChanged || payload.PropertyRemoved || payload.PropertyTypeAliasChanged)) + { + //if we get here it means that some aliases have changed and the indexes for those particular doc types will need to be updated + if (payload.Type == typeof(IContentType).Name) + { + //if it is content + contentTypesChanged.Add(payload.Alias); + } + else if (payload.Type == typeof(IMediaType).Name) + { + //if it is media + mediaTypesChanged.Add(payload.Alias); + } + else if (payload.Type == typeof(IMemberType).Name) + { + //if it is members + memberTypesChanged.Add(payload.Alias); + } + } + } + + //TODO: We need to update Examine to support re-indexing multiple items at once instead of one by one which will speed up + // the re-indexing process, we don't want to revert to rebuilding the whole thing! + + if (contentTypesChanged.Count > 0) + { + foreach (var alias in contentTypesChanged) + { + var ctType = ApplicationContext.Current.Services.ContentTypeService.GetContentType(alias); + if (ctType != null) + { + var contentItems = ApplicationContext.Current.Services.ContentService.GetContentOfContentType(ctType.Id); + foreach (var contentItem in contentItems) + { + ReIndexForContent(contentItem, contentItem.HasPublishedVersion && contentItem.Trashed == false); + } + } + } + } + if (mediaTypesChanged.Count > 0) + { + foreach (var alias in mediaTypesChanged) + { + var ctType = ApplicationContext.Current.Services.ContentTypeService.GetMediaType(alias); + if (ctType != null) + { + var mediaItems = ApplicationContext.Current.Services.MediaService.GetMediaOfMediaType(ctType.Id); + foreach (var mediaItem in mediaItems) + { + ReIndexForMedia(mediaItem, mediaItem.Trashed == false); + } + } + } + } + if (memberTypesChanged.Count > 0) + { + foreach (var alias in memberTypesChanged) + { + var ctType = ApplicationContext.Current.Services.MemberTypeService.Get(alias); + if (ctType != null) + { + var memberItems = ApplicationContext.Current.Services.MemberService.GetMembersByMemberType(ctType.Id); + foreach (var memberItem in memberItems) + { + ReIndexForMember(memberItem); + } + } + } + } + } + + } + + static void MemberCacheRefresherCacheUpdated(MemberCacheRefresher sender, CacheRefresherEventArgs e) + { + if (Suspendable.ExamineEvents.CanIndex == false) + return; + + switch (e.MessageType) + { + case MessageType.RefreshById: + var c1 = ApplicationContext.Current.Services.MemberService.GetById((int)e.MessageObject); + if (c1 != null) + { + ReIndexForMember(c1); + } + break; + case MessageType.RemoveById: + + // This is triggered when the item is permanently deleted + + DeleteIndexForEntity((int)e.MessageObject, false); + break; + case MessageType.RefreshByInstance: + var c3 = e.MessageObject as IMember; + if (c3 != null) + { + ReIndexForMember(c3); + } + break; + case MessageType.RemoveByInstance: + + // This is triggered when the item is permanently deleted + + var c4 = e.MessageObject as IMember; + if (c4 != null) + { + DeleteIndexForEntity(c4.Id, false); + } + break; + case MessageType.RefreshAll: + case MessageType.RefreshByJson: + default: + //We don't support these, these message types will not fire for unpublished content + break; + } + } + + /// + /// Handles index management for all media events - basically handling saving/copying/trashing/deleting + /// + /// + /// + static void MediaCacheRefresherCacheUpdated(MediaCacheRefresher sender, CacheRefresherEventArgs e) + { + if (Suspendable.ExamineEvents.CanIndex == false) + return; + + switch (e.MessageType) + { + case MessageType.RefreshById: + var c1 = ApplicationContext.Current.Services.MediaService.GetById((int)e.MessageObject); + if (c1 != null) + { + ReIndexForMedia(c1, c1.Trashed == false); + } + break; + case MessageType.RemoveById: + var c2 = ApplicationContext.Current.Services.MediaService.GetById((int)e.MessageObject); + if (c2 != null) + { + //This is triggered when the item has trashed. + // So we need to delete the index from all indexes not supporting unpublished content. + + DeleteIndexForEntity(c2.Id, true); + + //We then need to re-index this item for all indexes supporting unpublished content + + ReIndexForMedia(c2, false); + } + break; + case MessageType.RefreshByJson: + + var jsonPayloads = MediaCacheRefresher.DeserializeFromJsonPayload((string)e.MessageObject); + if (jsonPayloads.Any()) + { + foreach (var payload in jsonPayloads) + { + switch (payload.Operation) + { + case MediaCacheRefresher.OperationType.Saved: + var media1 = ApplicationContext.Current.Services.MediaService.GetById(payload.Id); + if (media1 != null) + { + ReIndexForMedia(media1, media1.Trashed == false); + } + break; + case MediaCacheRefresher.OperationType.Trashed: + + //keep if trashed for indexes supporting unpublished + //(delete the index from all indexes not supporting unpublished content) + + DeleteIndexForEntity(payload.Id, true); + + //We then need to re-index this item for all indexes supporting unpublished content + var media2 = ApplicationContext.Current.Services.MediaService.GetById(payload.Id); + if (media2 != null) + { + ReIndexForMedia(media2, false); + } + + break; + case MediaCacheRefresher.OperationType.Deleted: + + //permanently remove from all indexes + + DeleteIndexForEntity(payload.Id, false); + + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + } + + break; + case MessageType.RefreshByInstance: + case MessageType.RemoveByInstance: + case MessageType.RefreshAll: + default: + //We don't support these, these message types will not fire for media + break; + } + } + + /// + /// Handles index management for all published content events - basically handling published/unpublished + /// + /// + /// + /// + /// This will execute on all servers taking part in load balancing + /// + static void PublishedPageCacheRefresherCacheUpdated(PageCacheRefresher sender, CacheRefresherEventArgs e) + { + if (Suspendable.ExamineEvents.CanIndex == false) + return; + + switch (e.MessageType) + { + case MessageType.RefreshById: + var c1 = ApplicationContext.Current.Services.ContentService.GetById((int)e.MessageObject); + if (c1 != null) + { + ReIndexForContent(c1, true); + } + break; + case MessageType.RemoveById: + + //This is triggered when the item has been unpublished or trashed (which also performs an unpublish). + + var c2 = ApplicationContext.Current.Services.ContentService.GetById((int)e.MessageObject); + if (c2 != null) + { + // So we need to delete the index from all indexes not supporting unpublished content. + + DeleteIndexForEntity(c2.Id, true); + + // We then need to re-index this item for all indexes supporting unpublished content + + ReIndexForContent(c2, false); + } + break; + case MessageType.RefreshByInstance: + var c3 = e.MessageObject as IContent; + if (c3 != null) + { + ReIndexForContent(c3, true); + } + break; + case MessageType.RemoveByInstance: + + //This is triggered when the item has been unpublished or trashed (which also performs an unpublish). + + var c4 = e.MessageObject as IContent; + if (c4 != null) + { + // So we need to delete the index from all indexes not supporting unpublished content. + + DeleteIndexForEntity(c4.Id, true); + + // We then need to re-index this item for all indexes supporting unpublished content + + ReIndexForContent(c4, false); + } + break; + case MessageType.RefreshAll: + case MessageType.RefreshByJson: + default: + //We don't support these for examine indexing + break; + } + } + + /// + /// Handles index management for all unpublished content events - basically handling saving/copying/deleting + /// + /// + /// + /// + /// This will execute on all servers taking part in load balancing + /// + static void UnpublishedPageCacheRefresherCacheUpdated(UnpublishedPageCacheRefresher sender, CacheRefresherEventArgs e) + { + if (Suspendable.ExamineEvents.CanIndex == false) + return; + + switch (e.MessageType) + { + case MessageType.RefreshById: + var c1 = ApplicationContext.Current.Services.ContentService.GetById((int) e.MessageObject); + if (c1 != null) + { + ReIndexForContent(c1, false); + } + break; + case MessageType.RemoveById: + + // This is triggered when the item is permanently deleted + + DeleteIndexForEntity((int)e.MessageObject, false); + break; + case MessageType.RefreshByInstance: + var c3 = e.MessageObject as IContent; + if (c3 != null) + { + ReIndexForContent(c3, false); + } + break; + case MessageType.RemoveByInstance: + + // This is triggered when the item is permanently deleted + + var c4 = e.MessageObject as IContent; + if (c4 != null) + { + DeleteIndexForEntity(c4.Id, false); + } + break; + case MessageType.RefreshByJson: + + var jsonPayloads = UnpublishedPageCacheRefresher.DeserializeFromJsonPayload((string)e.MessageObject); + if (jsonPayloads.Any()) + { + foreach (var payload in jsonPayloads) + { + switch (payload.Operation) + { + case UnpublishedPageCacheRefresher.OperationType.Deleted: + + //permanently remove from all indexes + + DeleteIndexForEntity(payload.Id, false); + + break; + case UnpublishedPageCacheRefresher.OperationType.Refresh:// RefreshNode or RefreshBranch (maybe trashed) + var c2 = ApplicationContext.Current.Services.ContentService.GetById(payload.Id); + if (c2 != null) + { + ReIndexForContent(c2, false); + } + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + } + + break; + + case MessageType.RefreshAll: + default: + //We don't support these, these message types will not fire for unpublished content + break; + } + } + + private static void ReIndexForMember(IMember member) + { + var actions = DeferedActions.Get(ApplicationContext.Current.ScopeProvider); + if (actions != null) + actions.Add(new DeferedReIndexForMember(member)); + else + DeferedReIndexForMember.Execute(member); + } + + /// + /// Event handler to create a lower cased version of the node name, this is so we can support case-insensitive searching and still + /// use the Whitespace Analyzer + /// + /// + /// + + private static void IndexerDocumentWriting(object sender, DocumentWritingEventArgs e) + { + if (e.Fields.Keys.Contains("nodeName")) + { + //TODO: This logic should really be put into the content indexer instead of hidden here!! + + //add the lower cased version + e.Document.Add(new Field("__nodeName", + e.Fields["nodeName"].ToLower(), + Field.Store.YES, + Field.Index.ANALYZED, + Field.TermVector.NO + )); + } + } + + private static void ReIndexForMedia(IMedia sender, bool isMediaPublished) + { + var actions = DeferedActions.Get(ApplicationContext.Current.ScopeProvider); + if (actions != null) + actions.Add(new DeferedReIndexForMedia(sender, isMediaPublished)); + else + DeferedReIndexForMedia.Execute(sender, isMediaPublished); + } + + /// + /// Remove items from any index that doesn't support unpublished content + /// + /// + /// + /// 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 static void DeleteIndexForEntity(int entityId, bool keepIfUnpublished) + { + var actions = DeferedActions.Get(ApplicationContext.Current.ScopeProvider); + if (actions != null) + actions.Add(new DeferedDeleteIndex(entityId, keepIfUnpublished)); + else + DeferedDeleteIndex.Execute(entityId, keepIfUnpublished); + } + + /// + /// Re-indexes a content item whether published or not but only indexes them for indexes supporting unpublished content + /// + /// + /// + /// Value indicating whether the item is published or not + /// + private static void ReIndexForContent(IContent sender, bool isContentPublished) + { + var actions = DeferedActions.Get(ApplicationContext.Current.ScopeProvider); + if (actions != null) + actions.Add(new DeferedReIndexForContent(sender, isContentPublished)); + else + DeferedReIndexForContent.Execute(sender, isContentPublished); + } + + private class DeferedActions + { + private readonly List _actions = new List(); + + public static DeferedActions Get(IScopeProvider scopeProvider) + { + var scopeContext = scopeProvider.Context; + if (scopeContext == null) return null; + + 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 (var action in _actions) + action.Execute(); + } + } + + private abstract class DeferedAction + { + public virtual void Execute() + { } + } + + private class DeferedReIndexForContent : DeferedAction + { + private readonly IContent _content; + private readonly bool _isPublished; + + public DeferedReIndexForContent(IContent content, bool isPublished) + { + _content = content; + _isPublished = isPublished; + } + + public override void Execute() + { + Execute(_content, _isPublished); + } + + public static void Execute(IContent content, bool isPublished) + { + var xml = content.ToXml(); + //add an icon attribute to get indexed + xml.Add(new XAttribute("icon", content.ContentType.Icon)); + + ExamineManager.Instance.ReIndexNode( + xml, IndexTypes.Content, + ExamineManager.Instance.IndexProviderCollection.OfType() + + //Index this item for all indexers if the content is published, otherwise if the item is not published + // then only index this for indexers supporting unpublished content + + .Where(x => isPublished || (x.SupportUnpublishedContent)) + .Where(x => x.EnableDefaultEventHandler)); + } + } + + private class DeferedReIndexForMedia : DeferedAction + { + private readonly IMedia _media; + private readonly bool _isPublished; + + public DeferedReIndexForMedia(IMedia media, bool isPublished) + { + _media = media; + _isPublished = isPublished; + } + + public override void Execute() + { + Execute(_media, _isPublished); + } + + public static void Execute(IMedia media, bool isPublished) + { + var xml = media.ToXml(); + //add an icon attribute to get indexed + xml.Add(new XAttribute("icon", media.ContentType.Icon)); + + ExamineManager.Instance.ReIndexNode( + xml, IndexTypes.Media, + ExamineManager.Instance.IndexProviderCollection.OfType() + + //Index this item for all indexers if the media is not trashed, otherwise if the item is trashed + // then only index this for indexers supporting unpublished media + + .Where(x => isPublished || (x.SupportUnpublishedContent)) + .Where(x => x.EnableDefaultEventHandler)); + } + } + + private class DeferedReIndexForMember : DeferedAction + { + private readonly IMember _member; + + public DeferedReIndexForMember(IMember member) + { + _member = member; + } + + public override void Execute() + { + Execute(_member); + } + + public static void Execute(IMember member) + { + ExamineManager.Instance.ReIndexNode( + member.ToXml(), IndexTypes.Member, + ExamineManager.Instance.IndexProviderCollection.OfType() + //ensure that only the providers are flagged to listen execute + .Where(x => x.EnableDefaultEventHandler)); + } + } + + private class DeferedDeleteIndex : DeferedAction + { + private readonly int _id; + private readonly bool _keepIfUnpublished; + + public DeferedDeleteIndex(int id, bool keepIfUnpublished) + { + _id = id; + _keepIfUnpublished = keepIfUnpublished; + } + + public override void Execute() + { + Execute(_id, _keepIfUnpublished); + } + + public static void Execute(int id, bool keepIfUnpublished) + { + ExamineManager.Instance.DeleteFromIndex( + id.ToString(CultureInfo.InvariantCulture), + ExamineManager.Instance.IndexProviderCollection.OfType() + + //if keepIfUnpublished == true then only delete this item from indexes not supporting unpublished content, + // otherwise if keepIfUnpublished == false then remove from all indexes + + .Where(x => keepIfUnpublished == false || x.SupportUnpublishedContent == false) + .Where(x => x.EnableDefaultEventHandler)); + } + } + + /// + /// Converts a content node to XDocument + /// + /// + /// true if data is going to be returned from cache + /// + [Obsolete("This method is no longer used and will be removed from the core in future versions, the cacheOnly parameter has no effect. Use the other ToXDocument overload instead")] + public static XDocument ToXDocument(Content node, bool cacheOnly) + { + return ToXDocument(node); + } + + /// + /// Converts a content node to Xml + /// + /// + /// + private static XDocument ToXDocument(Content node) + { + if (TypeHelper.IsTypeAssignableFrom(node)) + { + return new XDocument(((Document) node).ContentEntity.ToXml()); + } + + if (TypeHelper.IsTypeAssignableFrom(node)) + { + return new XDocument(((global::umbraco.cms.businesslogic.media.Media) node).MediaItem.ToXml()); + } + + var xDoc = new XmlDocument(); + var xNode = xDoc.CreateNode(XmlNodeType.Element, "node", ""); + node.XmlPopulate(xDoc, ref xNode, false); + + if (xNode.Attributes["nodeTypeAlias"] == null) + { + //we'll add the nodeTypeAlias ourselves + XmlAttribute d = xDoc.CreateAttribute("nodeTypeAlias"); + d.Value = node.ContentType.Alias; + xNode.Attributes.Append(d); + } + + return new XDocument(ExamineXmlExtensions.ToXElement(xNode)); + } + } +} From 889f0fc08553c5e6ef47c70b81181bfb3517bf88 Mon Sep 17 00:00:00 2001 From: elitsa Date: Mon, 3 Feb 2020 11:39:11 +0100 Subject: [PATCH 21/69] Html encoding document name when it's rendered in the relation types html. --- .../umbraco/developer/RelationTypes/EditRelationType.aspx.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/EditRelationType.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/EditRelationType.aspx.cs index 33366681f5..c718183988 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/EditRelationType.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/EditRelationType.aspx.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text; +using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using umbraco.BasePages; @@ -105,9 +106,9 @@ namespace umbraco.cms.presentation.developer.RelationTypes readOnlyRelation.Id = reader.GetInt("id"); readOnlyRelation.ParentId = reader.GetInt("parentId"); - readOnlyRelation.ParentText = reader.GetString("parentText"); + readOnlyRelation.ParentText = HttpUtility.HtmlEncode(reader.GetString("parentText")); readOnlyRelation.ChildId = reader.GetInt("childId"); - readOnlyRelation.ChildText = reader.GetString("childText"); + readOnlyRelation.ChildText = HttpUtility.HtmlEncode(reader.GetString("childText")); readOnlyRelation.RelType = reader.GetInt("relType"); readOnlyRelation.DateTime = reader.GetDateTime("datetime"); readOnlyRelation.Comment = reader.GetString("comment"); From 3bfc2514301b14f03a6a9e6ac3c1cf515b838b84 Mon Sep 17 00:00:00 2001 From: elitsa Date: Mon, 3 Feb 2020 11:39:11 +0100 Subject: [PATCH 22/69] Html encoding document name when it's rendered in the relation types html. (cherry picked from commit 889f0fc08553c5e6ef47c70b81181bfb3517bf88) --- .../umbraco/developer/RelationTypes/EditRelationType.aspx.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/EditRelationType.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/EditRelationType.aspx.cs index 33366681f5..c718183988 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/EditRelationType.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/EditRelationType.aspx.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text; +using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using umbraco.BasePages; @@ -105,9 +106,9 @@ namespace umbraco.cms.presentation.developer.RelationTypes readOnlyRelation.Id = reader.GetInt("id"); readOnlyRelation.ParentId = reader.GetInt("parentId"); - readOnlyRelation.ParentText = reader.GetString("parentText"); + readOnlyRelation.ParentText = HttpUtility.HtmlEncode(reader.GetString("parentText")); readOnlyRelation.ChildId = reader.GetInt("childId"); - readOnlyRelation.ChildText = reader.GetString("childText"); + readOnlyRelation.ChildText = HttpUtility.HtmlEncode(reader.GetString("childText")); readOnlyRelation.RelType = reader.GetInt("relType"); readOnlyRelation.DateTime = reader.GetDateTime("datetime"); readOnlyRelation.Comment = reader.GetString("comment"); From 721d41da62846374510ec9dfdc18c78637c8a175 Mon Sep 17 00:00:00 2001 From: Benjamin Howarth <322383+benjaminhowarth1@users.noreply.github.com> Date: Wed, 12 Feb 2020 17:01:59 +0000 Subject: [PATCH 23/69] Update PropertyType.cs Please, *please* stop making things internal when it breaks legacy behaviour (such as obtaining properties by tab group, which is precisely what this allows for). --- src/Umbraco.Core/Models/PropertyType.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Models/PropertyType.cs b/src/Umbraco.Core/Models/PropertyType.cs index fd23756acb..bcafd549f0 100644 --- a/src/Umbraco.Core/Models/PropertyType.cs +++ b/src/Umbraco.Core/Models/PropertyType.cs @@ -178,7 +178,7 @@ namespace Umbraco.Core.Models /// /// For generic properties, the value is null. [DataMember] - internal Lazy PropertyGroupId + public Lazy PropertyGroupId { get => _propertyGroupId; set => SetPropertyValueAndDetectChanges(value, ref _propertyGroupId, nameof(PropertyGroupId)); From 7d0733f40d4c698742af6bd03e3891922cc074ce Mon Sep 17 00:00:00 2001 From: Anders Bjerner Date: Mon, 24 Feb 2020 20:36:38 +0100 Subject: [PATCH 24/69] Making DataEditor.GetValueEditor method virtual --- src/Umbraco.Core/PropertyEditors/DataEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/PropertyEditors/DataEditor.cs b/src/Umbraco.Core/PropertyEditors/DataEditor.cs index 7dc260e4c7..c749c61300 100644 --- a/src/Umbraco.Core/PropertyEditors/DataEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/DataEditor.cs @@ -105,7 +105,7 @@ namespace Umbraco.Core.PropertyEditors /// Technically, it could be cached by datatype but let's keep things /// simple enough for now. /// - public IDataValueEditor GetValueEditor(object configuration) + public virtual IDataValueEditor GetValueEditor(object configuration) { // if an explicit value editor has been set (by the manifest parser) // then return it, and ignore the configuration, which is going to be From 4dc5b02b89fdfa019b30eaaa6ff41f65010611ab Mon Sep 17 00:00:00 2001 From: Matthew-Wise <6782865+Matthew-Wise@users.noreply.github.com> Date: Wed, 26 Feb 2020 13:44:57 +0000 Subject: [PATCH 25/69] Changed showPage to Save and publish updated button style to link (#7649) * Changed showPage to Save and publish updated button style to link * Change language key from showPage to saveAndPreview --- .../src/common/mocks/services/localization.mocks.js | 2 +- .../src/views/components/content/edit.html | 4 ++-- src/Umbraco.Web.UI/Umbraco/config/lang/cs.xml | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/de.xml | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/es.xml | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/he.xml | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/it.xml | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/ja.xml | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/ko.xml | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/nl.xml | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/pl.xml | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/pt.xml | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/ru.xml | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/sv.xml | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/tr.xml | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/zh.xml | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/zh_tw.xml | 2 +- 22 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/services/localization.mocks.js b/src/Umbraco.Web.UI.Client/src/common/mocks/services/localization.mocks.js index 3874ff9bf6..ea7f3a6d4c 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/services/localization.mocks.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/services/localization.mocks.js @@ -84,7 +84,7 @@ angular.module('umbraco.mocks'). "buttons_save": "Save", "buttons_saveAndPublish": "Save and publish", "buttons_saveToPublish": "Save and send for approval", - "buttons_showPage": "Preview", + "buttons_saveAndPreview": "Save and preview", "buttons_showPageDisabled": "Preview is disabled because there's no template assigned", "buttons_styleChoose": "Choose style", "buttons_styleShow": "Show styles", diff --git a/src/Umbraco.Web.UI.Client/src/views/components/content/edit.html b/src/Umbraco.Web.UI.Client/src/views/components/content/edit.html index 8dd78883c2..e1eb5e454a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/content/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/content/edit.html @@ -45,9 +45,9 @@ alias="preview" ng-if="!page.isNew && content.allowPreview && page.showPreviewButton" type="button" - button-style="info" + button-style="link" action="preview(content)" - label-key="buttons_showPage"> + label-key="buttons_saveAndPreview"> Uložit Uložit a publikovat Uložit a odeslat ke schválení - Náhled + Náhled Náhled je deaktivován, protože není přiřazena žádná šablona Vybrat styl Zobrazit styly diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index ae8925e911..85eaa34319 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -139,7 +139,7 @@ Gem og send til udgivelse Gem listevisning Planlæg - Forhåndsvisning + Forhåndsvisning Forhåndsvisning er deaktiveret fordi der ikke er nogen skabelon tildelt Vælg formattering Vis koder diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/de.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/de.xml index 9c02879db9..529074f24d 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/de.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/de.xml @@ -140,7 +140,7 @@ Speichern und zur Abnahme übergeben Listenansicht sichern Veröffentlichung planen - Vorschau + Vorschau Die Vorschaufunktion ist deaktiviert, da keine Vorlage zugewiesen ist Stil auswählen Stil anzeigen diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index a85df5714b..0a372dbcb5 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -141,7 +141,7 @@ Save and send for approval Save list view Schedule - Preview + Save and preview Preview is disabled because there's no template assigned Choose style Show styles 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 d14fb03727..482973f5e7 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -139,7 +139,7 @@ Send for approval Save list view Schedule - Preview + Save and preview Preview is disabled because there's no template assigned Choose style Show styles diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/es.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/es.xml index 371e6de7a2..c35c84ebdc 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/es.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/es.xml @@ -124,7 +124,7 @@ Guardar y publicar Guardar y enviar para aprobación Guardar vista de lista - Previsualizar + Previsualizar La previsualización está deshabilitada porque no hay ninguna plantilla asignada Elegir estilo Mostrar estilos diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml index e3cfa32d62..b5d1c8feb2 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml @@ -124,7 +124,7 @@ Sauver et planifier Sauver et envoyer pour approbation Sauver la mise en page de la liste - Prévisualiser + Prévisualiser La prévisualisation est désactivée car aucun modèle n'a été assigné. Choisir un style Afficher les styles diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/he.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/he.xml index 9b816b4682..e100cb4301 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/he.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/he.xml @@ -68,7 +68,7 @@ שמור שמור ופרסם שמור ושלח לאישור - תצוגה מקדימה + תצוגה מקדימה בחר עיצוב הצג עיצוב הוספת טבלה diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/it.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/it.xml index d2a1d75ee7..4866fff843 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/it.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/it.xml @@ -69,7 +69,7 @@ Salva Salva e pubblica Salva e invia per approvazione - Anteprima + Anteprima Scegli lo stile Mostra gli stili diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/ja.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/ja.xml index dd68ed45e5..21559f915a 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/ja.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/ja.xml @@ -85,7 +85,7 @@ 保存及び公開 保存して承認に送る リスト ビューの保存 - プレビュー + プレビュー テンプレートが指定されていないのでプレビューは無効になっています スタイルの選択 スタイルの表示 diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/ko.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/ko.xml index 12f7c9ed50..a87f6f1410 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/ko.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/ko.xml @@ -67,7 +67,7 @@ 저장 저장 후 발행 저장 후 승인을 위해 전송 - 미리보기 + 미리보기 스타일 선택 스타일 보기 테이블 삽입 diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml index 78b0ebfb5a..731aea4a20 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml @@ -83,7 +83,7 @@ Lagre og publiser Lagre og planlegge Lagre og send til publisering - Forhåndsvis + Forhåndsvis Forhåndsvisning er deaktivert siden det ikke er angitt noen mal Velg formattering Vis stiler diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/nl.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/nl.xml index 90f06fe7a6..601626d896 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/nl.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/nl.xml @@ -90,7 +90,7 @@ Opslaan en publiceren Opslaan en verzenden voor goedkeuring Sla list view op - voorbeeld bekijken + voorbeeld bekijken Voorbeeld bekijken is uitgeschakeld omdat er geen template is geselecteerd Stijl kiezen Stijlen tonen diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/pl.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/pl.xml index de3e988118..fd806041c5 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/pl.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/pl.xml @@ -121,7 +121,7 @@ Zapisz i publikuj Zapisz i wyślij do zaakceptowania Zapisz widok listy - Podgląd + Podgląd Podgląd jest wyłączony, ponieważ żaden szablon nie został przydzielony Wybierz styl Pokaż style diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/pt.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/pt.xml index e7afd04acd..9fd4696c28 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/pt.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/pt.xml @@ -67,7 +67,7 @@ Salvar Salvar e publicar Salvar e mandar para aprovação - Prévia + Prévia Escolha estilo Mostrar estilos Inserir tabela diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/ru.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/ru.xml index c52e17e829..7a3e099262 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/ru.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/ru.xml @@ -155,7 +155,7 @@ Направить на публикацию Сохранить список Выбрать - Предварительный просмотр + Предварительный просмотр Предварительный просмотр запрещен, так как документу не сопоставлен шаблон Другие действия Выбрать стиль diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/sv.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/sv.xml index 152a40b965..e7e7abe2cd 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/sv.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/sv.xml @@ -134,7 +134,7 @@ Spara och skicka för godkännande Schemaläggning Välj - Förhandsgranska + Förhandsgranska Förhandsgranskning är avstängt på grund av att det inte finns någon mall tilldelad Ångra Välj stil diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/tr.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/tr.xml index 02069a53dc..f998080b06 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/tr.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/tr.xml @@ -85,7 +85,7 @@ Kaydet Kaydet ve Yayınla Kaydet ve Onay için gönder - Önizle + Önizle Önizleme kapalı, Atanmış şablon yok Stili seçin Stilleri Göster diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/zh.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/zh.xml index a8dd8a8bef..5210b46bcc 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/zh.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/zh.xml @@ -90,7 +90,7 @@ 保存并发布 保存并提交审核 保存列表视图 - 预览 + 预览 因未设置模板无法预览 选择样式 显示样式 diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/zh_tw.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/zh_tw.xml index bac817ad20..320c3f63d8 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/zh_tw.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/zh_tw.xml @@ -87,7 +87,7 @@ 保存並發佈 保存並提交審核 保存清單檢視 - 預覽 + 預覽 因未設置範本無法預覽 選擇樣式 顯示樣式 From 384746cd26a0e96caa63b4d080a6181c12775d27 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 26 Feb 2020 16:16:26 +0100 Subject: [PATCH 26/69] V8: Use current client culture when searching content pickers (#7123) * Use current client culture when searching content pickers * Use string interpolation --- src/Umbraco.Web/Editors/EntityController.cs | 3 ++- .../Models/Mapping/EntityMapDefinition.cs | 6 +++++ src/Umbraco.Web/Search/UmbracoTreeSearcher.cs | 25 +++++++++++++++---- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs index a894b681f0..3938ae5ab8 100644 --- a/src/Umbraco.Web/Editors/EntityController.cs +++ b/src/Umbraco.Web/Editors/EntityController.cs @@ -768,7 +768,8 @@ namespace Umbraco.Web.Editors /// private IEnumerable ExamineSearch(string query, UmbracoEntityTypes entityType, string searchFrom = null, bool ignoreUserStartNodes = false) { - return _treeSearcher.ExamineSearch(query, entityType, 200, 0, out _, searchFrom, ignoreUserStartNodes); + var culture = ClientCulture(); + return _treeSearcher.ExamineSearch(query, entityType, 200, 0, culture, out _, searchFrom, ignoreUserStartNodes); } private IEnumerable GetResultForChildren(int id, UmbracoEntityTypes entityType) diff --git a/src/Umbraco.Web/Models/Mapping/EntityMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/EntityMapDefinition.cs index 34b8f664f3..ddff093aab 100644 --- a/src/Umbraco.Web/Models/Mapping/EntityMapDefinition.cs +++ b/src/Umbraco.Web/Models/Mapping/EntityMapDefinition.cs @@ -177,6 +177,12 @@ namespace Umbraco.Web.Models.Mapping target.Name = source.Values.ContainsKey("nodeName") ? source.Values["nodeName"] : "[no name]"; + var culture = context.GetCulture(); + if(culture.IsNullOrWhiteSpace() == false) + { + target.Name = source.Values.ContainsKey($"nodeName_{culture}") ? source.Values[$"nodeName_{culture}"] : target.Name; + } + if (source.Values.TryGetValue(UmbracoExamineIndex.UmbracoFileFieldName, out var umbracoFile)) { if (umbracoFile != null) diff --git a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs index 146177f86f..dcc156e356 100644 --- a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs +++ b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs @@ -12,6 +12,7 @@ using Umbraco.Core.Persistence; using Umbraco.Core.Services; using Umbraco.Examine; using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Models.Mapping; using Umbraco.Web.Trees; namespace Umbraco.Web.Search @@ -65,6 +66,16 @@ namespace Umbraco.Web.Search UmbracoEntityTypes entityType, int pageSize, long pageIndex, out long totalFound, string searchFrom = null, bool ignoreUserStartNodes = false) + { + return ExamineSearch(query, entityType, pageSize, pageIndex, culture: null, out totalFound, searchFrom, ignoreUserStartNodes); + } + + public IEnumerable ExamineSearch( + string query, + UmbracoEntityTypes entityType, + int pageSize, + long pageIndex, string culture, + out long totalFound, string searchFrom = null, bool ignoreUserStartNodes = false) { var sb = new StringBuilder(); @@ -140,7 +151,7 @@ namespace Umbraco.Web.Search case UmbracoEntityTypes.Media: return MediaFromSearchResults(pagedResult); case UmbracoEntityTypes.Document: - return ContentFromSearchResults(pagedResult); + return ContentFromSearchResults(pagedResult, culture); default: throw new NotSupportedException("The " + typeof(UmbracoTreeSearcher) + " currently does not support searching against object type " + entityType); } @@ -443,13 +454,17 @@ namespace Umbraco.Web.Search /// /// /// - private IEnumerable ContentFromSearchResults(IEnumerable results) + private IEnumerable ContentFromSearchResults(IEnumerable results, string culture = null) { var defaultLang = _languageService.GetDefaultLanguageIsoCode(); - foreach (var result in results) { - var entity = _mapper.Map(result); + var entity = _mapper.Map(result, context => { + if(culture != null) { + context.SetCulture(culture); + } + } + ); var intId = entity.Id.TryConvertTo(); if (intId.Success) @@ -457,7 +472,7 @@ namespace Umbraco.Web.Search //if it varies by culture, return the default language URL if (result.Values.TryGetValue(UmbracoContentIndex.VariesByCultureFieldName, out var varies) && varies == "y") { - entity.AdditionalData["Url"] = _umbracoContext.Url(intId.Result, defaultLang); + entity.AdditionalData["Url"] = _umbracoContext.Url(intId.Result, culture ?? defaultLang); } else { From af7919ce65dd3c743cdded13d68be07c7f81929f Mon Sep 17 00:00:00 2001 From: Nathan Woulfe Date: Thu, 27 Feb 2020 09:07:45 +1000 Subject: [PATCH 27/69] make events public --- src/Umbraco.Core/Events/UserGroupWithUsers.cs | 2 +- src/Umbraco.Core/Services/Implement/UserService.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Core/Events/UserGroupWithUsers.cs b/src/Umbraco.Core/Events/UserGroupWithUsers.cs index b69650d33f..7d456a22ea 100644 --- a/src/Umbraco.Core/Events/UserGroupWithUsers.cs +++ b/src/Umbraco.Core/Events/UserGroupWithUsers.cs @@ -3,7 +3,7 @@ using Umbraco.Core.Models.Membership; namespace Umbraco.Core.Events { - internal class UserGroupWithUsers + public class UserGroupWithUsers { public UserGroupWithUsers(IUserGroup userGroup, IUser[] addedUsers, IUser[] removedUsers) { diff --git a/src/Umbraco.Core/Services/Implement/UserService.cs b/src/Umbraco.Core/Services/Implement/UserService.cs index 363bc72bc3..95ab5376ff 100644 --- a/src/Umbraco.Core/Services/Implement/UserService.cs +++ b/src/Umbraco.Core/Services/Implement/UserService.cs @@ -1197,12 +1197,12 @@ namespace Umbraco.Core.Services.Implement /// /// Occurs before Save /// - internal static event TypedEventHandler> SavingUserGroup; + public static event TypedEventHandler> SavingUserGroup; /// /// Occurs after Save /// - internal static event TypedEventHandler> SavedUserGroup; + public static event TypedEventHandler> SavedUserGroup; /// /// Occurs before Delete From 0f540bc74db2123bf6254932bdf613d8c14dee7c Mon Sep 17 00:00:00 2001 From: Poornima Nayar Date: Tue, 25 Feb 2020 22:50:41 +0000 Subject: [PATCH 28/69] RTE config tool bar options are messed up --- .../src/less/components/umb-form-check.less | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-form-check.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-form-check.less index 1c5c275642..a52f81b92a 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-form-check.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-form-check.less @@ -1,5 +1,8 @@ -@checkboxWidth: 16px; -@checkboxHeight: 16px; +@checkboxWidth: 18px; +@checkboxHeight: 18px; +label.umb-form-check--checkbox{ + margin:3px 0; +} .umb-form-check { display: flex; @@ -10,11 +13,11 @@ cursor: pointer !important; .umb-form-check__symbol { - margin-top: 1px; + margin-top: 4px; margin-right: 10px; } .umb-form-check__info { - + margin-left:20px; } @@ -95,6 +98,10 @@ &__state { display: flex; height: 18px; + position: absolute; + margin-top: 2px; + top: 0; + left: -1px; } &__check { From 2853448963bfafdb57e76e9b58b8bf9ce321a36e Mon Sep 17 00:00:00 2001 From: Poornima Nayar Date: Sat, 29 Feb 2020 20:50:50 +0000 Subject: [PATCH 29/69] Improve accessibility of grid prevalues screen (#6949) * WIP Improve accessibility of grid prevalues screen * Clean up of the view, improve accessibility and add localization fallbacks * forgot to commit the stylesheet * formatting fixes --- .../src/less/gridview.less | 22 +++++ .../propertyeditors/grid/grid.prevalues.html | 91 +++++++++++-------- 2 files changed, 73 insertions(+), 40 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/gridview.less b/src/Umbraco.Web.UI.Client/src/less/gridview.less index 238feead90..11ba7b2795 100644 --- a/src/Umbraco.Web.UI.Client/src/less/gridview.less +++ b/src/Umbraco.Web.UI.Client/src/less/gridview.less @@ -101,6 +101,9 @@ position:relative; } + .usky-grid .grid-layout { + max-width: 600px; +} // ROW // ------------------------- @@ -517,6 +520,25 @@ position:relative; } + .usky-grid .uSky-templates .layout { + margin-top: 5px; + margin-bottom: 20px; + float: left; +} + + +.usky-grid .uSky-templates .columns { + margin-top: 5px; + margin-bottom: 25px; + float: left; +} + + +.usky-grid .uSky-templates .columns .preview-cell p { + font-size: 6px; + line-height: 8px; + text-align: center; +} /**************************************************************************************************/ diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.prevalues.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.prevalues.html index 92d1a9ef26..986f0cbc7e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.prevalues.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.prevalues.html @@ -1,9 +1,15 @@
-
+
-

-

+

+ + Grid Layouts + +

+

+ Layouts are the overall work area for the grid editor, usually you only need one or two different layouts +

    + class="preview-rows layout">
    {{template.name}}
    - - +
-
-
+
-

-

+

+ Row Configurations +

+

+ Rows are predefined cells arranged horizontally +

    + class="preview-rows columns">
    -

    {{area.maxItems}}

    +

    {{area.maxItems}}

    @@ -74,17 +85,18 @@
    {{layout.label || layout.name}}
    - - - - +
@@ -93,7 +105,7 @@
-
+
@@ -108,22 +120,21 @@ ng-model="model.value.config">
  • - + - - +
    • -
    • - - - - - +
    • +
    @@ -137,22 +148,22 @@ ng-model="model.value.styles">
  • - + - - +
    • -
    • - - - - +
    • +
    From 867232531c626e328cc21fc2bee95abcbf611675 Mon Sep 17 00:00:00 2001 From: Poornima Nayar Date: Sat, 29 Feb 2020 21:36:56 +0000 Subject: [PATCH 30/69] Create Content Blueprints accessibility improvements (#7020) * Create Content Blueprints - Disallow element types and some accessibility improvements * re-enabled element types for content blueprint --- .../src/less/modals.less | 10 +++++--- .../src/views/contentblueprints/create.html | 23 ++++++++++--------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/modals.less b/src/Umbraco.Web.UI.Client/src/less/modals.less index 925f845c4c..4ce907d06f 100644 --- a/src/Umbraco.Web.UI.Client/src/less/modals.less +++ b/src/Umbraco.Web.UI.Client/src/less/modals.less @@ -88,9 +88,13 @@ background: @white; } -.umb-dialog .umb-btn-toolbar .umb-control-group{ - border: none; - padding: none; +.umb-dialog .abstract{ + margin-bottom:20px; +} + +.umb-dialog .umb-btn-toolbar .umb-control-group { + border: none; + padding: none; } .umb-dialog-body{ diff --git a/src/Umbraco.Web.UI.Client/src/views/contentblueprints/create.html b/src/Umbraco.Web.UI.Client/src/views/contentblueprints/create.html index 6146c007b1..2e1d398409 100644 --- a/src/Umbraco.Web.UI.Client/src/views/contentblueprints/create.html +++ b/src/Umbraco.Web.UI.Client/src/views/contentblueprints/create.html @@ -1,18 +1,20 @@
    [Required] public string Password { get; set; } - + /// /// The username of the model, if UsernameIsEmail is true then this is ignored. /// diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index b72cd8069c..7224955922 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -72,23 +72,23 @@ namespace Umbraco.Web // ensures that we can return the specified value [Obsolete("This method is only used in Obsolete properties")] T Ensure(T o) where T : class => o ?? throw new InvalidOperationException("This UmbracoHelper instance has not been initialized."); - - [Obsolete("Inject and use an instance of " + nameof(IUmbracoComponentRenderer) + " in the constructor for using it in classes or get it from Current in views")] + + [Obsolete("Inject and use an instance of " + nameof(IUmbracoComponentRenderer) + " in the constructor for using it in classes or get it from Current.UmbracoComponentRenderer in views.")] private IUmbracoComponentRenderer ComponentRenderer => Ensure(_componentRenderer); - [Obsolete("Inject and use an instance of " + nameof(ICultureDictionaryFactory) + " in the constructor instead")] + [Obsolete("Inject and use an instance of " + nameof(ICultureDictionaryFactory) + " in the constructor for using it in classes or get it from Current.CultureDictionaryFactory in views.")] private ICultureDictionaryFactory CultureDictionaryFactory => Ensure(_cultureDictionaryFactory); /// /// Gets the tag context. /// - [Obsolete("Inject and use an instance of " + nameof(ITagQuery) + " in the constructor instead")] + [Obsolete("Inject and use an instance of " + nameof(ITagQuery) + " in the constructor for using it in classes or get it from Current.TagQuery in views.")] public ITagQuery TagQuery => Ensure(_tagQuery); /// /// Gets the query context. /// - [Obsolete("Inject and use an instance of " + nameof(IPublishedContentQuery) + " in the constructor for using it in classes or get it from Current in views")] + [Obsolete("Inject and use an instance of " + nameof(IPublishedContentQuery) + " in the constructor for using it in classes or get it from Current.PublishedContentQuery in views")] public IPublishedContentQuery ContentQuery => Ensure(_publishedContentQuery); /// From 6241bee1b22afd68a54d4f6233de8b381e64f1f1 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 4 Mar 2020 13:37:05 +0100 Subject: [PATCH 39/69] Introduce methods on current, that can be necessary if not using the obsolete properties. --- src/Umbraco.Web/Composing/Current.cs | 6 ++++++ src/Umbraco.Web/Models/RegisterModel.cs | 3 +-- src/Umbraco.Web/UmbracoHelper.cs | 10 +++++----- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web/Composing/Current.cs b/src/Umbraco.Web/Composing/Current.cs index ec65046e84..2419eaa6d4 100644 --- a/src/Umbraco.Web/Composing/Current.cs +++ b/src/Umbraco.Web/Composing/Current.cs @@ -88,6 +88,12 @@ namespace Umbraco.Web.Composing public static UmbracoHelper UmbracoHelper => Factory.GetInstance(); + public static IUmbracoComponentRenderer UmbracoComponentRenderer + => Factory.GetInstance(); + public static ITagQuery TagQuery + => Factory.GetInstance(); + public static IPublishedContentQuery PublishedContentQuery + => Factory.GetInstance(); public static DistributedCache DistributedCache => Factory.GetInstance(); diff --git a/src/Umbraco.Web/Models/RegisterModel.cs b/src/Umbraco.Web/Models/RegisterModel.cs index 86a5459a74..fd6cadc04e 100644 --- a/src/Umbraco.Web/Models/RegisterModel.cs +++ b/src/Umbraco.Web/Models/RegisterModel.cs @@ -5,7 +5,6 @@ using System.ComponentModel.DataAnnotations; using System.Web; using System.Web.Mvc; using Umbraco.Core; -using Umbraco.Core.Composing; using Umbraco.Web.Security; using Current = Umbraco.Web.Composing.Current; @@ -65,7 +64,7 @@ namespace Umbraco.Web.Models /// [Required] public string Password { get; set; } - + /// /// The username of the model, if UsernameIsEmail is true then this is ignored. /// diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index b72cd8069c..7224955922 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -72,23 +72,23 @@ namespace Umbraco.Web // ensures that we can return the specified value [Obsolete("This method is only used in Obsolete properties")] T Ensure(T o) where T : class => o ?? throw new InvalidOperationException("This UmbracoHelper instance has not been initialized."); - - [Obsolete("Inject and use an instance of " + nameof(IUmbracoComponentRenderer) + " in the constructor for using it in classes or get it from Current in views")] + + [Obsolete("Inject and use an instance of " + nameof(IUmbracoComponentRenderer) + " in the constructor for using it in classes or get it from Current.UmbracoComponentRenderer in views.")] private IUmbracoComponentRenderer ComponentRenderer => Ensure(_componentRenderer); - [Obsolete("Inject and use an instance of " + nameof(ICultureDictionaryFactory) + " in the constructor instead")] + [Obsolete("Inject and use an instance of " + nameof(ICultureDictionaryFactory) + " in the constructor for using it in classes or get it from Current.CultureDictionaryFactory in views.")] private ICultureDictionaryFactory CultureDictionaryFactory => Ensure(_cultureDictionaryFactory); /// /// Gets the tag context. /// - [Obsolete("Inject and use an instance of " + nameof(ITagQuery) + " in the constructor instead")] + [Obsolete("Inject and use an instance of " + nameof(ITagQuery) + " in the constructor for using it in classes or get it from Current.TagQuery in views.")] public ITagQuery TagQuery => Ensure(_tagQuery); /// /// Gets the query context. /// - [Obsolete("Inject and use an instance of " + nameof(IPublishedContentQuery) + " in the constructor for using it in classes or get it from Current in views")] + [Obsolete("Inject and use an instance of " + nameof(IPublishedContentQuery) + " in the constructor for using it in classes or get it from Current.PublishedContentQuery in views")] public IPublishedContentQuery ContentQuery => Ensure(_publishedContentQuery); /// From 4ad73dc1833d960aa9f8081bd0bb6f578ad750b5 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 4 Mar 2020 12:50:50 +0100 Subject: [PATCH 40/69] Fix completely broken(tm) media pickers --- .../mediapicker/mediapicker.controller.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) 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 cde99d7b9e..340e6865ef 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 @@ -49,12 +49,10 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl // when there is no match for a selected id. This will ensure that the values being set on save, are the same as before. medias = ids.map(id => { - var found = medias.find(m => - // We could use coercion (two ='s) here .. but not sure if this works equally well in all browsers and - // it's prone to someone "fixing" it at some point without knowing the effects. Rather use toString() - // compares and be completely sure it works. - m.udi.toString() === id.toString() || m.id.toString() === id.toString()); - + // We could use coercion (two ='s) here .. but not sure if this works equally well in all browsers and + // 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()); if (found) { return found; } else { From 41c2d01078602852f97337a5af360b5404fda0a2 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 9 Mar 2020 11:03:42 +1100 Subject: [PATCH 41/69] initial POC code --- src/Umbraco.Core/Composing/TypeFinder.cs | 627 +++++++++++++++--- src/Umbraco.Core/Composing/TypeLoader.cs | 44 +- src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../Runtime/CoreRuntime.cs | 3 +- .../Cache/DeepCloneAppCacheTests.cs | 2 +- .../Cache/HttpRequestAppCacheTests.cs | 2 +- .../Cache/ObjectAppCacheTests.cs | 2 +- .../Components/ComponentTests.cs | 4 +- .../Composing/ComposingTestBase.cs | 2 +- .../Composing/CompositionTests.cs | 2 +- .../Composing/TypeFinderTests.cs | 7 +- .../Composing/TypeLoaderTests.cs | 2 +- src/Umbraco.Tests/Macros/MacroTests.cs | 2 +- src/Umbraco.Tests/Models/ContentTests.cs | 2 +- .../Published/PropertyCacheLevelTests.cs | 2 +- .../PublishedContent/NuCacheChildrenTests.cs | 2 +- .../PublishedContent/NuCacheTests.cs | 2 +- src/Umbraco.Tests/Runtimes/StandaloneTests.cs | 4 +- .../Scoping/ScopedNuCacheTests.cs | 2 +- .../ContentTypeServiceVariantsTests.cs | 2 +- .../TestHelpers/BaseUsingSqlCeSyntax.cs | 2 +- .../Stubs/TestControllerFactory.cs | 2 +- src/Umbraco.Tests/TestHelpers/TestObjects.cs | 2 +- src/Umbraco.Tests/Testing/UmbracoTestBase.cs | 2 +- src/Umbraco.Tests/Web/UmbracoHelperTests.cs | 2 +- .../Composing/BuildManagerTypeFinder.cs | 5 +- src/Umbraco.Web/Runtime/WebRuntime.cs | 2 +- 27 files changed, 591 insertions(+), 142 deletions(-) diff --git a/src/Umbraco.Core/Composing/TypeFinder.cs b/src/Umbraco.Core/Composing/TypeFinder.cs index 9d88153b0a..2f2d81a72c 100644 --- a/src/Umbraco.Core/Composing/TypeFinder.cs +++ b/src/Umbraco.Core/Composing/TypeFinder.cs @@ -1,15 +1,16 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Configuration; +using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; using System.Security; using System.Text; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Exceptions; -using Umbraco.Core.IO; using Umbraco.Core.Logging; namespace Umbraco.Core.Composing @@ -18,91 +19,26 @@ namespace Umbraco.Core.Composing public class TypeFinder : ITypeFinder { private readonly ILogger _logger; + private readonly IAssemblyProvider _assemblyProvider; - public TypeFinder(ILogger logger, ITypeFinderConfig typeFinderConfig = null) + //public TypeFinder(ILogger logger, ITypeFinderConfig typeFinderConfig = null) + // : this(logger, new DefaultUmbracoAssemblyProvider(Assembly.GetEntryAssembly()?.GetName()?.Name), typeFinderConfig) + //{ + //} + + public TypeFinder(ILogger logger, IAssemblyProvider assemblyProvider, ITypeFinderConfig typeFinderConfig = null) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _assembliesAcceptingLoadExceptions = typeFinderConfig?.AssembliesAcceptingLoadExceptions.Where(x => !x.IsNullOrWhiteSpace()).ToArray() ?? Array.Empty(); - _allAssemblies = new Lazy>(() => - { - HashSet assemblies = null; - try - { - //NOTE: we cannot use AppDomain.CurrentDomain.GetAssemblies() because this only returns assemblies that have - // already been loaded in to the app domain, instead we will look directly into the bin folder and load each one. - var binFolder = GetRootDirectorySafe(); - var binAssemblyFiles = Directory.GetFiles(binFolder, "*.dll", SearchOption.TopDirectoryOnly).ToList(); - //var binFolder = Assembly.GetExecutingAssembly().GetAssemblyFile().Directory; - //var binAssemblyFiles = Directory.GetFiles(binFolder.FullName, "*.dll", SearchOption.TopDirectoryOnly).ToList(); - assemblies = new HashSet(); - foreach (var a in binAssemblyFiles) - { - try - { - var assName = AssemblyName.GetAssemblyName(a); - var ass = Assembly.Load(assName); - assemblies.Add(ass); - } - catch (Exception e) - { - if (e is SecurityException || e is BadImageFormatException) - { - //swallow these exceptions - } - else - { - throw; - } - } - } - - //Since we are only loading in the /bin assemblies above, we will also load in anything that's already loaded (which will include gac items) - foreach (var a in AppDomain.CurrentDomain.GetAssemblies()) - { - assemblies.Add(a); - } - } - catch (InvalidOperationException e) - { - if (e.InnerException is SecurityException == false) - throw; - } - - return assemblies; - }); + _assemblyProvider = assemblyProvider; + _assembliesAcceptingLoadExceptions = typeFinderConfig?.AssembliesAcceptingLoadExceptions.Where(x => !x.IsNullOrWhiteSpace()).ToArray() ?? Array.Empty(); } - //Lazy access to the all assemblies list - private readonly Lazy> _allAssemblies; private volatile HashSet _localFilteredAssemblyCache; private readonly object _localFilteredAssemblyCacheLocker = new object(); private readonly List _notifiedLoadExceptionAssemblies = new List(); - private static readonly ConcurrentDictionary TypeNamesCache= new ConcurrentDictionary(); - private string _rootDir = ""; + private static readonly ConcurrentDictionary TypeNamesCache= new ConcurrentDictionary(); private readonly string[] _assembliesAcceptingLoadExceptions; - // FIXME - this is only an interim change, once the IIOHelper stuff is merged we should use IIOHelper here - private string GetRootDirectorySafe() - { - if (string.IsNullOrEmpty(_rootDir) == false) - { - return _rootDir; - } - - var codeBase = Assembly.GetExecutingAssembly().CodeBase; - var uri = new Uri(codeBase); - var path = uri.LocalPath; - var baseDirectory = Path.GetDirectoryName(path); - if (string.IsNullOrEmpty(baseDirectory)) - throw new PanicException("No root directory could be resolved."); - - _rootDir = baseDirectory.Contains("bin") - ? baseDirectory.Substring(0, baseDirectory.LastIndexOf("bin", StringComparison.OrdinalIgnoreCase) - 1) - : baseDirectory; - - return _rootDir; - } - private bool AcceptsLoadExceptions(Assembly a) { if (_assembliesAcceptingLoadExceptions.Length == 0) @@ -119,22 +55,8 @@ namespace Umbraco.Core.Composing }); } - /// - /// lazily load a reference to all assemblies and only local assemblies. - /// This is a modified version of: http://www.dominicpettifer.co.uk/Blog/44/how-to-get-a-reference-to-all-assemblies-in-the--bin-folder - /// - /// - /// We do this because we cannot use AppDomain.Current.GetAssemblies() as this will return only assemblies that have been - /// loaded in the CLR, not all assemblies. - /// See these threads: - /// http://issues.umbraco.org/issue/U5-198 - /// http://stackoverflow.com/questions/3552223/asp-net-appdomain-currentdomain-getassemblies-assemblies-missing-after-app - /// http://stackoverflow.com/questions/2477787/difference-between-appdomain-getassemblies-and-buildmanager-getreferencedassembl - /// - private IEnumerable GetAllAssemblies() - { - return _allAssemblies.Value; - } + + private IEnumerable GetAllAssemblies() => _assemblyProvider.Assemblies; /// public IEnumerable AssembliesToScan @@ -522,6 +444,525 @@ namespace Umbraco.Core.Composing #endregion + } + /// + /// lazily load a reference to all local assemblies and gac assemblies + /// + /// + /// This is a modified version of: http://www.dominicpettifer.co.uk/Blog/44/how-to-get-a-reference-to-all-assemblies-in-the--bin-folder + /// + /// We do this because we cannot use AppDomain.Current.GetAssemblies() as this will return only assemblies that have been + /// loaded in the CLR, not all assemblies. + /// See these threads: + /// http://issues.umbraco.org/issue/U5-198 + /// http://stackoverflow.com/questions/3552223/asp-net-appdomain-currentdomain-getassemblies-assemblies-missing-after-app + /// http://stackoverflow.com/questions/2477787/difference-between-appdomain-getassemblies-and-buildmanager-getreferencedassembl + /// + public class BruteForceAssemblyProvider : IAssemblyProvider + { + public BruteForceAssemblyProvider() + { + _allAssemblies = new Lazy>(() => + { + HashSet assemblies = null; + try + { + //NOTE: we cannot use AppDomain.CurrentDomain.GetAssemblies() because this only returns assemblies that have + // already been loaded in to the app domain, instead we will look directly into the bin folder and load each one. + var binFolder = GetRootDirectorySafe(); + var binAssemblyFiles = Directory.GetFiles(binFolder, "*.dll", SearchOption.TopDirectoryOnly).ToList(); + assemblies = new HashSet(); + foreach (var a in binAssemblyFiles) + { + try + { + var assName = AssemblyName.GetAssemblyName(a); + var ass = Assembly.Load(assName); + assemblies.Add(ass); + } + catch (Exception e) + { + if (e is SecurityException || e is BadImageFormatException) + { + //swallow these exceptions + } + else + { + throw; + } + } + } + + //Since we are only loading in the /bin assemblies above, we will also load in anything that's already loaded (which will include gac items) + foreach (var a in AppDomain.CurrentDomain.GetAssemblies()) + { + assemblies.Add(a); + } + } + catch (InvalidOperationException e) + { + if (e.InnerException is SecurityException == false) + throw; + } + + return assemblies; + }); + } + + private readonly Lazy> _allAssemblies; + private string _rootDir = string.Empty; + + public IEnumerable Assemblies => _allAssemblies.Value; + + // FIXME - this is only an interim change, once the IIOHelper stuff is merged we should use IIOHelper here + private string GetRootDirectorySafe() + { + if (string.IsNullOrEmpty(_rootDir) == false) + { + return _rootDir; + } + + var codeBase = Assembly.GetExecutingAssembly().CodeBase; + var uri = new Uri(codeBase); + var path = uri.LocalPath; + var baseDirectory = Path.GetDirectoryName(path); + if (string.IsNullOrEmpty(baseDirectory)) + throw new PanicException("No root directory could be resolved."); + + _rootDir = baseDirectory.Contains("bin") + ? baseDirectory.Substring(0, baseDirectory.LastIndexOf("bin", StringComparison.OrdinalIgnoreCase) - 1) + : baseDirectory; + + return _rootDir; + } + } + + /// + /// Provides a list of loaded assemblies that can be scanned + /// + public interface IAssemblyProvider + { + IEnumerable Assemblies { get; } + } + + /// + /// Discovers assemblies that are part of the Umbraco application using the DependencyContext. + /// + /// + /// Happily "borrowed" from aspnet: https://github.com/aspnet/Mvc/blob/230a13d0e13e4c7e192bc6623762bfd4cde726ef/src/Microsoft.AspNetCore.Mvc.Core/Internal/DefaultAssemblyPartDiscoveryProvider.cs + /// + /// TODO: Happily borrow their unit tests too + /// + public class DefaultUmbracoAssemblyProvider : IAssemblyProvider + { + private readonly string _entryPointAssemblyName; + + public DefaultUmbracoAssemblyProvider(string entryPointAssemblyName) + { + if (string.IsNullOrWhiteSpace(entryPointAssemblyName)) + throw new ArgumentException($"{entryPointAssemblyName} cannot be null or empty", nameof(entryPointAssemblyName)); + + _entryPointAssemblyName = entryPointAssemblyName; + } + + public IEnumerable Assemblies + { + get + { + var finder = new FindAssembliesWithReferencesTo(new[] { _entryPointAssemblyName }, new[] { "Umbraco.Core" }); + foreach(var found in finder.Find()) + { + yield return Assembly.Load(found); + } + } + } + + //public IEnumerable Assemblies => DiscoverAssemblyParts(_entryPointAssemblyName); + + //internal static HashSet ReferenceAssemblies { get; } = new HashSet(StringComparer.OrdinalIgnoreCase) + //{ + // "Umbraco.Core", + // "Umbraco.Web" + //}; + + //internal static IEnumerable DiscoverAssemblyParts(string entryPointAssemblyName) + //{ + // var entryAssembly = Assembly.Load(new AssemblyName(entryPointAssemblyName)); + // var context = DependencyContext.Load(entryAssembly); + + // var candidates = GetCandidateAssemblies(entryAssembly, context); + + // return candidates; + //} + + //internal static IEnumerable GetCandidateAssemblies(Assembly entryAssembly, DependencyContext dependencyContext) + //{ + // if (dependencyContext == null) + // { + // // Use the entry assembly as the sole candidate. + // return new[] { entryAssembly }; + // } + + // //includeRefLibs == true - so that Umbraco.Core is also returned! + // return GetCandidateLibraries(dependencyContext, includeRefLibs: true) + // .SelectMany(library => library.GetDefaultAssemblyNames(dependencyContext)) + // .Select(Assembly.Load); + //} + + ///// + ///// Returns a list of libraries that references the assemblies in . + ///// + ///// + ///// + ///// True to also include libs in the ReferenceAssemblies list + ///// + ///// + //internal static IEnumerable GetCandidateLibraries(DependencyContext dependencyContext, bool includeRefLibs) + //{ + // if (ReferenceAssemblies == null) + // { + // return Enumerable.Empty(); + // } + + // var candidatesResolver = new CandidateResolver(dependencyContext.RuntimeLibraries, ReferenceAssemblies, includeRefLibs); + // return candidatesResolver.GetCandidates(); + //} + + //private class CandidateResolver + //{ + // private readonly bool _includeRefLibs; + // private readonly IDictionary _dependencies; + + // /// + // /// Constructor + // /// + // /// + // /// + // /// + // /// True to also include libs in the ReferenceAssemblies list + // /// + // public CandidateResolver(IEnumerable dependencies, ISet referenceAssemblies, bool includeRefLibs) + // { + // _includeRefLibs = includeRefLibs; + + // _dependencies = dependencies + // .ToDictionary(d => d.Name, d => CreateDependency(d, referenceAssemblies), StringComparer.OrdinalIgnoreCase); + // } + + // /// + // /// Create a Dependency + // /// + // /// + // /// + // /// + // private static Dependency CreateDependency(RuntimeLibrary library, ISet referenceAssemblies) + // { + // var classification = DependencyClassification.Unknown; + // if (referenceAssemblies.Contains(library.Name)) + // { + // classification = DependencyClassification.UmbracoReference; + // } + + // return new Dependency(library, classification); + // } + + // private DependencyClassification ComputeClassification(string dependency) + // { + // Debug.Assert(_dependencies.ContainsKey(dependency)); + + // var candidateEntry = _dependencies[dependency]; + // if (candidateEntry.Classification != DependencyClassification.Unknown) + // { + // return candidateEntry.Classification; + // } + // else + // { + // var classification = DependencyClassification.NotCandidate; + // foreach (var candidateDependency in candidateEntry.Library.Dependencies) + // { + // var dependencyClassification = ComputeClassification(candidateDependency.Name); + // if (dependencyClassification == DependencyClassification.Candidate || + // dependencyClassification == DependencyClassification.UmbracoReference) + // { + // classification = DependencyClassification.Candidate; + // break; + // } + // } + + // candidateEntry.Classification = classification; + + // return classification; + // } + // } + + // public IEnumerable GetCandidates() + // { + // foreach (var dependency in _dependencies) + // { + // var classification = ComputeClassification(dependency.Key); + // if (classification == DependencyClassification.Candidate || + // //if the flag is set, also ensure to include any UmbracoReference classifications + // (_includeRefLibs && classification == DependencyClassification.UmbracoReference)) + // { + // yield return dependency.Value.Library; + // } + // } + // } + + // private class Dependency + // { + // public Dependency(RuntimeLibrary library, DependencyClassification classification) + // { + // Library = library; + // Classification = classification; + // } + + // public RuntimeLibrary Library { get; } + + // public DependencyClassification Classification { get; set; } + + // public override string ToString() + // { + // return $"Library: {Library.Name}, Classification: {Classification}"; + // } + // } + + // private enum DependencyClassification + // { + // Unknown = 0, + // Candidate = 1, + // NotCandidate = 2, + // UmbracoReference = 3 + // } + //} + } + + /// + /// Resolves assemblies that reference one of the specified "targetAssemblies" either directly or transitively. + /// + public class ReferenceResolver + { + private readonly HashSet _mvcAssemblies; + private readonly IReadOnlyList _assemblyItems; + private readonly Dictionary _classifications; + + public ReferenceResolver(IReadOnlyList targetAssemblies, IReadOnlyList assemblyItems) + { + _mvcAssemblies = new HashSet(targetAssemblies, StringComparer.Ordinal); + _assemblyItems = assemblyItems; + _classifications = new Dictionary(); + + Lookup = new Dictionary(StringComparer.Ordinal); + foreach (var item in assemblyItems) + { + Lookup[item.AssemblyName] = item; + } + } + + protected Dictionary Lookup { get; } + + public IReadOnlyList ResolveAssemblies() + { + var applicationParts = new List(); + + foreach (var item in _assemblyItems) + { + var classification = Resolve(item); + if (classification == Classification.ReferencesMvc) + { + applicationParts.Add(item.AssemblyName); + } + } + + return applicationParts; + } + + private Classification Resolve(AssemblyItem assemblyItem) + { + if (_classifications.TryGetValue(assemblyItem, out var classification)) + { + return classification; + } + + // Initialize the dictionary with a value to short-circuit recursive references. + classification = Classification.Unknown; + _classifications[assemblyItem] = classification; + + if (assemblyItem.Path == null) + { + // We encountered a dependency that isn't part of this assembly's dependency set. We'll see if it happens to be an MVC assembly + // since that's the only useful determination we can make given the assembly name. + classification = _mvcAssemblies.Contains(assemblyItem.AssemblyName) ? + Classification.IsMvc : + Classification.DoesNotReferenceMvc; + } + else if (assemblyItem.IsFrameworkReference) + { + // We do not allow transitive references to MVC via a framework reference to count. + // e.g. depending on Microsoft.AspNetCore.SomeThingNewThatDependsOnMvc would not result in an assembly being treated as + // referencing MVC. + classification = _mvcAssemblies.Contains(assemblyItem.AssemblyName) ? + Classification.IsMvc : + Classification.DoesNotReferenceMvc; + } + else if (_mvcAssemblies.Contains(assemblyItem.AssemblyName)) + { + classification = Classification.IsMvc; + } + else + { + classification = Classification.DoesNotReferenceMvc; + foreach (var reference in GetReferences(assemblyItem.Path)) + { + var referenceClassification = Resolve(reference); + + if (referenceClassification == Classification.IsMvc || referenceClassification == Classification.ReferencesMvc) + { + classification = Classification.ReferencesMvc; + break; + } + } + } + + Debug.Assert(classification != Classification.Unknown); + _classifications[assemblyItem] = classification; + return classification; + } + + protected virtual IReadOnlyList GetReferences(string file) + { + try + { + if (!File.Exists(file)) + { + throw new ReferenceAssemblyNotFoundException(file); + } + + using var peReader = new PEReader(File.OpenRead(file)); + if (!peReader.HasMetadata) + { + return Array.Empty(); // not a managed assembly + } + + var metadataReader = peReader.GetMetadataReader(); + + var references = new List(); + foreach (var handle in metadataReader.AssemblyReferences) + { + var reference = metadataReader.GetAssemblyReference(handle); + var referenceName = metadataReader.GetString(reference.Name); + + if (!Lookup.TryGetValue(referenceName, out var assemblyItem)) + { + // A dependency references an item that isn't referenced by this project. + // We'll construct an item for so that we can calculate the classification based on it's name. + assemblyItem = new AssemblyItem + { + AssemblyName = referenceName, + }; + + Lookup[referenceName] = assemblyItem; + } + + references.Add(assemblyItem); + } + + return references; + } + catch (BadImageFormatException) + { + // not a PE file, or invalid metadata + } + + return Array.Empty(); // not a managed assembly + } + + protected enum Classification + { + Unknown, + DoesNotReferenceMvc, + ReferencesMvc, + IsMvc, + } + } + + public class AssemblyItem + { + public string Path { get; set; } + + public bool IsFrameworkReference { get; set; } + + public string AssemblyName { get; set; } + } + + internal class ReferenceAssemblyNotFoundException : Exception + { + public ReferenceAssemblyNotFoundException(string fileName) + { + FileName = fileName; + } + + public string FileName { get; } + } + + // borrowed from here https://github.com/dotnet/aspnetcore-tooling/blob/master/src/Razor/src/Microsoft.NET.Sdk.Razor/FindAssembliesWithReferencesTo.cs + public class FindAssembliesWithReferencesTo + { + private readonly string[] _referenceAssemblies; + private readonly string[] _targetAssemblyNames; + + public FindAssembliesWithReferencesTo(string[] referenceAssemblies, string[] targetAssemblyNames) + { + _referenceAssemblies = referenceAssemblies; + _targetAssemblyNames = targetAssemblyNames; + } + + public IEnumerable Find() + { + var referenceItems = new List(); + foreach (var item in _referenceAssemblies) + { + //var assemblyName = new AssemblyName(item).Name; + var assembly = Assembly.Load(item); + referenceItems.Add(new AssemblyItem + { + AssemblyName = assembly.GetName().Name, //assemblyName, + IsFrameworkReference = false, + Path = GetAssemblyLocation(assembly) + }); + } + + var provider = new ReferenceResolver(_targetAssemblyNames, referenceItems); + try + { + var assemblyNames = provider.ResolveAssemblies(); + return assemblyNames.ToArray(); + } + catch (ReferenceAssemblyNotFoundException ex) + { + throw; + //// Print a warning and return. We cannot produce a correct document at this point. + //var warning = "Reference assembly {0} could not be found. This is typically caused by build errors in referenced projects."; + //Log.LogWarning(null, "RAZORSDK1007", null, null, 0, 0, 0, 0, warning, ex.FileName); + //return true; + } + catch (Exception ex) + { + throw; + //Log.LogErrorFromException(ex); + } + } + + internal static string GetAssemblyLocation(Assembly assembly) + { + if (Uri.TryCreate(assembly.CodeBase, UriKind.Absolute, out var result) && + result.IsFile && string.IsNullOrWhiteSpace(result.Fragment)) + { + return result.LocalPath; + } + + return assembly.Location; + } } } diff --git a/src/Umbraco.Core/Composing/TypeLoader.cs b/src/Umbraco.Core/Composing/TypeLoader.cs index 76d00c472d..4d8b5c984c 100644 --- a/src/Umbraco.Core/Composing/TypeLoader.cs +++ b/src/Umbraco.Core/Composing/TypeLoader.cs @@ -516,29 +516,29 @@ namespace Umbraco.Core.Composing #region Get Assembly Attributes - /// - /// Gets the assembly attributes of the specified type . - /// - /// The attribute type. - /// - /// The assembly attributes of the specified type . - /// - public IEnumerable GetAssemblyAttributes() - where T : Attribute - { - return AssembliesToScan.SelectMany(a => a.GetCustomAttributes()).ToList(); - } + ///// + ///// Gets the assembly attributes of the specified type . + ///// + ///// The attribute type. + ///// + ///// The assembly attributes of the specified type . + ///// + //public IEnumerable GetAssemblyAttributes() + // where T : Attribute + //{ + // return AssembliesToScan.SelectMany(a => a.GetCustomAttributes()).ToList(); + //} - /// - /// Gets all the assembly attributes. - /// - /// - /// All assembly attributes. - /// - public IEnumerable GetAssemblyAttributes() - { - return AssembliesToScan.SelectMany(a => a.GetCustomAttributes()).ToList(); - } + ///// + ///// Gets all the assembly attributes. + ///// + ///// + ///// All assembly attributes. + ///// + //public IEnumerable GetAssemblyAttributes() + //{ + // return AssembliesToScan.SelectMany(a => a.GetCustomAttributes()).ToList(); + //} /// /// Gets the assembly attributes of the specified . diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 7a15e7fbed..49d5090070 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -13,6 +13,7 @@ + diff --git a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs index 8e4401495d..2422e6018f 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Reflection; using Umbraco.Core.Cache; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; @@ -369,7 +370,7 @@ namespace Umbraco.Core.Runtime /// /// protected virtual ITypeFinder GetTypeFinder() - => new TypeFinder(Logger); + => new TypeFinder(Logger, new DefaultUmbracoAssemblyProvider(Assembly.GetEntryAssembly()?.GetName()?.Name)); /// diff --git a/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs b/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs index 3a504ad4e2..bba7ea0061 100644 --- a/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs +++ b/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs @@ -28,7 +28,7 @@ namespace Umbraco.Tests.Cache public override void Setup() { base.Setup(); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); _memberCache = new ObjectCacheAppCache(typeFinder); _provider = new DeepCloneAppCache(_memberCache); diff --git a/src/Umbraco.Tests/Cache/HttpRequestAppCacheTests.cs b/src/Umbraco.Tests/Cache/HttpRequestAppCacheTests.cs index b9c948c1de..d2d3b795d6 100644 --- a/src/Umbraco.Tests/Cache/HttpRequestAppCacheTests.cs +++ b/src/Umbraco.Tests/Cache/HttpRequestAppCacheTests.cs @@ -16,7 +16,7 @@ namespace Umbraco.Tests.Cache public override void Setup() { base.Setup(); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); _ctx = new FakeHttpContextFactory("http://localhost/test"); _appCache = new HttpRequestAppCache(() => _ctx.HttpContext.Items, typeFinder); } diff --git a/src/Umbraco.Tests/Cache/ObjectAppCacheTests.cs b/src/Umbraco.Tests/Cache/ObjectAppCacheTests.cs index 772e92cabe..5d6808f678 100644 --- a/src/Umbraco.Tests/Cache/ObjectAppCacheTests.cs +++ b/src/Umbraco.Tests/Cache/ObjectAppCacheTests.cs @@ -21,7 +21,7 @@ namespace Umbraco.Tests.Cache public override void Setup() { base.Setup(); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); _provider = new ObjectCacheAppCache(typeFinder); } diff --git a/src/Umbraco.Tests/Components/ComponentTests.cs b/src/Umbraco.Tests/Components/ComponentTests.cs index bef72e5fb7..9d44aa562e 100644 --- a/src/Umbraco.Tests/Components/ComponentTests.cs +++ b/src/Umbraco.Tests/Components/ComponentTests.cs @@ -32,7 +32,7 @@ namespace Umbraco.Tests.Components var mock = new Mock(); var logger = Mock.Of(); - var typeFinder = new TypeFinder(logger); + var typeFinder = new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(typeof(ComponentTests).Assembly.GetName().Name)); var f = new UmbracoDatabaseFactory(logger, new Lazy(() => new MapperCollection(Enumerable.Empty())), TestHelper.GetConfigs(), TestHelper.DbProviderFactoryCreator); var fs = new FileSystems(mock.Object, logger, TestHelper.IOHelper, SettingsForTests.GenerateMockGlobalSettings()); var coreDebug = Mock.Of(); @@ -371,7 +371,7 @@ namespace Umbraco.Tests.Components public void AllComposers() { var ioHelper = TestHelper.IOHelper; - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); var typeLoader = new TypeLoader(ioHelper, typeFinder, AppCaches.Disabled.RuntimeCache, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), Mock.Of()); var register = MockRegister(); diff --git a/src/Umbraco.Tests/Composing/ComposingTestBase.cs b/src/Umbraco.Tests/Composing/ComposingTestBase.cs index ac7dd8be2a..c4d048cd7b 100644 --- a/src/Umbraco.Tests/Composing/ComposingTestBase.cs +++ b/src/Umbraco.Tests/Composing/ComposingTestBase.cs @@ -22,7 +22,7 @@ namespace Umbraco.Tests.Composing { ProfilingLogger = new ProfilingLogger(Mock.Of(), Mock.Of()); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); var ioHelper = TestHelper.IOHelper; TypeLoader = new TypeLoader(ioHelper, typeFinder, NoAppCache.Instance, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), ProfilingLogger, false, AssembliesToScan); } diff --git a/src/Umbraco.Tests/Composing/CompositionTests.cs b/src/Umbraco.Tests/Composing/CompositionTests.cs index 4dfaf6871d..a720fe045f 100644 --- a/src/Umbraco.Tests/Composing/CompositionTests.cs +++ b/src/Umbraco.Tests/Composing/CompositionTests.cs @@ -38,7 +38,7 @@ namespace Umbraco.Tests.Composing .Returns(() => factoryFactory?.Invoke(mockedFactory)); var logger = new ProfilingLogger(Mock.Of(), Mock.Of()); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); var ioHelper = TestHelper.IOHelper; var typeLoader = new TypeLoader(ioHelper, typeFinder, Mock.Of(), new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), logger); var composition = new Composition(mockedRegister, typeLoader, logger, Mock.Of(), TestHelper.GetConfigs(), TestHelper.IOHelper, AppCaches.NoCache); diff --git a/src/Umbraco.Tests/Composing/TypeFinderTests.cs b/src/Umbraco.Tests/Composing/TypeFinderTests.cs index 5fe4c241d6..969be8c0fd 100644 --- a/src/Umbraco.Tests/Composing/TypeFinderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeFinderTests.cs @@ -57,7 +57,7 @@ namespace Umbraco.Tests.Composing [Test] public void Find_Class_Of_Type_With_Attribute() { - var typeFinder = new TypeFinder(GetTestProfilingLogger()); + var typeFinder = new TypeFinder(GetTestProfilingLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); var typesFound = typeFinder.FindClassesOfTypeWithAttribute(_assemblies); Assert.AreEqual(2, typesFound.Count()); } @@ -65,12 +65,15 @@ namespace Umbraco.Tests.Composing [Test] public void Find_Classes_With_Attribute() { - var typeFinder = new TypeFinder(GetTestProfilingLogger()); + var typeFinder = new TypeFinder(GetTestProfilingLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); var typesFound = typeFinder.FindClassesWithAttribute(_assemblies); Assert.AreEqual(0, typesFound.Count()); // 0 classes in _assemblies are marked with [Tree] typesFound = typeFinder.FindClassesWithAttribute(new[] { typeof (UmbracoContext).Assembly }); Assert.AreEqual(22, typesFound.Count()); // + classes in Umbraco.Web are marked with [Tree] + + typesFound = typeFinder.FindClassesWithAttribute(); + Assert.AreEqual(22, typesFound.Count()); // + classes in Umbraco.Web are marked with [Tree] } private static IProfilingLogger GetTestProfilingLogger() diff --git a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs index 6658c689e1..2d6f1b85e8 100644 --- a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs @@ -27,7 +27,7 @@ namespace Umbraco.Tests.Composing public void Initialize() { // this ensures it's reset - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); _typeLoader = new TypeLoader(TestHelper.IOHelper, typeFinder, NoAppCache.Instance, new DirectoryInfo(TestHelper.IOHelper.MapPath("~/App_Data/TEMP")), new ProfilingLogger(Mock.Of(), Mock.Of()), false, diff --git a/src/Umbraco.Tests/Macros/MacroTests.cs b/src/Umbraco.Tests/Macros/MacroTests.cs index 66adfc4a97..3cc77b6732 100644 --- a/src/Umbraco.Tests/Macros/MacroTests.cs +++ b/src/Umbraco.Tests/Macros/MacroTests.cs @@ -17,7 +17,7 @@ namespace Umbraco.Tests.Macros [SetUp] public void Setup() { - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); //we DO want cache enabled for these tests var cacheHelper = new AppCaches( new ObjectCacheAppCache(typeFinder), diff --git a/src/Umbraco.Tests/Models/ContentTests.cs b/src/Umbraco.Tests/Models/ContentTests.cs index 7dec69e3be..9a7e25944f 100644 --- a/src/Umbraco.Tests/Models/ContentTests.cs +++ b/src/Umbraco.Tests/Models/ContentTests.cs @@ -269,7 +269,7 @@ namespace Umbraco.Tests.Models content.UpdateDate = DateTime.Now; content.WriterId = 23; - var runtimeCache = new ObjectCacheAppCache(new TypeFinder(Mock.Of())); + var runtimeCache = new ObjectCacheAppCache(new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name))); runtimeCache.Insert(content.Id.ToString(CultureInfo.InvariantCulture), () => content); var proflog = GetTestProfilingLogger(); diff --git a/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs b/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs index d3e6dae26b..329626a984 100644 --- a/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs +++ b/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs @@ -127,7 +127,7 @@ namespace Umbraco.Tests.Published var setType1 = publishedContentTypeFactory.CreateContentType(1000, "set1", CreatePropertyTypes); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); var elementsCache = new FastDictionaryAppCache(typeFinder); var snapshotCache = new FastDictionaryAppCache(typeFinder); diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs index de12fcf7aa..d868e8bc2e 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs @@ -143,7 +143,7 @@ namespace Umbraco.Tests.PublishedContent // create a data source for NuCache _source = new TestDataSource(kits); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); var settings = Mock.Of(); diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs index 08da25ba9a..cf2be84f24 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs @@ -184,7 +184,7 @@ namespace Umbraco.Tests.PublishedContent // create a variation accessor _variationAccesor = new TestVariationContextAccessor(); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); var settings = Mock.Of(); // at last, create the complete NuCache snapshot service! diff --git a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs index 6811f9f8de..b97aae2e7e 100644 --- a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs +++ b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs @@ -63,7 +63,7 @@ namespace Umbraco.Tests.Runtimes var profilingLogger = new ProfilingLogger(logger, profiler); var appCaches = AppCaches.Disabled; var databaseFactory = new UmbracoDatabaseFactory(logger, new Lazy(() => factory.GetInstance()), TestHelper.GetConfigs(), TestHelper.DbProviderFactoryCreator); - var typeFinder = new TypeFinder(logger); + var typeFinder = new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); var ioHelper = TestHelper.IOHelper; var hostingEnvironment = Mock.Of(); var typeLoader = new TypeLoader(ioHelper, typeFinder, appCaches.RuntimeCache, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), profilingLogger); @@ -256,7 +256,7 @@ namespace Umbraco.Tests.Runtimes var profilingLogger = new ProfilingLogger(logger, profiler); var appCaches = AppCaches.Disabled; var databaseFactory = Mock.Of(); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); var ioHelper = TestHelper.IOHelper; var typeLoader = new TypeLoader(ioHelper, typeFinder, appCaches.RuntimeCache, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), profilingLogger); var runtimeState = Mock.Of(); diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index 783beafb2e..5cc10d735d 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -85,7 +85,7 @@ namespace Umbraco.Tests.Scoping var memberRepository = Mock.Of(); var hostingEnvironment = TestHelper.GetHostingEnvironment(); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); var settings = Mock.Of(); return new PublishedSnapshotService( diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs index 1b6b632a10..a2855aa1bd 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs @@ -58,7 +58,7 @@ namespace Umbraco.Tests.Services var memberRepository = Mock.Of(); var hostingEnvironment = Mock.Of(); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); var settings = Mock.Of(); return new PublishedSnapshotService( diff --git a/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs b/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs index 2bd36947c6..0f54725246 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs @@ -38,7 +38,7 @@ namespace Umbraco.Tests.TestHelpers var ioHelper = TestHelper.IOHelper; var logger = new ProfilingLogger(Mock.Of(), Mock.Of()); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); var typeLoader = new TypeLoader(ioHelper, typeFinder, NoAppCache.Instance, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), logger, diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs index c79a0f5c47..773ef85b1b 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs @@ -40,7 +40,7 @@ namespace Umbraco.Tests.TestHelpers.Stubs { if (_factory != null) return _factory(requestContext); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); var types = typeFinder.FindClassesOfType(new[] { Assembly.GetExecutingAssembly() }); var controllerTypes = types.Where(x => x.Name.Equals(controllerName + "Controller", StringComparison.InvariantCultureIgnoreCase)); diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects.cs b/src/Umbraco.Tests/TestHelpers/TestObjects.cs index f237fb0fc7..32b891f2eb 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects.cs @@ -247,7 +247,7 @@ namespace Umbraco.Tests.TestHelpers databaseFactory = new UmbracoDatabaseFactory(Constants.System.UmbracoConnectionName, logger, new Lazy(() => mappers), TestHelper.GetConfigs(), TestHelper.DbProviderFactoryCreator); } - typeFinder = typeFinder ?? new TypeFinder(logger); + typeFinder = typeFinder ?? new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); fileSystems = fileSystems ?? new FileSystems(Current.Factory, logger, TestHelper.IOHelper, SettingsForTests.GenerateMockGlobalSettings()); var coreDebug = Current.Configs.CoreDebug(); var mediaFileSystem = Mock.Of(); diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index de0db554f3..f064e7ffa9 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -171,7 +171,7 @@ namespace Umbraco.Tests.Testing var proflogger = new ProfilingLogger(logger, profiler); IOHelper = TestHelper.IOHelper; - TypeFinder = new TypeFinder(logger); + TypeFinder = new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); var appCaches = GetAppCaches(); var globalSettings = SettingsForTests.GetDefaultGlobalSettings(); var settings = SettingsForTests.GetDefaultUmbracoSettings(); diff --git a/src/Umbraco.Tests/Web/UmbracoHelperTests.cs b/src/Umbraco.Tests/Web/UmbracoHelperTests.cs index b479961896..e4ae4c9a58 100644 --- a/src/Umbraco.Tests/Web/UmbracoHelperTests.cs +++ b/src/Umbraco.Tests/Web/UmbracoHelperTests.cs @@ -28,7 +28,7 @@ namespace Umbraco.Tests.Web { // FIXME: bad in a unit test - but Udi has a static ctor that wants it?! var container = new Mock(); - var typeFinder = new TypeFinder(Mock.Of()); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); var ioHelper = TestHelper.IOHelper; container .Setup(x => x.GetInstance(typeof(TypeLoader))) diff --git a/src/Umbraco.Web/Composing/BuildManagerTypeFinder.cs b/src/Umbraco.Web/Composing/BuildManagerTypeFinder.cs index 994e8c26c4..bd583bf29d 100644 --- a/src/Umbraco.Web/Composing/BuildManagerTypeFinder.cs +++ b/src/Umbraco.Web/Composing/BuildManagerTypeFinder.cs @@ -27,7 +27,8 @@ namespace Umbraco.Web.Composing IIOHelper ioHelper, IHostingEnvironment hostingEnvironment, ILogger logger, - ITypeFinderConfig typeFinderConfig = null) : base(logger, typeFinderConfig) + IAssemblyProvider assemblyProvider, + ITypeFinderConfig typeFinderConfig = null) : base(logger, assemblyProvider, typeFinderConfig) { if (ioHelper == null) throw new ArgumentNullException(nameof(ioHelper)); if (hostingEnvironment == null) throw new ArgumentNullException(nameof(hostingEnvironment)); @@ -60,6 +61,8 @@ namespace Umbraco.Web.Composing logger.Error(typeof(TypeFinder), ex, "Could not load assembly App_Code"); } } + + return assemblies; } } catch (InvalidOperationException e) diff --git a/src/Umbraco.Web/Runtime/WebRuntime.cs b/src/Umbraco.Web/Runtime/WebRuntime.cs index 582f35db70..5fab9ff9d4 100644 --- a/src/Umbraco.Web/Runtime/WebRuntime.cs +++ b/src/Umbraco.Web/Runtime/WebRuntime.cs @@ -92,7 +92,7 @@ namespace Umbraco.Web.Runtime #region Getters - protected override ITypeFinder GetTypeFinder() => _typeFinder ?? (_typeFinder = new BuildManagerTypeFinder(IOHelper, HostingEnvironment, Logger, new BuildManagerTypeFinder.TypeFinderConfig(new TypeFinderSettings()))); + //protected override ITypeFinder GetTypeFinder() => _typeFinder ?? (_typeFinder = new BuildManagerTypeFinder(IOHelper, HostingEnvironment, Logger, new BuildManagerTypeFinder.TypeFinderConfig(new TypeFinderSettings()))); protected override AppCaches GetAppCaches() => new AppCaches( // we need to have the dep clone runtime cache provider to ensure From 0c8426f308fc2ace625195c68f6d0a55e74c2de5 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 9 Mar 2020 13:14:16 +1100 Subject: [PATCH 42/69] Cleans up POC code (still more to do) --- src/Umbraco.Core/Composing/TypeFinder.cs | 407 ++++-------------- .../Runtime/CoreRuntime.cs | 2 +- .../Cache/DeepCloneAppCacheTests.cs | 2 +- .../Cache/HttpRequestAppCacheTests.cs | 2 +- .../Cache/ObjectAppCacheTests.cs | 2 +- .../Components/ComponentTests.cs | 4 +- .../Composing/ComposingTestBase.cs | 2 +- .../Composing/CompositionTests.cs | 2 +- .../Composing/TypeFinderTests.cs | 4 +- .../Composing/TypeLoaderTests.cs | 2 +- src/Umbraco.Tests/Macros/MacroTests.cs | 2 +- src/Umbraco.Tests/Models/ContentTests.cs | 2 +- .../Published/PropertyCacheLevelTests.cs | 2 +- .../PublishedContent/NuCacheChildrenTests.cs | 2 +- .../PublishedContent/NuCacheTests.cs | 2 +- src/Umbraco.Tests/Runtimes/StandaloneTests.cs | 4 +- .../Scoping/ScopedNuCacheTests.cs | 2 +- .../ContentTypeServiceVariantsTests.cs | 2 +- .../TestHelpers/BaseUsingSqlCeSyntax.cs | 2 +- .../Stubs/TestControllerFactory.cs | 2 +- src/Umbraco.Tests/TestHelpers/TestObjects.cs | 2 +- src/Umbraco.Tests/Testing/UmbracoTestBase.cs | 2 +- src/Umbraco.Tests/Web/UmbracoHelperTests.cs | 2 +- 23 files changed, 112 insertions(+), 345 deletions(-) diff --git a/src/Umbraco.Core/Composing/TypeFinder.cs b/src/Umbraco.Core/Composing/TypeFinder.cs index 2f2d81a72c..a0aab7c088 100644 --- a/src/Umbraco.Core/Composing/TypeFinder.cs +++ b/src/Umbraco.Core/Composing/TypeFinder.cs @@ -103,7 +103,8 @@ namespace Umbraco.Core.Composing /// NOTE the comma vs period... comma delimits the name in an Assembly FullName property so if it ends with comma then its an exact name match /// NOTE this means that "foo." will NOT exclude "foo.dll" but only "foo.*.dll" /// - private static readonly string[] KnownAssemblyExclusionFilter = { + internal static readonly string[] KnownAssemblyExclusionFilter = { + "mscorlib", "Antlr3.", "AutoMapper,", "AutoMapper.", @@ -539,7 +540,7 @@ namespace Umbraco.Core.Composing } /// - /// Provides a list of loaded assemblies that can be scanned + /// Provides a list of assemblies that can be scanned /// public interface IAssemblyProvider { @@ -547,238 +548,77 @@ namespace Umbraco.Core.Composing } /// - /// Discovers assemblies that are part of the Umbraco application using the DependencyContext. + /// Returns a list of scannable assemblies based on an entry point assembly and it's references /// /// - /// Happily "borrowed" from aspnet: https://github.com/aspnet/Mvc/blob/230a13d0e13e4c7e192bc6623762bfd4cde726ef/src/Microsoft.AspNetCore.Mvc.Core/Internal/DefaultAssemblyPartDiscoveryProvider.cs - /// - /// TODO: Happily borrow their unit tests too + /// This will recursively search through the entry point's assemblies and Umbraco's core assemblies (Core/Web) and their references + /// to create a list of scannable assemblies based on whether they themselves or their transitive dependencies reference Umbraco core assemblies. /// public class DefaultUmbracoAssemblyProvider : IAssemblyProvider { - private readonly string _entryPointAssemblyName; + private readonly Assembly _entryPointAssembly; + private static readonly string[] UmbracoCoreAssemblyNames = new[] { "Umbraco.Core", "Umbraco.Web" }; - public DefaultUmbracoAssemblyProvider(string entryPointAssemblyName) + public DefaultUmbracoAssemblyProvider(Assembly entryPointAssembly) { - if (string.IsNullOrWhiteSpace(entryPointAssemblyName)) - throw new ArgumentException($"{entryPointAssemblyName} cannot be null or empty", nameof(entryPointAssemblyName)); - - _entryPointAssemblyName = entryPointAssemblyName; + _entryPointAssembly = entryPointAssembly ?? throw new ArgumentNullException(nameof(entryPointAssembly)); } public IEnumerable Assemblies { get { - var finder = new FindAssembliesWithReferencesTo(new[] { _entryPointAssemblyName }, new[] { "Umbraco.Core" }); + var finder = new FindAssembliesWithReferencesTo(new[] { _entryPointAssembly }, UmbracoCoreAssemblyNames, true); foreach(var found in finder.Find()) { - yield return Assembly.Load(found); + yield return found; } } } - - //public IEnumerable Assemblies => DiscoverAssemblyParts(_entryPointAssemblyName); - - //internal static HashSet ReferenceAssemblies { get; } = new HashSet(StringComparer.OrdinalIgnoreCase) - //{ - // "Umbraco.Core", - // "Umbraco.Web" - //}; - - //internal static IEnumerable DiscoverAssemblyParts(string entryPointAssemblyName) - //{ - // var entryAssembly = Assembly.Load(new AssemblyName(entryPointAssemblyName)); - // var context = DependencyContext.Load(entryAssembly); - - // var candidates = GetCandidateAssemblies(entryAssembly, context); - - // return candidates; - //} - - //internal static IEnumerable GetCandidateAssemblies(Assembly entryAssembly, DependencyContext dependencyContext) - //{ - // if (dependencyContext == null) - // { - // // Use the entry assembly as the sole candidate. - // return new[] { entryAssembly }; - // } - - // //includeRefLibs == true - so that Umbraco.Core is also returned! - // return GetCandidateLibraries(dependencyContext, includeRefLibs: true) - // .SelectMany(library => library.GetDefaultAssemblyNames(dependencyContext)) - // .Select(Assembly.Load); - //} - - ///// - ///// Returns a list of libraries that references the assemblies in . - ///// - ///// - ///// - ///// True to also include libs in the ReferenceAssemblies list - ///// - ///// - //internal static IEnumerable GetCandidateLibraries(DependencyContext dependencyContext, bool includeRefLibs) - //{ - // if (ReferenceAssemblies == null) - // { - // return Enumerable.Empty(); - // } - - // var candidatesResolver = new CandidateResolver(dependencyContext.RuntimeLibraries, ReferenceAssemblies, includeRefLibs); - // return candidatesResolver.GetCandidates(); - //} - - //private class CandidateResolver - //{ - // private readonly bool _includeRefLibs; - // private readonly IDictionary _dependencies; - - // /// - // /// Constructor - // /// - // /// - // /// - // /// - // /// True to also include libs in the ReferenceAssemblies list - // /// - // public CandidateResolver(IEnumerable dependencies, ISet referenceAssemblies, bool includeRefLibs) - // { - // _includeRefLibs = includeRefLibs; - - // _dependencies = dependencies - // .ToDictionary(d => d.Name, d => CreateDependency(d, referenceAssemblies), StringComparer.OrdinalIgnoreCase); - // } - - // /// - // /// Create a Dependency - // /// - // /// - // /// - // /// - // private static Dependency CreateDependency(RuntimeLibrary library, ISet referenceAssemblies) - // { - // var classification = DependencyClassification.Unknown; - // if (referenceAssemblies.Contains(library.Name)) - // { - // classification = DependencyClassification.UmbracoReference; - // } - - // return new Dependency(library, classification); - // } - - // private DependencyClassification ComputeClassification(string dependency) - // { - // Debug.Assert(_dependencies.ContainsKey(dependency)); - - // var candidateEntry = _dependencies[dependency]; - // if (candidateEntry.Classification != DependencyClassification.Unknown) - // { - // return candidateEntry.Classification; - // } - // else - // { - // var classification = DependencyClassification.NotCandidate; - // foreach (var candidateDependency in candidateEntry.Library.Dependencies) - // { - // var dependencyClassification = ComputeClassification(candidateDependency.Name); - // if (dependencyClassification == DependencyClassification.Candidate || - // dependencyClassification == DependencyClassification.UmbracoReference) - // { - // classification = DependencyClassification.Candidate; - // break; - // } - // } - - // candidateEntry.Classification = classification; - - // return classification; - // } - // } - - // public IEnumerable GetCandidates() - // { - // foreach (var dependency in _dependencies) - // { - // var classification = ComputeClassification(dependency.Key); - // if (classification == DependencyClassification.Candidate || - // //if the flag is set, also ensure to include any UmbracoReference classifications - // (_includeRefLibs && classification == DependencyClassification.UmbracoReference)) - // { - // yield return dependency.Value.Library; - // } - // } - // } - - // private class Dependency - // { - // public Dependency(RuntimeLibrary library, DependencyClassification classification) - // { - // Library = library; - // Classification = classification; - // } - - // public RuntimeLibrary Library { get; } - - // public DependencyClassification Classification { get; set; } - - // public override string ToString() - // { - // return $"Library: {Library.Name}, Classification: {Classification}"; - // } - // } - - // private enum DependencyClassification - // { - // Unknown = 0, - // Candidate = 1, - // NotCandidate = 2, - // UmbracoReference = 3 - // } - //} } /// /// Resolves assemblies that reference one of the specified "targetAssemblies" either directly or transitively. /// - public class ReferenceResolver + /// + /// Borrowed and modified from https://github.com/dotnet/aspnetcore-tooling/blob/master/src/Razor/src/Microsoft.NET.Sdk.Razor/ReferenceResolver.cs + /// + internal class ReferenceResolver { - private readonly HashSet _mvcAssemblies; - private readonly IReadOnlyList _assemblyItems; - private readonly Dictionary _classifications; + private readonly HashSet _umbracoAssemblies; + private readonly IReadOnlyList _assemblyItems; + private readonly Dictionary _classifications; + private readonly List _lookup = new List(); - public ReferenceResolver(IReadOnlyList targetAssemblies, IReadOnlyList assemblyItems) + public ReferenceResolver(IReadOnlyList targetAssemblies, IReadOnlyList assemblyItems) { - _mvcAssemblies = new HashSet(targetAssemblies, StringComparer.Ordinal); + _umbracoAssemblies = new HashSet(targetAssemblies, StringComparer.Ordinal); _assemblyItems = assemblyItems; - _classifications = new Dictionary(); + _classifications = new Dictionary(); - Lookup = new Dictionary(StringComparer.Ordinal); foreach (var item in assemblyItems) { - Lookup[item.AssemblyName] = item; + _lookup.Add(item); } } - protected Dictionary Lookup { get; } - - public IReadOnlyList ResolveAssemblies() + public IEnumerable ResolveAssemblies() { - var applicationParts = new List(); + var applicationParts = new List(); foreach (var item in _assemblyItems) { var classification = Resolve(item); - if (classification == Classification.ReferencesMvc) + if (classification == Classification.ReferencesUmbraco || classification == Classification.IsUmbraco) { - applicationParts.Add(item.AssemblyName); + applicationParts.Add(item); } } return applicationParts; } - private Classification Resolve(AssemblyItem assemblyItem) + private Classification Resolve(Assembly assemblyItem) { if (_classifications.TryGetValue(assemblyItem, out var classification)) { @@ -789,37 +629,21 @@ namespace Umbraco.Core.Composing classification = Classification.Unknown; _classifications[assemblyItem] = classification; - if (assemblyItem.Path == null) + if (_umbracoAssemblies.Contains(assemblyItem.GetName().Name)) { - // We encountered a dependency that isn't part of this assembly's dependency set. We'll see if it happens to be an MVC assembly - // since that's the only useful determination we can make given the assembly name. - classification = _mvcAssemblies.Contains(assemblyItem.AssemblyName) ? - Classification.IsMvc : - Classification.DoesNotReferenceMvc; - } - else if (assemblyItem.IsFrameworkReference) - { - // We do not allow transitive references to MVC via a framework reference to count. - // e.g. depending on Microsoft.AspNetCore.SomeThingNewThatDependsOnMvc would not result in an assembly being treated as - // referencing MVC. - classification = _mvcAssemblies.Contains(assemblyItem.AssemblyName) ? - Classification.IsMvc : - Classification.DoesNotReferenceMvc; - } - else if (_mvcAssemblies.Contains(assemblyItem.AssemblyName)) - { - classification = Classification.IsMvc; + classification = Classification.IsUmbraco; } else { - classification = Classification.DoesNotReferenceMvc; - foreach (var reference in GetReferences(assemblyItem.Path)) + classification = Classification.DoesNotReferenceUmbraco; + foreach (var reference in GetReferences(assemblyItem)) { + // recurse var referenceClassification = Resolve(reference); - if (referenceClassification == Classification.IsMvc || referenceClassification == Classification.ReferencesMvc) + if (referenceClassification == Classification.IsUmbraco || referenceClassification == Classification.ReferencesUmbraco) { - classification = Classification.ReferencesMvc; + classification = Classification.ReferencesUmbraco; break; } } @@ -830,139 +654,82 @@ namespace Umbraco.Core.Composing return classification; } - protected virtual IReadOnlyList GetReferences(string file) - { - try + protected virtual IEnumerable GetReferences(Assembly assembly) + { + foreach (var referenceName in assembly.GetReferencedAssemblies()) { - if (!File.Exists(file)) + // don't include if this is excluded + if (TypeFinder.KnownAssemblyExclusionFilter.Any(f => referenceName.FullName.StartsWith(f))) + continue; + + var reference = Assembly.Load(referenceName); + if (!_lookup.Contains(reference)) { - throw new ReferenceAssemblyNotFoundException(file); - } + // A dependency references an item that isn't referenced by this project. + // We'll construct an item for so that we can calculate the classification based on it's name. - using var peReader = new PEReader(File.OpenRead(file)); - if (!peReader.HasMetadata) - { - return Array.Empty(); // not a managed assembly - } + _lookup.Add(reference); - var metadataReader = peReader.GetMetadataReader(); - - var references = new List(); - foreach (var handle in metadataReader.AssemblyReferences) - { - var reference = metadataReader.GetAssemblyReference(handle); - var referenceName = metadataReader.GetString(reference.Name); - - if (!Lookup.TryGetValue(referenceName, out var assemblyItem)) - { - // A dependency references an item that isn't referenced by this project. - // We'll construct an item for so that we can calculate the classification based on it's name. - assemblyItem = new AssemblyItem - { - AssemblyName = referenceName, - }; - - Lookup[referenceName] = assemblyItem; - } - - references.Add(assemblyItem); - } - - return references; + yield return reference; + } } - catch (BadImageFormatException) - { - // not a PE file, or invalid metadata - } - - return Array.Empty(); // not a managed assembly } protected enum Classification { Unknown, - DoesNotReferenceMvc, - ReferencesMvc, - IsMvc, + DoesNotReferenceUmbraco, + ReferencesUmbraco, + IsUmbraco, } } - public class AssemblyItem + + /// + /// Finds Assemblies from the entry point assemblies, it's dependencies and it's transitive dependencies that reference that targetAssemblyNames + /// + /// + /// borrowed and modified from here https://github.com/dotnet/aspnetcore-tooling/blob/master/src/Razor/src/Microsoft.NET.Sdk.Razor/FindAssembliesWithReferencesTo.cs + /// + internal class FindAssembliesWithReferencesTo { - public string Path { get; set; } + private readonly Assembly[] _referenceAssemblies; + private readonly string[] _targetAssemblies; + private readonly bool _includeTargets; - public bool IsFrameworkReference { get; set; } - - public string AssemblyName { get; set; } - } - - internal class ReferenceAssemblyNotFoundException : Exception - { - public ReferenceAssemblyNotFoundException(string fileName) - { - FileName = fileName; - } - - public string FileName { get; } - } - - // borrowed from here https://github.com/dotnet/aspnetcore-tooling/blob/master/src/Razor/src/Microsoft.NET.Sdk.Razor/FindAssembliesWithReferencesTo.cs - public class FindAssembliesWithReferencesTo - { - private readonly string[] _referenceAssemblies; - private readonly string[] _targetAssemblyNames; - - public FindAssembliesWithReferencesTo(string[] referenceAssemblies, string[] targetAssemblyNames) + /// + /// Constructor + /// + /// Entry point assemblies + /// Used to check if the entry point or it's transitive assemblies reference these assembly names + /// If true will also use the target assembly names as entry point assemblies + public FindAssembliesWithReferencesTo(Assembly[] referenceAssemblies, string[] targetAssemblyNames, bool includeTargets) { _referenceAssemblies = referenceAssemblies; - _targetAssemblyNames = targetAssemblyNames; + _targetAssemblies = targetAssemblyNames; + _includeTargets = includeTargets; } - public IEnumerable Find() + public IEnumerable Find() { - var referenceItems = new List(); - foreach (var item in _referenceAssemblies) + var referenceItems = new List(); + foreach (var assembly in _referenceAssemblies) { - //var assemblyName = new AssemblyName(item).Name; - var assembly = Assembly.Load(item); - referenceItems.Add(new AssemblyItem + referenceItems.Add(assembly); + } + + if (_includeTargets) + { + foreach(var target in _targetAssemblies) { - AssemblyName = assembly.GetName().Name, //assemblyName, - IsFrameworkReference = false, - Path = GetAssemblyLocation(assembly) - }); + referenceItems.Add(Assembly.Load(target)); + } } - var provider = new ReferenceResolver(_targetAssemblyNames, referenceItems); - try - { - var assemblyNames = provider.ResolveAssemblies(); - return assemblyNames.ToArray(); - } - catch (ReferenceAssemblyNotFoundException ex) - { - throw; - //// Print a warning and return. We cannot produce a correct document at this point. - //var warning = "Reference assembly {0} could not be found. This is typically caused by build errors in referenced projects."; - //Log.LogWarning(null, "RAZORSDK1007", null, null, 0, 0, 0, 0, warning, ex.FileName); - //return true; - } - catch (Exception ex) - { - throw; - //Log.LogErrorFromException(ex); - } + var provider = new ReferenceResolver(_targetAssemblies, referenceItems); + var assemblyNames = provider.ResolveAssemblies(); + return assemblyNames.ToList(); } - internal static string GetAssemblyLocation(Assembly assembly) - { - if (Uri.TryCreate(assembly.CodeBase, UriKind.Absolute, out var result) && - result.IsFile && string.IsNullOrWhiteSpace(result.Fragment)) - { - return result.LocalPath; - } - - return assembly.Location; - } } } diff --git a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs index 2422e6018f..e12a81be36 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs @@ -370,7 +370,7 @@ namespace Umbraco.Core.Runtime /// /// protected virtual ITypeFinder GetTypeFinder() - => new TypeFinder(Logger, new DefaultUmbracoAssemblyProvider(Assembly.GetEntryAssembly()?.GetName()?.Name)); + => new TypeFinder(Logger, new DefaultUmbracoAssemblyProvider(Assembly.GetEntryAssembly())); /// diff --git a/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs b/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs index bba7ea0061..dec895a432 100644 --- a/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs +++ b/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs @@ -28,7 +28,7 @@ namespace Umbraco.Tests.Cache public override void Setup() { base.Setup(); - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); _memberCache = new ObjectCacheAppCache(typeFinder); _provider = new DeepCloneAppCache(_memberCache); diff --git a/src/Umbraco.Tests/Cache/HttpRequestAppCacheTests.cs b/src/Umbraco.Tests/Cache/HttpRequestAppCacheTests.cs index d2d3b795d6..28d9b42ab0 100644 --- a/src/Umbraco.Tests/Cache/HttpRequestAppCacheTests.cs +++ b/src/Umbraco.Tests/Cache/HttpRequestAppCacheTests.cs @@ -16,7 +16,7 @@ namespace Umbraco.Tests.Cache public override void Setup() { base.Setup(); - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); _ctx = new FakeHttpContextFactory("http://localhost/test"); _appCache = new HttpRequestAppCache(() => _ctx.HttpContext.Items, typeFinder); } diff --git a/src/Umbraco.Tests/Cache/ObjectAppCacheTests.cs b/src/Umbraco.Tests/Cache/ObjectAppCacheTests.cs index 5d6808f678..e8fc420f58 100644 --- a/src/Umbraco.Tests/Cache/ObjectAppCacheTests.cs +++ b/src/Umbraco.Tests/Cache/ObjectAppCacheTests.cs @@ -21,7 +21,7 @@ namespace Umbraco.Tests.Cache public override void Setup() { base.Setup(); - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); _provider = new ObjectCacheAppCache(typeFinder); } diff --git a/src/Umbraco.Tests/Components/ComponentTests.cs b/src/Umbraco.Tests/Components/ComponentTests.cs index 9d44aa562e..0b179d6d85 100644 --- a/src/Umbraco.Tests/Components/ComponentTests.cs +++ b/src/Umbraco.Tests/Components/ComponentTests.cs @@ -32,7 +32,7 @@ namespace Umbraco.Tests.Components var mock = new Mock(); var logger = Mock.Of(); - var typeFinder = new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(typeof(ComponentTests).Assembly.GetName().Name)); + var typeFinder = new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(typeof(ComponentTests).Assembly)); var f = new UmbracoDatabaseFactory(logger, new Lazy(() => new MapperCollection(Enumerable.Empty())), TestHelper.GetConfigs(), TestHelper.DbProviderFactoryCreator); var fs = new FileSystems(mock.Object, logger, TestHelper.IOHelper, SettingsForTests.GenerateMockGlobalSettings()); var coreDebug = Mock.Of(); @@ -371,7 +371,7 @@ namespace Umbraco.Tests.Components public void AllComposers() { var ioHelper = TestHelper.IOHelper; - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); var typeLoader = new TypeLoader(ioHelper, typeFinder, AppCaches.Disabled.RuntimeCache, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), Mock.Of()); var register = MockRegister(); diff --git a/src/Umbraco.Tests/Composing/ComposingTestBase.cs b/src/Umbraco.Tests/Composing/ComposingTestBase.cs index c4d048cd7b..5e90b777fe 100644 --- a/src/Umbraco.Tests/Composing/ComposingTestBase.cs +++ b/src/Umbraco.Tests/Composing/ComposingTestBase.cs @@ -22,7 +22,7 @@ namespace Umbraco.Tests.Composing { ProfilingLogger = new ProfilingLogger(Mock.Of(), Mock.Of()); - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); var ioHelper = TestHelper.IOHelper; TypeLoader = new TypeLoader(ioHelper, typeFinder, NoAppCache.Instance, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), ProfilingLogger, false, AssembliesToScan); } diff --git a/src/Umbraco.Tests/Composing/CompositionTests.cs b/src/Umbraco.Tests/Composing/CompositionTests.cs index a720fe045f..7a71afa625 100644 --- a/src/Umbraco.Tests/Composing/CompositionTests.cs +++ b/src/Umbraco.Tests/Composing/CompositionTests.cs @@ -38,7 +38,7 @@ namespace Umbraco.Tests.Composing .Returns(() => factoryFactory?.Invoke(mockedFactory)); var logger = new ProfilingLogger(Mock.Of(), Mock.Of()); - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); var ioHelper = TestHelper.IOHelper; var typeLoader = new TypeLoader(ioHelper, typeFinder, Mock.Of(), new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), logger); var composition = new Composition(mockedRegister, typeLoader, logger, Mock.Of(), TestHelper.GetConfigs(), TestHelper.IOHelper, AppCaches.NoCache); diff --git a/src/Umbraco.Tests/Composing/TypeFinderTests.cs b/src/Umbraco.Tests/Composing/TypeFinderTests.cs index 969be8c0fd..3bdfd09752 100644 --- a/src/Umbraco.Tests/Composing/TypeFinderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeFinderTests.cs @@ -57,7 +57,7 @@ namespace Umbraco.Tests.Composing [Test] public void Find_Class_Of_Type_With_Attribute() { - var typeFinder = new TypeFinder(GetTestProfilingLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); + var typeFinder = new TypeFinder(GetTestProfilingLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); var typesFound = typeFinder.FindClassesOfTypeWithAttribute(_assemblies); Assert.AreEqual(2, typesFound.Count()); } @@ -65,7 +65,7 @@ namespace Umbraco.Tests.Composing [Test] public void Find_Classes_With_Attribute() { - var typeFinder = new TypeFinder(GetTestProfilingLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); + var typeFinder = new TypeFinder(GetTestProfilingLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); var typesFound = typeFinder.FindClassesWithAttribute(_assemblies); Assert.AreEqual(0, typesFound.Count()); // 0 classes in _assemblies are marked with [Tree] diff --git a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs index 2d6f1b85e8..7d68f616fa 100644 --- a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs @@ -27,7 +27,7 @@ namespace Umbraco.Tests.Composing public void Initialize() { // this ensures it's reset - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); _typeLoader = new TypeLoader(TestHelper.IOHelper, typeFinder, NoAppCache.Instance, new DirectoryInfo(TestHelper.IOHelper.MapPath("~/App_Data/TEMP")), new ProfilingLogger(Mock.Of(), Mock.Of()), false, diff --git a/src/Umbraco.Tests/Macros/MacroTests.cs b/src/Umbraco.Tests/Macros/MacroTests.cs index 3cc77b6732..addc5afde0 100644 --- a/src/Umbraco.Tests/Macros/MacroTests.cs +++ b/src/Umbraco.Tests/Macros/MacroTests.cs @@ -17,7 +17,7 @@ namespace Umbraco.Tests.Macros [SetUp] public void Setup() { - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); //we DO want cache enabled for these tests var cacheHelper = new AppCaches( new ObjectCacheAppCache(typeFinder), diff --git a/src/Umbraco.Tests/Models/ContentTests.cs b/src/Umbraco.Tests/Models/ContentTests.cs index 9a7e25944f..c25197d79e 100644 --- a/src/Umbraco.Tests/Models/ContentTests.cs +++ b/src/Umbraco.Tests/Models/ContentTests.cs @@ -269,7 +269,7 @@ namespace Umbraco.Tests.Models content.UpdateDate = DateTime.Now; content.WriterId = 23; - var runtimeCache = new ObjectCacheAppCache(new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name))); + var runtimeCache = new ObjectCacheAppCache(new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly))); runtimeCache.Insert(content.Id.ToString(CultureInfo.InvariantCulture), () => content); var proflog = GetTestProfilingLogger(); diff --git a/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs b/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs index 329626a984..d0c348bc0c 100644 --- a/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs +++ b/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs @@ -127,7 +127,7 @@ namespace Umbraco.Tests.Published var setType1 = publishedContentTypeFactory.CreateContentType(1000, "set1", CreatePropertyTypes); - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); var elementsCache = new FastDictionaryAppCache(typeFinder); var snapshotCache = new FastDictionaryAppCache(typeFinder); diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs index d868e8bc2e..9947f76fb0 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs @@ -143,7 +143,7 @@ namespace Umbraco.Tests.PublishedContent // create a data source for NuCache _source = new TestDataSource(kits); - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); var settings = Mock.Of(); diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs index cf2be84f24..be2ba1b48b 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs @@ -184,7 +184,7 @@ namespace Umbraco.Tests.PublishedContent // create a variation accessor _variationAccesor = new TestVariationContextAccessor(); - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); var settings = Mock.Of(); // at last, create the complete NuCache snapshot service! diff --git a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs index b97aae2e7e..2bcfd2d98b 100644 --- a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs +++ b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs @@ -63,7 +63,7 @@ namespace Umbraco.Tests.Runtimes var profilingLogger = new ProfilingLogger(logger, profiler); var appCaches = AppCaches.Disabled; var databaseFactory = new UmbracoDatabaseFactory(logger, new Lazy(() => factory.GetInstance()), TestHelper.GetConfigs(), TestHelper.DbProviderFactoryCreator); - var typeFinder = new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); + var typeFinder = new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(GetType().Assembly)); var ioHelper = TestHelper.IOHelper; var hostingEnvironment = Mock.Of(); var typeLoader = new TypeLoader(ioHelper, typeFinder, appCaches.RuntimeCache, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), profilingLogger); @@ -256,7 +256,7 @@ namespace Umbraco.Tests.Runtimes var profilingLogger = new ProfilingLogger(logger, profiler); var appCaches = AppCaches.Disabled; var databaseFactory = Mock.Of(); - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); var ioHelper = TestHelper.IOHelper; var typeLoader = new TypeLoader(ioHelper, typeFinder, appCaches.RuntimeCache, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), profilingLogger); var runtimeState = Mock.Of(); diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index 5cc10d735d..b2252e705d 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -85,7 +85,7 @@ namespace Umbraco.Tests.Scoping var memberRepository = Mock.Of(); var hostingEnvironment = TestHelper.GetHostingEnvironment(); - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); var settings = Mock.Of(); return new PublishedSnapshotService( diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs index a2855aa1bd..a8d045c5e8 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs @@ -58,7 +58,7 @@ namespace Umbraco.Tests.Services var memberRepository = Mock.Of(); var hostingEnvironment = Mock.Of(); - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); var settings = Mock.Of(); return new PublishedSnapshotService( diff --git a/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs b/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs index 0f54725246..19b45c8ad5 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs @@ -38,7 +38,7 @@ namespace Umbraco.Tests.TestHelpers var ioHelper = TestHelper.IOHelper; var logger = new ProfilingLogger(Mock.Of(), Mock.Of()); - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); var typeLoader = new TypeLoader(ioHelper, typeFinder, NoAppCache.Instance, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), logger, diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs index 773ef85b1b..b2650695ac 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs @@ -40,7 +40,7 @@ namespace Umbraco.Tests.TestHelpers.Stubs { if (_factory != null) return _factory(requestContext); - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); var types = typeFinder.FindClassesOfType(new[] { Assembly.GetExecutingAssembly() }); var controllerTypes = types.Where(x => x.Name.Equals(controllerName + "Controller", StringComparison.InvariantCultureIgnoreCase)); diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects.cs b/src/Umbraco.Tests/TestHelpers/TestObjects.cs index 32b891f2eb..81b7369119 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects.cs @@ -247,7 +247,7 @@ namespace Umbraco.Tests.TestHelpers databaseFactory = new UmbracoDatabaseFactory(Constants.System.UmbracoConnectionName, logger, new Lazy(() => mappers), TestHelper.GetConfigs(), TestHelper.DbProviderFactoryCreator); } - typeFinder = typeFinder ?? new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); + typeFinder = typeFinder ?? new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(GetType().Assembly)); fileSystems = fileSystems ?? new FileSystems(Current.Factory, logger, TestHelper.IOHelper, SettingsForTests.GenerateMockGlobalSettings()); var coreDebug = Current.Configs.CoreDebug(); var mediaFileSystem = Mock.Of(); diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index f064e7ffa9..483b4033f7 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -171,7 +171,7 @@ namespace Umbraco.Tests.Testing var proflogger = new ProfilingLogger(logger, profiler); IOHelper = TestHelper.IOHelper; - TypeFinder = new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); + TypeFinder = new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(GetType().Assembly)); var appCaches = GetAppCaches(); var globalSettings = SettingsForTests.GetDefaultGlobalSettings(); var settings = SettingsForTests.GetDefaultUmbracoSettings(); diff --git a/src/Umbraco.Tests/Web/UmbracoHelperTests.cs b/src/Umbraco.Tests/Web/UmbracoHelperTests.cs index e4ae4c9a58..7846bf366b 100644 --- a/src/Umbraco.Tests/Web/UmbracoHelperTests.cs +++ b/src/Umbraco.Tests/Web/UmbracoHelperTests.cs @@ -28,7 +28,7 @@ namespace Umbraco.Tests.Web { // FIXME: bad in a unit test - but Udi has a static ctor that wants it?! var container = new Mock(); - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly.GetName().Name)); + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); var ioHelper = TestHelper.IOHelper; container .Setup(x => x.GetInstance(typeof(TypeLoader))) From d92fc8736ab136c67acadade9b1143d31ec57ca2 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 9 Mar 2020 13:31:56 +1100 Subject: [PATCH 43/69] More cleanup, cleans up tests, cleans up the BuildManagerAssemblyProvider (even though it's not used) --- .../Composing/BruteForceAssemblyProvider.cs | 102 ++++++ .../DefaultUmbracoAssemblyProvider.cs | 36 +++ .../FindAssembliesWithReferencesTo.cs | 55 ++++ .../Composing/IAssemblyProvider.cs | 13 + .../Composing/ReferenceResolver.cs | 115 +++++++ src/Umbraco.Core/Composing/TypeFinder.cs | 300 +----------------- src/Umbraco.Core/Umbraco.Core.csproj | 1 - .../Cache/DeepCloneAppCacheTests.cs | 3 +- .../Cache/HttpRequestAppCacheTests.cs | 2 +- .../Cache/ObjectAppCacheTests.cs | 9 +- .../Components/ComponentTests.cs | 4 +- .../Composing/ComposingTestBase.cs | 2 +- .../Composing/CompositionTests.cs | 2 +- .../Composing/TypeLoaderTests.cs | 2 +- src/Umbraco.Tests/Macros/MacroTests.cs | 2 +- src/Umbraco.Tests/Models/ContentTests.cs | 4 +- .../Published/PropertyCacheLevelTests.cs | 2 +- .../PublishedContent/NuCacheChildrenTests.cs | 2 +- .../PublishedContent/NuCacheTests.cs | 2 +- src/Umbraco.Tests/Runtimes/StandaloneTests.cs | 2 +- .../Scoping/ScopedNuCacheTests.cs | 2 +- .../ContentTypeServiceVariantsTests.cs | 2 +- .../TestHelpers/BaseUsingSqlCeSyntax.cs | 2 +- .../Stubs/TestControllerFactory.cs | 2 +- src/Umbraco.Tests/TestHelpers/TestHelper.cs | 6 + src/Umbraco.Tests/Web/UmbracoHelperTests.cs | 2 +- .../Composing/BuildManagerAssemblyProvider.cs | 70 ++++ .../Composing/BuildManagerTypeFinder.cs | 57 +--- src/Umbraco.Web/Umbraco.Web.csproj | 1 + 29 files changed, 426 insertions(+), 378 deletions(-) create mode 100644 src/Umbraco.Core/Composing/BruteForceAssemblyProvider.cs create mode 100644 src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs create mode 100644 src/Umbraco.Core/Composing/FindAssembliesWithReferencesTo.cs create mode 100644 src/Umbraco.Core/Composing/IAssemblyProvider.cs create mode 100644 src/Umbraco.Core/Composing/ReferenceResolver.cs create mode 100644 src/Umbraco.Web/Composing/BuildManagerAssemblyProvider.cs diff --git a/src/Umbraco.Core/Composing/BruteForceAssemblyProvider.cs b/src/Umbraco.Core/Composing/BruteForceAssemblyProvider.cs new file mode 100644 index 0000000000..04d0d70475 --- /dev/null +++ b/src/Umbraco.Core/Composing/BruteForceAssemblyProvider.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Security; +using Umbraco.Core.Exceptions; + +namespace Umbraco.Core.Composing +{ + /// + /// lazily load a reference to all local assemblies and gac assemblies + /// + /// + /// This is a modified version of: http://www.dominicpettifer.co.uk/Blog/44/how-to-get-a-reference-to-all-assemblies-in-the--bin-folder + /// + /// We do this because we cannot use AppDomain.Current.GetAssemblies() as this will return only assemblies that have been + /// loaded in the CLR, not all assemblies. + /// See these threads: + /// http://issues.umbraco.org/issue/U5-198 + /// http://stackoverflow.com/questions/3552223/asp-net-appdomain-currentdomain-getassemblies-assemblies-missing-after-app + /// http://stackoverflow.com/questions/2477787/difference-between-appdomain-getassemblies-and-buildmanager-getreferencedassembl + /// + public class BruteForceAssemblyProvider : IAssemblyProvider + { + public BruteForceAssemblyProvider() + { + _allAssemblies = new Lazy>(() => + { + HashSet assemblies = null; + try + { + //NOTE: we cannot use AppDomain.CurrentDomain.GetAssemblies() because this only returns assemblies that have + // already been loaded in to the app domain, instead we will look directly into the bin folder and load each one. + var binFolder = GetRootDirectorySafe(); + var binAssemblyFiles = Directory.GetFiles(binFolder, "*.dll", SearchOption.TopDirectoryOnly).ToList(); + assemblies = new HashSet(); + foreach (var a in binAssemblyFiles) + { + try + { + var assName = AssemblyName.GetAssemblyName(a); + var ass = Assembly.Load(assName); + assemblies.Add(ass); + } + catch (Exception e) + { + if (e is SecurityException || e is BadImageFormatException) + { + //swallow these exceptions + } + else + { + throw; + } + } + } + + //Since we are only loading in the /bin assemblies above, we will also load in anything that's already loaded (which will include gac items) + foreach (var a in AppDomain.CurrentDomain.GetAssemblies()) + { + assemblies.Add(a); + } + } + catch (InvalidOperationException e) + { + if (e.InnerException is SecurityException == false) + throw; + } + + return assemblies; + }); + } + + private readonly Lazy> _allAssemblies; + private string _rootDir = string.Empty; + + public IEnumerable Assemblies => _allAssemblies.Value; + + // FIXME - this is only an interim change, once the IIOHelper stuff is merged we should use IIOHelper here + private string GetRootDirectorySafe() + { + if (string.IsNullOrEmpty(_rootDir) == false) + { + return _rootDir; + } + + var codeBase = Assembly.GetExecutingAssembly().CodeBase; + var uri = new Uri(codeBase); + var path = uri.LocalPath; + var baseDirectory = Path.GetDirectoryName(path); + if (string.IsNullOrEmpty(baseDirectory)) + throw new PanicException("No root directory could be resolved."); + + _rootDir = baseDirectory.Contains("bin") + ? baseDirectory.Substring(0, baseDirectory.LastIndexOf("bin", StringComparison.OrdinalIgnoreCase) - 1) + : baseDirectory; + + return _rootDir; + } + } +} diff --git a/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs b/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs new file mode 100644 index 0000000000..1322dcbfa2 --- /dev/null +++ b/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace Umbraco.Core.Composing +{ + /// + /// Returns a list of scannable assemblies based on an entry point assembly and it's references + /// + /// + /// This will recursively search through the entry point's assemblies and Umbraco's core assemblies (Core/Web) and their references + /// to create a list of scannable assemblies based on whether they themselves or their transitive dependencies reference Umbraco core assemblies. + /// + public class DefaultUmbracoAssemblyProvider : IAssemblyProvider + { + private readonly Assembly _entryPointAssembly; + private static readonly string[] UmbracoCoreAssemblyNames = new[] { "Umbraco.Core", "Umbraco.Web" }; + + public DefaultUmbracoAssemblyProvider(Assembly entryPointAssembly) + { + _entryPointAssembly = entryPointAssembly ?? throw new ArgumentNullException(nameof(entryPointAssembly)); + } + + public IEnumerable Assemblies + { + get + { + var finder = new FindAssembliesWithReferencesTo(new[] { _entryPointAssembly }, UmbracoCoreAssemblyNames, true); + foreach(var found in finder.Find()) + { + yield return found; + } + } + } + } +} diff --git a/src/Umbraco.Core/Composing/FindAssembliesWithReferencesTo.cs b/src/Umbraco.Core/Composing/FindAssembliesWithReferencesTo.cs new file mode 100644 index 0000000000..91225ff973 --- /dev/null +++ b/src/Umbraco.Core/Composing/FindAssembliesWithReferencesTo.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Umbraco.Core.Composing +{ + /// + /// Finds Assemblies from the entry point assemblies, it's dependencies and it's transitive dependencies that reference that targetAssemblyNames + /// + /// + /// borrowed and modified from here https://github.com/dotnet/aspnetcore-tooling/blob/master/src/Razor/src/Microsoft.NET.Sdk.Razor/FindAssembliesWithReferencesTo.cs + /// + internal class FindAssembliesWithReferencesTo + { + private readonly Assembly[] _referenceAssemblies; + private readonly string[] _targetAssemblies; + private readonly bool _includeTargets; + + /// + /// Constructor + /// + /// Entry point assemblies + /// Used to check if the entry point or it's transitive assemblies reference these assembly names + /// If true will also use the target assembly names as entry point assemblies + public FindAssembliesWithReferencesTo(Assembly[] referenceAssemblies, string[] targetAssemblyNames, bool includeTargets) + { + _referenceAssemblies = referenceAssemblies; + _targetAssemblies = targetAssemblyNames; + _includeTargets = includeTargets; + } + + public IEnumerable Find() + { + var referenceItems = new List(); + foreach (var assembly in _referenceAssemblies) + { + referenceItems.Add(assembly); + } + + if (_includeTargets) + { + foreach(var target in _targetAssemblies) + { + referenceItems.Add(Assembly.Load(target)); + } + } + + var provider = new ReferenceResolver(_targetAssemblies, referenceItems); + var assemblyNames = provider.ResolveAssemblies(); + return assemblyNames.ToList(); + } + + } +} diff --git a/src/Umbraco.Core/Composing/IAssemblyProvider.cs b/src/Umbraco.Core/Composing/IAssemblyProvider.cs new file mode 100644 index 0000000000..bde97a9556 --- /dev/null +++ b/src/Umbraco.Core/Composing/IAssemblyProvider.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using System.Reflection; + +namespace Umbraco.Core.Composing +{ + /// + /// Provides a list of assemblies that can be scanned + /// + public interface IAssemblyProvider + { + IEnumerable Assemblies { get; } + } +} diff --git a/src/Umbraco.Core/Composing/ReferenceResolver.cs b/src/Umbraco.Core/Composing/ReferenceResolver.cs new file mode 100644 index 0000000000..6d77afd414 --- /dev/null +++ b/src/Umbraco.Core/Composing/ReferenceResolver.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; + +namespace Umbraco.Core.Composing +{ + /// + /// Resolves assemblies that reference one of the specified "targetAssemblies" either directly or transitively. + /// + /// + /// Borrowed and modified from https://github.com/dotnet/aspnetcore-tooling/blob/master/src/Razor/src/Microsoft.NET.Sdk.Razor/ReferenceResolver.cs + /// + internal class ReferenceResolver + { + private readonly HashSet _umbracoAssemblies; + private readonly IReadOnlyList _assemblyItems; + private readonly Dictionary _classifications; + private readonly List _lookup = new List(); + + public ReferenceResolver(IReadOnlyList targetAssemblies, IReadOnlyList assemblyItems) + { + _umbracoAssemblies = new HashSet(targetAssemblies, StringComparer.Ordinal); + _assemblyItems = assemblyItems; + _classifications = new Dictionary(); + + foreach (var item in assemblyItems) + { + _lookup.Add(item); + } + } + + public IEnumerable ResolveAssemblies() + { + var applicationParts = new List(); + + foreach (var item in _assemblyItems) + { + var classification = Resolve(item); + if (classification == Classification.ReferencesUmbraco || classification == Classification.IsUmbraco) + { + applicationParts.Add(item); + } + } + + return applicationParts; + } + + private Classification Resolve(Assembly assemblyItem) + { + if (_classifications.TryGetValue(assemblyItem, out var classification)) + { + return classification; + } + + // Initialize the dictionary with a value to short-circuit recursive references. + classification = Classification.Unknown; + _classifications[assemblyItem] = classification; + + if (_umbracoAssemblies.Contains(assemblyItem.GetName().Name)) + { + classification = Classification.IsUmbraco; + } + else + { + classification = Classification.DoesNotReferenceUmbraco; + foreach (var reference in GetReferences(assemblyItem)) + { + // recurse + var referenceClassification = Resolve(reference); + + if (referenceClassification == Classification.IsUmbraco || referenceClassification == Classification.ReferencesUmbraco) + { + classification = Classification.ReferencesUmbraco; + break; + } + } + } + + Debug.Assert(classification != Classification.Unknown); + _classifications[assemblyItem] = classification; + return classification; + } + + protected virtual IEnumerable GetReferences(Assembly assembly) + { + foreach (var referenceName in assembly.GetReferencedAssemblies()) + { + // don't include if this is excluded + if (TypeFinder.KnownAssemblyExclusionFilter.Any(f => referenceName.FullName.StartsWith(f))) + continue; + + var reference = Assembly.Load(referenceName); + if (!_lookup.Contains(reference)) + { + // A dependency references an item that isn't referenced by this project. + // We'll construct an item for so that we can calculate the classification based on it's name. + + _lookup.Add(reference); + + yield return reference; + } + } + } + + protected enum Classification + { + Unknown, + DoesNotReferenceUmbraco, + ReferencesUmbraco, + IsUmbraco, + } + } +} diff --git a/src/Umbraco.Core/Composing/TypeFinder.cs b/src/Umbraco.Core/Composing/TypeFinder.cs index a0aab7c088..bc15cb63f2 100644 --- a/src/Umbraco.Core/Composing/TypeFinder.cs +++ b/src/Umbraco.Core/Composing/TypeFinder.cs @@ -1,16 +1,11 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Diagnostics; -using System.IO; using System.Linq; using System.Reflection; -using System.Reflection.Metadata; -using System.Reflection.PortableExecutable; using System.Security; using System.Text; using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.Exceptions; using Umbraco.Core.Logging; namespace Umbraco.Core.Composing @@ -21,11 +16,6 @@ namespace Umbraco.Core.Composing private readonly ILogger _logger; private readonly IAssemblyProvider _assemblyProvider; - //public TypeFinder(ILogger logger, ITypeFinderConfig typeFinderConfig = null) - // : this(logger, new DefaultUmbracoAssemblyProvider(Assembly.GetEntryAssembly()?.GetName()?.Name), typeFinderConfig) - //{ - //} - public TypeFinder(ILogger logger, IAssemblyProvider assemblyProvider, ITypeFinderConfig typeFinderConfig = null) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); @@ -151,7 +141,9 @@ namespace Umbraco.Core.Composing "WebDriver,", "itextsharp,", "mscorlib,", - "nunit.framework,", + "NUnit,", + "NUnit3TestAdapter,", + "Selenium.", }; /// @@ -446,290 +438,4 @@ namespace Umbraco.Core.Composing #endregion } - - /// - /// lazily load a reference to all local assemblies and gac assemblies - /// - /// - /// This is a modified version of: http://www.dominicpettifer.co.uk/Blog/44/how-to-get-a-reference-to-all-assemblies-in-the--bin-folder - /// - /// We do this because we cannot use AppDomain.Current.GetAssemblies() as this will return only assemblies that have been - /// loaded in the CLR, not all assemblies. - /// See these threads: - /// http://issues.umbraco.org/issue/U5-198 - /// http://stackoverflow.com/questions/3552223/asp-net-appdomain-currentdomain-getassemblies-assemblies-missing-after-app - /// http://stackoverflow.com/questions/2477787/difference-between-appdomain-getassemblies-and-buildmanager-getreferencedassembl - /// - public class BruteForceAssemblyProvider : IAssemblyProvider - { - public BruteForceAssemblyProvider() - { - _allAssemblies = new Lazy>(() => - { - HashSet assemblies = null; - try - { - //NOTE: we cannot use AppDomain.CurrentDomain.GetAssemblies() because this only returns assemblies that have - // already been loaded in to the app domain, instead we will look directly into the bin folder and load each one. - var binFolder = GetRootDirectorySafe(); - var binAssemblyFiles = Directory.GetFiles(binFolder, "*.dll", SearchOption.TopDirectoryOnly).ToList(); - assemblies = new HashSet(); - foreach (var a in binAssemblyFiles) - { - try - { - var assName = AssemblyName.GetAssemblyName(a); - var ass = Assembly.Load(assName); - assemblies.Add(ass); - } - catch (Exception e) - { - if (e is SecurityException || e is BadImageFormatException) - { - //swallow these exceptions - } - else - { - throw; - } - } - } - - //Since we are only loading in the /bin assemblies above, we will also load in anything that's already loaded (which will include gac items) - foreach (var a in AppDomain.CurrentDomain.GetAssemblies()) - { - assemblies.Add(a); - } - } - catch (InvalidOperationException e) - { - if (e.InnerException is SecurityException == false) - throw; - } - - return assemblies; - }); - } - - private readonly Lazy> _allAssemblies; - private string _rootDir = string.Empty; - - public IEnumerable Assemblies => _allAssemblies.Value; - - // FIXME - this is only an interim change, once the IIOHelper stuff is merged we should use IIOHelper here - private string GetRootDirectorySafe() - { - if (string.IsNullOrEmpty(_rootDir) == false) - { - return _rootDir; - } - - var codeBase = Assembly.GetExecutingAssembly().CodeBase; - var uri = new Uri(codeBase); - var path = uri.LocalPath; - var baseDirectory = Path.GetDirectoryName(path); - if (string.IsNullOrEmpty(baseDirectory)) - throw new PanicException("No root directory could be resolved."); - - _rootDir = baseDirectory.Contains("bin") - ? baseDirectory.Substring(0, baseDirectory.LastIndexOf("bin", StringComparison.OrdinalIgnoreCase) - 1) - : baseDirectory; - - return _rootDir; - } - } - - /// - /// Provides a list of assemblies that can be scanned - /// - public interface IAssemblyProvider - { - IEnumerable Assemblies { get; } - } - - /// - /// Returns a list of scannable assemblies based on an entry point assembly and it's references - /// - /// - /// This will recursively search through the entry point's assemblies and Umbraco's core assemblies (Core/Web) and their references - /// to create a list of scannable assemblies based on whether they themselves or their transitive dependencies reference Umbraco core assemblies. - /// - public class DefaultUmbracoAssemblyProvider : IAssemblyProvider - { - private readonly Assembly _entryPointAssembly; - private static readonly string[] UmbracoCoreAssemblyNames = new[] { "Umbraco.Core", "Umbraco.Web" }; - - public DefaultUmbracoAssemblyProvider(Assembly entryPointAssembly) - { - _entryPointAssembly = entryPointAssembly ?? throw new ArgumentNullException(nameof(entryPointAssembly)); - } - - public IEnumerable Assemblies - { - get - { - var finder = new FindAssembliesWithReferencesTo(new[] { _entryPointAssembly }, UmbracoCoreAssemblyNames, true); - foreach(var found in finder.Find()) - { - yield return found; - } - } - } - } - - /// - /// Resolves assemblies that reference one of the specified "targetAssemblies" either directly or transitively. - /// - /// - /// Borrowed and modified from https://github.com/dotnet/aspnetcore-tooling/blob/master/src/Razor/src/Microsoft.NET.Sdk.Razor/ReferenceResolver.cs - /// - internal class ReferenceResolver - { - private readonly HashSet _umbracoAssemblies; - private readonly IReadOnlyList _assemblyItems; - private readonly Dictionary _classifications; - private readonly List _lookup = new List(); - - public ReferenceResolver(IReadOnlyList targetAssemblies, IReadOnlyList assemblyItems) - { - _umbracoAssemblies = new HashSet(targetAssemblies, StringComparer.Ordinal); - _assemblyItems = assemblyItems; - _classifications = new Dictionary(); - - foreach (var item in assemblyItems) - { - _lookup.Add(item); - } - } - - public IEnumerable ResolveAssemblies() - { - var applicationParts = new List(); - - foreach (var item in _assemblyItems) - { - var classification = Resolve(item); - if (classification == Classification.ReferencesUmbraco || classification == Classification.IsUmbraco) - { - applicationParts.Add(item); - } - } - - return applicationParts; - } - - private Classification Resolve(Assembly assemblyItem) - { - if (_classifications.TryGetValue(assemblyItem, out var classification)) - { - return classification; - } - - // Initialize the dictionary with a value to short-circuit recursive references. - classification = Classification.Unknown; - _classifications[assemblyItem] = classification; - - if (_umbracoAssemblies.Contains(assemblyItem.GetName().Name)) - { - classification = Classification.IsUmbraco; - } - else - { - classification = Classification.DoesNotReferenceUmbraco; - foreach (var reference in GetReferences(assemblyItem)) - { - // recurse - var referenceClassification = Resolve(reference); - - if (referenceClassification == Classification.IsUmbraco || referenceClassification == Classification.ReferencesUmbraco) - { - classification = Classification.ReferencesUmbraco; - break; - } - } - } - - Debug.Assert(classification != Classification.Unknown); - _classifications[assemblyItem] = classification; - return classification; - } - - protected virtual IEnumerable GetReferences(Assembly assembly) - { - foreach (var referenceName in assembly.GetReferencedAssemblies()) - { - // don't include if this is excluded - if (TypeFinder.KnownAssemblyExclusionFilter.Any(f => referenceName.FullName.StartsWith(f))) - continue; - - var reference = Assembly.Load(referenceName); - if (!_lookup.Contains(reference)) - { - // A dependency references an item that isn't referenced by this project. - // We'll construct an item for so that we can calculate the classification based on it's name. - - _lookup.Add(reference); - - yield return reference; - } - } - } - - protected enum Classification - { - Unknown, - DoesNotReferenceUmbraco, - ReferencesUmbraco, - IsUmbraco, - } - } - - - /// - /// Finds Assemblies from the entry point assemblies, it's dependencies and it's transitive dependencies that reference that targetAssemblyNames - /// - /// - /// borrowed and modified from here https://github.com/dotnet/aspnetcore-tooling/blob/master/src/Razor/src/Microsoft.NET.Sdk.Razor/FindAssembliesWithReferencesTo.cs - /// - internal class FindAssembliesWithReferencesTo - { - private readonly Assembly[] _referenceAssemblies; - private readonly string[] _targetAssemblies; - private readonly bool _includeTargets; - - /// - /// Constructor - /// - /// Entry point assemblies - /// Used to check if the entry point or it's transitive assemblies reference these assembly names - /// If true will also use the target assembly names as entry point assemblies - public FindAssembliesWithReferencesTo(Assembly[] referenceAssemblies, string[] targetAssemblyNames, bool includeTargets) - { - _referenceAssemblies = referenceAssemblies; - _targetAssemblies = targetAssemblyNames; - _includeTargets = includeTargets; - } - - public IEnumerable Find() - { - var referenceItems = new List(); - foreach (var assembly in _referenceAssemblies) - { - referenceItems.Add(assembly); - } - - if (_includeTargets) - { - foreach(var target in _targetAssemblies) - { - referenceItems.Add(Assembly.Load(target)); - } - } - - var provider = new ReferenceResolver(_targetAssemblies, referenceItems); - var assemblyNames = provider.ResolveAssemblies(); - return assemblyNames.ToList(); - } - - } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 49d5090070..7a15e7fbed 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -13,7 +13,6 @@ - diff --git a/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs b/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs index dec895a432..e4844cc6be 100644 --- a/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs +++ b/src/Umbraco.Tests/Cache/DeepCloneAppCacheTests.cs @@ -13,6 +13,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Tests.Collections; +using Umbraco.Tests.TestHelpers; using Umbraco.Web.Cache; namespace Umbraco.Tests.Cache @@ -28,7 +29,7 @@ namespace Umbraco.Tests.Cache public override void Setup() { base.Setup(); - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + var typeFinder = TestHelper.GetTypeFinder(); _memberCache = new ObjectCacheAppCache(typeFinder); _provider = new DeepCloneAppCache(_memberCache); diff --git a/src/Umbraco.Tests/Cache/HttpRequestAppCacheTests.cs b/src/Umbraco.Tests/Cache/HttpRequestAppCacheTests.cs index 28d9b42ab0..dbda6fb429 100644 --- a/src/Umbraco.Tests/Cache/HttpRequestAppCacheTests.cs +++ b/src/Umbraco.Tests/Cache/HttpRequestAppCacheTests.cs @@ -16,7 +16,7 @@ namespace Umbraco.Tests.Cache public override void Setup() { base.Setup(); - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + var typeFinder = TestHelper.GetTypeFinder(); _ctx = new FakeHttpContextFactory("http://localhost/test"); _appCache = new HttpRequestAppCache(() => _ctx.HttpContext.Items, typeFinder); } diff --git a/src/Umbraco.Tests/Cache/ObjectAppCacheTests.cs b/src/Umbraco.Tests/Cache/ObjectAppCacheTests.cs index e8fc420f58..7957026ad8 100644 --- a/src/Umbraco.Tests/Cache/ObjectAppCacheTests.cs +++ b/src/Umbraco.Tests/Cache/ObjectAppCacheTests.cs @@ -1,10 +1,7 @@ -using System.Collections.Generic; -using System.Linq; -using Moq; +using System.Linq; using NUnit.Framework; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; -using Umbraco.Core.Logging; +using Umbraco.Tests.TestHelpers; namespace Umbraco.Tests.Cache { @@ -21,7 +18,7 @@ namespace Umbraco.Tests.Cache public override void Setup() { base.Setup(); - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + var typeFinder = TestHelper.GetTypeFinder(); _provider = new ObjectCacheAppCache(typeFinder); } diff --git a/src/Umbraco.Tests/Components/ComponentTests.cs b/src/Umbraco.Tests/Components/ComponentTests.cs index 0b179d6d85..cf54b8e9ec 100644 --- a/src/Umbraco.Tests/Components/ComponentTests.cs +++ b/src/Umbraco.Tests/Components/ComponentTests.cs @@ -32,7 +32,7 @@ namespace Umbraco.Tests.Components var mock = new Mock(); var logger = Mock.Of(); - var typeFinder = new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(typeof(ComponentTests).Assembly)); + var typeFinder = TestHelper.GetTypeFinder(); var f = new UmbracoDatabaseFactory(logger, new Lazy(() => new MapperCollection(Enumerable.Empty())), TestHelper.GetConfigs(), TestHelper.DbProviderFactoryCreator); var fs = new FileSystems(mock.Object, logger, TestHelper.IOHelper, SettingsForTests.GenerateMockGlobalSettings()); var coreDebug = Mock.Of(); @@ -371,7 +371,7 @@ namespace Umbraco.Tests.Components public void AllComposers() { var ioHelper = TestHelper.IOHelper; - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + var typeFinder = TestHelper.GetTypeFinder(); var typeLoader = new TypeLoader(ioHelper, typeFinder, AppCaches.Disabled.RuntimeCache, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), Mock.Of()); var register = MockRegister(); diff --git a/src/Umbraco.Tests/Composing/ComposingTestBase.cs b/src/Umbraco.Tests/Composing/ComposingTestBase.cs index 5e90b777fe..6c5ccd5510 100644 --- a/src/Umbraco.Tests/Composing/ComposingTestBase.cs +++ b/src/Umbraco.Tests/Composing/ComposingTestBase.cs @@ -22,7 +22,7 @@ namespace Umbraco.Tests.Composing { ProfilingLogger = new ProfilingLogger(Mock.Of(), Mock.Of()); - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + var typeFinder = TestHelper.GetTypeFinder(); var ioHelper = TestHelper.IOHelper; TypeLoader = new TypeLoader(ioHelper, typeFinder, NoAppCache.Instance, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), ProfilingLogger, false, AssembliesToScan); } diff --git a/src/Umbraco.Tests/Composing/CompositionTests.cs b/src/Umbraco.Tests/Composing/CompositionTests.cs index 7a71afa625..ce3cdfac17 100644 --- a/src/Umbraco.Tests/Composing/CompositionTests.cs +++ b/src/Umbraco.Tests/Composing/CompositionTests.cs @@ -38,7 +38,7 @@ namespace Umbraco.Tests.Composing .Returns(() => factoryFactory?.Invoke(mockedFactory)); var logger = new ProfilingLogger(Mock.Of(), Mock.Of()); - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + var typeFinder = TestHelper.GetTypeFinder(); var ioHelper = TestHelper.IOHelper; var typeLoader = new TypeLoader(ioHelper, typeFinder, Mock.Of(), new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), logger); var composition = new Composition(mockedRegister, typeLoader, logger, Mock.Of(), TestHelper.GetConfigs(), TestHelper.IOHelper, AppCaches.NoCache); diff --git a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs index 7d68f616fa..d0181563a8 100644 --- a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs @@ -27,7 +27,7 @@ namespace Umbraco.Tests.Composing public void Initialize() { // this ensures it's reset - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + var typeFinder = TestHelper.GetTypeFinder(); _typeLoader = new TypeLoader(TestHelper.IOHelper, typeFinder, NoAppCache.Instance, new DirectoryInfo(TestHelper.IOHelper.MapPath("~/App_Data/TEMP")), new ProfilingLogger(Mock.Of(), Mock.Of()), false, diff --git a/src/Umbraco.Tests/Macros/MacroTests.cs b/src/Umbraco.Tests/Macros/MacroTests.cs index addc5afde0..d6ed8f33c2 100644 --- a/src/Umbraco.Tests/Macros/MacroTests.cs +++ b/src/Umbraco.Tests/Macros/MacroTests.cs @@ -17,7 +17,7 @@ namespace Umbraco.Tests.Macros [SetUp] public void Setup() { - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + var typeFinder = TestHelper.GetTypeFinder(); //we DO want cache enabled for these tests var cacheHelper = new AppCaches( new ObjectCacheAppCache(typeFinder), diff --git a/src/Umbraco.Tests/Models/ContentTests.cs b/src/Umbraco.Tests/Models/ContentTests.cs index c25197d79e..e33f707ee1 100644 --- a/src/Umbraco.Tests/Models/ContentTests.cs +++ b/src/Umbraco.Tests/Models/ContentTests.cs @@ -22,6 +22,7 @@ using Umbraco.Tests.TestHelpers.Entities; using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Tests.Testing; using Umbraco.Web.PropertyEditors; +using Umbraco.Tests.TestHelpers; namespace Umbraco.Tests.Models { @@ -269,7 +270,8 @@ namespace Umbraco.Tests.Models content.UpdateDate = DateTime.Now; content.WriterId = 23; - var runtimeCache = new ObjectCacheAppCache(new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly))); + var typeFinder = TestHelper.GetTypeFinder(); + var runtimeCache = new ObjectCacheAppCache(typeFinder); runtimeCache.Insert(content.Id.ToString(CultureInfo.InvariantCulture), () => content); var proflog = GetTestProfilingLogger(); diff --git a/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs b/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs index d0c348bc0c..769985d515 100644 --- a/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs +++ b/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs @@ -127,7 +127,7 @@ namespace Umbraco.Tests.Published var setType1 = publishedContentTypeFactory.CreateContentType(1000, "set1", CreatePropertyTypes); - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + var typeFinder = TestHelper.GetTypeFinder(); var elementsCache = new FastDictionaryAppCache(typeFinder); var snapshotCache = new FastDictionaryAppCache(typeFinder); diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs index 9947f76fb0..e8296320a5 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs @@ -143,7 +143,7 @@ namespace Umbraco.Tests.PublishedContent // create a data source for NuCache _source = new TestDataSource(kits); - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + var typeFinder = TestHelper.GetTypeFinder(); var settings = Mock.Of(); diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs index be2ba1b48b..1f6895296f 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs @@ -184,7 +184,7 @@ namespace Umbraco.Tests.PublishedContent // create a variation accessor _variationAccesor = new TestVariationContextAccessor(); - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + var typeFinder = TestHelper.GetTypeFinder(); var settings = Mock.Of(); // at last, create the complete NuCache snapshot service! diff --git a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs index 2bcfd2d98b..ecc5094166 100644 --- a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs +++ b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs @@ -256,7 +256,7 @@ namespace Umbraco.Tests.Runtimes var profilingLogger = new ProfilingLogger(logger, profiler); var appCaches = AppCaches.Disabled; var databaseFactory = Mock.Of(); - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + var typeFinder = TestHelper.GetTypeFinder(); var ioHelper = TestHelper.IOHelper; var typeLoader = new TypeLoader(ioHelper, typeFinder, appCaches.RuntimeCache, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), profilingLogger); var runtimeState = Mock.Of(); diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index b2252e705d..aada99da71 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -85,7 +85,7 @@ namespace Umbraco.Tests.Scoping var memberRepository = Mock.Of(); var hostingEnvironment = TestHelper.GetHostingEnvironment(); - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + var typeFinder = TestHelper.GetTypeFinder(); var settings = Mock.Of(); return new PublishedSnapshotService( diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs index a8d045c5e8..33e8b0010e 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs @@ -58,7 +58,7 @@ namespace Umbraco.Tests.Services var memberRepository = Mock.Of(); var hostingEnvironment = Mock.Of(); - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + var typeFinder = TestHelper.GetTypeFinder(); var settings = Mock.Of(); return new PublishedSnapshotService( diff --git a/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs b/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs index 19b45c8ad5..e035eaa807 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs @@ -38,7 +38,7 @@ namespace Umbraco.Tests.TestHelpers var ioHelper = TestHelper.IOHelper; var logger = new ProfilingLogger(Mock.Of(), Mock.Of()); - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + var typeFinder = TestHelper.GetTypeFinder(); var typeLoader = new TypeLoader(ioHelper, typeFinder, NoAppCache.Instance, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), logger, diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs index b2650695ac..f5d18e05ba 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs @@ -40,7 +40,7 @@ namespace Umbraco.Tests.TestHelpers.Stubs { if (_factory != null) return _factory(requestContext); - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + var typeFinder = TestHelper.GetTypeFinder(); var types = typeFinder.FindClassesOfType(new[] { Assembly.GetExecutingAssembly() }); var controllerTypes = types.Where(x => x.Name.Equals(controllerName + "Controller", StringComparison.InvariantCultureIgnoreCase)); diff --git a/src/Umbraco.Tests/TestHelpers/TestHelper.cs b/src/Umbraco.Tests/TestHelpers/TestHelper.cs index 41b97ac580..177fc2d518 100644 --- a/src/Umbraco.Tests/TestHelpers/TestHelper.cs +++ b/src/Umbraco.Tests/TestHelpers/TestHelper.cs @@ -43,6 +43,12 @@ namespace Umbraco.Tests.TestHelpers public static class TestHelper { + public static ITypeFinder GetTypeFinder() + { + var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(typeof(TestHelper).Assembly)); + return typeFinder; + } + public static TypeLoader GetMockedTypeLoader() { return new TypeLoader(IOHelper, Mock.Of(), Mock.Of(), new DirectoryInfo(IOHelper.MapPath("~/App_Data/TEMP")), Mock.Of()); diff --git a/src/Umbraco.Tests/Web/UmbracoHelperTests.cs b/src/Umbraco.Tests/Web/UmbracoHelperTests.cs index 7846bf366b..62d7e941d7 100644 --- a/src/Umbraco.Tests/Web/UmbracoHelperTests.cs +++ b/src/Umbraco.Tests/Web/UmbracoHelperTests.cs @@ -28,7 +28,7 @@ namespace Umbraco.Tests.Web { // FIXME: bad in a unit test - but Udi has a static ctor that wants it?! var container = new Mock(); - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + var typeFinder = TestHelper.GetTypeFinder(); var ioHelper = TestHelper.IOHelper; container .Setup(x => x.GetInstance(typeof(TypeLoader))) diff --git a/src/Umbraco.Web/Composing/BuildManagerAssemblyProvider.cs b/src/Umbraco.Web/Composing/BuildManagerAssemblyProvider.cs new file mode 100644 index 0000000000..3c5cebe03e --- /dev/null +++ b/src/Umbraco.Web/Composing/BuildManagerAssemblyProvider.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Security; +using System.Web.Compilation; +using Umbraco.Core.Composing; +using Umbraco.Core.Hosting; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; + +namespace Umbraco.Web.Composing +{ + /// + /// Uses the BuildManager to provide a list of assemblies to scan + /// + internal class BuildManagerAssemblyProvider : BruteForceAssemblyProvider, IAssemblyProvider + { + private readonly Lazy> _allAssemblies; + + public BuildManagerAssemblyProvider(IIOHelper ioHelper, + IHostingEnvironment hostingEnvironment, + ILogger logger) + { + _allAssemblies = new Lazy>(() => + { + var isHosted = hostingEnvironment.IsHosted; + try + { + if (isHosted) + { + var assemblies = new HashSet(BuildManager.GetReferencedAssemblies().Cast()); + + //here we are trying to get the App_Code assembly + var fileExtensions = new[] { ".cs", ".vb" }; //only vb and cs files are supported + var appCodeFolder = new DirectoryInfo(ioHelper.MapPath(ioHelper.ResolveUrl("~/App_code"))); + //check if the folder exists and if there are any files in it with the supported file extensions + if (appCodeFolder.Exists && fileExtensions.Any(x => appCodeFolder.GetFiles("*" + x).Any())) + { + try + { + var appCodeAssembly = Assembly.Load("App_Code"); + if (assemblies.Contains(appCodeAssembly) == false) // BuildManager will find App_Code already + assemblies.Add(appCodeAssembly); + } + catch (FileNotFoundException ex) + { + //this will occur if it cannot load the assembly + logger.Error(typeof(TypeFinder), ex, "Could not load assembly App_Code"); + } + } + + return assemblies; + } + } + catch (InvalidOperationException e) + { + if (e.InnerException is SecurityException == false) + throw; + } + + // Not hosted, just use the default implementation + return new HashSet(base.Assemblies); + }); + } + + IEnumerable IAssemblyProvider.Assemblies => _allAssemblies.Value; + } +} diff --git a/src/Umbraco.Web/Composing/BuildManagerTypeFinder.cs b/src/Umbraco.Web/Composing/BuildManagerTypeFinder.cs index bd583bf29d..d3502c36fb 100644 --- a/src/Umbraco.Web/Composing/BuildManagerTypeFinder.cs +++ b/src/Umbraco.Web/Composing/BuildManagerTypeFinder.cs @@ -1,19 +1,15 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Reflection; -using System.Security; using System.Web.Compilation; using Umbraco.Core.Configuration; using Umbraco.Core.Composing; using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.Hosting; -using Umbraco.Core.IO; using Umbraco.Core.Logging; namespace Umbraco.Web.Composing { + /// /// An implementation of TypeFinder that uses the BuildManager to resolve references for aspnet framework hosted websites /// @@ -24,60 +20,13 @@ namespace Umbraco.Web.Composing { public BuildManagerTypeFinder( - IIOHelper ioHelper, - IHostingEnvironment hostingEnvironment, ILogger logger, IAssemblyProvider assemblyProvider, ITypeFinderConfig typeFinderConfig = null) : base(logger, assemblyProvider, typeFinderConfig) { - if (ioHelper == null) throw new ArgumentNullException(nameof(ioHelper)); - if (hostingEnvironment == null) throw new ArgumentNullException(nameof(hostingEnvironment)); if (logger == null) throw new ArgumentNullException(nameof(logger)); - - _allAssemblies = new Lazy>(() => - { - var isHosted = hostingEnvironment.IsHosted; - try - { - if (isHosted) - { - var assemblies = new HashSet(BuildManager.GetReferencedAssemblies().Cast()); - - //here we are trying to get the App_Code assembly - var fileExtensions = new[] { ".cs", ".vb" }; //only vb and cs files are supported - var appCodeFolder = new DirectoryInfo(ioHelper.MapPath(ioHelper.ResolveUrl("~/App_code"))); - //check if the folder exists and if there are any files in it with the supported file extensions - if (appCodeFolder.Exists && fileExtensions.Any(x => appCodeFolder.GetFiles("*" + x).Any())) - { - try - { - var appCodeAssembly = Assembly.Load("App_Code"); - if (assemblies.Contains(appCodeAssembly) == false) // BuildManager will find App_Code already - assemblies.Add(appCodeAssembly); - } - catch (FileNotFoundException ex) - { - //this will occur if it cannot load the assembly - logger.Error(typeof(TypeFinder), ex, "Could not load assembly App_Code"); - } - } - - return assemblies; - } - } - catch (InvalidOperationException e) - { - if (e.InnerException is SecurityException == false) - throw; - } - - // Not hosted, just use the default implementation - return new HashSet(base.AssembliesToScan); - }); } - private readonly Lazy> _allAssemblies; - /// /// Explicitly implement and return result from BuildManager /// @@ -85,10 +34,6 @@ namespace Umbraco.Web.Composing /// Type ITypeFinder.GetTypeByName (string name) => BuildManager.GetType(name, false); - /// - /// Explicitly implement and return result from BuildManager - /// - IEnumerable ITypeFinder.AssembliesToScan => _allAssemblies.Value; /// /// TypeFinder config via appSettings diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index fad79b0296..27ea9443a0 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -143,6 +143,7 @@ + From fce27fd42d91f46458e9e28fb995cd1b70826193 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 9 Mar 2020 14:15:02 +1100 Subject: [PATCH 44/69] Ensures all assemblies at the same location as the entry point assemblies are queried. --- .../Composing/ReferenceResolver.cs | 67 +++++++++++++++++-- src/Umbraco.Core/Composing/TypeFinder.cs | 6 +- 2 files changed, 66 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Core/Composing/ReferenceResolver.cs b/src/Umbraco.Core/Composing/ReferenceResolver.cs index 6d77afd414..c705af685a 100644 --- a/src/Umbraco.Core/Composing/ReferenceResolver.cs +++ b/src/Umbraco.Core/Composing/ReferenceResolver.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; using System.Reflection; @@ -15,27 +16,63 @@ namespace Umbraco.Core.Composing internal class ReferenceResolver { private readonly HashSet _umbracoAssemblies; - private readonly IReadOnlyList _assemblyItems; + private readonly IReadOnlyList _assemblies; private readonly Dictionary _classifications; private readonly List _lookup = new List(); - public ReferenceResolver(IReadOnlyList targetAssemblies, IReadOnlyList assemblyItems) + public ReferenceResolver(IReadOnlyList targetAssemblies, IReadOnlyList entryPointAssemblies) { _umbracoAssemblies = new HashSet(targetAssemblies, StringComparer.Ordinal); - _assemblyItems = assemblyItems; + _assemblies = entryPointAssemblies; _classifications = new Dictionary(); - foreach (var item in assemblyItems) + foreach (var item in entryPointAssemblies) { _lookup.Add(item); } } + /// + /// Returns a list of assemblies that directly reference or transitively reference the targetAssemblies + /// + /// + /// + /// This includes all assemblies in the same location as the entry point assemblies + /// public IEnumerable ResolveAssemblies() { var applicationParts = new List(); - foreach (var item in _assemblyItems) + var assemblies = new HashSet(_assemblies); + + // Get the unique directories of the assemblies + var assemblyLocations = GetAssemblyLocations(assemblies).ToList(); + + // Load in each assembly in the directory of the entry assembly to be included in the search + // for Umbraco dependencies/transitive dependencies + foreach(var location in assemblyLocations) + { + var dir = Path.GetDirectoryName(location); + + foreach(var dll in Directory.EnumerateFiles(dir, "*.dll")) + { + var assemblyName = AssemblyName.GetAssemblyName(dll); + + // don't include if this is excluded + if (TypeFinder.KnownAssemblyExclusionFilter.Any(f => assemblyName.FullName.StartsWith(f, StringComparison.InvariantCultureIgnoreCase))) + continue; + + // don't include this item if it's Umbraco + // TODO: We should maybe pass an explicit list of these names in? + if (assemblyName.FullName.StartsWith("Umbraco.")) + continue; + + var assembly = Assembly.Load(assemblyName); + assemblies.Add(assembly); + } + } + + foreach (var item in assemblies) { var classification = Resolve(item); if (classification == Classification.ReferencesUmbraco || classification == Classification.IsUmbraco) @@ -47,6 +84,24 @@ namespace Umbraco.Core.Composing return applicationParts; } + + private IEnumerable GetAssemblyLocations(IEnumerable assemblies) + { + return assemblies.Select(x => GetAssemblyLocation(x).ToLowerInvariant()).Distinct(); + } + + // borrowed from https://github.com/dotnet/aspnetcore/blob/master/src/Mvc/Mvc.Core/src/ApplicationParts/RelatedAssemblyAttribute.cs + private string GetAssemblyLocation(Assembly assembly) + { + if (Uri.TryCreate(assembly.CodeBase, UriKind.Absolute, out var result) && + result.IsFile && string.IsNullOrWhiteSpace(result.Fragment)) + { + return result.LocalPath; + } + + return assembly.Location; + } + private Classification Resolve(Assembly assemblyItem) { if (_classifications.TryGetValue(assemblyItem, out var classification)) @@ -88,7 +143,7 @@ namespace Umbraco.Core.Composing foreach (var referenceName in assembly.GetReferencedAssemblies()) { // don't include if this is excluded - if (TypeFinder.KnownAssemblyExclusionFilter.Any(f => referenceName.FullName.StartsWith(f))) + if (TypeFinder.KnownAssemblyExclusionFilter.Any(f => referenceName.FullName.StartsWith(f, StringComparison.InvariantCultureIgnoreCase))) continue; var reference = Assembly.Load(referenceName); diff --git a/src/Umbraco.Core/Composing/TypeFinder.cs b/src/Umbraco.Core/Composing/TypeFinder.cs index bc15cb63f2..1813c8d8f4 100644 --- a/src/Umbraco.Core/Composing/TypeFinder.cs +++ b/src/Umbraco.Core/Composing/TypeFinder.cs @@ -142,8 +142,12 @@ namespace Umbraco.Core.Composing "itextsharp,", "mscorlib,", "NUnit,", - "NUnit3TestAdapter,", + "NUnit.", + "NUnit3.", "Selenium.", + "ImageProcessor", + "MiniProfiler.", + "Owin,", }; /// From 46686ffd908d4c11d79e5d5f71fe5a4dc5f6c459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Broh=C3=A4ll?= Date: Fri, 6 Mar 2020 18:04:01 +0100 Subject: [PATCH 45/69] Corrected document outline --- src/Umbraco.Web/Routing/PublishedContentNotFoundHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/Routing/PublishedContentNotFoundHandler.cs b/src/Umbraco.Web/Routing/PublishedContentNotFoundHandler.cs index ae8da1fc6c..e1f9022f27 100644 --- a/src/Umbraco.Web/Routing/PublishedContentNotFoundHandler.cs +++ b/src/Umbraco.Web/Routing/PublishedContentNotFoundHandler.cs @@ -37,9 +37,9 @@ namespace Umbraco.Web.Routing reason = "No template exists to render the document at url '{0}'."; response.Write("

    Page not found

    "); - response.Write("

    "); + response.Write("

    "); response.Write(string.Format(reason, HttpUtility.HtmlEncode(Current.UmbracoContext.OriginalRequestUrl.PathAndQuery))); - response.Write("

    "); + 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\".

    "); From bad1913e0e59445e56ed8e990054647b88e012ba Mon Sep 17 00:00:00 2001 From: Jason Elkin Date: Sat, 7 Mar 2020 21:33:35 +0000 Subject: [PATCH 46/69] update Umbraco.ModelsBuilder.Embedded attributes to match assembly name --- .../Building/TextBuilder.cs | 2 +- .../Building/TextHeaderWriter.cs | 2 +- .../Properties/AssemblyInfo.cs | 2 +- .../ModelsBuilder/BuilderTests.cs | 24 +++++++++---------- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.ModelsBuilder.Embedded/Building/TextBuilder.cs b/src/Umbraco.ModelsBuilder.Embedded/Building/TextBuilder.cs index d1190a0374..7ef8112d11 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/Building/TextBuilder.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/Building/TextBuilder.cs @@ -94,7 +94,7 @@ namespace Umbraco.ModelsBuilder.Embedded.Building // private static void WriteGeneratedCodeAttribute(StringBuilder sb, string tabs) { - sb.AppendFormat("{0}[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"Umbraco.ModelsBuilder\", \"{1}\")]\n", tabs, ApiVersion.Current.Version); + sb.AppendFormat("{0}[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"Umbraco.ModelsBuilder.Embedded\", \"{1}\")]\n", tabs, ApiVersion.Current.Version); } private void WriteContentType(StringBuilder sb, TypeModel type) diff --git a/src/Umbraco.ModelsBuilder.Embedded/Building/TextHeaderWriter.cs b/src/Umbraco.ModelsBuilder.Embedded/Building/TextHeaderWriter.cs index a93df97806..0ffad1c5bc 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/Building/TextHeaderWriter.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/Building/TextHeaderWriter.cs @@ -14,7 +14,7 @@ namespace Umbraco.ModelsBuilder.Embedded.Building sb.Append("// \n"); sb.Append("// This code was generated by a tool.\n"); sb.Append("//\n"); - sb.AppendFormat("// Umbraco.ModelsBuilder v{0}\n", ApiVersion.Current.Version); + sb.AppendFormat("// Umbraco.ModelsBuilder.Embedded v{0}\n", ApiVersion.Current.Version); sb.Append("//\n"); sb.Append("// Changes to this file will be lost if the code is regenerated.\n"); sb.Append("// \n"); diff --git a/src/Umbraco.ModelsBuilder.Embedded/Properties/AssemblyInfo.cs b/src/Umbraco.ModelsBuilder.Embedded/Properties/AssemblyInfo.cs index 68c149adde..5fa17d3c77 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/Properties/AssemblyInfo.cs +++ b/src/Umbraco.ModelsBuilder.Embedded/Properties/AssemblyInfo.cs @@ -2,7 +2,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -[assembly: AssemblyTitle("Umbraco.ModelsBuilder")] +[assembly: AssemblyTitle("Umbraco.ModelsBuilder.Embedded")] [assembly: AssemblyDescription("Umbraco ModelsBuilder")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyProduct("Umbraco CMS")] diff --git a/src/Umbraco.Tests/ModelsBuilder/BuilderTests.cs b/src/Umbraco.Tests/ModelsBuilder/BuilderTests.cs index e1c3ecc891..4f35c57d04 100644 --- a/src/Umbraco.Tests/ModelsBuilder/BuilderTests.cs +++ b/src/Umbraco.Tests/ModelsBuilder/BuilderTests.cs @@ -54,7 +54,7 @@ namespace Umbraco.Tests.ModelsBuilder // // This code was generated by a tool. // -// Umbraco.ModelsBuilder v" + version + @" +// Umbraco.ModelsBuilder.Embedded v" + version + @" // // Changes to this file will be lost if the code is regenerated. // @@ -76,14 +76,14 @@ namespace Umbraco.Web.PublishedModels { // helpers #pragma warning disable 0109 // new is redundant - [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder"", """ + version + @""")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] public new const string ModelTypeAlias = ""type1""; - [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder"", """ + version + @""")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] public new const PublishedItemType ModelItemType = PublishedItemType.Content; - [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder"", """ + version + @""")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] public new static IPublishedContentType GetModelContentType() => PublishedModelUtility.GetModelContentType(ModelItemType, ModelTypeAlias); - [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder"", """ + version + @""")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] public static IPublishedPropertyType GetModelPropertyType(Expression> selector) => PublishedModelUtility.GetModelPropertyType(GetModelContentType(), selector); #pragma warning restore 0109 @@ -95,7 +95,7 @@ namespace Umbraco.Web.PublishedModels // properties - [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder"", """ + version + @""")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] [ImplementPropertyType(""prop1"")] public string Prop1 => this.Value(""prop1""); } @@ -169,7 +169,7 @@ namespace Umbraco.Web.PublishedModels // // This code was generated by a tool. // -// Umbraco.ModelsBuilder v" + version + @" +// Umbraco.ModelsBuilder.Embedded v" + version + @" // // Changes to this file will be lost if the code is regenerated. // @@ -191,14 +191,14 @@ namespace Umbraco.Web.PublishedModels { // helpers #pragma warning disable 0109 // new is redundant - [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder"", """ + version + @""")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] public new const string ModelTypeAlias = ""type1""; - [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder"", """ + version + @""")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] public new const PublishedItemType ModelItemType = PublishedItemType.Content; - [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder"", """ + version + @""")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] public new static IPublishedContentType GetModelContentType() => PublishedModelUtility.GetModelContentType(ModelItemType, ModelTypeAlias); - [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder"", """ + version + @""")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] public static IPublishedPropertyType GetModelPropertyType(Expression> selector) => PublishedModelUtility.GetModelPropertyType(GetModelContentType(), selector); #pragma warning restore 0109 @@ -210,7 +210,7 @@ namespace Umbraco.Web.PublishedModels // properties - [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder"", """ + version + @""")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""Umbraco.ModelsBuilder.Embedded"", """ + version + @""")] [ImplementPropertyType(""foo"")] public global::System.Collections.Generic.IEnumerable Foo => this.Value>(""foo""); } From 8cb39ba58300ae3ce3d7b1c004adfe5a630ad59e Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 9 Mar 2020 21:23:38 +1100 Subject: [PATCH 47/69] Fixes tests and some issues discovered --- .../DefaultUmbracoAssemblyProvider.cs | 17 +++++--- .../Composing/ReferenceResolver.cs | 41 ++++++++++--------- src/Umbraco.Core/Composing/TypeFinder.cs | 5 ++- .../Components/ComponentTests.cs | 3 +- .../Runtimes/CoreRuntimeTests.cs | 4 ++ src/Umbraco.Tests/TestHelpers/TestHelper.cs | 4 +- 6 files changed, 46 insertions(+), 28 deletions(-) diff --git a/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs b/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs index 1322dcbfa2..c13a42ece7 100644 --- a/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs +++ b/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs @@ -8,13 +8,21 @@ namespace Umbraco.Core.Composing /// Returns a list of scannable assemblies based on an entry point assembly and it's references ///
    /// - /// This will recursively search through the entry point's assemblies and Umbraco's core assemblies (Core/Web) and their references + /// This will recursively search through the entry point's assemblies and Umbraco's core assemblies and their references /// to create a list of scannable assemblies based on whether they themselves or their transitive dependencies reference Umbraco core assemblies. /// public class DefaultUmbracoAssemblyProvider : IAssemblyProvider { private readonly Assembly _entryPointAssembly; - private static readonly string[] UmbracoCoreAssemblyNames = new[] { "Umbraco.Core", "Umbraco.Web" }; + private static readonly string[] UmbracoCoreAssemblyNames = new[] + { + "Umbraco.Core", + "Umbraco.Web", + "Umbraco.Infrastructure", + "Umbraco.PublishedCache.NuCache", + "Umbraco.ModelsBuilder.Embedded", + "Umbraco.Examine.Lucene", + }; public DefaultUmbracoAssemblyProvider(Assembly entryPointAssembly) { @@ -26,10 +34,7 @@ namespace Umbraco.Core.Composing get { var finder = new FindAssembliesWithReferencesTo(new[] { _entryPointAssembly }, UmbracoCoreAssemblyNames, true); - foreach(var found in finder.Find()) - { - yield return found; - } + return finder.Find(); } } } diff --git a/src/Umbraco.Core/Composing/ReferenceResolver.cs b/src/Umbraco.Core/Composing/ReferenceResolver.cs index c705af685a..65dba8bf23 100644 --- a/src/Umbraco.Core/Composing/ReferenceResolver.cs +++ b/src/Umbraco.Core/Composing/ReferenceResolver.cs @@ -46,14 +46,12 @@ namespace Umbraco.Core.Composing var assemblies = new HashSet(_assemblies); // Get the unique directories of the assemblies - var assemblyLocations = GetAssemblyLocations(assemblies).ToList(); + var assemblyLocations = GetAssemblyFolders(assemblies).ToList(); // Load in each assembly in the directory of the entry assembly to be included in the search // for Umbraco dependencies/transitive dependencies - foreach(var location in assemblyLocations) - { - var dir = Path.GetDirectoryName(location); - + foreach(var dir in assemblyLocations) + { foreach(var dll in Directory.EnumerateFiles(dir, "*.dll")) { var assemblyName = AssemblyName.GetAssemblyName(dll); @@ -85,9 +83,9 @@ namespace Umbraco.Core.Composing } - private IEnumerable GetAssemblyLocations(IEnumerable assemblies) + private IEnumerable GetAssemblyFolders(IEnumerable assemblies) { - return assemblies.Select(x => GetAssemblyLocation(x).ToLowerInvariant()).Distinct(); + return assemblies.Select(x => Path.GetDirectoryName(GetAssemblyLocation(x)).ToLowerInvariant()).Distinct(); } // borrowed from https://github.com/dotnet/aspnetcore/blob/master/src/Mvc/Mvc.Core/src/ApplicationParts/RelatedAssemblyAttribute.cs @@ -102,25 +100,30 @@ namespace Umbraco.Core.Composing return assembly.Location; } - private Classification Resolve(Assembly assemblyItem) + private Classification Resolve(Assembly assembly) { - if (_classifications.TryGetValue(assemblyItem, out var classification)) + if (_classifications.TryGetValue(assembly, out var classification)) { return classification; } // Initialize the dictionary with a value to short-circuit recursive references. classification = Classification.Unknown; - _classifications[assemblyItem] = classification; - - if (_umbracoAssemblies.Contains(assemblyItem.GetName().Name)) + _classifications[assembly] = classification; + + if (TypeFinder.KnownAssemblyExclusionFilter.Any(f => assembly.FullName.StartsWith(f, StringComparison.InvariantCultureIgnoreCase))) + { + // if its part of the filter it doesn't reference umbraco + classification = Classification.DoesNotReferenceUmbraco; + } + else if (_umbracoAssemblies.Contains(assembly.GetName().Name)) { classification = Classification.IsUmbraco; } else { classification = Classification.DoesNotReferenceUmbraco; - foreach (var reference in GetReferences(assemblyItem)) + foreach (var reference in GetReferences(assembly)) { // recurse var referenceClassification = Resolve(reference); @@ -134,7 +137,7 @@ namespace Umbraco.Core.Composing } Debug.Assert(classification != Classification.Unknown); - _classifications[assemblyItem] = classification; + _classifications[assembly] = classification; return classification; } @@ -147,15 +150,15 @@ namespace Umbraco.Core.Composing continue; var reference = Assembly.Load(referenceName); + if (!_lookup.Contains(reference)) { // A dependency references an item that isn't referenced by this project. - // We'll construct an item for so that we can calculate the classification based on it's name. + // We'll add this reference so that we can calculate the classification. - _lookup.Add(reference); - - yield return reference; - } + _lookup.Add(reference); + } + yield return reference; } } diff --git a/src/Umbraco.Core/Composing/TypeFinder.cs b/src/Umbraco.Core/Composing/TypeFinder.cs index 1813c8d8f4..27b13a8365 100644 --- a/src/Umbraco.Core/Composing/TypeFinder.cs +++ b/src/Umbraco.Core/Composing/TypeFinder.cs @@ -94,7 +94,9 @@ namespace Umbraco.Core.Composing /// NOTE this means that "foo." will NOT exclude "foo.dll" but only "foo.*.dll" /// internal static readonly string[] KnownAssemblyExclusionFilter = { - "mscorlib", + "mscorlib,", + "netstandard,", + "System,", "Antlr3.", "AutoMapper,", "AutoMapper.", @@ -148,6 +150,7 @@ namespace Umbraco.Core.Composing "ImageProcessor", "MiniProfiler.", "Owin,", + "SQLite", }; /// diff --git a/src/Umbraco.Tests/Components/ComponentTests.cs b/src/Umbraco.Tests/Components/ComponentTests.cs index cf54b8e9ec..7dc6025b0a 100644 --- a/src/Umbraco.Tests/Components/ComponentTests.cs +++ b/src/Umbraco.Tests/Components/ComponentTests.cs @@ -378,7 +378,8 @@ namespace Umbraco.Tests.Components var composition = new Composition(register, typeLoader, Mock.Of(), MockRuntimeState(RuntimeLevel.Run), Configs, TestHelper.IOHelper, AppCaches.NoCache); - var types = typeLoader.GetTypes().Where(x => x.FullName.StartsWith("Umbraco.Core.") || x.FullName.StartsWith("Umbraco.Web")); + var allComposers = typeLoader.GetTypes().ToList(); + var types = allComposers.Where(x => x.FullName.StartsWith("Umbraco.Core.") || x.FullName.StartsWith("Umbraco.Web")).ToList(); var composers = new Composers(composition, types, Enumerable.Empty(), Mock.Of()); var requirements = composers.GetRequirements(); var report = Composers.GetComposersReport(requirements); diff --git a/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs b/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs index 729184eb1d..4dc94cc50a 100644 --- a/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs +++ b/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs @@ -125,6 +125,10 @@ namespace Umbraco.Tests.Runtimes } + // override because we cannot use Assembly.GetEntryAssembly in Nunit tests since that is always null + protected override ITypeFinder GetTypeFinder() + => new TypeFinder(Logger, new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + // must override the database factory // else BootFailedException because U cannot connect to the configured db protected internal override IUmbracoDatabaseFactory GetDatabaseFactory() diff --git a/src/Umbraco.Tests/TestHelpers/TestHelper.cs b/src/Umbraco.Tests/TestHelpers/TestHelper.cs index 177fc2d518..2475072bd6 100644 --- a/src/Umbraco.Tests/TestHelpers/TestHelper.cs +++ b/src/Umbraco.Tests/TestHelpers/TestHelper.cs @@ -45,7 +45,9 @@ namespace Umbraco.Tests.TestHelpers public static ITypeFinder GetTypeFinder() { - var typeFinder = new TypeFinder(Mock.Of(), new DefaultUmbracoAssemblyProvider(typeof(TestHelper).Assembly)); + + var typeFinder = new TypeFinder(Mock.Of(), + new DefaultUmbracoAssemblyProvider(typeof(TestHelper).Assembly)); return typeFinder; } From d3ebdb6198a8b13a43a15c71450dff80a93bd7a1 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 9 Mar 2020 22:25:58 +1100 Subject: [PATCH 48/69] fixes test --- src/Umbraco.Core/Models/PropertyType.cs | 1 + src/Umbraco.Tests/Models/PropertyTypeTests.cs | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Models/PropertyType.cs b/src/Umbraco.Core/Models/PropertyType.cs index bcafd549f0..75c2a7cc00 100644 --- a/src/Umbraco.Core/Models/PropertyType.cs +++ b/src/Umbraco.Core/Models/PropertyType.cs @@ -178,6 +178,7 @@ namespace Umbraco.Core.Models /// /// For generic properties, the value is null. [DataMember] + [DoNotClone] public Lazy PropertyGroupId { get => _propertyGroupId; diff --git a/src/Umbraco.Tests/Models/PropertyTypeTests.cs b/src/Umbraco.Tests/Models/PropertyTypeTests.cs index 568d12264d..f4c563971b 100644 --- a/src/Umbraco.Tests/Models/PropertyTypeTests.cs +++ b/src/Umbraco.Tests/Models/PropertyTypeTests.cs @@ -1,5 +1,7 @@ using System; using System.Diagnostics; +using System.Linq; +using System.Reflection; using NUnit.Framework; using Umbraco.Core.Models; using Umbraco.Core.Serialization; @@ -49,9 +51,9 @@ namespace Umbraco.Tests.Models Assert.AreEqual(clone.ValidationRegExp, pt.ValidationRegExp); Assert.AreEqual(clone.ValueStorageType, pt.ValueStorageType); - //This double verifies by reflection + //This double verifies by reflection (don't test properties marked with [DoNotClone] var allProps = clone.GetType().GetProperties(); - foreach (var propertyInfo in allProps) + foreach (var propertyInfo in allProps.Where(p => p.GetCustomAttribute(false) == null)) { Assert.AreEqual(propertyInfo.GetValue(clone, null), propertyInfo.GetValue(pt, null)); } From 9be30f3e91028ac40a83ef2b440f7a67e350f4cb Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 10 Mar 2020 10:36:34 +0100 Subject: [PATCH 49/69] Added info to obsolete message --- src/Umbraco.Web/UmbracoHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index 7224955922..6a2fba1152 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -94,7 +94,7 @@ namespace Umbraco.Web /// /// Gets the membership helper. /// - [Obsolete("Inject and use an instance of " + nameof(Security.MembershipHelper) + " in the constructor instead")] + [Obsolete("Inject and use an instance of " + nameof(Security.MembershipHelper) + " in the constructor instead. In views you can use @Members.")] public MembershipHelper MembershipHelper => Ensure(_membershipHelper); /// From 7ca6781d09b8cc398db3d4a08f1f9d6f1ec02e29 Mon Sep 17 00:00:00 2001 From: Marc Goodson Date: Tue, 10 Mar 2020 20:49:59 +0000 Subject: [PATCH 50/69] v7: Fix false matches for AltTemplate convention Urls (#7150) --- .../ContentFinderByNiceUrlAndTemplate.cs | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs b/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs index f19886c4f2..f97e81791e 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs @@ -17,7 +17,7 @@ namespace Umbraco.Web.Routing /// /// Tries to find and assign an Umbraco document to a PublishedContentRequest. /// - /// The PublishedContentRequest. + /// The PublishedContentRequest. /// A value indicating whether an Umbraco document was found and assigned. /// If successful, also assigns the template. public override bool TryFindContent(PublishedContentRequest docRequest) @@ -43,16 +43,24 @@ namespace Umbraco.Web.Routing var route = docRequest.HasDomain ? (docRequest.Domain.RootNodeId.ToString() + path) : path; node = FindContent(docRequest, route); - - if (node.IsAllowedTemplate(template.Id)) + //not guaranteed to find a node - just because last portion of url contains a template alias, doesn't mean remaining part of the url is a published node + if (node != null) { - docRequest.TemplateModel = template; + if (node.IsAllowedTemplate(template.Id)) + { + docRequest.TemplateModel = template; + } + else + { + LogHelper.Warn("Configuration settings prevent template \"{0}\" from showing for node \"{1}\"", () => templateAlias, () => node.Id); + docRequest.PublishedContent = null; + node = null; + } } else { - LogHelper.Warn("Configuration settings prevent template \"{0}\" from showing for node \"{1}\"", () => templateAlias, () => node.Id); + LogHelper.Debug("Attempt to find content by alternative template alias: \"{0}\" triggered because end portion of url matched template alias, but no node exists for the url without the alt template alias at the route: \"{1}\"", () => templateAlias, () => route); docRequest.PublishedContent = null; - node = null; } } else @@ -68,4 +76,4 @@ namespace Umbraco.Web.Routing return node != null; } } -} \ No newline at end of file +} From d11a1946f573f055c88a219bee3f7f1d6b058aa7 Mon Sep 17 00:00:00 2001 From: Steve Megson Date: Tue, 10 Mar 2020 20:52:53 +0000 Subject: [PATCH 51/69] V7: Don't call ClearPreviewXmlContent from ClearDocumentXmlCache (#6631) --- src/Umbraco.Web/umbraco.presentation/content.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Umbraco.Web/umbraco.presentation/content.cs b/src/Umbraco.Web/umbraco.presentation/content.cs index 484014656c..ca847f628b 100644 --- a/src/Umbraco.Web/umbraco.presentation/content.cs +++ b/src/Umbraco.Web/umbraco.presentation/content.cs @@ -478,8 +478,6 @@ namespace umbraco safeXml.AcceptChanges(); } } - - ClearPreviewXmlContent(id); } /// From 5ee22864367cb1628ae2720cf484d05f3579669f Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 11 Mar 2020 12:43:46 +1100 Subject: [PATCH 52/69] Removes build manager type finder and friends --- .../Composing/BruteForceAssemblyProvider.cs | 102 ------------------ src/Umbraco.Core/Composing/TypeFinder.cs | 6 ++ .../Composing/TypeFinderConfig.cs | 36 +++++++ .../Runtime/CoreRuntime.cs | 2 + .../Composing/BuildManagerAssemblyProvider.cs | 70 ------------ .../Composing/BuildManagerTypeFinder.cs | 66 ------------ src/Umbraco.Web/Runtime/WebRuntime.cs | 4 - src/Umbraco.Web/Umbraco.Web.csproj | 2 - 8 files changed, 44 insertions(+), 244 deletions(-) delete mode 100644 src/Umbraco.Core/Composing/BruteForceAssemblyProvider.cs create mode 100644 src/Umbraco.Core/Composing/TypeFinderConfig.cs delete mode 100644 src/Umbraco.Web/Composing/BuildManagerAssemblyProvider.cs delete mode 100644 src/Umbraco.Web/Composing/BuildManagerTypeFinder.cs diff --git a/src/Umbraco.Core/Composing/BruteForceAssemblyProvider.cs b/src/Umbraco.Core/Composing/BruteForceAssemblyProvider.cs deleted file mode 100644 index 04d0d70475..0000000000 --- a/src/Umbraco.Core/Composing/BruteForceAssemblyProvider.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Security; -using Umbraco.Core.Exceptions; - -namespace Umbraco.Core.Composing -{ - /// - /// lazily load a reference to all local assemblies and gac assemblies - /// - /// - /// This is a modified version of: http://www.dominicpettifer.co.uk/Blog/44/how-to-get-a-reference-to-all-assemblies-in-the--bin-folder - /// - /// We do this because we cannot use AppDomain.Current.GetAssemblies() as this will return only assemblies that have been - /// loaded in the CLR, not all assemblies. - /// See these threads: - /// http://issues.umbraco.org/issue/U5-198 - /// http://stackoverflow.com/questions/3552223/asp-net-appdomain-currentdomain-getassemblies-assemblies-missing-after-app - /// http://stackoverflow.com/questions/2477787/difference-between-appdomain-getassemblies-and-buildmanager-getreferencedassembl - /// - public class BruteForceAssemblyProvider : IAssemblyProvider - { - public BruteForceAssemblyProvider() - { - _allAssemblies = new Lazy>(() => - { - HashSet assemblies = null; - try - { - //NOTE: we cannot use AppDomain.CurrentDomain.GetAssemblies() because this only returns assemblies that have - // already been loaded in to the app domain, instead we will look directly into the bin folder and load each one. - var binFolder = GetRootDirectorySafe(); - var binAssemblyFiles = Directory.GetFiles(binFolder, "*.dll", SearchOption.TopDirectoryOnly).ToList(); - assemblies = new HashSet(); - foreach (var a in binAssemblyFiles) - { - try - { - var assName = AssemblyName.GetAssemblyName(a); - var ass = Assembly.Load(assName); - assemblies.Add(ass); - } - catch (Exception e) - { - if (e is SecurityException || e is BadImageFormatException) - { - //swallow these exceptions - } - else - { - throw; - } - } - } - - //Since we are only loading in the /bin assemblies above, we will also load in anything that's already loaded (which will include gac items) - foreach (var a in AppDomain.CurrentDomain.GetAssemblies()) - { - assemblies.Add(a); - } - } - catch (InvalidOperationException e) - { - if (e.InnerException is SecurityException == false) - throw; - } - - return assemblies; - }); - } - - private readonly Lazy> _allAssemblies; - private string _rootDir = string.Empty; - - public IEnumerable Assemblies => _allAssemblies.Value; - - // FIXME - this is only an interim change, once the IIOHelper stuff is merged we should use IIOHelper here - private string GetRootDirectorySafe() - { - if (string.IsNullOrEmpty(_rootDir) == false) - { - return _rootDir; - } - - var codeBase = Assembly.GetExecutingAssembly().CodeBase; - var uri = new Uri(codeBase); - var path = uri.LocalPath; - var baseDirectory = Path.GetDirectoryName(path); - if (string.IsNullOrEmpty(baseDirectory)) - throw new PanicException("No root directory could be resolved."); - - _rootDir = baseDirectory.Contains("bin") - ? baseDirectory.Substring(0, baseDirectory.LastIndexOf("bin", StringComparison.OrdinalIgnoreCase) - 1) - : baseDirectory; - - return _rootDir; - } - } -} diff --git a/src/Umbraco.Core/Composing/TypeFinder.cs b/src/Umbraco.Core/Composing/TypeFinder.cs index 27b13a8365..ee900756c8 100644 --- a/src/Umbraco.Core/Composing/TypeFinder.cs +++ b/src/Umbraco.Core/Composing/TypeFinder.cs @@ -10,6 +10,7 @@ using Umbraco.Core.Logging; namespace Umbraco.Core.Composing { + /// public class TypeFinder : ITypeFinder { @@ -212,6 +213,11 @@ namespace Umbraco.Core.Composing /// public virtual Type GetTypeByName(string name) { + + //NOTE: This will not find types in dynamic assemblies unless those assemblies are already loaded + //into the appdomain. + + // This is exactly what the BuildManager does, if the type is an assembly qualified type // name it will find it. if (TypeNameContainsAssembly(name)) diff --git a/src/Umbraco.Core/Composing/TypeFinderConfig.cs b/src/Umbraco.Core/Composing/TypeFinderConfig.cs new file mode 100644 index 0000000000..3dc672b27c --- /dev/null +++ b/src/Umbraco.Core/Composing/TypeFinderConfig.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; + +namespace Umbraco.Core.Composing +{ + /// + /// TypeFinder config via appSettings + /// + internal class TypeFinderConfig : ITypeFinderConfig + { + private readonly ITypeFinderSettings _settings; + private IEnumerable _assembliesAcceptingLoadExceptions; + + public TypeFinderConfig(ITypeFinderSettings settings) + { + _settings = settings; + } + + public IEnumerable AssembliesAcceptingLoadExceptions + { + get + { + if (_assembliesAcceptingLoadExceptions != null) + return _assembliesAcceptingLoadExceptions; + + var s = _settings.AssembliesAcceptingLoadExceptions; + return _assembliesAcceptingLoadExceptions = string.IsNullOrWhiteSpace(s) + ? Array.Empty() + : s.Split(',').Select(x => x.Trim()).ToArray(); + } + } + } +} diff --git a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs index e12a81be36..39f5a4209f 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs @@ -370,6 +370,8 @@ namespace Umbraco.Core.Runtime /// /// protected virtual ITypeFinder GetTypeFinder() + // TODO: Currently we are not passing in any TypeFinderConfig (with ITypeFinderSettings) which we should do, however + // this is not critical right now and would require loading in some config before boot time so just leaving this as-is for now. => new TypeFinder(Logger, new DefaultUmbracoAssemblyProvider(Assembly.GetEntryAssembly())); diff --git a/src/Umbraco.Web/Composing/BuildManagerAssemblyProvider.cs b/src/Umbraco.Web/Composing/BuildManagerAssemblyProvider.cs deleted file mode 100644 index 3c5cebe03e..0000000000 --- a/src/Umbraco.Web/Composing/BuildManagerAssemblyProvider.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Security; -using System.Web.Compilation; -using Umbraco.Core.Composing; -using Umbraco.Core.Hosting; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; - -namespace Umbraco.Web.Composing -{ - /// - /// Uses the BuildManager to provide a list of assemblies to scan - /// - internal class BuildManagerAssemblyProvider : BruteForceAssemblyProvider, IAssemblyProvider - { - private readonly Lazy> _allAssemblies; - - public BuildManagerAssemblyProvider(IIOHelper ioHelper, - IHostingEnvironment hostingEnvironment, - ILogger logger) - { - _allAssemblies = new Lazy>(() => - { - var isHosted = hostingEnvironment.IsHosted; - try - { - if (isHosted) - { - var assemblies = new HashSet(BuildManager.GetReferencedAssemblies().Cast()); - - //here we are trying to get the App_Code assembly - var fileExtensions = new[] { ".cs", ".vb" }; //only vb and cs files are supported - var appCodeFolder = new DirectoryInfo(ioHelper.MapPath(ioHelper.ResolveUrl("~/App_code"))); - //check if the folder exists and if there are any files in it with the supported file extensions - if (appCodeFolder.Exists && fileExtensions.Any(x => appCodeFolder.GetFiles("*" + x).Any())) - { - try - { - var appCodeAssembly = Assembly.Load("App_Code"); - if (assemblies.Contains(appCodeAssembly) == false) // BuildManager will find App_Code already - assemblies.Add(appCodeAssembly); - } - catch (FileNotFoundException ex) - { - //this will occur if it cannot load the assembly - logger.Error(typeof(TypeFinder), ex, "Could not load assembly App_Code"); - } - } - - return assemblies; - } - } - catch (InvalidOperationException e) - { - if (e.InnerException is SecurityException == false) - throw; - } - - // Not hosted, just use the default implementation - return new HashSet(base.Assemblies); - }); - } - - IEnumerable IAssemblyProvider.Assemblies => _allAssemblies.Value; - } -} diff --git a/src/Umbraco.Web/Composing/BuildManagerTypeFinder.cs b/src/Umbraco.Web/Composing/BuildManagerTypeFinder.cs deleted file mode 100644 index d3502c36fb..0000000000 --- a/src/Umbraco.Web/Composing/BuildManagerTypeFinder.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web.Compilation; -using Umbraco.Core.Configuration; -using Umbraco.Core.Composing; -using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.Logging; - -namespace Umbraco.Web.Composing -{ - - /// - /// An implementation of TypeFinder that uses the BuildManager to resolve references for aspnet framework hosted websites - /// - /// - /// This finder will also try to resolve dynamic assemblies created from App_Code - /// - internal class BuildManagerTypeFinder : TypeFinder, ITypeFinder - { - - public BuildManagerTypeFinder( - ILogger logger, - IAssemblyProvider assemblyProvider, - ITypeFinderConfig typeFinderConfig = null) : base(logger, assemblyProvider, typeFinderConfig) - { - if (logger == null) throw new ArgumentNullException(nameof(logger)); - } - - /// - /// Explicitly implement and return result from BuildManager - /// - /// - /// - Type ITypeFinder.GetTypeByName (string name) => BuildManager.GetType(name, false); - - - /// - /// TypeFinder config via appSettings - /// - internal class TypeFinderConfig : ITypeFinderConfig - { - private readonly ITypeFinderSettings _settings; - private IEnumerable _assembliesAcceptingLoadExceptions; - - public TypeFinderConfig(ITypeFinderSettings settings) - { - _settings = settings; - } - - public IEnumerable AssembliesAcceptingLoadExceptions - { - get - { - if (_assembliesAcceptingLoadExceptions != null) - return _assembliesAcceptingLoadExceptions; - - var s = _settings.AssembliesAcceptingLoadExceptions; - return _assembliesAcceptingLoadExceptions = string.IsNullOrWhiteSpace(s) - ? Array.Empty() - : s.Split(',').Select(x => x.Trim()).ToArray(); - } - } - } - } -} diff --git a/src/Umbraco.Web/Runtime/WebRuntime.cs b/src/Umbraco.Web/Runtime/WebRuntime.cs index 5fab9ff9d4..ea08dc9135 100644 --- a/src/Umbraco.Web/Runtime/WebRuntime.cs +++ b/src/Umbraco.Web/Runtime/WebRuntime.cs @@ -21,8 +21,6 @@ namespace Umbraco.Web.Runtime /// On top of CoreRuntime, handles all of the web-related runtime aspects of Umbraco. public class WebRuntime : CoreRuntime { - private BuildManagerTypeFinder _typeFinder; - /// /// Initializes a new instance of the class. /// @@ -92,8 +90,6 @@ namespace Umbraco.Web.Runtime #region Getters - //protected override ITypeFinder GetTypeFinder() => _typeFinder ?? (_typeFinder = new BuildManagerTypeFinder(IOHelper, HostingEnvironment, Logger, new BuildManagerTypeFinder.TypeFinderConfig(new TypeFinderSettings()))); - protected override AppCaches GetAppCaches() => new AppCaches( // we need to have the dep clone runtime cache provider to ensure // all entities are cached properly (cloned in and cloned out) diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 27ea9443a0..e9a4e04e58 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -143,8 +143,6 @@ - - From 66cd25d8f56e0e459ed881e9fa3e633fb8b1ed1f Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 11 Mar 2020 13:10:34 +1100 Subject: [PATCH 53/69] Adds notes --- .../Composing/DefaultUmbracoAssemblyProvider.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs b/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs index c13a42ece7..61d7cff240 100644 --- a/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs +++ b/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs @@ -29,6 +29,12 @@ namespace Umbraco.Core.Composing _entryPointAssembly = entryPointAssembly ?? throw new ArgumentNullException(nameof(entryPointAssembly)); } + // TODO: It would be worth investigating a netcore3 version of this which would use + // var allAssemblies = System.Runtime.Loader.AssemblyLoadContext.All.SelectMany(x => x.Assemblies); + // that will still only resolve Assemblies that are already loaded but it would also make it possible to + // query dynamically generated assemblies once they are added. It would also provide the ability to probe + // assembly locations that are not in the same place as the entry point assemblies. + public IEnumerable Assemblies { get From 3bfa2e76cb91902af18215e34bf8ef00ccdb847d Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 11 Mar 2020 15:28:08 +1100 Subject: [PATCH 54/69] Adjust type finder and adds benchmark --- src/Umbraco.Core/Composing/TypeFinder.cs | 51 +++++++++++++------ src/Umbraco.Core/Composing/TypeHelper.cs | 6 +-- .../TypeFinderBenchmarks.cs | 30 +++++++++++ .../Umbraco.Tests.Benchmarks.csproj | 1 + 4 files changed, 69 insertions(+), 19 deletions(-) create mode 100644 src/Umbraco.Tests.Benchmarks/TypeFinderBenchmarks.cs diff --git a/src/Umbraco.Core/Composing/TypeFinder.cs b/src/Umbraco.Core/Composing/TypeFinder.cs index ee900756c8..cfed155d83 100644 --- a/src/Umbraco.Core/Composing/TypeFinder.cs +++ b/src/Umbraco.Core/Composing/TypeFinder.cs @@ -16,6 +16,13 @@ namespace Umbraco.Core.Composing { private readonly ILogger _logger; private readonly IAssemblyProvider _assemblyProvider; + private volatile HashSet _localFilteredAssemblyCache; + private readonly object _localFilteredAssemblyCacheLocker = new object(); + private readonly List _notifiedLoadExceptionAssemblies = new List(); + private static readonly ConcurrentDictionary TypeNamesCache = new ConcurrentDictionary(); + private readonly string[] _assembliesAcceptingLoadExceptions; + + internal bool QueryWithReferencingAssemblies = true; public TypeFinder(ILogger logger, IAssemblyProvider assemblyProvider, ITypeFinderConfig typeFinderConfig = null) { @@ -24,12 +31,6 @@ namespace Umbraco.Core.Composing _assembliesAcceptingLoadExceptions = typeFinderConfig?.AssembliesAcceptingLoadExceptions.Where(x => !x.IsNullOrWhiteSpace()).ToArray() ?? Array.Empty(); } - private volatile HashSet _localFilteredAssemblyCache; - private readonly object _localFilteredAssemblyCacheLocker = new object(); - private readonly List _notifiedLoadExceptionAssemblies = new List(); - private static readonly ConcurrentDictionary TypeNamesCache= new ConcurrentDictionary(); - private readonly string[] _assembliesAcceptingLoadExceptions; - private bool AcceptsLoadExceptions(Assembly a) { if (_assembliesAcceptingLoadExceptions.Length == 0) @@ -268,18 +269,24 @@ namespace Umbraco.Core.Composing var stack = new Stack(); stack.Push(attributeType.Assembly); + if (!QueryWithReferencingAssemblies) + { + foreach (var a in candidateAssemblies) + stack.Push(a); + } + while (stack.Count > 0) { var assembly = stack.Pop(); - Type[] assemblyTypes = null; + IReadOnlyList assemblyTypes = null; if (assembly != attributeType.Assembly || attributeAssemblyIsCandidate) { // get all assembly types that can be assigned to baseType try { assemblyTypes = GetTypesWithFormattedException(assembly) - .ToArray(); // in try block + .ToList(); // in try block } catch (TypeLoadException ex) { @@ -299,10 +306,13 @@ namespace Umbraco.Core.Composing if (assembly != attributeType.Assembly && assemblyTypes.Where(attributeType.IsAssignableFrom).Any() == false) continue; - foreach (var referencing in TypeHelper.GetReferencingAssemblies(assembly, candidateAssemblies)) + if (QueryWithReferencingAssemblies) { - candidateAssemblies.Remove(referencing); - stack.Push(referencing); + foreach (var referencing in TypeHelper.GetReferencingAssemblies(assembly, candidateAssemblies)) + { + candidateAssemblies.Remove(referencing); + stack.Push(referencing); + } } } @@ -333,19 +343,25 @@ namespace Umbraco.Core.Composing var stack = new Stack(); stack.Push(baseType.Assembly); + if (!QueryWithReferencingAssemblies) + { + foreach (var a in candidateAssemblies) + stack.Push(a); + } + while (stack.Count > 0) { var assembly = stack.Pop(); // get all assembly types that can be assigned to baseType - Type[] assemblyTypes = null; + IReadOnlyList assemblyTypes = null; if (assembly != baseType.Assembly || baseTypeAssemblyIsCandidate) { try { assemblyTypes = GetTypesWithFormattedException(assembly) .Where(baseType.IsAssignableFrom) - .ToArray(); // in try block + .ToList(); // in try block } catch (TypeLoadException ex) { @@ -365,10 +381,13 @@ namespace Umbraco.Core.Composing if (assembly != baseType.Assembly && assemblyTypes.All(x => x.IsSealed)) continue; - foreach (var referencing in TypeHelper.GetReferencingAssemblies(assembly, candidateAssemblies)) + if (QueryWithReferencingAssemblies) { - candidateAssemblies.Remove(referencing); - stack.Push(referencing); + foreach (var referencing in TypeHelper.GetReferencingAssemblies(assembly, candidateAssemblies)) + { + candidateAssemblies.Remove(referencing); + stack.Push(referencing); + } } } diff --git a/src/Umbraco.Core/Composing/TypeHelper.cs b/src/Umbraco.Core/Composing/TypeHelper.cs index 28eab6a5ec..1987a4059c 100644 --- a/src/Umbraco.Core/Composing/TypeHelper.cs +++ b/src/Umbraco.Core/Composing/TypeHelper.cs @@ -82,9 +82,9 @@ namespace Umbraco.Core.Composing /// If the assembly of the assignTypeFrom Type is in the App_Code assembly, then we return nothing since things cannot /// reference that assembly, same with the global.asax assembly. /// - public static Assembly[] GetReferencingAssemblies(Assembly assembly, IEnumerable assemblies) + public static IReadOnlyList GetReferencingAssemblies(Assembly assembly, IEnumerable assemblies) { - if (assembly.IsAppCodeAssembly() || assembly.IsGlobalAsaxAssembly()) + if (assembly.IsDynamic || assembly.IsAppCodeAssembly() || assembly.IsGlobalAsaxAssembly()) return EmptyAssemblies; @@ -92,7 +92,7 @@ namespace Umbraco.Core.Composing // should only be scanning those assemblies because any other assembly will definitely not // contain sub type's of the one we're currently looking for var name = assembly.GetName().Name; - return assemblies.Where(x => x == assembly || HasReference(x, name)).ToArray(); + return assemblies.Where(x => x == assembly || HasReference(x, name)).ToList(); } /// diff --git a/src/Umbraco.Tests.Benchmarks/TypeFinderBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/TypeFinderBenchmarks.cs new file mode 100644 index 0000000000..7b4322bfac --- /dev/null +++ b/src/Umbraco.Tests.Benchmarks/TypeFinderBenchmarks.cs @@ -0,0 +1,30 @@ +using BenchmarkDotNet.Attributes; +using System; +using System.Linq; +using Umbraco.Core.Composing; +using Umbraco.Core.Logging; +using Umbraco.Tests.Benchmarks.Config; + +namespace Umbraco.Tests.Benchmarks +{ + [MediumRunJob] + [MemoryDiagnoser] + public class TypeFinderBenchmarks + { + + [Benchmark(Baseline = true)] + public void WithGetReferencingAssembliesCheck() + { + var typeFinder1 = new TypeFinder(new NullLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + var found = typeFinder1.FindClassesOfType().Count(); + } + + [Benchmark] + public void WithoutGetReferencingAssembliesCheck() + { + var typeFinder2 = new TypeFinder(new NullLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)); + typeFinder2.QueryWithReferencingAssemblies = false; + var found = typeFinder2.FindClassesOfType().Count(); + } + } +} diff --git a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj index 7566d8ab85..84ec535b9d 100644 --- a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj +++ b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj @@ -61,6 +61,7 @@ + From e3997a4ab2455d65f97a2b0cb07055d32c44e285 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 11 Mar 2020 15:32:26 +1100 Subject: [PATCH 55/69] adds notes --- src/Umbraco.Core/Composing/TypeFinder.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Core/Composing/TypeFinder.cs b/src/Umbraco.Core/Composing/TypeFinder.cs index cfed155d83..79fddad1ca 100644 --- a/src/Umbraco.Core/Composing/TypeFinder.cs +++ b/src/Umbraco.Core/Composing/TypeFinder.cs @@ -22,6 +22,7 @@ namespace Umbraco.Core.Composing private static readonly ConcurrentDictionary TypeNamesCache = new ConcurrentDictionary(); private readonly string[] _assembliesAcceptingLoadExceptions; + // used for benchmark tests internal bool QueryWithReferencingAssemblies = true; public TypeFinder(ILogger logger, IAssemblyProvider assemblyProvider, ITypeFinderConfig typeFinderConfig = null) From e07c9abd582bd960b862a58e1eef2410c7cbeeb2 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 11 Mar 2020 12:54:33 +0100 Subject: [PATCH 56/69] Updated js code in UI project --- .../Umbraco/js/navigation.controller.js | 61 +++++++++++++------ 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/src/Umbraco.Web.UI/Umbraco/js/navigation.controller.js b/src/Umbraco.Web.UI/Umbraco/js/navigation.controller.js index 194c45afe6..281be2d331 100644 --- a/src/Umbraco.Web.UI/Umbraco/js/navigation.controller.js +++ b/src/Umbraco.Web.UI/Umbraco/js/navigation.controller.js @@ -257,6 +257,7 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar evts.push(eventsService.on("app.ready", function (evt, data) { $scope.authenticated = true; ensureInit(); + ensureMainCulture(); })); // event for infinite editors @@ -279,8 +280,22 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar } })); - - + /** + * For multi language sites, this ensures that mculture is set to either the last selected language or the default one + */ + function ensureMainCulture() { + if ($location.search().mculture) { + return; + } + var language = lastLanguageOrDefault(); + if (!language) { + return; + } + // trigger a language selection in the next digest cycle + $timeout(function () { + $scope.selectLanguage(language); + }); + } /** * Based on the current state of the application, this configures the scope variables that control the main tree and language drop down @@ -385,28 +400,19 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar if ($scope.languages.length > 1) { //if there's already one set, check if it exists - var currCulture = null; + var language = null; var mainCulture = $location.search().mculture; if (mainCulture) { - currCulture = _.find($scope.languages, function (l) { + language = _.find($scope.languages, function (l) { return l.culture.toLowerCase() === mainCulture.toLowerCase(); }); } - if (!currCulture) { - // no culture in the request, let's look for one in the cookie that's set when changing language - var defaultCulture = $cookies.get("UMB_MCULTURE"); - if (!defaultCulture || !_.find($scope.languages, function (l) { - return l.culture.toLowerCase() === defaultCulture.toLowerCase(); - })) { - // no luck either, look for the default language - var defaultLang = _.find($scope.languages, function (l) { - return l.isDefault; - }); - if (defaultLang) { - defaultCulture = defaultLang.culture; - } + if (!language) { + language = lastLanguageOrDefault(); + + if (language) { + $location.search("mculture", language.culture); } - $location.search("mculture", defaultCulture ? defaultCulture : null); } } @@ -431,6 +437,25 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar }); }); } + + function lastLanguageOrDefault() { + if (!$scope.languages || $scope.languages.length <= 1) { + return null; + } + // see if we can find a culture in the cookie set when changing language + var lastCulture = $cookies.get("UMB_MCULTURE"); + var language = lastCulture ? _.find($scope.languages, function (l) { + return l.culture.toLowerCase() === lastCulture.toLowerCase(); + }) : null; + if (!language) { + // no luck, look for the default language + language = _.find($scope.languages, function (l) { + return l.isDefault; + }); + } + return language; + } + function nodeExpandedHandler(args) { //store the reference to the expanded node path if (args.node) { From 84847267ba7dec6e8863da83a0a14cd6476355ab Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 12 Mar 2020 15:26:06 +1100 Subject: [PATCH 57/69] updates CoreRuntime with notes and fallback --- src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs index 39f5a4209f..fe51609c88 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs @@ -372,7 +372,19 @@ namespace Umbraco.Core.Runtime protected virtual ITypeFinder GetTypeFinder() // TODO: Currently we are not passing in any TypeFinderConfig (with ITypeFinderSettings) which we should do, however // this is not critical right now and would require loading in some config before boot time so just leaving this as-is for now. - => new TypeFinder(Logger, new DefaultUmbracoAssemblyProvider(Assembly.GetEntryAssembly())); + => new TypeFinder(Logger, new DefaultUmbracoAssemblyProvider( + // GetEntryAssembly was actually an exposed API by request of the aspnetcore team which works in aspnet core because a website + // in that case is essentially an exe. However in netframework there is no entry assembly, things don't really work that way since + // the process that is running the site is iisexpress, so this returns null. The best we can do is fallback to GetExecutingAssembly() + // which will just return Umbraco.Infrastructure (currently with netframework) and for our purposes that is OK. + // If you are curious... There is really no way to get the entry assembly in netframework without the hosting website having it's own + // code compiled for the global.asax which is the entry point. Because the default global.asax for umbraco websites is just a file inheriting + // from Umbraco.Web.UmbracoApplication, the global.asax file gets dynamically compiled into a DLL in the dynamic folder (we can get an instance + // of that, but this doesn't really help us) but the actually entry execution is still Umbraco.Web. So that is the 'highest' level entry point + // assembly we can get and we can only get that if we put this code into the WebRuntime since the executing assembly is the 'current' one. + // For this purpose, it doesn't matter if it's Umbraco.Web or Umbraco.Infrastructure since all assemblies are in that same path and we are + // getting rid of netframework. + Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly())); /// From f446e52d8fe08ad15285457800c74b82ca8b0053 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Thu, 12 Mar 2020 08:24:02 +0100 Subject: [PATCH 58/69] Cleaned up condig for Logging and Tours --- src/Umbraco.Configuration/ConfigsFactory.cs | 6 + .../ConfigurationManagerConfigBase.cs | 22 + .../Implementations/LoggingSettings.cs | 9 + .../Implementations/TourSettings.cs | 9 + .../UmbracoSettings/BackOfficeElement.cs | 2 +- .../UmbracoSettings/LoggingElement.cs | 4 +- .../UmbracoSettings/TourConfigElement.cs | 2 +- .../UmbracoSettings/UmbracoSettingsSection.cs | 4 - .../UmbracoSettings/IBackOfficeSection.cs | 2 +- ...ILoggingSection.cs => ILoggingSettings.cs} | 2 +- .../{ITourSection.cs => ITourSettings.cs} | 2 +- .../IUmbracoSettingsSection.cs | 4 - .../Scheduling/LogScrubber.cs | 12 +- .../Scheduling/SchedulerComponent.cs | 13 +- .../LoggingElementDefaultTests.cs | 2 +- .../UmbracoSettings/LoggingElementTests.cs | 4 +- .../UmbracoSettings/UmbracoSettingsTests.cs | 11 +- .../TestHelpers/SettingsForTests.cs | 3 +- src/Umbraco.Web.UI.Client/package-lock.json | 844 +++++++++--------- src/Umbraco.Web/Editors/TourController.cs | 10 +- 20 files changed, 512 insertions(+), 455 deletions(-) create mode 100644 src/Umbraco.Configuration/Implementations/ConfigurationManagerConfigBase.cs create mode 100644 src/Umbraco.Configuration/Implementations/LoggingSettings.cs create mode 100644 src/Umbraco.Configuration/Implementations/TourSettings.cs rename src/Umbraco.Core/Configuration/UmbracoSettings/{ILoggingSection.cs => ILoggingSettings.cs} (66%) rename src/Umbraco.Core/Configuration/UmbracoSettings/{ITourSection.cs => ITourSettings.cs} (75%) diff --git a/src/Umbraco.Configuration/ConfigsFactory.cs b/src/Umbraco.Configuration/ConfigsFactory.cs index 3742b7d7fa..0b40da6947 100644 --- a/src/Umbraco.Configuration/ConfigsFactory.cs +++ b/src/Umbraco.Configuration/ConfigsFactory.cs @@ -1,5 +1,6 @@ using System.Configuration; using Umbraco.Configuration; +using Umbraco.Configuration.Implementations; using Umbraco.Core.Configuration.HealthChecks; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; @@ -18,6 +19,8 @@ namespace Umbraco.Core.Configuration public IRuntimeSettings RuntimeSettings { get; } = new RuntimeSettings(); public IActiveDirectorySettings ActiveDirectorySettings { get; } = new ActiveDirectorySettings(); public IExceptionFilterSettings ExceptionFilterSettings { get; } = new ExceptionFilterSettings(); + public ITourSettings TourSettings { get; } = new TourSettings(); + public ILoggingSettings LoggingSettings { get; } = new LoggingSettings(); public IUmbracoSettingsSection UmbracoSettings { get; } @@ -47,6 +50,9 @@ namespace Umbraco.Core.Configuration configs.Add(() => ActiveDirectorySettings); configs.Add(() => ExceptionFilterSettings); + configs.Add(() => TourSettings); + configs.Add(() => LoggingSettings); + configs.AddCoreConfigs(ioHelper); return configs; } diff --git a/src/Umbraco.Configuration/Implementations/ConfigurationManagerConfigBase.cs b/src/Umbraco.Configuration/Implementations/ConfigurationManagerConfigBase.cs new file mode 100644 index 0000000000..d218ac803b --- /dev/null +++ b/src/Umbraco.Configuration/Implementations/ConfigurationManagerConfigBase.cs @@ -0,0 +1,22 @@ +using System.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; + +namespace Umbraco.Configuration.Implementations +{ + internal abstract class ConfigurationManagerConfigBase + { + private UmbracoSettingsSection _umbracoSettingsSection; + + public UmbracoSettingsSection UmbracoSettingsSection + { + get + { + if (_umbracoSettingsSection is null) + { + _umbracoSettingsSection = ConfigurationManager.GetSection("umbracoConfiguration/settings") as UmbracoSettingsSection; + } + return _umbracoSettingsSection; + } + } + } +} diff --git a/src/Umbraco.Configuration/Implementations/LoggingSettings.cs b/src/Umbraco.Configuration/Implementations/LoggingSettings.cs new file mode 100644 index 0000000000..020b0c0e64 --- /dev/null +++ b/src/Umbraco.Configuration/Implementations/LoggingSettings.cs @@ -0,0 +1,9 @@ +using Umbraco.Core.Configuration.UmbracoSettings; + +namespace Umbraco.Configuration.Implementations +{ + internal class LoggingSettings : ConfigurationManagerConfigBase, ILoggingSettings + { + public int MaxLogAge => UmbracoSettingsSection.Logging.MaxLogAge; + } +} diff --git a/src/Umbraco.Configuration/Implementations/TourSettings.cs b/src/Umbraco.Configuration/Implementations/TourSettings.cs new file mode 100644 index 0000000000..134c3c48d5 --- /dev/null +++ b/src/Umbraco.Configuration/Implementations/TourSettings.cs @@ -0,0 +1,9 @@ +using Umbraco.Core.Configuration.UmbracoSettings; + +namespace Umbraco.Configuration.Implementations +{ + internal class TourSettings : ConfigurationManagerConfigBase, ITourSettings + { + public bool EnableTours => UmbracoSettingsSection.BackOffice.Tours.EnableTours; + } +} diff --git a/src/Umbraco.Configuration/UmbracoSettings/BackOfficeElement.cs b/src/Umbraco.Configuration/UmbracoSettings/BackOfficeElement.cs index 79bff51d05..46b9bf32a9 100644 --- a/src/Umbraco.Configuration/UmbracoSettings/BackOfficeElement.cs +++ b/src/Umbraco.Configuration/UmbracoSettings/BackOfficeElement.cs @@ -7,6 +7,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings [ConfigurationProperty("tours")] internal TourConfigElement Tours => (TourConfigElement)this["tours"]; - ITourSection IBackOfficeSection.Tours => Tours; + ITourSettings IBackOfficeSection.Tours => Tours; } } diff --git a/src/Umbraco.Configuration/UmbracoSettings/LoggingElement.cs b/src/Umbraco.Configuration/UmbracoSettings/LoggingElement.cs index 106b6cc134..2fdd61e169 100644 --- a/src/Umbraco.Configuration/UmbracoSettings/LoggingElement.cs +++ b/src/Umbraco.Configuration/UmbracoSettings/LoggingElement.cs @@ -3,12 +3,12 @@ using System.Configuration; namespace Umbraco.Core.Configuration.UmbracoSettings { - internal class LoggingElement : UmbracoConfigurationElement, ILoggingSection + internal class LoggingElement : UmbracoConfigurationElement, ILoggingSettings { [ConfigurationProperty("maxLogAge")] internal InnerTextConfigurationElement MaxLogAge => GetOptionalTextElement("maxLogAge", -1); - int ILoggingSection.MaxLogAge => MaxLogAge; + int ILoggingSettings.MaxLogAge => MaxLogAge; } } diff --git a/src/Umbraco.Configuration/UmbracoSettings/TourConfigElement.cs b/src/Umbraco.Configuration/UmbracoSettings/TourConfigElement.cs index dab69f3da0..f75b71fc57 100644 --- a/src/Umbraco.Configuration/UmbracoSettings/TourConfigElement.cs +++ b/src/Umbraco.Configuration/UmbracoSettings/TourConfigElement.cs @@ -2,7 +2,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - internal class TourConfigElement : UmbracoConfigurationElement, ITourSection + internal class TourConfigElement : UmbracoConfigurationElement, ITourSettings { //disabled by default so that upgraders don't get it enabled by default // TODO: we probably just want to disable the initial one from automatically loading ? diff --git a/src/Umbraco.Configuration/UmbracoSettings/UmbracoSettingsSection.cs b/src/Umbraco.Configuration/UmbracoSettings/UmbracoSettingsSection.cs index e605b86edf..9187cecd40 100644 --- a/src/Umbraco.Configuration/UmbracoSettings/UmbracoSettingsSection.cs +++ b/src/Umbraco.Configuration/UmbracoSettings/UmbracoSettingsSection.cs @@ -31,10 +31,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings IRequestHandlerSection IUmbracoSettingsSection.RequestHandler => RequestHandler; - IBackOfficeSection IUmbracoSettingsSection.BackOffice => BackOffice; - - ILoggingSection IUmbracoSettingsSection.Logging => Logging; - IWebRoutingSection IUmbracoSettingsSection.WebRouting => WebRouting; IKeepAliveSection IUmbracoSettingsSection.KeepAlive => KeepAlive; diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IBackOfficeSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IBackOfficeSection.cs index 36dd6a22ed..85df4540c0 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IBackOfficeSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IBackOfficeSection.cs @@ -2,6 +2,6 @@ { public interface IBackOfficeSection { - ITourSection Tours { get; } + ITourSettings Tours { get; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ILoggingSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ILoggingSettings.cs similarity index 66% rename from src/Umbraco.Core/Configuration/UmbracoSettings/ILoggingSection.cs rename to src/Umbraco.Core/Configuration/UmbracoSettings/ILoggingSettings.cs index 6c1be8ade5..ee5647ee27 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/ILoggingSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ILoggingSettings.cs @@ -2,7 +2,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - public interface ILoggingSection : IUmbracoConfigurationSection + public interface ILoggingSettings : IUmbracoConfigurationSection { int MaxLogAge { get; } } diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ITourSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ITourSettings.cs similarity index 75% rename from src/Umbraco.Core/Configuration/UmbracoSettings/ITourSection.cs rename to src/Umbraco.Core/Configuration/UmbracoSettings/ITourSettings.cs index 938642521e..d3d8293140 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/ITourSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ITourSettings.cs @@ -1,6 +1,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - public interface ITourSection + public interface ITourSettings { bool EnableTours { get; } } diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs index acd9c92588..ff11d223ac 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs @@ -4,15 +4,11 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { public interface IUmbracoSettingsSection : IUmbracoConfigurationSection { - IBackOfficeSection BackOffice { get; } - IContentSection Content { get; } ISecuritySection Security { get; } IRequestHandlerSection RequestHandler { get; } - - ILoggingSection Logging { get; } IWebRoutingSection WebRouting { get; } diff --git a/src/Umbraco.Infrastructure/Scheduling/LogScrubber.cs b/src/Umbraco.Infrastructure/Scheduling/LogScrubber.cs index e624c4e591..563d79a193 100644 --- a/src/Umbraco.Infrastructure/Scheduling/LogScrubber.cs +++ b/src/Umbraco.Infrastructure/Scheduling/LogScrubber.cs @@ -13,12 +13,12 @@ namespace Umbraco.Web.Scheduling { private readonly IRuntimeState _runtime; private readonly IAuditService _auditService; - private readonly IUmbracoSettingsSection _settings; + private readonly ILoggingSettings _settings; private readonly IProfilingLogger _logger; private readonly IScopeProvider _scopeProvider; public LogScrubber(IBackgroundTaskRunner runner, int delayMilliseconds, int periodMilliseconds, - IRuntimeState runtime, IAuditService auditService, IUmbracoSettingsSection settings, IScopeProvider scopeProvider, IProfilingLogger logger) + IRuntimeState runtime, IAuditService auditService, ILoggingSettings settings, IScopeProvider scopeProvider, IProfilingLogger logger) : base(runner, delayMilliseconds, periodMilliseconds) { _runtime = runtime; @@ -29,13 +29,13 @@ namespace Umbraco.Web.Scheduling } // maximum age, in minutes - private int GetLogScrubbingMaximumAge(IUmbracoSettingsSection settings) + private int GetLogScrubbingMaximumAge(ILoggingSettings settings) { var maximumAge = 24 * 60; // 24 hours, in minutes try { - if (settings.Logging.MaxLogAge > -1) - maximumAge = settings.Logging.MaxLogAge; + if (settings.MaxLogAge > -1) + maximumAge = settings.MaxLogAge; } catch (Exception ex) { @@ -45,7 +45,7 @@ namespace Umbraco.Web.Scheduling } - public static int GetLogScrubbingInterval(IUmbracoSettingsSection settings, ILogger logger) + public static int GetLogScrubbingInterval() { const int interval = 4 * 60 * 60 * 1000; // 4 hours, in milliseconds return interval; diff --git a/src/Umbraco.Infrastructure/Scheduling/SchedulerComponent.cs b/src/Umbraco.Infrastructure/Scheduling/SchedulerComponent.cs index 6e5d0ee81e..a1385257e9 100644 --- a/src/Umbraco.Infrastructure/Scheduling/SchedulerComponent.cs +++ b/src/Umbraco.Infrastructure/Scheduling/SchedulerComponent.cs @@ -39,6 +39,7 @@ namespace Umbraco.Web.Scheduling private readonly IIOHelper _ioHelper; private readonly IServerMessenger _serverMessenger; private readonly IRequestAccessor _requestAccessor; + private readonly ILoggingSettings _loggingSettings; private BackgroundTaskRunner _keepAliveRunner; private BackgroundTaskRunner _publishingRunner; @@ -56,7 +57,8 @@ namespace Umbraco.Web.Scheduling HealthCheckCollection healthChecks, HealthCheckNotificationMethodCollection notifications, IScopeProvider scopeProvider, IUmbracoContextFactory umbracoContextFactory, IProfilingLogger logger, IHostingEnvironment hostingEnvironment, IHealthChecks healthChecksConfig, - IUmbracoSettingsSection umbracoSettingsSection, IIOHelper ioHelper, IServerMessenger serverMessenger, IRequestAccessor requestAccessor) + IUmbracoSettingsSection umbracoSettingsSection, IIOHelper ioHelper, IServerMessenger serverMessenger, IRequestAccessor requestAccessor, + ILoggingSettings loggingSettings) { _runtime = runtime; _contentService = contentService; @@ -69,10 +71,11 @@ namespace Umbraco.Web.Scheduling _healthChecks = healthChecks; _notifications = notifications; _healthChecksConfig = healthChecksConfig ?? throw new ArgumentNullException(nameof(healthChecksConfig)); - _umbracoSettingsSection = umbracoSettingsSection ?? throw new ArgumentNullException(nameof(umbracoSettingsSection)); + _umbracoSettingsSection = umbracoSettingsSection; _ioHelper = ioHelper; _serverMessenger = serverMessenger; _requestAccessor = requestAccessor; + _loggingSettings = loggingSettings; } public void Initialize() @@ -121,7 +124,7 @@ namespace Umbraco.Web.Scheduling } tasks.Add(RegisterScheduledPublishing()); - tasks.Add(RegisterLogScrubber(settings)); + tasks.Add(RegisterLogScrubber(_loggingSettings)); tasks.Add(RegisterTempFileCleanup()); var healthCheckConfig = _healthChecksConfig; @@ -176,11 +179,11 @@ namespace Umbraco.Web.Scheduling return task; } - private IBackgroundTask RegisterLogScrubber(IUmbracoSettingsSection settings) + private IBackgroundTask RegisterLogScrubber(ILoggingSettings settings) { // log scrubbing // install on all, will only run on non-replica servers - var task = new LogScrubber(_scrubberRunner, DefaultDelayMilliseconds, LogScrubber.GetLogScrubbingInterval(settings, _logger), _runtime, _auditService, settings, _scopeProvider, _logger); + var task = new LogScrubber(_scrubberRunner, DefaultDelayMilliseconds, LogScrubber.GetLogScrubbingInterval(), _runtime, _auditService, settings, _scopeProvider, _logger); _scrubberRunner.TryAdd(task); return task; } diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/LoggingElementDefaultTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/LoggingElementDefaultTests.cs index c0819ad828..cdd5855730 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/LoggingElementDefaultTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/LoggingElementDefaultTests.cs @@ -14,7 +14,7 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings [Test] public override void MaxLogAge() { - Assert.IsTrue(SettingsSection.Logging.MaxLogAge == -1); + Assert.IsTrue(LoggingSettings.MaxLogAge == -1); } } } diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/LoggingElementTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/LoggingElementTests.cs index 31edc87a7c..be495ad9d0 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/LoggingElementTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/LoggingElementTests.cs @@ -6,11 +6,11 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings [TestFixture] public class LoggingElementTests : UmbracoSettingsTests { - + [Test] public virtual void MaxLogAge() { - Assert.IsTrue(SettingsSection.Logging.MaxLogAge == 1440); + Assert.IsTrue(LoggingSettings.MaxLogAge == 1440); } diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/UmbracoSettingsTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/UmbracoSettingsTests.cs index e537bad504..a6da02e281 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/UmbracoSettingsTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/UmbracoSettingsTests.cs @@ -22,16 +22,19 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings Debug.WriteLine("Testing defaults? {0}", TestingDefaults); if (TestingDefaults) { - SettingsSection = configuration.GetSection("umbracoConfiguration/defaultSettings") as UmbracoSettingsSection; + Settings = configuration.GetSection("umbracoConfiguration/defaultSettings") as UmbracoSettingsSection; } else { - SettingsSection = configuration.GetSection("umbracoConfiguration/settings") as UmbracoSettingsSection; + Settings = configuration.GetSection("umbracoConfiguration/settings") as UmbracoSettingsSection; } - Assert.IsNotNull(SettingsSection); + Assert.IsNotNull(Settings); } - protected IUmbracoSettingsSection SettingsSection { get; private set; } + protected IUmbracoSettingsSection SettingsSection => Settings; + private UmbracoSettingsSection Settings { get; set; } + + protected ILoggingSettings LoggingSettings => Settings.Logging; } } diff --git a/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs b/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs index 56fdc96aa7..e1badf33d6 100644 --- a/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs +++ b/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs @@ -45,7 +45,7 @@ namespace Umbraco.Tests.TestHelpers var content = new Mock(); var security = new Mock(); var requestHandler = new Mock(); - var logging = new Mock(); + var logging = new Mock(); var routing = new Mock(); var userPasswordConfig = new Mock(); @@ -56,7 +56,6 @@ namespace Umbraco.Tests.TestHelpers settings.Setup(x => x.Content).Returns(content.Object); settings.Setup(x => x.Security).Returns(security.Object); settings.Setup(x => x.RequestHandler).Returns(requestHandler.Object); - settings.Setup(x => x.Logging).Returns(logging.Object); settings.Setup(x => x.WebRouting).Returns(routing.Object); //Now configure some defaults - the defaults in the config section classes do NOT pertain to the mocked data!! diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 42a89c5d13..2566f56244 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 } } @@ -902,7 +902,7 @@ "@nodelib/fs.scandir": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", - "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", + "integrity": "sha1-Olgr21OATGum0UZXnEblITDPSjs=", "dev": true, "requires": { "@nodelib/fs.stat": "2.0.3", @@ -912,13 +912,13 @@ "@nodelib/fs.stat": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", - "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", + "integrity": "sha1-NNxfTKu8cg9OYPdadH5+zWwXW9M=", "dev": true }, "@nodelib/fs.walk": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", - "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", + "integrity": "sha1-ARuSAqcKY2bkNspcBlhEUoqwSXY=", "dev": true, "requires": { "@nodelib/fs.scandir": "2.1.3", @@ -928,7 +928,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 }, @@ -940,13 +940,13 @@ "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", - "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", + "integrity": "sha1-KGLz9Yqaf3w+eNefEw3U1xwlwqc=", "dev": true }, "@types/glob": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", - "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "integrity": "sha1-qlmhxuP7xCHgfM0xqUTDDrpSFXU=", "dev": true, "requires": { "@types/events": "*", @@ -957,7 +957,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": { @@ -969,13 +969,13 @@ "@types/q": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz", - "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==", + "integrity": "sha1-aQoUdbhPKohP0HzXl8APXzE1bqg=", "dev": true }, "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", @@ -985,7 +985,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", @@ -1032,7 +1032,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", @@ -1101,17 +1101,17 @@ "angular": { "version": "1.7.9", "resolved": "https://registry.npmjs.org/angular/-/angular-1.7.9.tgz", - "integrity": "sha512-5se7ZpcOtu0MBFlzGv5dsM1quQDoDeUTwZrWjGtTNA7O88cD8TEk5IEKCTDa3uECV9XnvKREVUr7du1ACiWGFQ==" + "integrity": "sha1-5SYW6HAcF3JMPCOM/k+URv1XC8Q=" }, "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", @@ -1136,12 +1136,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" } @@ -1149,7 +1149,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", @@ -1159,32 +1159,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", @@ -1247,7 +1247,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" @@ -1389,7 +1389,7 @@ "arch": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/arch/-/arch-2.1.1.tgz", - "integrity": "sha512-BLM56aPo9vLLFVa8+/+pJLnrZ7QGGTVHWsCwieAWT9o9K8UeGaQbzZbGoabWLOo2ksBCztoXdqBZBplqLDDCSg==", + "integrity": "sha1-j1wnMao1owkpIhuwZA7tZRdeyE4=", "dev": true, "optional": true }, @@ -1501,7 +1501,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 } } @@ -1509,7 +1509,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" @@ -1518,7 +1518,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 } } @@ -1532,7 +1532,7 @@ "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", @@ -1543,7 +1543,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 } } @@ -1551,7 +1551,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": { @@ -1582,7 +1582,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=", "dev": true, "requires": { "safer-buffer": "~2.1.0" @@ -1603,13 +1603,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" @@ -1626,7 +1626,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", @@ -1638,13 +1638,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=", "dev": true }, "async-settle": { @@ -1665,13 +1665,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", @@ -1706,7 +1706,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" @@ -1805,7 +1805,7 @@ "base64-js": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "integrity": "sha1-WOzoy3XdB+ce0IxzarxfrE2/jfE=", "dev": true }, "base64id": { @@ -1841,7 +1841,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": { @@ -1855,7 +1855,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": { @@ -1866,7 +1866,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": { @@ -1877,7 +1877,7 @@ "execa": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "integrity": "sha1-xiNqW7TfbW8V6I5/AXeYIWdJ3dg=", "dev": true, "optional": true, "requires": { @@ -1893,7 +1893,7 @@ "get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "integrity": "sha1-wbJVV189wh1Zv8ec09K0axw6VLU=", "dev": true, "optional": true, "requires": { @@ -1903,7 +1903,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": { @@ -1916,7 +1916,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": { @@ -1928,7 +1928,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": { @@ -1943,7 +1943,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": { @@ -1973,14 +1973,14 @@ "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 }, "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": { @@ -2015,7 +2015,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": { @@ -2034,14 +2034,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": { @@ -2051,7 +2051,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, "requires": { "p-finally": "^1.0.0" @@ -2060,7 +2060,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 }, @@ -2086,7 +2086,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": { @@ -2134,7 +2134,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": { @@ -2146,7 +2146,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", @@ -2164,7 +2164,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" @@ -2179,7 +2179,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 } } @@ -2193,7 +2193,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", @@ -2301,7 +2301,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": { @@ -2316,7 +2316,7 @@ "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": { @@ -2362,7 +2362,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": { @@ -2449,7 +2449,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", @@ -2473,7 +2473,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, "requires": { "get-proxy": "^2.0.0", @@ -2495,7 +2495,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", @@ -2506,7 +2506,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": { @@ -2530,7 +2530,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" } @@ -2538,7 +2538,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", @@ -2588,7 +2588,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 } } @@ -2619,7 +2619,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" @@ -2628,7 +2628,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 } } @@ -2636,7 +2636,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", @@ -2665,7 +2665,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", @@ -2714,7 +2714,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", @@ -2757,7 +2757,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", @@ -2795,7 +2795,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", @@ -2827,12 +2827,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", @@ -2854,13 +2854,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", @@ -2870,7 +2870,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", @@ -2897,7 +2897,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=", "dev": true, "requires": { "delayed-stream": "~1.0.0" @@ -2921,7 +2921,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": { @@ -2939,7 +2939,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", @@ -3000,7 +3000,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, "requires": { "ini": "^1.3.4", @@ -3010,7 +3010,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", @@ -3022,7 +3022,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" @@ -3046,7 +3046,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" @@ -3055,7 +3055,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, "requires": { "safe-buffer": "5.1.2" @@ -3091,7 +3091,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", @@ -3151,7 +3151,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", @@ -3163,7 +3163,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", @@ -3176,7 +3176,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", @@ -3188,7 +3188,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 } } @@ -3202,7 +3202,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", @@ -3224,7 +3224,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": { @@ -3258,7 +3258,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", @@ -3270,7 +3270,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", @@ -3320,7 +3320,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" @@ -3329,7 +3329,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": { @@ -3378,7 +3378,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", @@ -3397,7 +3397,7 @@ "date-format": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz", - "integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA==", + "integrity": "sha1-MdW16iEc9f12TNOLr50DPffhJc8=", "dev": true }, "dateformat": { @@ -3409,7 +3409,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" @@ -3418,7 +3418,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", @@ -3429,7 +3429,7 @@ "debug": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "integrity": "sha1-6D0X3hbYp++3cX7b5fsQE17uYps=", "dev": true, "requires": { "ms": "^2.1.1" @@ -3474,7 +3474,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, "requires": { "pify": "^3.0.0" @@ -3502,7 +3502,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, "requires": { "file-type": "^5.2.0", @@ -3521,7 +3521,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, "requires": { "decompress-tar": "^4.1.0", @@ -3534,7 +3534,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 } } @@ -3542,7 +3542,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, "requires": { "decompress-tar": "^4.1.1", @@ -3603,7 +3603,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" @@ -3612,7 +3612,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 } } @@ -3626,7 +3626,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" @@ -3682,7 +3682,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", @@ -3711,7 +3711,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", @@ -3722,12 +3722,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" @@ -3736,7 +3736,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 } } @@ -3744,7 +3744,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" @@ -3783,13 +3783,13 @@ "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 }, "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" @@ -3798,7 +3798,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", @@ -3817,7 +3817,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": { @@ -3844,7 +3844,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": { @@ -3877,7 +3877,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", @@ -3921,7 +3921,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", @@ -3980,7 +3980,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" @@ -4000,7 +4000,7 @@ "engine.io": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz", - "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==", + "integrity": "sha1-tgKBw1SEpw7gNR6g6/+D7IyVIqI=", "dev": true, "requires": { "accepts": "~1.3.4", @@ -4073,7 +4073,7 @@ "engine.io-parser": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz", - "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==", + "integrity": "sha1-dXq5cPvy37Mse3SwMyFtVznveaY=", "dev": true, "requires": { "after": "0.8.2", @@ -4092,7 +4092,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": { @@ -4114,7 +4114,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" @@ -4174,7 +4174,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": { @@ -4190,7 +4190,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", @@ -4344,7 +4344,7 @@ "eslint-scope": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", - "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "integrity": "sha1-6HyIh8c+jR7ITxylkWRcNYv8j7k=", "dev": true, "requires": { "esrecurse": "^4.1.0", @@ -4354,7 +4354,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" @@ -4363,7 +4363,7 @@ "eslint-visitor-keys": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", - "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "integrity": "sha1-4qgs6oT/JGrW+1f5veW0ZiFFnsI=", "dev": true }, "espree": { @@ -4380,7 +4380,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": { @@ -4422,13 +4422,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": { @@ -4444,7 +4444,7 @@ "eventemitter3": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", - "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==", + "integrity": "sha1-1lF2FjiH7lnzhtZMgmELaWpKdOs=", "dev": true }, "exec-buffer": { @@ -4501,7 +4501,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": { @@ -4526,7 +4526,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" @@ -4570,7 +4570,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", @@ -4627,7 +4627,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, "requires": { "mime-db": "^1.28.0" @@ -4636,7 +4636,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, "requires": { "ext-list": "^2.0.0", @@ -4646,7 +4646,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=", "dev": true }, "extend-shallow": { @@ -4673,7 +4673,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", @@ -4761,7 +4761,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" @@ -4802,7 +4802,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", @@ -4935,7 +4935,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" @@ -4962,7 +4962,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, "requires": { "filename-reserved-regex": "^2.0.0", @@ -4996,7 +4996,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", @@ -5011,7 +5011,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" @@ -5070,7 +5070,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", @@ -5124,13 +5124,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", @@ -5141,18 +5141,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", @@ -5240,7 +5240,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=", "dev": true, "requires": { "asynckit": "^0.4.0", @@ -5347,7 +5347,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" @@ -5400,12 +5400,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5420,17 +5422,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -5547,7 +5552,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -5559,6 +5565,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -5573,6 +5580,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5580,12 +5588,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -5604,6 +5614,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -5684,7 +5695,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -5696,6 +5708,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -5817,6 +5830,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -5903,13 +5917,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, "requires": { "npm-conf": "^1.1.0" @@ -5945,7 +5959,7 @@ "gifsicle": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/gifsicle/-/gifsicle-4.0.1.tgz", - "integrity": "sha512-A/kiCLfDdV+ERV/UB+2O41mifd+RxH8jlRG8DMxZO84Bma/Fw0htqZ+hY2iaalLRNyUu7tYZQslqUBJxBggxbg==", + "integrity": "sha1-MOHmHj7kiE73AmQbLpihXCEnsuI=", "dev": true, "optional": true, "requires": { @@ -5958,7 +5972,7 @@ "execa": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "integrity": "sha1-xiNqW7TfbW8V6I5/AXeYIWdJ3dg=", "dev": true, "optional": true, "requires": { @@ -5974,7 +5988,7 @@ "get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "integrity": "sha1-wbJVV189wh1Zv8ec09K0axw6VLU=", "dev": true, "optional": true, "requires": { @@ -5984,7 +5998,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": { @@ -6107,7 +6121,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", @@ -6157,7 +6171,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": { @@ -6193,7 +6207,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" @@ -6210,7 +6224,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": { @@ -6251,7 +6265,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", @@ -6284,7 +6298,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", @@ -6304,7 +6318,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", @@ -6342,7 +6356,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" @@ -6353,7 +6367,7 @@ "gulp-cli": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.2.0.tgz", - "integrity": "sha512-rGs3bVYHdyJpLqR0TUBnlcZ1O5O++Zs4bA0ajm+zr3WFCfiSLjGwoCBqFs18wzN+ZxahT9DkOK5nDf26iDsWjA==", + "integrity": "sha1-VTMSbut/5BWn4+hKKX0zTVz3Drw=", "dev": true, "requires": { "ansi-colors": "^1.0.1", @@ -6472,7 +6486,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", @@ -6483,7 +6497,7 @@ "gulp-imagemin": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/gulp-imagemin/-/gulp-imagemin-6.1.1.tgz", - "integrity": "sha512-fqaSR8bMc5lhqa6HzRPuJaDY6lY7rcCipe6WtqQ5+hNYTCSPYjXic+1gvFG1+8X879gjVJmIxwmqIbfjuMqTpQ==", + "integrity": "sha1-jm/ezQKZMPLObhKbJmAf0sJhPW4=", "dev": true, "requires": { "chalk": "^2.4.1", @@ -6502,7 +6516,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", @@ -6581,7 +6595,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", @@ -6633,7 +6647,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", @@ -6643,7 +6657,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" @@ -6667,7 +6681,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", @@ -6680,7 +6694,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": { @@ -6695,7 +6709,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", @@ -6720,7 +6734,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 } } @@ -6781,7 +6795,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", @@ -6882,7 +6896,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", @@ -6926,7 +6940,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" @@ -6992,7 +7006,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=", "dev": true, "requires": { "ajv": "^6.5.5", @@ -7002,7 +7016,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" @@ -7058,7 +7072,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 }, "has-symbols": { @@ -7070,7 +7084,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, "requires": { "has-symbol-support-x": "^1.4.1" @@ -7121,13 +7135,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" @@ -7154,7 +7168,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 }, "htmlparser2": { @@ -7174,7 +7188,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": { @@ -7212,14 +7226,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", @@ -7240,7 +7254,7 @@ "http-proxy": { "version": "1.18.0", "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz", - "integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==", + "integrity": "sha1-2+VfY+daNH2389mZdPJpKjFKajo=", "dev": true, "requires": { "eventemitter3": "^4.0.0", @@ -7262,7 +7276,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" @@ -7271,13 +7285,13 @@ "ieee754": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "integrity": "sha1-7BaFWOlaoYH9h9N/VcMrvLZwi4Q=", "dev": true }, "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": { @@ -7312,7 +7326,7 @@ "imagemin-gifsicle": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/imagemin-gifsicle/-/imagemin-gifsicle-6.0.1.tgz", - "integrity": "sha512-kuu47c6iKDQ6R9J10xCwL0lgs0+sMz3LRHqRcJ2CRBWdcNmo3T5hUaM8hSZfksptZXJLGKk8heSAvwtSdB1Fng==", + "integrity": "sha1-arrU6VVm1S5aEEq6HCS087SFgbM=", "dev": true, "optional": true, "requires": { @@ -7324,7 +7338,7 @@ "imagemin-jpegtran": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/imagemin-jpegtran/-/imagemin-jpegtran-6.0.0.tgz", - "integrity": "sha512-Ih+NgThzqYfEWv9t58EItncaaXIHR0u9RuhKa8CtVBlMBvY0dCIxgQJQCfwImA4AV1PMfmUKlkyIHJjb7V4z1g==", + "integrity": "sha1-yNO8+27JxWHCCphxQoVL5w2QsE8=", "dev": true, "optional": true, "requires": { @@ -7336,7 +7350,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": { @@ -7387,7 +7401,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 }, @@ -7437,7 +7451,7 @@ "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": { @@ -7522,7 +7536,7 @@ "interpret": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", - "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", + "integrity": "sha1-1QYaYiS+WOgIOYX1AU2EQ1lXYpY=", "dev": true }, "into-stream": { @@ -7539,7 +7553,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" @@ -7554,13 +7568,13 @@ "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": { @@ -7739,7 +7753,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": { @@ -7749,7 +7763,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 } @@ -7833,7 +7847,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 }, @@ -7882,7 +7896,7 @@ "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 }, "is-stream": { @@ -7894,7 +7908,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" @@ -7957,7 +7971,7 @@ "isbinaryfile": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", - "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", + "integrity": "sha1-XW3vPt6/boyoyunDAYOoBLX4voA=", "dev": true, "requires": { "buffer-alloc": "^1.2.0" @@ -7984,7 +7998,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, "requires": { "has-to-string-tag-x": "^1.2.0", @@ -7994,13 +8008,13 @@ "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 }, "jpegtran-bin": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jpegtran-bin/-/jpegtran-bin-4.0.0.tgz", - "integrity": "sha512-2cRl1ism+wJUoYAYFt6O/rLBfpXNWG2dUWbgcEkTt5WGMnqI46eEro8T4C5zGROxKRqyKpCBSdHPvt5UYCtxaQ==", + "integrity": "sha1-0ArtgJ+6eqbzCBflnu5N3xmPjxA=", "dev": true, "optional": true, "requires": { @@ -8012,7 +8026,7 @@ "jquery": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.4.1.tgz", - "integrity": "sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw==" + "integrity": "sha1-cU8fjZ3eS9+lV2S6N+8hRjDYDvI=" }, "jquery-ui-dist": { "version": "1.12.1", @@ -8027,19 +8041,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", @@ -8055,7 +8069,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": { @@ -8068,7 +8082,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": { @@ -8098,7 +8112,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" @@ -8137,7 +8151,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": { @@ -8149,7 +8163,7 @@ "karma": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/karma/-/karma-4.4.1.tgz", - "integrity": "sha512-L5SIaXEYqzrh6b1wqYC42tNsFMx2PWuxky84pK9coK09MvmL7mxii3G3bZBh/0rvD27lqDd0le9jyhzvwif73A==", + "integrity": "sha1-bZqqsDejETbcB0ACYg7hHowuMqs=", "dev": true, "requires": { "bluebird": "^3.3.0", @@ -8183,7 +8197,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", @@ -8193,13 +8207,13 @@ "binary-extensions": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", - "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", + "integrity": "sha1-I8DfFPaogHf1+YbA0WfsA8PVU3w=", "dev": true }, "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" @@ -8224,7 +8238,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" @@ -8240,7 +8254,7 @@ "glob-parent": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", - "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", + "integrity": "sha1-X0wdHnSNMM1zrSlEs1d6gbCB6MI=", "dev": true, "requires": { "is-glob": "^4.0.1" @@ -8249,7 +8263,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" @@ -8267,19 +8281,19 @@ "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": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", - "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", + "integrity": "sha1-vXuRE1/GsBzePpuuM9ZZtj2IV+U=", "dev": true }, "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": { @@ -8294,13 +8308,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 }, "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" @@ -8311,7 +8325,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" @@ -8320,7 +8334,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", @@ -8355,7 +8369,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": { @@ -8389,7 +8403,7 @@ "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" @@ -8478,7 +8492,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", @@ -8501,7 +8515,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 } @@ -8559,7 +8573,7 @@ "lodash": { "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "integrity": "sha1-tEf2ZwoEVbv+7dETku/zMOoJdUg=", "dev": true }, "lodash._basecopy": { @@ -8675,7 +8689,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": { @@ -8732,7 +8746,7 @@ "log4js": { "version": "4.5.1", "resolved": "https://registry.npmjs.org/log4js/-/log4js-4.5.1.tgz", - "integrity": "sha512-EEEgFcE9bLgaYUKuozyFfytQM2wDHtXn4tAN41pkaxpNjAykv11GVdeI4tHtmPWW4Xrgh9R/2d7XYghDVjbKKw==", + "integrity": "sha1-5UNiXpfZ5vPm58n8GW3WqyyuMLU=", "dev": true, "requires": { "date-format": "^2.0.0", @@ -8782,7 +8796,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" @@ -8821,7 +8835,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", @@ -8887,7 +8901,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": { @@ -8905,13 +8919,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": { @@ -8923,7 +8937,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", @@ -8967,13 +8981,13 @@ "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": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz", - "integrity": "sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==", + "integrity": "sha1-WzZu6DsvFYLEj4fkfPGpNSEDyoE=", "dev": true }, "micromatch": { @@ -9000,7 +9014,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 }, @@ -9028,13 +9042,13 @@ "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 }, "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" @@ -9049,7 +9063,7 @@ "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", @@ -9064,7 +9078,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", @@ -9074,7 +9088,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" @@ -9107,7 +9121,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": { @@ -9122,7 +9136,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": { @@ -9141,7 +9155,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", @@ -9166,7 +9180,7 @@ "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": { @@ -9183,13 +9197,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", @@ -9211,7 +9225,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", @@ -9221,7 +9235,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", @@ -9248,18 +9262,18 @@ "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": { "version": "14.1.1", "resolved": "https://registry.npmjs.org/nouislider/-/nouislider-14.1.1.tgz", - "integrity": "sha512-3/+Z/pTBoWoJf2YXSEWRmS27LW2XxOBmGEzkPyRzB/J6QvL+0mS3QwcQp0SmWhgO5CMzbSxPmb1lDDD4HP12bg==" + "integrity": "sha1-3vgSsqqqLM+eekHdAUSiXatWc+U=" }, "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" @@ -9268,7 +9282,7 @@ "npm": { "version": "6.13.6", "resolved": "https://registry.npmjs.org/npm/-/npm-6.13.6.tgz", - "integrity": "sha512-NomC08kv7HIl1FOyLOe9Hp89kYsOsvx52huVIJ7i8hFW8Xp65lDwe/8wTIrh9q9SaQhA8hTrfXPh3BEL3TmMpw==", + "integrity": "sha1-ht+DBaTYJp0JNOyQeSDnqwec9dk=", "requires": { "JSONStream": "^1.3.5", "abbrev": "~1.1.1", @@ -12329,7 +12343,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, "requires": { "config-chain": "^1.1.11", @@ -12356,7 +12370,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" @@ -12377,7 +12391,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=", "dev": true }, "object-assign": { @@ -12432,7 +12446,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": { @@ -12447,7 +12461,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", @@ -12608,7 +12622,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": { @@ -12661,7 +12675,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": { @@ -12686,7 +12700,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 }, @@ -12726,7 +12740,7 @@ "p-pipe": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-pipe/-/p-pipe-3.0.0.tgz", - "integrity": "sha512-gwwdRFmaxsT3IU+Tl3vYKVRdjfhg8Bbdjw7B+E0y6F7Yz6l+eaQLn0BRmGMXIhcPDONPtOkMoNwx1etZh4zPJA==", + "integrity": "sha1-qx+4fAuN15s7sDqKI2gPydBU4TI=", "dev": true }, "p-reduce": { @@ -12748,7 +12762,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" @@ -12757,7 +12771,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 } } @@ -12815,7 +12829,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": { @@ -12845,7 +12859,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": { @@ -12884,7 +12898,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": { @@ -12994,7 +13008,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", @@ -13006,7 +13020,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" @@ -13061,7 +13075,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", @@ -13074,7 +13088,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", @@ -13084,7 +13098,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" @@ -13093,7 +13107,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" @@ -13102,7 +13116,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" @@ -13111,7 +13125,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" @@ -13120,7 +13134,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", @@ -13130,7 +13144,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", @@ -13142,7 +13156,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", @@ -13169,7 +13183,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", @@ -13179,7 +13193,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", @@ -13191,7 +13205,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", @@ -13205,7 +13219,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", @@ -13230,7 +13244,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" @@ -13239,7 +13253,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", @@ -13250,7 +13264,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", @@ -13262,7 +13276,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", @@ -13274,7 +13288,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", @@ -13285,7 +13299,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", @@ -13296,7 +13310,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", @@ -13307,7 +13321,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", @@ -13319,7 +13333,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", @@ -13329,7 +13343,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", @@ -13340,7 +13354,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", @@ -13352,7 +13366,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", @@ -13375,7 +13389,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", @@ -13387,7 +13401,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", @@ -13423,7 +13437,7 @@ "pretty-bytes": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.3.0.tgz", - "integrity": "sha512-hjGrh+P926p4R4WbaB6OckyRtO0F0/lQBiT+0gnxjV+5kjPBrfVBFCsCLbMqVQeydvIoouYTCmmEURiH3R1Bdg==", + "integrity": "sha1-8oSeJ9t5+01s/iR2T8QTTxZZifI=", "dev": true }, "pretty-hrtime": { @@ -13435,19 +13449,19 @@ "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": { @@ -13488,7 +13502,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", @@ -13498,7 +13512,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", @@ -13509,7 +13523,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=", "dev": true }, "q": { @@ -13527,13 +13541,13 @@ "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=", "dev": true }, "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": { @@ -13554,7 +13568,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", @@ -13565,7 +13579,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 } } @@ -13573,13 +13587,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", @@ -13624,7 +13638,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", @@ -13693,7 +13707,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": { @@ -13736,7 +13750,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": { @@ -13756,7 +13770,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": { @@ -13779,7 +13793,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", @@ -13806,7 +13820,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": { @@ -13976,13 +13990,13 @@ "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": { @@ -14009,7 +14023,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" @@ -14027,7 +14041,7 @@ "run-parallel": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", - "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", + "integrity": "sha1-yd06fPn0ssS2JE4XOm7YZuYd1nk=", "dev": true }, "run-sequence": { @@ -14171,7 +14185,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": { @@ -14186,7 +14200,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 }, @@ -14209,7 +14223,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", @@ -14232,7 +14246,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": { @@ -14253,7 +14267,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": { @@ -14265,7 +14279,7 @@ "signalr": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/signalr/-/signalr-2.4.0.tgz", - "integrity": "sha512-GPJHb3pcNk3IUui5/WG8lMuarEn+Vpc8wEvJ60w0KQ43W9FHnJcuNcF8dkZePr81eBslzicsRdyEunKNF7KjZQ==", + "integrity": "sha1-kq8AjmtSetSzbpT7s0DhNQh6YNI=", "requires": { "jquery": ">=1.6.4" } @@ -14282,7 +14296,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 } } @@ -14296,7 +14310,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", @@ -14331,7 +14345,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" @@ -14437,7 +14451,7 @@ "socket.io": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz", - "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==", + "integrity": "sha1-oGnF/qvuPmshSnW0DOBlLhz7mYA=", "dev": true, "requires": { "debug": "~3.1.0", @@ -14451,7 +14465,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -14474,7 +14488,7 @@ "socket.io-client": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz", - "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==", + "integrity": "sha1-3LOBA0NqtFeN2wJmOK4vIbYjZx8=", "dev": true, "requires": { "backo2": "1.0.2", @@ -14502,7 +14516,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -14602,13 +14616,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", @@ -14618,7 +14632,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": { @@ -14634,7 +14648,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-colorpicker": { @@ -14702,7 +14716,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=", "dev": true, "requires": { "asn1": "~0.2.3", @@ -14758,7 +14772,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": { @@ -14770,7 +14784,7 @@ "streamroller": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-1.0.6.tgz", - "integrity": "sha512-3QC47Mhv3/aZNFpDDVO44qQb9gwB9QggMEE0sQmkTAwBVYdBRWISdsywlkfm5II1Q5y/pmrHflti/IgmIzdDBg==", + "integrity": "sha1-gWfYSW7Z8Z8F7ksVjZYRMhuMrNk=", "dev": true, "requires": { "async": "^2.6.2", @@ -14783,7 +14797,7 @@ "debug": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "integrity": "sha1-6D0X3hbYp++3cX7b5fsQE17uYps=", "dev": true, "requires": { "ms": "^2.1.1" @@ -14892,7 +14906,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, "requires": { "is-natural-number": "^4.0.1" @@ -14917,7 +14931,7 @@ "strip-json-comments": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", - "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "integrity": "sha1-hXE5dakfuHvxswXMp3OV5A0qZKc=", "dev": true }, "strip-outer": { @@ -14932,7 +14946,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", @@ -14956,7 +14970,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" @@ -14996,7 +15010,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", @@ -15008,7 +15022,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": { @@ -15020,7 +15034,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", @@ -15031,7 +15045,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" @@ -15042,7 +15056,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, "requires": { "bl": "^1.0.0", @@ -15105,7 +15119,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": { @@ -15129,7 +15143,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", @@ -15171,7 +15185,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" @@ -15180,7 +15194,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", @@ -15202,7 +15216,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", @@ -15218,12 +15232,12 @@ "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.7", "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-4.9.7.tgz", - "integrity": "sha512-cj0HvUuniTuIjOAJdRt5BUfeQqM5yHjbA2NOub9HUHXlCrT9OwD9WBPU6tGlaPC2l2I4eGoOnT8llosZRdQU5Q==" + "integrity": "sha1-dM5U8Hz8AmilrF409hAERljJL0c=" }, "tmp": { "version": "0.0.33", @@ -15316,7 +15330,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": { @@ -15383,7 +15397,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": { @@ -15398,7 +15412,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", @@ -15483,7 +15497,7 @@ "unbzip2-stream": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz", - "integrity": "sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg==", + "integrity": "sha1-0VbSBeZw2NjDk+HALr1QZCKHP2o=", "dev": true, "requires": { "buffer": "^5.2.1", @@ -15499,12 +15513,12 @@ "underscore": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", - "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" + "integrity": "sha1-BtzjSg5op7q8KbNluOdLiSUgOWE=" }, "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", @@ -15527,13 +15541,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", @@ -15555,7 +15569,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", @@ -15579,7 +15593,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", @@ -15589,7 +15603,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": { @@ -15653,7 +15667,7 @@ "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": { @@ -15690,13 +15704,13 @@ "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": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz", - "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==", + "integrity": "sha1-IX+UOtVAyyEoZYqyP8lg9qiMmXI=", "dev": true, "requires": { "lru-cache": "4.1.x", @@ -15734,13 +15748,13 @@ "v8-compile-cache": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", - "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", + "integrity": "sha1-4U3jezGm0ZT1aQ1n78Tn9vxqsw4=", "dev": true }, "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", @@ -15820,7 +15834,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", @@ -15979,7 +15993,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" @@ -16022,7 +16036,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" @@ -16042,7 +16056,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 }, "xmlhttprequest-ssl": { @@ -16054,7 +16068,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/Editors/TourController.cs b/src/Umbraco.Web/Editors/TourController.cs index 6b586ed162..93ec4252df 100644 --- a/src/Umbraco.Web/Editors/TourController.cs +++ b/src/Umbraco.Web/Editors/TourController.cs @@ -25,8 +25,8 @@ namespace Umbraco.Web.Editors public class TourController : UmbracoAuthorizedJsonController { private readonly TourFilterCollection _filters; - private readonly IUmbracoSettingsSection _umbracoSettingsSection; private readonly IIOHelper _ioHelper; + private readonly ITourSettings _tourSettings; public TourController( IGlobalSettings globalSettings, @@ -39,21 +39,21 @@ namespace Umbraco.Web.Editors IShortStringHelper shortStringHelper, UmbracoMapper umbracoMapper, TourFilterCollection filters, - IUmbracoSettingsSection umbracoSettingsSection, IIOHelper ioHelper, - IPublishedUrlProvider publishedUrlProvider) + IPublishedUrlProvider publishedUrlProvider, + ITourSettings tourSettings) : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider) { _filters = filters; - _umbracoSettingsSection = umbracoSettingsSection ?? throw new ArgumentNullException(nameof(umbracoSettingsSection)); _ioHelper = ioHelper; + _tourSettings = tourSettings; } public IEnumerable GetTours() { var result = new List(); - if (_umbracoSettingsSection.BackOffice.Tours.EnableTours == false) + if (_tourSettings.EnableTours == false) return result; var user = UmbracoContext.Security.CurrentUser; From e4ff4810531a685a441af6eaf06ab1693f995c27 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Thu, 12 Mar 2020 08:28:31 +0100 Subject: [PATCH 59/69] Cleaned up config for Keep Alive settings --- src/Umbraco.Configuration/ConfigsFactory.cs | 2 ++ .../Implementations/KeepAliveSettings.cs | 10 ++++++++++ .../UmbracoSettings/KeepAliveElement.cs | 2 +- .../UmbracoSettings/UmbracoSettingsSection.cs | 2 -- ...eepAliveSection.cs => IKeepAliveSettings.cs} | 2 +- .../UmbracoSettings/IUmbracoSettingsSection.cs | 2 -- src/Umbraco.Core/Scheduling/KeepAlive.cs | 8 ++++---- .../Scheduling/SchedulerComponent.cs | 17 ++++++++--------- 8 files changed, 26 insertions(+), 19 deletions(-) create mode 100644 src/Umbraco.Configuration/Implementations/KeepAliveSettings.cs rename src/Umbraco.Core/Configuration/UmbracoSettings/{IKeepAliveSection.cs => IKeepAliveSettings.cs} (68%) diff --git a/src/Umbraco.Configuration/ConfigsFactory.cs b/src/Umbraco.Configuration/ConfigsFactory.cs index 0b40da6947..edcafac497 100644 --- a/src/Umbraco.Configuration/ConfigsFactory.cs +++ b/src/Umbraco.Configuration/ConfigsFactory.cs @@ -21,6 +21,7 @@ namespace Umbraco.Core.Configuration public IExceptionFilterSettings ExceptionFilterSettings { get; } = new ExceptionFilterSettings(); public ITourSettings TourSettings { get; } = new TourSettings(); public ILoggingSettings LoggingSettings { get; } = new LoggingSettings(); + public IKeepAliveSettings KeepAliveSettings { get; } = new KeepAliveSettings(); public IUmbracoSettingsSection UmbracoSettings { get; } @@ -52,6 +53,7 @@ namespace Umbraco.Core.Configuration configs.Add(() => TourSettings); configs.Add(() => LoggingSettings); + configs.Add(() => KeepAliveSettings); configs.AddCoreConfigs(ioHelper); return configs; diff --git a/src/Umbraco.Configuration/Implementations/KeepAliveSettings.cs b/src/Umbraco.Configuration/Implementations/KeepAliveSettings.cs new file mode 100644 index 0000000000..0b8315d447 --- /dev/null +++ b/src/Umbraco.Configuration/Implementations/KeepAliveSettings.cs @@ -0,0 +1,10 @@ +using Umbraco.Core.Configuration.UmbracoSettings; + +namespace Umbraco.Configuration.Implementations +{ + internal class KeepAliveSettings : ConfigurationManagerConfigBase, IKeepAliveSettings + { + public bool DisableKeepAliveTask => UmbracoSettingsSection.KeepAlive.DisableKeepAliveTask; + public string KeepAlivePingUrl => UmbracoSettingsSection.KeepAlive.KeepAlivePingUrl; + } +} diff --git a/src/Umbraco.Configuration/UmbracoSettings/KeepAliveElement.cs b/src/Umbraco.Configuration/UmbracoSettings/KeepAliveElement.cs index 89ba9be54d..2297fb4e20 100644 --- a/src/Umbraco.Configuration/UmbracoSettings/KeepAliveElement.cs +++ b/src/Umbraco.Configuration/UmbracoSettings/KeepAliveElement.cs @@ -2,7 +2,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - internal class KeepAliveElement : ConfigurationElement, IKeepAliveSection + internal class KeepAliveElement : ConfigurationElement, IKeepAliveSettings { [ConfigurationProperty("disableKeepAliveTask", DefaultValue = "false")] public bool DisableKeepAliveTask => (bool)base["disableKeepAliveTask"]; diff --git a/src/Umbraco.Configuration/UmbracoSettings/UmbracoSettingsSection.cs b/src/Umbraco.Configuration/UmbracoSettings/UmbracoSettingsSection.cs index 9187cecd40..11b4b8fc39 100644 --- a/src/Umbraco.Configuration/UmbracoSettings/UmbracoSettingsSection.cs +++ b/src/Umbraco.Configuration/UmbracoSettings/UmbracoSettingsSection.cs @@ -32,7 +32,5 @@ namespace Umbraco.Core.Configuration.UmbracoSettings IRequestHandlerSection IUmbracoSettingsSection.RequestHandler => RequestHandler; IWebRoutingSection IUmbracoSettingsSection.WebRouting => WebRouting; - - IKeepAliveSection IUmbracoSettingsSection.KeepAlive => KeepAlive; } } diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IKeepAliveSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IKeepAliveSettings.cs similarity index 68% rename from src/Umbraco.Core/Configuration/UmbracoSettings/IKeepAliveSection.cs rename to src/Umbraco.Core/Configuration/UmbracoSettings/IKeepAliveSettings.cs index 3a0ad258c5..58a8151474 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IKeepAliveSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IKeepAliveSettings.cs @@ -1,6 +1,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - public interface IKeepAliveSection : IUmbracoConfigurationSection + public interface IKeepAliveSettings : IUmbracoConfigurationSection { bool DisableKeepAliveTask { get; } string KeepAlivePingUrl { get; } diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs index ff11d223ac..e6fd76b72e 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs @@ -11,7 +11,5 @@ namespace Umbraco.Core.Configuration.UmbracoSettings IRequestHandlerSection RequestHandler { get; } IWebRoutingSection WebRouting { get; } - - IKeepAliveSection KeepAlive { get; } } } diff --git a/src/Umbraco.Core/Scheduling/KeepAlive.cs b/src/Umbraco.Core/Scheduling/KeepAlive.cs index c677c66b83..515251b105 100644 --- a/src/Umbraco.Core/Scheduling/KeepAlive.cs +++ b/src/Umbraco.Core/Scheduling/KeepAlive.cs @@ -12,16 +12,16 @@ namespace Umbraco.Web.Scheduling public class KeepAlive : RecurringTaskBase { private readonly IRuntimeState _runtime; - private readonly IKeepAliveSection _keepAliveSection; + private readonly IKeepAliveSettings _keepAliveSettings; private readonly IProfilingLogger _logger; private static HttpClient _httpClient; public KeepAlive(IBackgroundTaskRunner runner, int delayMilliseconds, int periodMilliseconds, - IRuntimeState runtime, IKeepAliveSection keepAliveSection, IProfilingLogger logger) + IRuntimeState runtime, IKeepAliveSettings keepAliveSettings, IProfilingLogger logger) : base(runner, delayMilliseconds, periodMilliseconds) { _runtime = runtime; - _keepAliveSection = keepAliveSection; + _keepAliveSettings = keepAliveSettings; _logger = logger; if (_httpClient == null) _httpClient = new HttpClient(); @@ -49,7 +49,7 @@ namespace Umbraco.Web.Scheduling using (_logger.DebugDuration("Keep alive executing", "Keep alive complete")) { - var keepAlivePingUrl = _keepAliveSection.KeepAlivePingUrl; + var keepAlivePingUrl = _keepAliveSettings.KeepAlivePingUrl; try { if (keepAlivePingUrl.Contains("{umbracoApplicationUrl}")) diff --git a/src/Umbraco.Infrastructure/Scheduling/SchedulerComponent.cs b/src/Umbraco.Infrastructure/Scheduling/SchedulerComponent.cs index a1385257e9..1d4a654830 100644 --- a/src/Umbraco.Infrastructure/Scheduling/SchedulerComponent.cs +++ b/src/Umbraco.Infrastructure/Scheduling/SchedulerComponent.cs @@ -35,11 +35,11 @@ namespace Umbraco.Web.Scheduling private readonly HealthCheckNotificationMethodCollection _notifications; private readonly IUmbracoContextFactory _umbracoContextFactory; private readonly IHealthChecks _healthChecksConfig; - private readonly IUmbracoSettingsSection _umbracoSettingsSection; private readonly IIOHelper _ioHelper; private readonly IServerMessenger _serverMessenger; private readonly IRequestAccessor _requestAccessor; private readonly ILoggingSettings _loggingSettings; + private readonly IKeepAliveSettings _keepAliveSettings; private BackgroundTaskRunner _keepAliveRunner; private BackgroundTaskRunner _publishingRunner; @@ -57,8 +57,8 @@ namespace Umbraco.Web.Scheduling HealthCheckCollection healthChecks, HealthCheckNotificationMethodCollection notifications, IScopeProvider scopeProvider, IUmbracoContextFactory umbracoContextFactory, IProfilingLogger logger, IHostingEnvironment hostingEnvironment, IHealthChecks healthChecksConfig, - IUmbracoSettingsSection umbracoSettingsSection, IIOHelper ioHelper, IServerMessenger serverMessenger, IRequestAccessor requestAccessor, - ILoggingSettings loggingSettings) + IIOHelper ioHelper, IServerMessenger serverMessenger, IRequestAccessor requestAccessor, + ILoggingSettings loggingSettings, IKeepAliveSettings keepAliveSettings) { _runtime = runtime; _contentService = contentService; @@ -71,11 +71,11 @@ namespace Umbraco.Web.Scheduling _healthChecks = healthChecks; _notifications = notifications; _healthChecksConfig = healthChecksConfig ?? throw new ArgumentNullException(nameof(healthChecksConfig)); - _umbracoSettingsSection = umbracoSettingsSection; _ioHelper = ioHelper; _serverMessenger = serverMessenger; _requestAccessor = requestAccessor; _loggingSettings = loggingSettings; + _keepAliveSettings = keepAliveSettings; } public void Initialize() @@ -114,13 +114,12 @@ namespace Umbraco.Web.Scheduling LazyInitializer.EnsureInitialized(ref _tasks, ref _started, ref _locker, () => { _logger.Debug("Initializing the scheduler"); - var settings = _umbracoSettingsSection; var tasks = new List(); - if (settings.KeepAlive.DisableKeepAliveTask == false) + if (_keepAliveSettings.DisableKeepAliveTask == false) { - tasks.Add(RegisterKeepAlive(settings.KeepAlive)); + tasks.Add(RegisterKeepAlive(_keepAliveSettings)); } tasks.Add(RegisterScheduledPublishing()); @@ -135,11 +134,11 @@ namespace Umbraco.Web.Scheduling }); } - private IBackgroundTask RegisterKeepAlive(IKeepAliveSection keepAliveSection) + private IBackgroundTask RegisterKeepAlive(IKeepAliveSettings keepAliveSettings) { // ping/keepalive // on all servers - var task = new KeepAlive(_keepAliveRunner, DefaultDelayMilliseconds, FiveMinuteMilliseconds, _runtime, keepAliveSection, _logger); + var task = new KeepAlive(_keepAliveRunner, DefaultDelayMilliseconds, FiveMinuteMilliseconds, _runtime, keepAliveSettings, _logger); _keepAliveRunner.TryAdd(task); return task; } From e2dfc68718a5c01de9424c41f1ca65fe646227a2 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Thu, 12 Mar 2020 09:52:34 +0100 Subject: [PATCH 60/69] Cleaned up config for WebRouting settings --- src/Umbraco.Configuration/ConfigsFactory.cs | 2 ++ .../Implementations/WebRoutingSettings.cs | 16 ++++++++++++++++ .../UmbracoSettings/UmbracoSettingsSection.cs | 2 -- .../UmbracoSettings/WebRoutingElement.cs | 2 +- .../Configuration/ConfigsExtensions.cs | 3 +++ .../UmbracoSettings/IUmbracoSettingsSection.cs | 2 -- ...RoutingSection.cs => IWebRoutingSettings.cs} | 2 +- src/Umbraco.Core/PublishedContentExtensions.cs | 6 +++--- .../Routing/ContentFinderByIdPath.cs | 8 ++++---- .../Routing/ContentFinderByUrlAndTemplate.cs | 11 ++++++----- src/Umbraco.Core/Routing/PublishedRequest.cs | 8 ++++---- src/Umbraco.Core/Routing/PublishedRouter.cs | 17 +++++++---------- src/Umbraco.Core/Routing/UrlProvider.cs | 2 +- .../Routing/RedirectTrackingComponent.cs | 9 +++++---- .../UmbracoSettings/UmbracoSettingsTests.cs | 1 + .../WebRoutingElementDefaultTests.cs | 10 +++++----- .../UmbracoSettings/WebRoutingElementTests.cs | 6 +++--- .../Routing/ContentFinderByIdTests.cs | 2 +- .../ContentFinderByUrlAndTemplateTests.cs | 2 +- .../Routing/GetContentUrlsTests.cs | 4 ++-- .../Routing/MediaUrlProviderTests.cs | 2 +- src/Umbraco.Tests/Routing/UrlProviderTests.cs | 2 +- .../Routing/UrlsProviderWithDomainsTests.cs | 2 +- .../Routing/UrlsWithNestedDomains.cs | 2 +- src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs | 4 ++-- .../Templates/HtmlImageSourceParserTests.cs | 2 +- .../Templates/HtmlLocalLinkParserTests.cs | 2 +- src/Umbraco.Tests/TestHelpers/BaseWebTest.cs | 7 +++---- .../TestHelpers/SettingsForTests.cs | 16 +++++++++++++--- src/Umbraco.Tests/TestHelpers/TestHelper.cs | 4 ++-- .../TestHelpers/TestObjects-Mocks.cs | 3 +-- .../Testing/TestingTests/MockTests.cs | 2 +- src/Umbraco.Tests/Testing/UmbracoTestBase.cs | 8 ++++---- .../Web/Mvc/SurfaceControllerTests.cs | 2 +- .../Web/Mvc/UmbracoViewPageTests.cs | 2 +- src/Umbraco.Web/AspNet/AspNetBackOfficeInfo.cs | 8 ++++---- .../Editors/RedirectUrlManagementController.cs | 8 ++++---- .../Mvc/StatusCodeFilterAttribute.cs | 2 +- src/Umbraco.Web/PublishedContentExtensions.cs | 8 ++++---- src/Umbraco.Web/Runtime/WebInitialComposer.cs | 1 - src/Umbraco.Web/Templates/TemplateRenderer.cs | 8 ++++---- src/Umbraco.Web/UmbracoApplicationBase.cs | 2 +- src/Umbraco.Web/UmbracoModule.cs | 2 +- 43 files changed, 119 insertions(+), 95 deletions(-) create mode 100644 src/Umbraco.Configuration/Implementations/WebRoutingSettings.cs rename src/Umbraco.Core/Configuration/UmbracoSettings/{IWebRoutingSection.cs => IWebRoutingSettings.cs} (86%) diff --git a/src/Umbraco.Configuration/ConfigsFactory.cs b/src/Umbraco.Configuration/ConfigsFactory.cs index edcafac497..a671a08a8b 100644 --- a/src/Umbraco.Configuration/ConfigsFactory.cs +++ b/src/Umbraco.Configuration/ConfigsFactory.cs @@ -22,6 +22,7 @@ namespace Umbraco.Core.Configuration public ITourSettings TourSettings { get; } = new TourSettings(); public ILoggingSettings LoggingSettings { get; } = new LoggingSettings(); public IKeepAliveSettings KeepAliveSettings { get; } = new KeepAliveSettings(); + public IWebRoutingSettings WebRoutingSettings { get; } = new WebRoutingSettings(); public IUmbracoSettingsSection UmbracoSettings { get; } @@ -54,6 +55,7 @@ namespace Umbraco.Core.Configuration configs.Add(() => TourSettings); configs.Add(() => LoggingSettings); configs.Add(() => KeepAliveSettings); + configs.Add(() => WebRoutingSettings); configs.AddCoreConfigs(ioHelper); return configs; diff --git a/src/Umbraco.Configuration/Implementations/WebRoutingSettings.cs b/src/Umbraco.Configuration/Implementations/WebRoutingSettings.cs new file mode 100644 index 0000000000..5da3de20a0 --- /dev/null +++ b/src/Umbraco.Configuration/Implementations/WebRoutingSettings.cs @@ -0,0 +1,16 @@ +using Umbraco.Core.Configuration.UmbracoSettings; + +namespace Umbraco.Configuration.Implementations +{ + internal class WebRoutingSettings : ConfigurationManagerConfigBase, IWebRoutingSettings + { + public bool TrySkipIisCustomErrors => UmbracoSettingsSection.WebRouting.TrySkipIisCustomErrors; + public bool InternalRedirectPreservesTemplate => UmbracoSettingsSection.WebRouting.InternalRedirectPreservesTemplate; + public bool DisableAlternativeTemplates => UmbracoSettingsSection.WebRouting.DisableAlternativeTemplates; + public bool ValidateAlternativeTemplates => UmbracoSettingsSection.WebRouting.ValidateAlternativeTemplates; + public bool DisableFindContentByIdPath => UmbracoSettingsSection.WebRouting.DisableFindContentByIdPath; + public bool DisableRedirectUrlTracking => UmbracoSettingsSection.WebRouting.DisableRedirectUrlTracking; + public string UrlProviderMode => UmbracoSettingsSection.WebRouting.UrlProviderMode; + public string UmbracoApplicationUrl => UmbracoSettingsSection.WebRouting.UmbracoApplicationUrl; + } +} diff --git a/src/Umbraco.Configuration/UmbracoSettings/UmbracoSettingsSection.cs b/src/Umbraco.Configuration/UmbracoSettings/UmbracoSettingsSection.cs index 11b4b8fc39..ce420c7f63 100644 --- a/src/Umbraco.Configuration/UmbracoSettings/UmbracoSettingsSection.cs +++ b/src/Umbraco.Configuration/UmbracoSettings/UmbracoSettingsSection.cs @@ -30,7 +30,5 @@ namespace Umbraco.Core.Configuration.UmbracoSettings ISecuritySection IUmbracoSettingsSection.Security => Security; IRequestHandlerSection IUmbracoSettingsSection.RequestHandler => RequestHandler; - - IWebRoutingSection IUmbracoSettingsSection.WebRouting => WebRouting; } } diff --git a/src/Umbraco.Configuration/UmbracoSettings/WebRoutingElement.cs b/src/Umbraco.Configuration/UmbracoSettings/WebRoutingElement.cs index 7b7102f2e7..206fc213d2 100644 --- a/src/Umbraco.Configuration/UmbracoSettings/WebRoutingElement.cs +++ b/src/Umbraco.Configuration/UmbracoSettings/WebRoutingElement.cs @@ -2,7 +2,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - internal class WebRoutingElement : ConfigurationElement, IWebRoutingSection + internal class WebRoutingElement : ConfigurationElement, IWebRoutingSettings { [ConfigurationProperty("trySkipIisCustomErrors", DefaultValue = "false")] public bool TrySkipIisCustomErrors => (bool) base["trySkipIisCustomErrors"]; diff --git a/src/Umbraco.Core/Configuration/ConfigsExtensions.cs b/src/Umbraco.Core/Configuration/ConfigsExtensions.cs index 1723785069..00e6c50499 100644 --- a/src/Umbraco.Core/Configuration/ConfigsExtensions.cs +++ b/src/Umbraco.Core/Configuration/ConfigsExtensions.cs @@ -28,6 +28,9 @@ namespace Umbraco.Core public static IUmbracoSettingsSection Settings(this Configs configs) => configs.GetConfig(); + public static IWebRoutingSettings WebRouting(this Configs configs) + => configs.GetConfig(); + public static IHealthChecks HealthChecks(this Configs configs) => configs.GetConfig(); diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs index e6fd76b72e..51fe34adf4 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs @@ -9,7 +9,5 @@ namespace Umbraco.Core.Configuration.UmbracoSettings ISecuritySection Security { get; } IRequestHandlerSection RequestHandler { get; } - - IWebRoutingSection WebRouting { get; } } } diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSettings.cs similarity index 86% rename from src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs rename to src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSettings.cs index f0a986efe2..f7f6a94d30 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSettings.cs @@ -1,6 +1,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - public interface IWebRoutingSection : IUmbracoConfigurationSection + public interface IWebRoutingSettings : IUmbracoConfigurationSection { bool TrySkipIisCustomErrors { get; } diff --git a/src/Umbraco.Core/PublishedContentExtensions.cs b/src/Umbraco.Core/PublishedContentExtensions.cs index 84ee8c314b..03ce0c066a 100644 --- a/src/Umbraco.Core/PublishedContentExtensions.cs +++ b/src/Umbraco.Core/PublishedContentExtensions.cs @@ -149,11 +149,11 @@ namespace Umbraco.Core } public static bool IsAllowedTemplate(this IPublishedContent content, IContentTypeService contentTypeService, - IUmbracoSettingsSection umbracoSettingsSection, int templateId) + IWebRoutingSettings webRoutingSettings, int templateId) { return content.IsAllowedTemplate(contentTypeService, - umbracoSettingsSection.WebRouting.DisableAlternativeTemplates, - umbracoSettingsSection.WebRouting.ValidateAlternativeTemplates, templateId); + webRoutingSettings.DisableAlternativeTemplates, + webRoutingSettings.ValidateAlternativeTemplates, templateId); } public static bool IsAllowedTemplate(this IPublishedContent content, IContentTypeService contentTypeService, bool disableAlternativeTemplates, bool validateAlternativeTemplates, int templateId) diff --git a/src/Umbraco.Core/Routing/ContentFinderByIdPath.cs b/src/Umbraco.Core/Routing/ContentFinderByIdPath.cs index c4bfd5a697..49a6ff6bfe 100644 --- a/src/Umbraco.Core/Routing/ContentFinderByIdPath.cs +++ b/src/Umbraco.Core/Routing/ContentFinderByIdPath.cs @@ -18,11 +18,11 @@ namespace Umbraco.Web.Routing { private readonly ILogger _logger; private readonly IRequestAccessor _requestAccessor; - private readonly IWebRoutingSection _webRoutingSection; + private readonly IWebRoutingSettings _webRoutingSettings; - public ContentFinderByIdPath(IWebRoutingSection webRoutingSection, ILogger logger, IRequestAccessor requestAccessor) + public ContentFinderByIdPath(IWebRoutingSettings webRoutingSettings, ILogger logger, IRequestAccessor requestAccessor) { - _webRoutingSection = webRoutingSection ?? throw new System.ArgumentNullException(nameof(webRoutingSection)); + _webRoutingSettings = webRoutingSettings ?? throw new System.ArgumentNullException(nameof(webRoutingSettings)); _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); _requestAccessor = requestAccessor; } @@ -36,7 +36,7 @@ namespace Umbraco.Web.Routing { if (frequest.UmbracoContext != null && frequest.UmbracoContext.InPreviewMode == false - && _webRoutingSection.DisableFindContentByIdPath) + && _webRoutingSettings.DisableFindContentByIdPath) return false; IPublishedContent node = null; diff --git a/src/Umbraco.Core/Routing/ContentFinderByUrlAndTemplate.cs b/src/Umbraco.Core/Routing/ContentFinderByUrlAndTemplate.cs index 933ab47150..7bcea4681e 100644 --- a/src/Umbraco.Core/Routing/ContentFinderByUrlAndTemplate.cs +++ b/src/Umbraco.Core/Routing/ContentFinderByUrlAndTemplate.cs @@ -17,15 +17,16 @@ namespace Umbraco.Web.Routing public class ContentFinderByUrlAndTemplate : ContentFinderByUrl { private readonly IFileService _fileService; - private readonly IUmbracoSettingsSection _umbracoSettingsSection; - private readonly IContentTypeService _contentTypeService; - public ContentFinderByUrlAndTemplate(ILogger logger, IFileService fileService, IUmbracoSettingsSection umbracoSettingsSection, IContentTypeService contentTypeService) + private readonly IContentTypeService _contentTypeService; + private readonly IWebRoutingSettings _webRoutingSettings; + + public ContentFinderByUrlAndTemplate(ILogger logger, IFileService fileService, IContentTypeService contentTypeService, IWebRoutingSettings webRoutingSettings) : base(logger) { _fileService = fileService; - _umbracoSettingsSection = umbracoSettingsSection; _contentTypeService = contentTypeService; + _webRoutingSettings = webRoutingSettings; } /// @@ -75,7 +76,7 @@ namespace Umbraco.Web.Routing } // IsAllowedTemplate deals both with DisableAlternativeTemplates and ValidateAlternativeTemplates settings - if (!node.IsAllowedTemplate(_contentTypeService, _umbracoSettingsSection, template.Id)) + if (!node.IsAllowedTemplate(_contentTypeService, _webRoutingSettings, template.Id)) { Logger.Warn("Alternative template '{TemplateAlias}' is not allowed on node {NodeId}.", template.Alias, node.Id); frequest.PublishedContent = null; // clear diff --git a/src/Umbraco.Core/Routing/PublishedRequest.cs b/src/Umbraco.Core/Routing/PublishedRequest.cs index 9b00e59deb..6e4d0008a9 100644 --- a/src/Umbraco.Core/Routing/PublishedRequest.cs +++ b/src/Umbraco.Core/Routing/PublishedRequest.cs @@ -16,7 +16,7 @@ namespace Umbraco.Web.Routing public class PublishedRequest : IPublishedRequest { private readonly IPublishedRouter _publishedRouter; - private readonly IUmbracoSettingsSection _umbracoSettingsSection; + private readonly IWebRoutingSettings _webRoutingSettings; private bool _readonly; // after prepared private bool _readonlyUri; // after preparing @@ -33,11 +33,11 @@ namespace Umbraco.Web.Routing /// The published router. /// The Umbraco context. /// The request Uri. - internal PublishedRequest(IPublishedRouter publishedRouter, IUmbracoContext umbracoContext, IUmbracoSettingsSection umbracoSettingsSection, Uri uri = null) + internal PublishedRequest(IPublishedRouter publishedRouter, IUmbracoContext umbracoContext, IWebRoutingSettings webRoutingSettings, Uri uri = null) { UmbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext)); _publishedRouter = publishedRouter ?? throw new ArgumentNullException(nameof(publishedRouter)); - _umbracoSettingsSection = umbracoSettingsSection ?? throw new ArgumentNullException(nameof(umbracoSettingsSection)); + _webRoutingSettings = webRoutingSettings; Uri = uri ?? umbracoContext.CleanedUmbracoUrl; } @@ -178,7 +178,7 @@ namespace Umbraco.Web.Routing IsInternalRedirectPublishedContent = isInternalRedirect; // must restore the template if it's an internal redirect & the config option is set - if (isInternalRedirect && _umbracoSettingsSection.WebRouting.InternalRedirectPreservesTemplate) + if (isInternalRedirect && _webRoutingSettings.InternalRedirectPreservesTemplate) { // restore TemplateModel = template; diff --git a/src/Umbraco.Core/Routing/PublishedRouter.cs b/src/Umbraco.Core/Routing/PublishedRouter.cs index f521c3a95a..af4ec60e4d 100644 --- a/src/Umbraco.Core/Routing/PublishedRouter.cs +++ b/src/Umbraco.Core/Routing/PublishedRouter.cs @@ -19,13 +19,12 @@ namespace Umbraco.Web.Routing /// public class PublishedRouter : IPublishedRouter { - private readonly IWebRoutingSection _webRoutingSection; + private readonly IWebRoutingSettings _webRoutingSettings; private readonly ContentFinderCollection _contentFinders; private readonly IContentLastChanceFinder _contentLastChanceFinder; private readonly IProfilingLogger _profilingLogger; private readonly IVariationContextAccessor _variationContextAccessor; private readonly ILogger _logger; - private readonly IUmbracoSettingsSection _umbracoSettingsSection; private readonly IPublishedUrlProvider _publishedUrlProvider; private readonly IRequestAccessor _requestAccessor; private readonly IPublishedValueFallback _publishedValueFallback; @@ -38,12 +37,11 @@ namespace Umbraco.Web.Routing /// Initializes a new instance of the class. /// public PublishedRouter( - IWebRoutingSection webRoutingSection, + IWebRoutingSettings webRoutingSettings, ContentFinderCollection contentFinders, IContentLastChanceFinder contentLastChanceFinder, IVariationContextAccessor variationContextAccessor, IProfilingLogger proflog, - IUmbracoSettingsSection umbracoSettingsSection, IPublishedUrlProvider publishedUrlProvider, IRequestAccessor requestAccessor, IPublishedValueFallback publishedValueFallback, @@ -52,13 +50,12 @@ namespace Umbraco.Web.Routing IContentTypeService contentTypeService, IPublicAccessService publicAccessService) { - _webRoutingSection = webRoutingSection ?? throw new ArgumentNullException(nameof(webRoutingSection)); + _webRoutingSettings = webRoutingSettings ?? throw new ArgumentNullException(nameof(webRoutingSettings)); _contentFinders = contentFinders ?? throw new ArgumentNullException(nameof(contentFinders)); _contentLastChanceFinder = contentLastChanceFinder ?? throw new ArgumentNullException(nameof(contentLastChanceFinder)); _profilingLogger = proflog ?? throw new ArgumentNullException(nameof(proflog)); _variationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor)); _logger = proflog; - _umbracoSettingsSection = umbracoSettingsSection ?? throw new ArgumentNullException(nameof(umbracoSettingsSection)); _publishedUrlProvider = publishedUrlProvider; _requestAccessor = requestAccessor; _publishedValueFallback = publishedValueFallback; @@ -71,7 +68,7 @@ namespace Umbraco.Web.Routing /// public IPublishedRequest CreateRequest(IUmbracoContext umbracoContext, Uri uri = null) { - return new PublishedRequest(this, umbracoContext, _umbracoSettingsSection, uri ?? umbracoContext.CleanedUmbracoUrl); + return new PublishedRequest(this, umbracoContext, _webRoutingSettings, uri ?? umbracoContext.CleanedUmbracoUrl); } #region Request @@ -628,7 +625,7 @@ namespace Umbraco.Web.Routing // does not apply // + optionally, apply the alternate template on internal redirects var useAltTemplate = request.IsInitialPublishedContent - || (_webRoutingSection.InternalRedirectPreservesTemplate && request.IsInternalRedirectPublishedContent); + || (_webRoutingSettings.InternalRedirectPreservesTemplate && request.IsInternalRedirectPublishedContent); var altTemplate = useAltTemplate ? _requestAccessor.GetRequestValue(Constants.Conventions.Url.AltTemplate) : null; @@ -670,8 +667,8 @@ namespace Umbraco.Web.Routing if (request.PublishedContent.IsAllowedTemplate( _fileService, _contentTypeService, - _umbracoSettingsSection.WebRouting.DisableAlternativeTemplates, - _umbracoSettingsSection.WebRouting.ValidateAlternativeTemplates, + _webRoutingSettings.DisableAlternativeTemplates, + _webRoutingSettings.ValidateAlternativeTemplates, altTemplate)) { // allowed, use diff --git a/src/Umbraco.Core/Routing/UrlProvider.cs b/src/Umbraco.Core/Routing/UrlProvider.cs index 80169e54e2..fa764cf7ff 100644 --- a/src/Umbraco.Core/Routing/UrlProvider.cs +++ b/src/Umbraco.Core/Routing/UrlProvider.cs @@ -24,7 +24,7 @@ namespace Umbraco.Web.Routing /// The list of media url providers. /// The current variation accessor. /// - public UrlProvider(IUmbracoContextAccessor umbracoContextAccessor, IWebRoutingSection routingSettings, UrlProviderCollection urlProviders, MediaUrlProviderCollection mediaUrlProviders, IVariationContextAccessor variationContextAccessor) + public UrlProvider(IUmbracoContextAccessor umbracoContextAccessor, IWebRoutingSettings routingSettings, UrlProviderCollection urlProviders, MediaUrlProviderCollection mediaUrlProviders, IVariationContextAccessor variationContextAccessor) { if (routingSettings == null) throw new ArgumentNullException(nameof(routingSettings)); diff --git a/src/Umbraco.Infrastructure/Routing/RedirectTrackingComponent.cs b/src/Umbraco.Infrastructure/Routing/RedirectTrackingComponent.cs index 0eae54bf7d..f9256b3692 100644 --- a/src/Umbraco.Infrastructure/Routing/RedirectTrackingComponent.cs +++ b/src/Umbraco.Infrastructure/Routing/RedirectTrackingComponent.cs @@ -24,14 +24,15 @@ namespace Umbraco.Web.Routing { private const string _eventStateKey = "Umbraco.Web.Redirects.RedirectTrackingEventHandler"; - private readonly IUmbracoSettingsSection _umbracoSettings; + + private readonly IWebRoutingSettings _webRoutingSettings; private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; private readonly IRedirectUrlService _redirectUrlService; private readonly IVariationContextAccessor _variationContextAccessor; - public RedirectTrackingComponent(IUmbracoSettingsSection umbracoSettings, IPublishedSnapshotAccessor publishedSnapshotAccessor, IRedirectUrlService redirectUrlService, IVariationContextAccessor variationContextAccessor) + public RedirectTrackingComponent(IWebRoutingSettings webRoutingSettings, IPublishedSnapshotAccessor publishedSnapshotAccessor, IRedirectUrlService redirectUrlService, IVariationContextAccessor variationContextAccessor) { - _umbracoSettings = umbracoSettings; + _webRoutingSettings = webRoutingSettings; _publishedSnapshotAccessor = publishedSnapshotAccessor; _redirectUrlService = redirectUrlService; _variationContextAccessor = variationContextAccessor; @@ -40,7 +41,7 @@ namespace Umbraco.Web.Routing public void Initialize() { // don't let the event handlers kick in if Redirect Tracking is turned off in the config - if (_umbracoSettings.WebRouting.DisableRedirectUrlTracking) return; + if (_webRoutingSettings.DisableRedirectUrlTracking) return; ContentService.Publishing += ContentService_Publishing; ContentService.Published += ContentService_Published; diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/UmbracoSettingsTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/UmbracoSettingsTests.cs index a6da02e281..7fb160fdd4 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/UmbracoSettingsTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/UmbracoSettingsTests.cs @@ -36,5 +36,6 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings private UmbracoSettingsSection Settings { get; set; } protected ILoggingSettings LoggingSettings => Settings.Logging; + protected IWebRoutingSettings WebRoutingSettings => Settings.WebRouting; } } diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementDefaultTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementDefaultTests.cs index b593e9082e..73483ee8d2 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementDefaultTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementDefaultTests.cs @@ -14,31 +14,31 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings [Test] public override void UrlProviderMode() { - Assert.IsTrue(SettingsSection.WebRouting.UrlProviderMode == "Auto"); + Assert.IsTrue(WebRoutingSettings.UrlProviderMode == "Auto"); } [Test] public void DisableAlternativeTemplates() { - Assert.IsTrue(SettingsSection.WebRouting.DisableAlternativeTemplates == false); + Assert.IsTrue(WebRoutingSettings.DisableAlternativeTemplates == false); } [Test] public void ValidateAlternativeTemplates() { - Assert.IsTrue(SettingsSection.WebRouting.ValidateAlternativeTemplates == false); + Assert.IsTrue(WebRoutingSettings.ValidateAlternativeTemplates == false); } [Test] public void DisableFindContentByIdPath() { - Assert.IsTrue(SettingsSection.WebRouting.DisableFindContentByIdPath == false); + Assert.IsTrue(WebRoutingSettings.DisableFindContentByIdPath == false); } [Test] public void DisableRedirectUrlTracking() { - Assert.IsTrue(SettingsSection.WebRouting.DisableRedirectUrlTracking == false); + Assert.IsTrue(WebRoutingSettings.DisableRedirectUrlTracking == false); } } } diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementTests.cs index 0fa1fb6681..8068c1605f 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementTests.cs @@ -8,19 +8,19 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings [Test] public void TrySkipIisCustomErrors() { - Assert.IsTrue(SettingsSection.WebRouting.TrySkipIisCustomErrors == false); + Assert.IsTrue(WebRoutingSettings.TrySkipIisCustomErrors == false); } [Test] public void InternalRedirectPreservesTemplate() { - Assert.IsTrue(SettingsSection.WebRouting.InternalRedirectPreservesTemplate == false); + Assert.IsTrue(WebRoutingSettings.InternalRedirectPreservesTemplate == false); } [Test] public virtual void UrlProviderMode() { - Assert.IsTrue(SettingsSection.WebRouting.UrlProviderMode == "Auto"); + Assert.IsTrue(WebRoutingSettings.UrlProviderMode == "Auto"); } } } diff --git a/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs index 227a7c26e3..ee1e056aff 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs @@ -18,7 +18,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext(urlAsString); var publishedRouter = CreatePublishedRouter(); var frequest = publishedRouter.CreateRequest(umbracoContext); - var lookup = new ContentFinderByIdPath(Factory.GetInstance().WebRouting, Logger, Factory.GetInstance()); + var lookup = new ContentFinderByIdPath(SettingsForTests.GenerateMockWebRoutingSettings(), Logger, Factory.GetInstance()); var result = lookup.TryFindContent(frequest); diff --git a/src/Umbraco.Tests/Routing/ContentFinderByUrlAndTemplateTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByUrlAndTemplateTests.cs index 208ec20517..e8b8bab22b 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByUrlAndTemplateTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByUrlAndTemplateTests.cs @@ -38,7 +38,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext(urlAsString, template1.Id, globalSettings:globalSettings.Object); var publishedRouter = CreatePublishedRouter(); var frequest = publishedRouter.CreateRequest(umbracoContext); - var lookup = new ContentFinderByUrlAndTemplate(Logger, ServiceContext.FileService, TestObjects.GetUmbracoSettings(), ServiceContext.ContentTypeService); + var lookup = new ContentFinderByUrlAndTemplate(Logger, ServiceContext.FileService, ServiceContext.ContentTypeService, SettingsForTests.GenerateMockWebRoutingSettings()); var result = lookup.TryFindContent(frequest); diff --git a/src/Umbraco.Tests/Routing/GetContentUrlsTests.cs b/src/Umbraco.Tests/Routing/GetContentUrlsTests.cs index b9d669902d..647f7d8813 100644 --- a/src/Umbraco.Tests/Routing/GetContentUrlsTests.cs +++ b/src/Umbraco.Tests/Routing/GetContentUrlsTests.cs @@ -85,7 +85,7 @@ namespace Umbraco.Tests.Routing umbracoContextAccessor, UriUtility); var publishedUrlProvider = new UrlProvider( umbracoContextAccessor, - TestHelper.WebRoutingSection, + TestHelper.WebRoutingSettings, new UrlProviderCollection(new []{urlProvider}), new MediaUrlProviderCollection(Enumerable.Empty()), Mock.Of() @@ -130,7 +130,7 @@ namespace Umbraco.Tests.Routing var urlProvider = new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, TestObjects.GetGlobalSettings(), new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = new UrlProvider( umbracoContextAccessor, - TestHelper.WebRoutingSection, + TestHelper.WebRoutingSettings, new UrlProviderCollection(new []{urlProvider}), new MediaUrlProviderCollection(Enumerable.Empty()), Mock.Of() diff --git a/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs b/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs index fb61fcffe8..a8d45984f1 100644 --- a/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs @@ -153,7 +153,7 @@ namespace Umbraco.Tests.Routing { return new UrlProvider( new TestUmbracoContextAccessor(umbracoContext), - TestHelper.WebRoutingSection, + TestHelper.WebRoutingSettings, new UrlProviderCollection(Enumerable.Empty()), new MediaUrlProviderCollection(new []{_mediaUrlProvider}), Mock.Of() diff --git a/src/Umbraco.Tests/Routing/UrlProviderTests.cs b/src/Umbraco.Tests/Routing/UrlProviderTests.cs index 481d03bce5..9cd93ccfa6 100644 --- a/src/Umbraco.Tests/Routing/UrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/UrlProviderTests.cs @@ -102,7 +102,7 @@ namespace Umbraco.Tests.Routing { return new UrlProvider( new TestUmbracoContextAccessor(umbracoContext), - TestHelper.WebRoutingSection, + TestHelper.WebRoutingSettings, new UrlProviderCollection(new []{urlProvider}), new MediaUrlProviderCollection(Enumerable.Empty()), Mock.Of() diff --git a/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs b/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs index ab2883ac37..48e34b6be2 100644 --- a/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs @@ -410,7 +410,7 @@ namespace Umbraco.Tests.Routing { return new UrlProvider( new TestUmbracoContextAccessor(umbracoContext), - TestHelper.WebRoutingSection, + TestHelper.WebRoutingSettings, new UrlProviderCollection(new []{urlProvider}), new MediaUrlProviderCollection(Enumerable.Empty()), Mock.Of() diff --git a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs index ad6e7b5408..e0e5564f14 100644 --- a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs +++ b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs @@ -99,7 +99,7 @@ namespace Umbraco.Tests.Routing { return new UrlProvider( new TestUmbracoContextAccessor(umbracoContext), - TestHelper.WebRoutingSection, + TestHelper.WebRoutingSettings, new UrlProviderCollection(new []{urlProvider}), new MediaUrlProviderCollection(Enumerable.Empty()), Mock.Of() diff --git a/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs b/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs index 729184eb1d..0d26e516ff 100644 --- a/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs +++ b/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs @@ -82,7 +82,7 @@ namespace Umbraco.Tests.Runtimes // test application public class TestUmbracoApplication : UmbracoApplicationBase { - public TestUmbracoApplication() : base(_logger, _configs, _ioHelper, _profiler, new AspNetHostingEnvironment(_hostingSettings), new AspNetBackOfficeInfo(_globalSettings, _ioHelper, _settings, _logger)) + public TestUmbracoApplication() : base(_logger, _configs, _ioHelper, _profiler, new AspNetHostingEnvironment(_hostingSettings), new AspNetBackOfficeInfo(_globalSettings, _ioHelper, _logger, _settings)) { } @@ -92,7 +92,7 @@ namespace Umbraco.Tests.Runtimes private static readonly Configs _configs = GetConfigs(); private static readonly IGlobalSettings _globalSettings = _configs.Global(); private static readonly IHostingSettings _hostingSettings = _configs.Hosting(); - private static readonly IUmbracoSettingsSection _settings = _configs.Settings(); + private static readonly IWebRoutingSettings _settings = _configs.WebRouting(); private static Configs GetConfigs() { diff --git a/src/Umbraco.Tests/Templates/HtmlImageSourceParserTests.cs b/src/Umbraco.Tests/Templates/HtmlImageSourceParserTests.cs index 43d97523d2..71e4c5ef3d 100644 --- a/src/Umbraco.Tests/Templates/HtmlImageSourceParserTests.cs +++ b/src/Umbraco.Tests/Templates/HtmlImageSourceParserTests.cs @@ -78,7 +78,7 @@ namespace Umbraco.Tests.Templates var publishedUrlProvider = new UrlProvider(umbracoContextAccessor, - TestHelper.WebRoutingSection, + TestHelper.WebRoutingSettings, new UrlProviderCollection(Enumerable.Empty()), new MediaUrlProviderCollection(new []{mediaUrlProvider.Object}), Mock.Of() diff --git a/src/Umbraco.Tests/Templates/HtmlLocalLinkParserTests.cs b/src/Umbraco.Tests/Templates/HtmlLocalLinkParserTests.cs index f350a5d702..21641ea84f 100644 --- a/src/Umbraco.Tests/Templates/HtmlLocalLinkParserTests.cs +++ b/src/Umbraco.Tests/Templates/HtmlLocalLinkParserTests.cs @@ -73,7 +73,7 @@ namespace Umbraco.Tests.Templates umbracoContextAccessor: umbracoContextAccessor); var publishedUrlProvider = new UrlProvider(umbracoContextAccessor, - TestHelper.WebRoutingSection, + TestHelper.WebRoutingSettings, new UrlProviderCollection(new []{contentUrlProvider.Object}), new MediaUrlProviderCollection(new []{mediaUrlProvider.Object}), Mock.Of() diff --git a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs index b09620103b..c92469a22a 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs @@ -90,18 +90,17 @@ namespace Umbraco.Tests.TestHelpers internal PublishedRouter CreatePublishedRouter(IFactory container = null, ContentFinderCollection contentFinders = null) { - return CreatePublishedRouter(TestObjects.GetUmbracoSettings().WebRouting, container ?? Factory, contentFinders); + return CreatePublishedRouter(SettingsForTests.GenerateMockWebRoutingSettings(), container ?? Factory, contentFinders); } - internal static PublishedRouter CreatePublishedRouter(IWebRoutingSection webRoutingSection, IFactory container = null, ContentFinderCollection contentFinders = null) + internal static PublishedRouter CreatePublishedRouter(IWebRoutingSettings webRoutingSettings, IFactory container = null, ContentFinderCollection contentFinders = null) { return new PublishedRouter( - webRoutingSection, + webRoutingSettings, contentFinders ?? new ContentFinderCollection(Enumerable.Empty()), new TestLastChanceFinder(), new TestVariationContextAccessor(), new ProfilingLogger(Mock.Of(), Mock.Of()), - container?.TryGetInstance() ?? Current.Factory.GetInstance(), Mock.Of(), Mock.Of(), container?.GetInstance() ?? Current.Factory.GetInstance(), diff --git a/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs b/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs index e1badf33d6..60b4105490 100644 --- a/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs +++ b/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs @@ -6,6 +6,7 @@ using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; +using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Tests.TestHelpers { @@ -46,7 +47,7 @@ namespace Umbraco.Tests.TestHelpers var security = new Mock(); var requestHandler = new Mock(); var logging = new Mock(); - var routing = new Mock(); + var routing = new Mock(); var userPasswordConfig = new Mock(); var memberPasswordConfig = new Mock(); @@ -56,14 +57,12 @@ namespace Umbraco.Tests.TestHelpers settings.Setup(x => x.Content).Returns(content.Object); settings.Setup(x => x.Security).Returns(security.Object); settings.Setup(x => x.RequestHandler).Returns(requestHandler.Object); - settings.Setup(x => x.WebRouting).Returns(routing.Object); //Now configure some defaults - the defaults in the config section classes do NOT pertain to the mocked data!! settings.Setup(x => x.Content.ImageAutoFillProperties).Returns(ContentImagingElement.GetDefaultImageAutoFillProperties()); settings.Setup(x => x.Content.ImageFileTypes).Returns(ContentImagingElement.GetDefaultImageFileTypes()); settings.Setup(x => x.RequestHandler.AddTrailingSlash).Returns(true); settings.Setup(x => x.RequestHandler.CharCollection).Returns(RequestHandlerElement.GetDefaultCharReplacements()); - settings.Setup(x => x.WebRouting.UrlProviderMode).Returns("Auto"); return settings.Object; } @@ -164,5 +163,16 @@ namespace Umbraco.Tests.TestHelpers return _defaultUmbracoSettings; } + + public static IWebRoutingSettings GenerateMockWebRoutingSettings() + { + var mock = new Mock(); + + mock.Setup(x => x.TrySkipIisCustomErrors).Returns(false); + mock.Setup(x => x.InternalRedirectPreservesTemplate).Returns(false); + mock.Setup(x => x.UrlProviderMode).Returns(UrlMode.Auto.ToString()); + + return mock.Object; + } } } diff --git a/src/Umbraco.Tests/TestHelpers/TestHelper.cs b/src/Umbraco.Tests/TestHelpers/TestHelper.cs index 41b97ac580..53eee0fbb2 100644 --- a/src/Umbraco.Tests/TestHelpers/TestHelper.cs +++ b/src/Umbraco.Tests/TestHelpers/TestHelper.cs @@ -68,7 +68,7 @@ namespace Umbraco.Tests.TestHelpers public static IBackOfficeInfo GetBackOfficeInfo() { - return new AspNetBackOfficeInfo(SettingsForTests.GenerateMockGlobalSettings(), TestHelper.IOHelper, SettingsForTests.GenerateMockUmbracoSettings(), Mock.Of()); + return new AspNetBackOfficeInfo(SettingsForTests.GenerateMockGlobalSettings(), TestHelper.IOHelper, Mock.Of(), SettingsForTests.GenerateMockWebRoutingSettings()); } public static IConfigsFactory GetConfigsFactory() @@ -104,7 +104,7 @@ namespace Umbraco.Tests.TestHelpers public static IMainDom MainDom { get; } = new MainDom(Mock.Of(), GetHostingEnvironment(), new MainDomSemaphoreLock(Mock.Of(), GetHostingEnvironment())); public static UriUtility UriUtility { get; } = new UriUtility(GetHostingEnvironment()); - public static IWebRoutingSection WebRoutingSection => SettingsForTests.GetDefaultUmbracoSettings().WebRouting; + public static IWebRoutingSettings WebRoutingSettings => SettingsForTests.GenerateMockWebRoutingSettings(); /// /// Maps the given making it rooted on . must start with ~/ diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs index 5dabb7ed06..c1bc2f913b 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs @@ -146,9 +146,8 @@ namespace Umbraco.Tests.TestHelpers // FIXME: Shouldn't we use the default ones so they are the same instance for each test? var umbracoSettingsMock = new Mock(); - var webRoutingSectionMock = new Mock(); + var webRoutingSectionMock = new Mock(); webRoutingSectionMock.Setup(x => x.UrlProviderMode).Returns(UrlMode.Auto.ToString()); - umbracoSettingsMock.Setup(x => x.WebRouting).Returns(webRoutingSectionMock.Object); return umbracoSettingsMock.Object; } diff --git a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs index 439c9b17f2..dfdeb60dbe 100644 --- a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs +++ b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs @@ -85,7 +85,7 @@ namespace Umbraco.Tests.Testing.TestingTests var theUrlProvider = new UrlProvider( new TestUmbracoContextAccessor(umbracoContext), - TestHelper.WebRoutingSection, + TestHelper.WebRoutingSettings, new UrlProviderCollection(new [] { urlProvider }), new MediaUrlProviderCollection( Enumerable.Empty()) , umbracoContext.VariationContextAccessor); diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index de0db554f3..854242d36b 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -174,9 +174,9 @@ namespace Umbraco.Tests.Testing TypeFinder = new TypeFinder(logger); var appCaches = GetAppCaches(); var globalSettings = SettingsForTests.GetDefaultGlobalSettings(); - var settings = SettingsForTests.GetDefaultUmbracoSettings(); + var settings = SettingsForTests.GenerateMockWebRoutingSettings(); - IBackOfficeInfo backOfficeInfo = new AspNetBackOfficeInfo(globalSettings, IOHelper, settings, logger); + IBackOfficeInfo backOfficeInfo = new AspNetBackOfficeInfo(globalSettings, IOHelper, logger, settings); IIpResolver ipResolver = new AspNetIpResolver(); UmbracoVersion = new UmbracoVersion(globalSettings); @@ -321,7 +321,7 @@ namespace Umbraco.Tests.Testing Composition.RegisterUnique(factory => new UrlProvider( factory.GetInstance(), - TestObjects.GetUmbracoSettings().WebRouting, + SettingsForTests.GenerateMockWebRoutingSettings(), new UrlProviderCollection(Enumerable.Empty()), new MediaUrlProviderCollection(Enumerable.Empty()), factory.GetInstance() @@ -428,7 +428,7 @@ namespace Umbraco.Tests.Testing // register basic stuff that might need to be there for some container resolvers to work Composition.RegisterUnique(factory => factory.GetInstance().Content); - Composition.RegisterUnique(factory => factory.GetInstance().WebRouting); + Composition.RegisterUnique(factory => factory.GetInstance()); Composition.RegisterUnique(); diff --git a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs index 73d447d3b2..240fdf835d 100644 --- a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs @@ -156,7 +156,7 @@ namespace Umbraco.Tests.Web.Mvc var content = Mock.Of(publishedContent => publishedContent.Id == 12345); - var publishedRouter = BaseWebTest.CreatePublishedRouter(TestObjects.GetUmbracoSettings().WebRouting); + var publishedRouter = BaseWebTest.CreatePublishedRouter(SettingsForTests.GenerateMockWebRoutingSettings()); var frequest = publishedRouter.CreateRequest(umbracoContext, new Uri("http://localhost/test")); frequest.PublishedContent = content; diff --git a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs index bc2b896b49..81b1ea60aa 100644 --- a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs @@ -391,7 +391,7 @@ namespace Umbraco.Tests.Web.Mvc logger, settings, "/dang", 0); - var publishedRouter = BaseWebTest.CreatePublishedRouter(TestObjects.GetUmbracoSettings().WebRouting); + var publishedRouter = BaseWebTest.CreatePublishedRouter(SettingsForTests.GenerateMockWebRoutingSettings()); var frequest = publishedRouter.CreateRequest(umbracoContext, new Uri("http://localhost/dang")); frequest.Culture = CultureInfo.InvariantCulture; diff --git a/src/Umbraco.Web/AspNet/AspNetBackOfficeInfo.cs b/src/Umbraco.Web/AspNet/AspNetBackOfficeInfo.cs index cabe56062c..1e60fedeea 100644 --- a/src/Umbraco.Web/AspNet/AspNetBackOfficeInfo.cs +++ b/src/Umbraco.Web/AspNet/AspNetBackOfficeInfo.cs @@ -11,15 +11,15 @@ namespace Umbraco.Web { private readonly IGlobalSettings _globalSettings; private readonly IIOHelper _ioHelper; - private readonly IUmbracoSettingsSection _settings; private readonly ILogger _logger; + private readonly IWebRoutingSettings _webRoutingSettings; - public AspNetBackOfficeInfo(IGlobalSettings globalSettings, IIOHelper ioHelper, IUmbracoSettingsSection settings, ILogger logger) + public AspNetBackOfficeInfo(IGlobalSettings globalSettings, IIOHelper ioHelper, ILogger logger, IWebRoutingSettings webRoutingSettings) { _globalSettings = globalSettings; _ioHelper = ioHelper; - _settings = settings; _logger = logger; + _webRoutingSettings = webRoutingSettings; } /// @@ -27,7 +27,7 @@ namespace Umbraco.Web private string GetAbsoluteUrlFromConfig() { - var url = _settings.WebRouting.UmbracoApplicationUrl; + var url = _webRoutingSettings.UmbracoApplicationUrl; if (url.IsNullOrWhiteSpace() == false) { var umbracoApplicationUrl = url.TrimEnd('/'); diff --git a/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs b/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs index e6a4f82a77..d1bb3f873c 100644 --- a/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs +++ b/src/Umbraco.Web/Editors/RedirectUrlManagementController.cs @@ -19,12 +19,12 @@ namespace Umbraco.Web.Editors public class RedirectUrlManagementController : UmbracoAuthorizedApiController { private readonly ILogger _logger; - private readonly IUmbracoSettingsSection _umbracoSettingsSection; + private readonly IWebRoutingSettings _webRoutingSettings; - public RedirectUrlManagementController(ILogger logger, IUmbracoSettingsSection umbracoSettingsSection) + public RedirectUrlManagementController(ILogger logger, IWebRoutingSettings webRoutingSettings) { _logger = logger; - _umbracoSettingsSection = umbracoSettingsSection ?? throw new ArgumentNullException(nameof(umbracoSettingsSection)); + _webRoutingSettings = webRoutingSettings; } /// @@ -34,7 +34,7 @@ namespace Umbraco.Web.Editors [HttpGet] public IHttpActionResult GetEnableState() { - var enabled = _umbracoSettingsSection.WebRouting.DisableRedirectUrlTracking == false; + var enabled = _webRoutingSettings.DisableRedirectUrlTracking == false; var userIsAdmin = UmbracoContext.Security.CurrentUser.IsAdmin(); return Ok(new { enabled, userIsAdmin }); } diff --git a/src/Umbraco.Web/Mvc/StatusCodeFilterAttribute.cs b/src/Umbraco.Web/Mvc/StatusCodeFilterAttribute.cs index 4a78d201ca..b1836c6ba4 100644 --- a/src/Umbraco.Web/Mvc/StatusCodeFilterAttribute.cs +++ b/src/Umbraco.Web/Mvc/StatusCodeFilterAttribute.cs @@ -22,7 +22,7 @@ namespace Umbraco.Web.Mvc base.OnActionExecuted(filterContext); filterContext.HttpContext.Response.StatusCode = (int)_statusCode; - filterContext.HttpContext.Response.TrySkipIisCustomErrors = Current.Configs.Settings().WebRouting.TrySkipIisCustomErrors; + filterContext.HttpContext.Response.TrySkipIisCustomErrors = Current.Configs.WebRouting().TrySkipIisCustomErrors; } } } diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 6da8e815ca..bcbcb8e08f 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -73,8 +73,8 @@ namespace Umbraco.Web { return content.IsAllowedTemplate( Current.Services.ContentTypeService, - Current.Configs.Settings().WebRouting.DisableAlternativeTemplates, - Current.Configs.Settings().WebRouting.ValidateAlternativeTemplates, + Current.Configs.WebRouting().DisableAlternativeTemplates, + Current.Configs.WebRouting().ValidateAlternativeTemplates, templateId); } @@ -83,8 +83,8 @@ namespace Umbraco.Web return content.IsAllowedTemplate( Current.Services.FileService, Current.Services.ContentTypeService, - Current.Configs.Settings().WebRouting.DisableAlternativeTemplates, - Current.Configs.Settings().WebRouting.ValidateAlternativeTemplates, + Current.Configs.WebRouting().DisableAlternativeTemplates, + Current.Configs.WebRouting().ValidateAlternativeTemplates, templateAlias); } diff --git a/src/Umbraco.Web/Runtime/WebInitialComposer.cs b/src/Umbraco.Web/Runtime/WebInitialComposer.cs index da58ff1c0c..8b7d96e44f 100644 --- a/src/Umbraco.Web/Runtime/WebInitialComposer.cs +++ b/src/Umbraco.Web/Runtime/WebInitialComposer.cs @@ -240,7 +240,6 @@ namespace Umbraco.Web.Runtime // register published router composition.RegisterUnique(); - composition.Register(_ => composition.Configs.Settings().WebRouting); // register preview SignalR hub composition.RegisterUnique(_ => GlobalHost.ConnectionManager.GetHubContext()); diff --git a/src/Umbraco.Web/Templates/TemplateRenderer.cs b/src/Umbraco.Web/Templates/TemplateRenderer.cs index 8cf310d7ee..4a1a6c97ec 100644 --- a/src/Umbraco.Web/Templates/TemplateRenderer.cs +++ b/src/Umbraco.Web/Templates/TemplateRenderer.cs @@ -26,17 +26,17 @@ namespace Umbraco.Web.Templates private readonly IPublishedRouter _publishedRouter; private readonly IFileService _fileService; private readonly ILocalizationService _languageService; - private readonly IWebRoutingSection _webRoutingSection; + private readonly IWebRoutingSettings _webRoutingSettings; private readonly IShortStringHelper _shortStringHelper; private readonly IHttpContextAccessor _httpContextAccessor; - public TemplateRenderer(IUmbracoContextAccessor umbracoContextAccessor, IPublishedRouter publishedRouter, IFileService fileService, ILocalizationService textService, IWebRoutingSection webRoutingSection, IShortStringHelper shortStringHelper, IHttpContextAccessor httpContextAccessor) + public TemplateRenderer(IUmbracoContextAccessor umbracoContextAccessor, IPublishedRouter publishedRouter, IFileService fileService, ILocalizationService textService, IWebRoutingSettings webRoutingSettings, IShortStringHelper shortStringHelper, IHttpContextAccessor httpContextAccessor) { _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); _publishedRouter = publishedRouter ?? throw new ArgumentNullException(nameof(publishedRouter)); _fileService = fileService ?? throw new ArgumentNullException(nameof(fileService)); _languageService = textService ?? throw new ArgumentNullException(nameof(textService)); - _webRoutingSection = webRoutingSection ?? throw new ArgumentNullException(nameof(webRoutingSection)); + _webRoutingSettings = webRoutingSettings ?? throw new ArgumentNullException(nameof(webRoutingSettings)); _shortStringHelper = shortStringHelper ?? throw new ArgumentNullException(nameof(shortStringHelper)); _httpContextAccessor = httpContextAccessor; } @@ -77,7 +77,7 @@ namespace Umbraco.Web.Templates //set the doc that was found by id contentRequest.PublishedContent = doc; //set the template, either based on the AltTemplate found or the standard template of the doc - var templateId = _webRoutingSection.DisableAlternativeTemplates || !altTemplateId.HasValue + var templateId = _webRoutingSettings.DisableAlternativeTemplates || !altTemplateId.HasValue ? doc.TemplateId : altTemplateId.Value; if (templateId.HasValue) diff --git a/src/Umbraco.Web/UmbracoApplicationBase.cs b/src/Umbraco.Web/UmbracoApplicationBase.cs index 1111d028f8..cd107b179a 100644 --- a/src/Umbraco.Web/UmbracoApplicationBase.cs +++ b/src/Umbraco.Web/UmbracoApplicationBase.cs @@ -41,7 +41,7 @@ namespace Umbraco.Web var configs = configFactory.Create(ioHelper); var logger = SerilogLogger.CreateWithDefaultConfiguration(hostingEnvironment, new AspNetSessionManager(), () => _factory?.GetInstance(), coreDebug, ioHelper, new FrameworkMarchal()); - var backOfficeInfo = new AspNetBackOfficeInfo(configs.Global(), ioHelper, configs.Settings(), logger); + var backOfficeInfo = new AspNetBackOfficeInfo(configs.Global(), ioHelper, logger, configFactory.WebRoutingSettings); var profiler = new LogProfiler(logger); Umbraco.Composing.Current.Initialize(logger, configs, ioHelper, hostingEnvironment, backOfficeInfo, profiler); } diff --git a/src/Umbraco.Web/UmbracoModule.cs b/src/Umbraco.Web/UmbracoModule.cs index 6197e824f5..2dffe13851 100644 --- a/src/Umbraco.Web/UmbracoModule.cs +++ b/src/Umbraco.Web/UmbracoModule.cs @@ -75,7 +75,7 @@ namespace Umbraco.Web else if (pcr.Is404) { response.StatusCode = 404; - response.TrySkipIisCustomErrors = Current.Configs.Settings().WebRouting.TrySkipIisCustomErrors; + response.TrySkipIisCustomErrors = Current.Configs.WebRouting().TrySkipIisCustomErrors; if (response.TrySkipIisCustomErrors == false) logger.Warn("Status code is 404 yet TrySkipIisCustomErrors is false - IIS will take over."); From e27a05c3e84cf7351259db0ff24a4740d9a0e7f6 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Thu, 12 Mar 2020 12:46:08 +0100 Subject: [PATCH 61/69] Cleaned up config for RequestHandler settings --- src/Umbraco.Configuration/ConfigsFactory.cs | 2 + .../Implementations/RequestHandlerSettings.cs | 14 +++++ .../UmbracoSettings/RequestHandlerElement.cs | 10 ++-- .../UmbracoSettings/UmbracoSettingsSection.cs | 2 - .../Configuration/ConfigsExtensions.cs | 3 ++ ...rSection.cs => IRequestHandlerSettings.cs} | 2 +- .../IUmbracoSettingsSection.cs | 2 - src/Umbraco.Core/Routing/AliasUrlProvider.cs | 4 +- .../Routing/DefaultUrlProvider.cs | 4 +- src/Umbraco.Core/Routing/UriUtility.cs | 2 +- .../Strings/DefaultShortStringHelper.cs | 4 +- .../Strings/DefaultShortStringHelperConfig.cs | 10 ++-- .../CompositionExtensions/Configuration.cs | 1 - .../Runtime/CoreInitialComposer.cs | 2 +- .../RequestHandlerElementTests.cs | 10 ++-- .../UmbracoSettings/UmbracoSettingsTests.cs | 1 + .../CoreThings/TryConvertToTests.cs | 2 +- src/Umbraco.Tests/Misc/UriUtilityTests.cs | 6 +-- .../PropertyEditorValueEditorTests.cs | 2 +- .../Routing/GetContentUrlsTests.cs | 8 +-- .../Routing/RenderRouteHandlerTests.cs | 2 +- src/Umbraco.Tests/Routing/UrlProviderTests.cs | 36 ++++++------- .../Routing/UrlsProviderWithDomainsTests.cs | 28 +++++----- .../Routing/UrlsWithNestedDomains.cs | 4 +- .../Strings/CmsHelperCasingTests.cs | 2 +- .../Strings/DefaultShortStringHelperTests.cs | 54 +++++++++---------- .../TestHelpers/SettingsForTests.cs | 21 ++++++-- src/Umbraco.Tests/Testing/UmbracoTestBase.cs | 3 ++ .../UmbracoExamine/ExamineBaseTest.cs | 2 +- 29 files changed, 135 insertions(+), 108 deletions(-) create mode 100644 src/Umbraco.Configuration/Implementations/RequestHandlerSettings.cs rename src/Umbraco.Core/Configuration/UmbracoSettings/{IRequestHandlerSection.cs => IRequestHandlerSettings.cs} (78%) diff --git a/src/Umbraco.Configuration/ConfigsFactory.cs b/src/Umbraco.Configuration/ConfigsFactory.cs index a671a08a8b..8930edf36b 100644 --- a/src/Umbraco.Configuration/ConfigsFactory.cs +++ b/src/Umbraco.Configuration/ConfigsFactory.cs @@ -23,6 +23,7 @@ namespace Umbraco.Core.Configuration public ILoggingSettings LoggingSettings { get; } = new LoggingSettings(); public IKeepAliveSettings KeepAliveSettings { get; } = new KeepAliveSettings(); public IWebRoutingSettings WebRoutingSettings { get; } = new WebRoutingSettings(); + public IRequestHandlerSettings RequestHandlerSettings { get; } = new RequestHandlerSettings(); public IUmbracoSettingsSection UmbracoSettings { get; } @@ -56,6 +57,7 @@ namespace Umbraco.Core.Configuration configs.Add(() => LoggingSettings); configs.Add(() => KeepAliveSettings); configs.Add(() => WebRoutingSettings); + configs.Add(() => RequestHandlerSettings); configs.AddCoreConfigs(ioHelper); return configs; diff --git a/src/Umbraco.Configuration/Implementations/RequestHandlerSettings.cs b/src/Umbraco.Configuration/Implementations/RequestHandlerSettings.cs new file mode 100644 index 0000000000..0cd011c863 --- /dev/null +++ b/src/Umbraco.Configuration/Implementations/RequestHandlerSettings.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using Umbraco.Core; +using Umbraco.Core.Configuration.UmbracoSettings; + +namespace Umbraco.Configuration.Implementations +{ + internal class RequestHandlerSettings : ConfigurationManagerConfigBase, IRequestHandlerSettings + { + public bool AddTrailingSlash => UmbracoSettingsSection.RequestHandler.AddTrailingSlash; + public bool ConvertUrlsToAscii => UmbracoSettingsSection.RequestHandler.UrlReplacing.ConvertUrlsToAscii.InvariantEquals("true"); + public bool TryConvertUrlsToAscii => UmbracoSettingsSection.RequestHandler.UrlReplacing.ConvertUrlsToAscii.InvariantEquals("try"); + public IEnumerable CharCollection => UmbracoSettingsSection.RequestHandler.UrlReplacing.CharCollection; + } +} diff --git a/src/Umbraco.Configuration/UmbracoSettings/RequestHandlerElement.cs b/src/Umbraco.Configuration/UmbracoSettings/RequestHandlerElement.cs index 80fcb6ca1a..f959a56e71 100644 --- a/src/Umbraco.Configuration/UmbracoSettings/RequestHandlerElement.cs +++ b/src/Umbraco.Configuration/UmbracoSettings/RequestHandlerElement.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; namespace Umbraco.Core.Configuration.UmbracoSettings { - internal class RequestHandlerElement : UmbracoConfigurationElement, IRequestHandlerSection + internal class RequestHandlerElement : UmbracoConfigurationElement, IRequestHandlerSettings { [ConfigurationProperty("addTrailingSlash")] public InnerTextConfigurationElement AddTrailingSlash => GetOptionalTextElement("addTrailingSlash", true); @@ -85,12 +85,12 @@ namespace Umbraco.Core.Configuration.UmbracoSettings return collection; } - bool IRequestHandlerSection.AddTrailingSlash => AddTrailingSlash; + bool IRequestHandlerSettings.AddTrailingSlash => AddTrailingSlash; - bool IRequestHandlerSection.ConvertUrlsToAscii => UrlReplacing.ConvertUrlsToAscii.InvariantEquals("true"); + bool IRequestHandlerSettings.ConvertUrlsToAscii => UrlReplacing.ConvertUrlsToAscii.InvariantEquals("true"); - bool IRequestHandlerSection.TryConvertUrlsToAscii => UrlReplacing.ConvertUrlsToAscii.InvariantEquals("try"); + bool IRequestHandlerSettings.TryConvertUrlsToAscii => UrlReplacing.ConvertUrlsToAscii.InvariantEquals("try"); - IEnumerable IRequestHandlerSection.CharCollection => UrlReplacing.CharCollection; + IEnumerable IRequestHandlerSettings.CharCollection => UrlReplacing.CharCollection; } } diff --git a/src/Umbraco.Configuration/UmbracoSettings/UmbracoSettingsSection.cs b/src/Umbraco.Configuration/UmbracoSettings/UmbracoSettingsSection.cs index ce420c7f63..2b980d133c 100644 --- a/src/Umbraco.Configuration/UmbracoSettings/UmbracoSettingsSection.cs +++ b/src/Umbraco.Configuration/UmbracoSettings/UmbracoSettingsSection.cs @@ -28,7 +28,5 @@ namespace Umbraco.Core.Configuration.UmbracoSettings IContentSection IUmbracoSettingsSection.Content => Content; ISecuritySection IUmbracoSettingsSection.Security => Security; - - IRequestHandlerSection IUmbracoSettingsSection.RequestHandler => RequestHandler; } } diff --git a/src/Umbraco.Core/Configuration/ConfigsExtensions.cs b/src/Umbraco.Core/Configuration/ConfigsExtensions.cs index 00e6c50499..b800b42454 100644 --- a/src/Umbraco.Core/Configuration/ConfigsExtensions.cs +++ b/src/Umbraco.Core/Configuration/ConfigsExtensions.cs @@ -28,6 +28,9 @@ namespace Umbraco.Core public static IUmbracoSettingsSection Settings(this Configs configs) => configs.GetConfig(); + public static IRequestHandlerSettings RequestHandler(this Configs configs) + => configs.GetConfig(); + public static IWebRoutingSettings WebRouting(this Configs configs) => configs.GetConfig(); diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IRequestHandlerSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IRequestHandlerSettings.cs similarity index 78% rename from src/Umbraco.Core/Configuration/UmbracoSettings/IRequestHandlerSection.cs rename to src/Umbraco.Core/Configuration/UmbracoSettings/IRequestHandlerSettings.cs index 2393c5af63..11fdaa8310 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IRequestHandlerSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IRequestHandlerSettings.cs @@ -2,7 +2,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - public interface IRequestHandlerSection : IUmbracoConfigurationSection + public interface IRequestHandlerSettings : IUmbracoConfigurationSection { bool AddTrailingSlash { get; } diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs index 51fe34adf4..69715f8e46 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs @@ -7,7 +7,5 @@ namespace Umbraco.Core.Configuration.UmbracoSettings IContentSection Content { get; } ISecuritySection Security { get; } - - IRequestHandlerSection RequestHandler { get; } } } diff --git a/src/Umbraco.Core/Routing/AliasUrlProvider.cs b/src/Umbraco.Core/Routing/AliasUrlProvider.cs index c9cc3d5156..e71de2f6c6 100644 --- a/src/Umbraco.Core/Routing/AliasUrlProvider.cs +++ b/src/Umbraco.Core/Routing/AliasUrlProvider.cs @@ -14,13 +14,13 @@ namespace Umbraco.Web.Routing public class AliasUrlProvider : IUrlProvider { private readonly IGlobalSettings _globalSettings; - private readonly IRequestHandlerSection _requestConfig; + private readonly IRequestHandlerSettings _requestConfig; private readonly ISiteDomainHelper _siteDomainHelper; private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly UriUtility _uriUtility; private readonly IPublishedValueFallback _publishedValueFallback; - public AliasUrlProvider(IGlobalSettings globalSettings, IRequestHandlerSection requestConfig, ISiteDomainHelper siteDomainHelper, UriUtility uriUtility, IPublishedValueFallback publishedValueFallback, IUmbracoContextAccessor umbracoContextAccessor) + public AliasUrlProvider(IGlobalSettings globalSettings, IRequestHandlerSettings requestConfig, ISiteDomainHelper siteDomainHelper, UriUtility uriUtility, IPublishedValueFallback publishedValueFallback, IUmbracoContextAccessor umbracoContextAccessor) { _globalSettings = globalSettings; _requestConfig = requestConfig; diff --git a/src/Umbraco.Core/Routing/DefaultUrlProvider.cs b/src/Umbraco.Core/Routing/DefaultUrlProvider.cs index 81102810e8..f56d96b6b3 100644 --- a/src/Umbraco.Core/Routing/DefaultUrlProvider.cs +++ b/src/Umbraco.Core/Routing/DefaultUrlProvider.cs @@ -12,14 +12,14 @@ namespace Umbraco.Web.Routing /// public class DefaultUrlProvider : IUrlProvider { - private readonly IRequestHandlerSection _requestSettings; + private readonly IRequestHandlerSettings _requestSettings; private readonly ILogger _logger; private readonly IGlobalSettings _globalSettings; private readonly ISiteDomainHelper _siteDomainHelper; private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly UriUtility _uriUtility; - public DefaultUrlProvider(IRequestHandlerSection requestSettings, ILogger logger, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper, IUmbracoContextAccessor umbracoContextAccessor, UriUtility uriUtility) + public DefaultUrlProvider(IRequestHandlerSettings requestSettings, ILogger logger, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper, IUmbracoContextAccessor umbracoContextAccessor, UriUtility uriUtility) { _requestSettings = requestSettings; _logger = logger; diff --git a/src/Umbraco.Core/Routing/UriUtility.cs b/src/Umbraco.Core/Routing/UriUtility.cs index 11bb1dabfb..9cb6bf50f6 100644 --- a/src/Umbraco.Core/Routing/UriUtility.cs +++ b/src/Umbraco.Core/Routing/UriUtility.cs @@ -61,7 +61,7 @@ namespace Umbraco.Web // maps an internal umbraco uri to a public uri // ie with virtual directory, .aspx if required... - public Uri UriFromUmbraco(Uri uri, IGlobalSettings globalSettings, IRequestHandlerSection requestConfig) + public Uri UriFromUmbraco(Uri uri, IGlobalSettings globalSettings, IRequestHandlerSettings requestConfig) { var path = uri.GetSafeAbsolutePath(); diff --git a/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs index 6361186604..c3e7fa85c3 100644 --- a/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs +++ b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs @@ -19,7 +19,7 @@ namespace Umbraco.Core.Strings { #region Ctor, consts and vars - public DefaultShortStringHelper(IUmbracoSettingsSection settings) + public DefaultShortStringHelper(IRequestHandlerSettings settings) { _config = new DefaultShortStringHelperConfig().WithDefault(settings); } @@ -619,6 +619,6 @@ namespace Umbraco.Core.Strings return new string(output, 0, opos); } - #endregion + #endregion } } diff --git a/src/Umbraco.Core/Strings/DefaultShortStringHelperConfig.cs b/src/Umbraco.Core/Strings/DefaultShortStringHelperConfig.cs index e75e00defb..25ee781ae9 100644 --- a/src/Umbraco.Core/Strings/DefaultShortStringHelperConfig.cs +++ b/src/Umbraco.Core/Strings/DefaultShortStringHelperConfig.cs @@ -30,7 +30,7 @@ namespace Umbraco.Core.Strings public string DefaultCulture { get; set; } = ""; // invariant public Dictionary UrlReplaceCharacters { get; set; } - + public DefaultShortStringHelperConfig WithConfig(Config config) { return WithConfig(DefaultCulture, CleanStringType.RoleMask, config); @@ -57,16 +57,16 @@ namespace Umbraco.Core.Strings /// Sets the default configuration. /// /// The short string helper. - public DefaultShortStringHelperConfig WithDefault(IUmbracoSettingsSection umbracoSettings) + public DefaultShortStringHelperConfig WithDefault(IRequestHandlerSettings requestHandlerSettings) { - UrlReplaceCharacters = umbracoSettings.RequestHandler.CharCollection + UrlReplaceCharacters = requestHandlerSettings.CharCollection .Where(x => string.IsNullOrEmpty(x.Char) == false) .ToDictionary(x => x.Char, x => x.Replacement); var urlSegmentConvertTo = CleanStringType.Utf8; - if (umbracoSettings.RequestHandler.ConvertUrlsToAscii) + if (requestHandlerSettings.ConvertUrlsToAscii) urlSegmentConvertTo = CleanStringType.Ascii; - if (umbracoSettings.RequestHandler.TryConvertUrlsToAscii) + if (requestHandlerSettings.TryConvertUrlsToAscii) urlSegmentConvertTo = CleanStringType.TryAscii; return WithConfig(CleanStringType.UrlSegment, new Config diff --git a/src/Umbraco.Infrastructure/Composing/CompositionExtensions/Configuration.cs b/src/Umbraco.Infrastructure/Composing/CompositionExtensions/Configuration.cs index 5c931a225b..48e43156ae 100644 --- a/src/Umbraco.Infrastructure/Composing/CompositionExtensions/Configuration.cs +++ b/src/Umbraco.Infrastructure/Composing/CompositionExtensions/Configuration.cs @@ -15,7 +15,6 @@ namespace Umbraco.Core.Composing.CompositionExtensions // register others composition.RegisterUnique(factory => factory.GetInstance().Content); - composition.RegisterUnique(factory => factory.GetInstance().RequestHandler); composition.RegisterUnique(factory => factory.GetInstance().Security); return composition; diff --git a/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs b/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs index 2586fcd9f5..8cd9ed8c4f 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs @@ -139,7 +139,7 @@ namespace Umbraco.Core.Runtime composition.RegisterUnique(); composition.RegisterUnique(factory - => new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(factory.GetInstance()))); + => new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(factory.GetInstance()))); composition.UrlSegmentProviders() .Append(); diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/RequestHandlerElementTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/RequestHandlerElementTests.cs index b085c4104c..bb4d1efd1e 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/RequestHandlerElementTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/RequestHandlerElementTests.cs @@ -10,7 +10,7 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings [Test] public void AddTrailingSlash() { - Assert.IsTrue(SettingsSection.RequestHandler.AddTrailingSlash == true); + Assert.IsTrue(RequestHandlerSettings.AddTrailingSlash == true); } [Test] @@ -18,14 +18,14 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings { var chars = @" ,"",',%,.,;,/,\,:,#,+,*,&,?,æ,ø,å,ä,ö,ü,ß,Ä,Ö,|,<,>"; var items = chars.Split(','); - Assert.AreEqual(items.Length, SettingsSection.RequestHandler.CharCollection.Count()); - Assert.IsTrue(SettingsSection.RequestHandler.CharCollection + Assert.AreEqual(items.Length, RequestHandlerSettings.CharCollection.Count()); + Assert.IsTrue(RequestHandlerSettings.CharCollection .All(x => items.Contains(x.Char))); var vals = @"-,plus,star,ae,oe,aa,ae,oe,ue,ss,ae,oe,-"; var splitVals = vals.Split(','); - Assert.AreEqual(splitVals.Length, SettingsSection.RequestHandler.CharCollection.Count(x => x.Replacement.IsNullOrWhiteSpace() == false)); - Assert.IsTrue(SettingsSection.RequestHandler.CharCollection + Assert.AreEqual(splitVals.Length, RequestHandlerSettings.CharCollection.Count(x => x.Replacement.IsNullOrWhiteSpace() == false)); + Assert.IsTrue(RequestHandlerSettings.CharCollection .All(x => string.IsNullOrEmpty(x.Replacement) || vals.Split(',').Contains(x.Replacement))); } diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/UmbracoSettingsTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/UmbracoSettingsTests.cs index 7fb160fdd4..b19e4d522a 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/UmbracoSettingsTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/UmbracoSettingsTests.cs @@ -37,5 +37,6 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings protected ILoggingSettings LoggingSettings => Settings.Logging; protected IWebRoutingSettings WebRoutingSettings => Settings.WebRouting; + protected IRequestHandlerSettings RequestHandlerSettings => Settings.RequestHandler; } } diff --git a/src/Umbraco.Tests/CoreThings/TryConvertToTests.cs b/src/Umbraco.Tests/CoreThings/TryConvertToTests.cs index 5723b51bab..fbb89b1c5d 100644 --- a/src/Umbraco.Tests/CoreThings/TryConvertToTests.cs +++ b/src/Umbraco.Tests/CoreThings/TryConvertToTests.cs @@ -14,7 +14,7 @@ namespace Umbraco.Tests.CoreThings { base.Compose(); - Composition.RegisterUnique(f => new DefaultShortStringHelper(f.GetInstance())); + Composition.RegisterUnique(f => new DefaultShortStringHelper(f.GetInstance())); } [Test] diff --git a/src/Umbraco.Tests/Misc/UriUtilityTests.cs b/src/Umbraco.Tests/Misc/UriUtilityTests.cs index 0d0f6db61e..a10d6e2179 100644 --- a/src/Umbraco.Tests/Misc/UriUtilityTests.cs +++ b/src/Umbraco.Tests/Misc/UriUtilityTests.cs @@ -77,15 +77,15 @@ namespace Umbraco.Tests.Misc { var globalConfig = Mock.Get(SettingsForTests.GenerateMockGlobalSettings()); - var settings = SettingsForTests.GenerateMockUmbracoSettings(); - var requestMock = Mock.Get(settings.RequestHandler); + var settings = SettingsForTests.GenerateMockRequestHandlerSettings(); + var requestMock = Mock.Get(settings); requestMock.Setup(x => x.AddTrailingSlash).Returns(trailingSlash); UriUtility.SetAppDomainAppVirtualPath("/"); var expectedUri = NewUri(expectedUrl); var sourceUri = NewUri(sourceUrl); - var resultUri = UriUtility.UriFromUmbraco(sourceUri, globalConfig.Object, settings.RequestHandler); + var resultUri = UriUtility.UriFromUmbraco(sourceUri, globalConfig.Object, settings); Assert.AreEqual(expectedUri.ToString(), resultUri.ToString()); } diff --git a/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueEditorTests.cs b/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueEditorTests.cs index a24b4cbafd..6301be051d 100644 --- a/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueEditorTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueEditorTests.cs @@ -28,7 +28,7 @@ namespace Umbraco.Tests.PropertyEditors var composition = new Composition(register, TestHelper.GetMockedTypeLoader(), Mock.Of(), ComponentTests.MockRuntimeState(RuntimeLevel.Run), TestHelper.GetConfigs(), TestHelper.IOHelper, AppCaches.NoCache); register.Register(_ - => new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()))); + => new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()))); Current.Factory = composition.CreateFactory(); } diff --git a/src/Umbraco.Tests/Routing/GetContentUrlsTests.cs b/src/Umbraco.Tests/Routing/GetContentUrlsTests.cs index 647f7d8813..c6793d20c8 100644 --- a/src/Umbraco.Tests/Routing/GetContentUrlsTests.cs +++ b/src/Umbraco.Tests/Routing/GetContentUrlsTests.cs @@ -77,11 +77,11 @@ namespace Umbraco.Tests.Routing content.Path = "-1,1046"; content.Published = true; - var umbracoSettings = Current.Configs.Settings(); + var umbracoSettings = Current.Configs.RequestHandler(); var umbContext = GetUmbracoContext("http://localhost:8000"); var umbracoContextAccessor = new TestUmbracoContextAccessor(umbContext); - var urlProvider = new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, TestObjects.GetGlobalSettings(), new SiteDomainHelper(), + var urlProvider = new DefaultUrlProvider(umbracoSettings, Logger, TestObjects.GetGlobalSettings(), new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = new UrlProvider( umbracoContextAccessor, @@ -122,12 +122,12 @@ namespace Umbraco.Tests.Routing child.Path = "-1,1046,1173"; child.Published = true; - var umbracoSettings = Current.Configs.Settings(); + var umbracoSettings = Current.Configs.RequestHandler(); var umbContext = GetUmbracoContext("http://localhost:8000"); var umbracoContextAccessor = new TestUmbracoContextAccessor(umbContext); - var urlProvider = new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, TestObjects.GetGlobalSettings(), new SiteDomainHelper(), umbracoContextAccessor, UriUtility); + var urlProvider = new DefaultUrlProvider(umbracoSettings, Logger, TestObjects.GetGlobalSettings(), new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = new UrlProvider( umbracoContextAccessor, TestHelper.WebRoutingSettings, diff --git a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs index 48436d6690..864b6d0c95 100644 --- a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs +++ b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs @@ -69,7 +69,7 @@ namespace Umbraco.Tests.Routing var umbracoApiControllerTypes = new UmbracoApiControllerTypeCollection(Composition.TypeLoader.GetUmbracoApiControllers()); Composition.RegisterUnique(umbracoApiControllerTypes); - Composition.RegisterUnique(_ => new DefaultShortStringHelper(SettingsForTests.GetDefaultUmbracoSettings())); + Composition.RegisterUnique(_ => new DefaultShortStringHelper(SettingsForTests.GenerateMockRequestHandlerSettings())); } public override void TearDown() diff --git a/src/Umbraco.Tests/Routing/UrlProviderTests.cs b/src/Umbraco.Tests/Routing/UrlProviderTests.cs index 9cd93ccfa6..b57c881404 100644 --- a/src/Umbraco.Tests/Routing/UrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/UrlProviderTests.cs @@ -48,15 +48,15 @@ namespace Umbraco.Tests.Routing var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - var umbracoSettings = Current.Configs.Settings(); + var requestHandlerSettings = SettingsForTests.GenerateMockRequestHandlerSettings(); var umbracoContext = GetUmbracoContext("/test", 1111, globalSettings: globalSettings.Object); var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var urlProvider = new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, globalSettings.Object, + var urlProvider = new DefaultUrlProvider(requestHandlerSettings, Logger, globalSettings.Object, new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); - var requestHandlerMock = Mock.Get(umbracoSettings.RequestHandler); + var requestHandlerMock = Mock.Get(requestHandlerSettings); requestHandlerMock.Setup(x => x.AddTrailingSlash).Returns(false);// (cached routes have none) var samples = new Dictionary { @@ -123,12 +123,12 @@ namespace Umbraco.Tests.Routing var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - var umbracoSettings = Current.Configs.Settings(); + var requestHandlerSettings = SettingsForTests.GenerateMockRequestHandlerSettings(); var umbracoContext = GetUmbracoContext("/test", 1111, globalSettings: globalSettings.Object); var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var urlProvider = new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, globalSettings.Object, + var urlProvider = new DefaultUrlProvider(requestHandlerSettings, Logger, globalSettings.Object, new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); @@ -152,12 +152,10 @@ namespace Umbraco.Tests.Routing var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(true); - var umbracoSettings = Current.Configs.Settings(); - - + var requestHandlerSettings = SettingsForTests.GenerateMockRequestHandlerSettings(); var umbracoContext = GetUmbracoContext("/test", 1111, globalSettings: globalSettings.Object); var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var urlProvider = new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, globalSettings.Object, + var urlProvider = new DefaultUrlProvider(requestHandlerSettings, Logger, globalSettings.Object, new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); @@ -173,7 +171,7 @@ namespace Umbraco.Tests.Routing var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - var umbracoSettings = Current.Configs.Settings(); + var requestHandlerSettings = SettingsForTests.GenerateMockRequestHandlerSettings(); var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Culture); @@ -200,7 +198,7 @@ namespace Umbraco.Tests.Routing globalSettings: globalSettings.Object, snapshotService: snapshotService.Object); var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var urlProvider = new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, globalSettings.Object, + var urlProvider = new DefaultUrlProvider(requestHandlerSettings, Logger, globalSettings.Object, new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); @@ -221,7 +219,7 @@ namespace Umbraco.Tests.Routing var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - var umbracoSettings = Current.Configs.Settings(); + var requestHandlerSettings = SettingsForTests.GenerateMockRequestHandlerSettings(); var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Culture); var publishedContent = new SolidPublishedContent(contentType) { Id = 1234 }; @@ -256,7 +254,7 @@ namespace Umbraco.Tests.Routing globalSettings: globalSettings.Object, snapshotService: snapshotService.Object); var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var urlProvider = new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, globalSettings.Object, + var urlProvider = new DefaultUrlProvider(requestHandlerSettings, Logger, globalSettings.Object, new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); @@ -277,7 +275,7 @@ namespace Umbraco.Tests.Routing var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - var umbracoSettings = Current.Configs.Settings(); + var requestHandlerSettings = SettingsForTests.GenerateMockRequestHandlerSettings(); var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Culture); var publishedContent = new SolidPublishedContent(contentType) { Id = 1234 }; @@ -311,7 +309,7 @@ namespace Umbraco.Tests.Routing globalSettings: globalSettings.Object, snapshotService: snapshotService.Object); var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var urlProvider = new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, globalSettings.Object, + var urlProvider = new DefaultUrlProvider(requestHandlerSettings, Logger, globalSettings.Object, new SiteDomainHelper(), umbracoContextAccessor, UriUtility); @@ -328,12 +326,12 @@ namespace Umbraco.Tests.Routing var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - var umbracoSettings = Current.Configs.Settings(); + var requestHandlerSettings = SettingsForTests.GenerateMockRequestHandlerSettings(); var umbracoContext = GetUmbracoContext("http://example.com/test", 1111, globalSettings: globalSettings.Object); var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var urlProvider = new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, globalSettings.Object, + var urlProvider = new DefaultUrlProvider(requestHandlerSettings, Logger, globalSettings.Object, new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); @@ -349,9 +347,9 @@ namespace Umbraco.Tests.Routing var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - var umbracoSettings = Current.Configs.Settings(); + var requestHandlerSettings = SettingsForTests.GenerateMockRequestHandlerSettings(); - var urlProvider = new DefaultUrlProvider(umbracoSettings.RequestHandler, Logger, globalSettings.Object, + var urlProvider = new DefaultUrlProvider(requestHandlerSettings, Logger, globalSettings.Object, new SiteDomainHelper(), UmbracoContextAccessor, UriUtility); var umbracoContext = GetUmbracoContext("http://example.com/test", 1111, globalSettings: globalSettings.Object); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); diff --git a/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs b/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs index 48e34b6be2..9232649c48 100644 --- a/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs @@ -178,14 +178,14 @@ namespace Umbraco.Tests.Routing [TestCase(10011, "https://domain1.com", false, "/1001-1/")] public void Get_Url_SimpleDomain(int nodeId, string currentUrl, bool absolute, string expected) { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); + var settings = SettingsForTests.GenerateMockRequestHandlerSettings(); var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains var umbracoContext = GetUmbracoContext("/test", 1111, globalSettings:globalSettings.Object); var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var urlProvider = new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, + var urlProvider = new DefaultUrlProvider(settings, Logger, globalSettings.Object, new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); @@ -212,14 +212,14 @@ namespace Umbraco.Tests.Routing [TestCase(10011, "https://domain1.com", false, "http://domain1.com/foo/1001-1/")] public void Get_Url_SimpleWithSchemeAndPath(int nodeId, string currentUrl, bool absolute, string expected) { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); + var settings = SettingsForTests.GenerateMockRequestHandlerSettings(); var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains var umbracoContext = GetUmbracoContext("/test", 1111, globalSettings:globalSettings.Object); var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var urlProvider = new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, + var urlProvider = new DefaultUrlProvider(settings, Logger, globalSettings.Object, new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); @@ -238,14 +238,14 @@ namespace Umbraco.Tests.Routing [TestCase(1002, "http://domain1.com", false, "/1002/")] public void Get_Url_DeepDomain(int nodeId, string currentUrl, bool absolute, string expected) { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); + var settings = SettingsForTests.GenerateMockRequestHandlerSettings(); var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains var umbracoContext = GetUmbracoContext("/test", 1111, globalSettings:globalSettings.Object); var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var urlProvider = new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, + var urlProvider = new DefaultUrlProvider(settings, Logger, globalSettings.Object, new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); @@ -270,14 +270,14 @@ namespace Umbraco.Tests.Routing [TestCase(100321, "http://domain3.com", false, "/fr/1003-2-1/")] public void Get_Url_NestedDomains(int nodeId, string currentUrl, bool absolute, string expected) { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); + var settings = SettingsForTests.GenerateMockRequestHandlerSettings(); var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains var umbracoContext = GetUmbracoContext("/test", 1111, globalSettings:globalSettings.Object); var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var urlProvider = new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, + var urlProvider = new DefaultUrlProvider(settings, Logger, globalSettings.Object, new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); @@ -292,14 +292,14 @@ namespace Umbraco.Tests.Routing [Test] public void Get_Url_DomainsAndCache() { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); + var settings = SettingsForTests.GenerateMockRequestHandlerSettings(); var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains var umbracoContext = GetUmbracoContext("/test", 1111, globalSettings:globalSettings.Object); var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var urlProvider = new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, + var urlProvider = new DefaultUrlProvider(settings, Logger, globalSettings.Object, new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); @@ -356,14 +356,14 @@ namespace Umbraco.Tests.Routing [Test] public void Get_Url_Relative_Or_Absolute() { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); + var settings = SettingsForTests.GenerateMockRequestHandlerSettings(); var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains var umbracoContext = GetUmbracoContext("http://domain1.com/test", 1111, globalSettings:globalSettings.Object); var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var urlProvider = new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, + var urlProvider = new DefaultUrlProvider(settings, Logger, globalSettings.Object, new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); @@ -381,14 +381,14 @@ namespace Umbraco.Tests.Routing [Test] public void Get_Url_Alternate() { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); + var settings = SettingsForTests.GenerateMockRequestHandlerSettings(); var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains var umbracoContext = GetUmbracoContext("http://domain1.com/en/test", 1111, globalSettings:globalSettings.Object); var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var urlProvider = new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, + var urlProvider = new DefaultUrlProvider(settings, Logger, globalSettings.Object, new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); diff --git a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs index e0e5564f14..44f1f252b1 100644 --- a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs +++ b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs @@ -36,7 +36,7 @@ namespace Umbraco.Tests.Routing var globalSettings = Mock.Get(Factory.GetInstance()); //this will modify the IGlobalSettings instance stored in the container globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - var settings = SettingsForTests.GenerateMockUmbracoSettings(); + var settings = SettingsForTests.GenerateMockRequestHandlerSettings(); SetDomains1(); @@ -45,7 +45,7 @@ namespace Umbraco.Tests.Routing // get the nice url for 100111 var umbracoContext = GetUmbracoContext(url, 9999, globalSettings:globalSettings.Object); var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var urlProvider = new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, + var urlProvider = new DefaultUrlProvider(settings, Logger, globalSettings.Object, new SiteDomainHelper(), umbracoContextAccessor, UriUtility); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); diff --git a/src/Umbraco.Tests/Strings/CmsHelperCasingTests.cs b/src/Umbraco.Tests/Strings/CmsHelperCasingTests.cs index ea2041cd9c..84ffa3b696 100644 --- a/src/Umbraco.Tests/Strings/CmsHelperCasingTests.cs +++ b/src/Umbraco.Tests/Strings/CmsHelperCasingTests.cs @@ -30,7 +30,7 @@ namespace Umbraco.Tests.Strings [TestCase("WhoIsNumber6InTheVillage", "Who Is Number6 In The Village")] // issue is fixed public void CompatibleDefaultReplacement(string input, string expected) { - var helper = new DefaultShortStringHelper(SettingsForTests.GetDefaultUmbracoSettings()); + var helper = new DefaultShortStringHelper(SettingsForTests.GenerateMockRequestHandlerSettings()); var output = input.Length < 2 ? input : helper.SplitPascalCasing(input, ' ').ToFirstUpperInvariant(); Assert.AreEqual(expected, output); } diff --git a/src/Umbraco.Tests/Strings/DefaultShortStringHelperTests.cs b/src/Umbraco.Tests/Strings/DefaultShortStringHelperTests.cs index 5fd5710a79..109142f51b 100644 --- a/src/Umbraco.Tests/Strings/DefaultShortStringHelperTests.cs +++ b/src/Umbraco.Tests/Strings/DefaultShortStringHelperTests.cs @@ -26,7 +26,7 @@ namespace Umbraco.Tests.Strings // NOTE pre-filters runs _before_ Recode takes place // so there still may be utf8 chars even though you want ascii - _helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + _helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.FileName, new DefaultShortStringHelperConfig.Config { //PreFilter = ClearFileChars, // done in IsTerm @@ -94,8 +94,8 @@ namespace Umbraco.Tests.Strings [Test] public void U4_4056() { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); - var contentMock = Mock.Get(settings.RequestHandler); + var settings = SettingsForTests.GenerateMockRequestHandlerSettings(); + var contentMock = Mock.Get(settings); contentMock.Setup(x => x.CharCollection).Returns(Enumerable.Empty()); contentMock.Setup(x => x.ConvertUrlsToAscii).Returns(false); @@ -119,8 +119,8 @@ namespace Umbraco.Tests.Strings [Test] public void U4_4056_TryAscii() { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); - var contentMock = Mock.Get(settings.RequestHandler); + var settings = SettingsForTests.GenerateMockRequestHandlerSettings(); + var contentMock = Mock.Get(settings); contentMock.Setup(x => x.CharCollection).Returns(Enumerable.Empty()); contentMock.Setup(x => x.ConvertUrlsToAscii).Returns(false); @@ -145,7 +145,7 @@ namespace Umbraco.Tests.Strings [Test] public void CleanStringUnderscoreInTerm() { - var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { // underscore is accepted within terms @@ -155,7 +155,7 @@ namespace Umbraco.Tests.Strings })); Assert.AreEqual("foo_bar*nil", helper.CleanString("foo_bar nil", CleanStringType.Alias)); - helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { // underscore is not accepted within terms @@ -169,7 +169,7 @@ namespace Umbraco.Tests.Strings [Test] public void CleanStringLeadingChars() { - var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { // letters and digits are valid leading chars @@ -179,7 +179,7 @@ namespace Umbraco.Tests.Strings })); Assert.AreEqual("0123foo*bar*543*nil*321", helper.CleanString("0123foo_bar 543 nil 321", CleanStringType.Alias)); - helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { // only letters are valid leading chars @@ -190,14 +190,14 @@ namespace Umbraco.Tests.Strings Assert.AreEqual("foo*bar*543*nil*321", helper.CleanString("0123foo_bar 543 nil 321", CleanStringType.Alias)); Assert.AreEqual("foo*bar*543*nil*321", helper.CleanString("0123 foo_bar 543 nil 321", CleanStringType.Alias)); - helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings())); + helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings())); Assert.AreEqual("child2", helper.CleanStringForSafeAlias("1child2")); } [Test] public void CleanStringTermOnUpper() { - var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -207,7 +207,7 @@ namespace Umbraco.Tests.Strings })); Assert.AreEqual("foo*Bar", helper.CleanString("fooBar", CleanStringType.Alias)); - helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -221,7 +221,7 @@ namespace Umbraco.Tests.Strings [Test] public void CleanStringAcronymOnNonUpper() { - var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -234,7 +234,7 @@ namespace Umbraco.Tests.Strings Assert.AreEqual("foo*BAnil", helper.CleanString("foo BAnil", CleanStringType.Alias)); Assert.AreEqual("foo*Bnil", helper.CleanString("foo Bnil", CleanStringType.Alias)); - helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -251,7 +251,7 @@ namespace Umbraco.Tests.Strings [Test] public void CleanStringGreedyAcronyms() { - var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -264,7 +264,7 @@ namespace Umbraco.Tests.Strings Assert.AreEqual("foo*BA*nil", helper.CleanString("foo BAnil", CleanStringType.Alias)); Assert.AreEqual("foo*Bnil", helper.CleanString("foo Bnil", CleanStringType.Alias)); - helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -281,7 +281,7 @@ namespace Umbraco.Tests.Strings [Test] public void CleanStringWhiteSpace() { - var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -294,7 +294,7 @@ namespace Umbraco.Tests.Strings [Test] public void CleanStringSeparator() { - var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -302,7 +302,7 @@ namespace Umbraco.Tests.Strings })); Assert.AreEqual("foo*bar", helper.CleanString("foo bar", CleanStringType.Alias)); - helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -310,14 +310,14 @@ namespace Umbraco.Tests.Strings })); Assert.AreEqual("foo bar", helper.CleanString("foo bar", CleanStringType.Alias)); - helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged })); Assert.AreEqual("foobar", helper.CleanString("foo bar", CleanStringType.Alias)); - helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -329,7 +329,7 @@ namespace Umbraco.Tests.Strings [Test] public void CleanStringSymbols() { - var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -383,7 +383,7 @@ namespace Umbraco.Tests.Strings [Test] public void CleanStringEncoding() { - var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, @@ -392,7 +392,7 @@ namespace Umbraco.Tests.Strings Assert.AreEqual("中文测试", helper.CleanString("中文测试", CleanStringType.Alias)); Assert.AreEqual("léger*中文测试*ZÔRG", helper.CleanString("léger 中文测试 ZÔRG", CleanStringType.Alias)); - helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Ascii | CleanStringType.Unchanged, @@ -405,8 +405,8 @@ namespace Umbraco.Tests.Strings [Test] public void CleanStringDefaultConfig() { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); - var contentMock = Mock.Get(settings.RequestHandler); + var settings = SettingsForTests.GenerateMockRequestHandlerSettings(); + var contentMock = Mock.Get(settings); contentMock.Setup(x => x.CharCollection).Returns(Enumerable.Empty()); contentMock.Setup(x => x.ConvertUrlsToAscii).Returns(false); @@ -431,7 +431,7 @@ namespace Umbraco.Tests.Strings [Test] public void CleanStringCasing() { - var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GetDefaultUmbracoSettings()) + var helper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(SettingsForTests.GenerateMockRequestHandlerSettings()) .WithConfig(CleanStringType.Alias, new DefaultShortStringHelperConfig.Config { StringType = CleanStringType.Utf8 | CleanStringType.Unchanged, diff --git a/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs b/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs index 60b4105490..f3d03e1ea4 100644 --- a/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs +++ b/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs @@ -1,5 +1,6 @@ using System.IO; using System.Configuration; +using System.Linq; using Moq; using Umbraco.Core; using Umbraco.Core.Composing; @@ -45,7 +46,7 @@ namespace Umbraco.Tests.TestHelpers var content = new Mock(); var security = new Mock(); - var requestHandler = new Mock(); + var requestHandler = new Mock(); var logging = new Mock(); var routing = new Mock(); @@ -56,13 +57,11 @@ namespace Umbraco.Tests.TestHelpers settings.Setup(x => x.Content).Returns(content.Object); settings.Setup(x => x.Security).Returns(security.Object); - settings.Setup(x => x.RequestHandler).Returns(requestHandler.Object); //Now configure some defaults - the defaults in the config section classes do NOT pertain to the mocked data!! settings.Setup(x => x.Content.ImageAutoFillProperties).Returns(ContentImagingElement.GetDefaultImageAutoFillProperties()); settings.Setup(x => x.Content.ImageFileTypes).Returns(ContentImagingElement.GetDefaultImageFileTypes()); - settings.Setup(x => x.RequestHandler.AddTrailingSlash).Returns(true); - settings.Setup(x => x.RequestHandler.CharCollection).Returns(RequestHandlerElement.GetDefaultCharReplacements()); + return settings.Object; } @@ -168,11 +167,23 @@ namespace Umbraco.Tests.TestHelpers { var mock = new Mock(); - mock.Setup(x => x.TrySkipIisCustomErrors).Returns(false); + mock.Setup(x => x.DisableRedirectUrlTracking).Returns(false); mock.Setup(x => x.InternalRedirectPreservesTemplate).Returns(false); mock.Setup(x => x.UrlProviderMode).Returns(UrlMode.Auto.ToString()); return mock.Object; } + + public static IRequestHandlerSettings GenerateMockRequestHandlerSettings() + { + var mock = new Mock(); + + mock.Setup(x => x.AddTrailingSlash).Returns(true); + mock.Setup(x => x.ConvertUrlsToAscii).Returns(false); + mock.Setup(x => x.TryConvertUrlsToAscii).Returns(false); + mock.Setup(x => x.CharCollection).Returns(RequestHandlerElement.GetDefaultCharReplacements); + + return mock.Object; + } } } diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index 854242d36b..6b177c16cc 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -414,6 +414,9 @@ namespace Umbraco.Tests.Testing Composition.Configs.Add(SettingsForTests.GetDefaultUmbracoSettings); Composition.Configs.Add(SettingsForTests.GetDefaultGlobalSettings); Composition.Configs.Add(SettingsForTests.GetDefaultHostingSettings); + Composition.Configs.Add(SettingsForTests.GenerateMockRequestHandlerSettings); + Composition.Configs.Add(SettingsForTests.GenerateMockWebRoutingSettings); + //Composition.Configs.Add(() => new DefaultUserPasswordConfig()); } diff --git a/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs b/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs index 2266bd0104..98c4dc96ca 100644 --- a/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs @@ -33,7 +33,7 @@ namespace Umbraco.Tests.UmbracoExamine { base.Compose(); - Composition.RegisterUnique(_ => new DefaultShortStringHelper(SettingsForTests.GetDefaultUmbracoSettings())); + Composition.RegisterUnique(_ => new DefaultShortStringHelper(SettingsForTests.GenerateMockRequestHandlerSettings())); } } } From cb0994a9294fca034d53424e1cee2f0d6963535c Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Thu, 12 Mar 2020 14:36:25 +0100 Subject: [PATCH 62/69] Cleaned up config for Security settings --- src/Umbraco.Configuration/ConfigsFactory.cs | 10 +++-- .../MemberPasswordConfigurationSettings.cs | 16 +++++++ .../Implementations/SecuritySettings.cs | 14 +++++++ .../UserPasswordConfigurationSettings.cs | 15 +++++++ .../MemberPasswordConfigurationElement.cs | 2 +- .../UmbracoSettings/SecurityElement.cs | 18 ++++---- .../UmbracoSettings/UmbracoSettingsSection.cs | 2 - .../UserPasswordConfigurationElement.cs | 2 +- .../Configuration/ConfigsExtensions.cs | 27 ++++-------- .../MemberPasswordConfiguration.cs | 6 +-- .../Configuration/PasswordConfiguration.cs | 22 +++++----- .../IMemberPasswordConfigurationSection.cs | 6 --- ...ecuritySection.cs => ISecuritySettings.cs} | 8 +--- .../IUmbracoSettingsSection.cs | 2 - .../IUserPasswordConfigurationSection.cs | 6 --- .../UserPasswordConfiguration.cs | 6 +-- .../CompositionExtensions/Configuration.cs | 1 - .../Models/ContentEditing/UserInvite.cs | 2 +- .../UmbracoSettings/SecurityElementTests.cs | 42 +++++++++---------- .../UmbracoSettings/UmbracoSettingsTests.cs | 4 ++ .../TestHelpers/SettingsForTests.cs | 31 +++++++++----- src/Umbraco.Tests/Testing/UmbracoTestBase.cs | 3 ++ .../AuthenticationControllerTests.cs | 2 +- .../Web/Controllers/UsersControllerTests.cs | 12 ++++-- .../Umbraco/Views/AuthorizeUpgrade.cshtml | 2 +- .../Umbraco/Views/Default.cshtml | 2 +- .../Editors/AuthenticationController.cs | 8 ++-- .../Editors/BackOfficeController.cs | 13 +++--- src/Umbraco.Web/Editors/BackOfficeModel.cs | 7 +++- .../Editors/BackOfficePreviewModel.cs | 4 +- .../Editors/BackOfficeServerVariables.cs | 11 +++-- src/Umbraco.Web/Editors/PreviewController.cs | 7 +++- src/Umbraco.Web/Editors/UsersController.cs | 15 ++++--- .../HtmlHelperBackOfficeExtensions.cs | 4 +- .../Install/InstallSteps/NewInstallStep.cs | 8 ++-- .../Security/AppBuilderExtensions.cs | 35 ++++++++-------- .../Security/AuthenticationExtensions.cs | 6 +-- .../BackOfficeCookieAuthenticationProvider.cs | 8 ++-- .../Security/GetUserSecondsMiddleWare.cs | 4 +- .../UmbracoBackOfficeCookieAuthOptions.cs | 6 +-- src/Umbraco.Web/UmbracoDefaultOwinStartup.cs | 5 ++- 41 files changed, 228 insertions(+), 176 deletions(-) create mode 100644 src/Umbraco.Configuration/Implementations/MemberPasswordConfigurationSettings.cs create mode 100644 src/Umbraco.Configuration/Implementations/SecuritySettings.cs create mode 100644 src/Umbraco.Configuration/Implementations/UserPasswordConfigurationSettings.cs delete mode 100644 src/Umbraco.Core/Configuration/UmbracoSettings/IMemberPasswordConfigurationSection.cs rename src/Umbraco.Core/Configuration/UmbracoSettings/{ISecuritySection.cs => ISecuritySettings.cs} (78%) delete mode 100644 src/Umbraco.Core/Configuration/UmbracoSettings/IUserPasswordConfigurationSection.cs diff --git a/src/Umbraco.Configuration/ConfigsFactory.cs b/src/Umbraco.Configuration/ConfigsFactory.cs index 8930edf36b..d9305e0ee8 100644 --- a/src/Umbraco.Configuration/ConfigsFactory.cs +++ b/src/Umbraco.Configuration/ConfigsFactory.cs @@ -24,6 +24,9 @@ namespace Umbraco.Core.Configuration public IKeepAliveSettings KeepAliveSettings { get; } = new KeepAliveSettings(); public IWebRoutingSettings WebRoutingSettings { get; } = new WebRoutingSettings(); public IRequestHandlerSettings RequestHandlerSettings { get; } = new RequestHandlerSettings(); + public ISecuritySettings SecuritySettings { get; } = new SecuritySettings(); + public IUserPasswordConfiguration UserPasswordConfigurationSettings { get; } = new UserPasswordConfigurationSettings(); + public IMemberPasswordConfiguration MemberPasswordConfigurationSettings { get; } = new MemberPasswordConfigurationSettings(); public IUmbracoSettingsSection UmbracoSettings { get; } @@ -36,10 +39,6 @@ namespace Umbraco.Core.Configuration configs.Add("umbracoConfiguration/settings"); configs.Add("umbracoConfiguration/HealthChecks"); - // Password configuration is held within IUmbracoSettingsSection from umbracoConfiguration/settings but we'll add explicitly - // so it can be independently retrieved in classes that need it. - configs.AddPasswordConfigurations(); - configs.Add(() => CoreDebug); configs.Add(() => MachineKeyConfig); configs.Add(() => new ConnectionStrings(ioHelper)); @@ -58,6 +57,9 @@ namespace Umbraco.Core.Configuration configs.Add(() => KeepAliveSettings); configs.Add(() => WebRoutingSettings); configs.Add(() => RequestHandlerSettings); + configs.Add(() => SecuritySettings); + configs.Add(() => UserPasswordConfigurationSettings); + configs.Add(() => MemberPasswordConfigurationSettings); configs.AddCoreConfigs(ioHelper); return configs; diff --git a/src/Umbraco.Configuration/Implementations/MemberPasswordConfigurationSettings.cs b/src/Umbraco.Configuration/Implementations/MemberPasswordConfigurationSettings.cs new file mode 100644 index 0000000000..e42b02de73 --- /dev/null +++ b/src/Umbraco.Configuration/Implementations/MemberPasswordConfigurationSettings.cs @@ -0,0 +1,16 @@ +using Umbraco.Core.Configuration; + +namespace Umbraco.Configuration.Implementations +{ + internal class MemberPasswordConfigurationSettings : ConfigurationManagerConfigBase, IMemberPasswordConfiguration + { + public int RequiredLength => UmbracoSettingsSection.Security.UserPasswordConfiguration.RequiredLength; + public bool RequireNonLetterOrDigit => UmbracoSettingsSection.Security.UserPasswordConfiguration.RequireNonLetterOrDigit; + public bool RequireDigit => UmbracoSettingsSection.Security.UserPasswordConfiguration.RequireDigit; + public bool RequireLowercase=> UmbracoSettingsSection.Security.UserPasswordConfiguration.RequireLowercase; + public bool RequireUppercase=> UmbracoSettingsSection.Security.UserPasswordConfiguration.RequireUppercase; + public bool UseLegacyEncoding=> UmbracoSettingsSection.Security.UserPasswordConfiguration.UseLegacyEncoding; + public string HashAlgorithmType=> UmbracoSettingsSection.Security.UserPasswordConfiguration.HashAlgorithmType; + public int MaxFailedAccessAttemptsBeforeLockout => UmbracoSettingsSection.Security.UserPasswordConfiguration.MaxFailedAccessAttemptsBeforeLockout; + } +} diff --git a/src/Umbraco.Configuration/Implementations/SecuritySettings.cs b/src/Umbraco.Configuration/Implementations/SecuritySettings.cs new file mode 100644 index 0000000000..b7e39b0608 --- /dev/null +++ b/src/Umbraco.Configuration/Implementations/SecuritySettings.cs @@ -0,0 +1,14 @@ +using Umbraco.Core.Configuration.UmbracoSettings; + +namespace Umbraco.Configuration.Implementations +{ + internal class SecuritySettings : ConfigurationManagerConfigBase, ISecuritySettings + { + public bool KeepUserLoggedIn => UmbracoSettingsSection.Security.KeepUserLoggedIn; + public bool HideDisabledUsersInBackoffice => UmbracoSettingsSection.Security.HideDisabledUsersInBackoffice; + public bool AllowPasswordReset => UmbracoSettingsSection.Security.AllowPasswordReset; + public string AuthCookieName => UmbracoSettingsSection.Security.AuthCookieName; + public string AuthCookieDomain => UmbracoSettingsSection.Security.AuthCookieDomain; + public bool UsernameIsEmail => UmbracoSettingsSection.Security.UsernameIsEmail; + } +} diff --git a/src/Umbraco.Configuration/Implementations/UserPasswordConfigurationSettings.cs b/src/Umbraco.Configuration/Implementations/UserPasswordConfigurationSettings.cs new file mode 100644 index 0000000000..51dd645c42 --- /dev/null +++ b/src/Umbraco.Configuration/Implementations/UserPasswordConfigurationSettings.cs @@ -0,0 +1,15 @@ +using Umbraco.Core.Configuration; +namespace Umbraco.Configuration.Implementations +{ + internal class UserPasswordConfigurationSettings : ConfigurationManagerConfigBase, IUserPasswordConfiguration + { + public int RequiredLength => UmbracoSettingsSection.Security.UserPasswordConfiguration.RequiredLength; + public bool RequireNonLetterOrDigit => UmbracoSettingsSection.Security.UserPasswordConfiguration.RequireNonLetterOrDigit; + public bool RequireDigit => UmbracoSettingsSection.Security.UserPasswordConfiguration.RequireDigit; + public bool RequireLowercase=> UmbracoSettingsSection.Security.UserPasswordConfiguration.RequireLowercase; + public bool RequireUppercase=> UmbracoSettingsSection.Security.UserPasswordConfiguration.RequireUppercase; + public bool UseLegacyEncoding=> UmbracoSettingsSection.Security.UserPasswordConfiguration.UseLegacyEncoding; + public string HashAlgorithmType=> UmbracoSettingsSection.Security.UserPasswordConfiguration.HashAlgorithmType; + public int MaxFailedAccessAttemptsBeforeLockout => UmbracoSettingsSection.Security.UserPasswordConfiguration.MaxFailedAccessAttemptsBeforeLockout; + } +} diff --git a/src/Umbraco.Configuration/UmbracoSettings/MemberPasswordConfigurationElement.cs b/src/Umbraco.Configuration/UmbracoSettings/MemberPasswordConfigurationElement.cs index 93c7c20159..92cd112630 100644 --- a/src/Umbraco.Configuration/UmbracoSettings/MemberPasswordConfigurationElement.cs +++ b/src/Umbraco.Configuration/UmbracoSettings/MemberPasswordConfigurationElement.cs @@ -1,6 +1,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - internal class MemberPasswordConfigurationElement : PasswordConfigurationElement, IMemberPasswordConfigurationSection + internal class MemberPasswordConfigurationElement : PasswordConfigurationElement, IMemberPasswordConfiguration { } } diff --git a/src/Umbraco.Configuration/UmbracoSettings/SecurityElement.cs b/src/Umbraco.Configuration/UmbracoSettings/SecurityElement.cs index 82012cfd0f..aec6809298 100644 --- a/src/Umbraco.Configuration/UmbracoSettings/SecurityElement.cs +++ b/src/Umbraco.Configuration/UmbracoSettings/SecurityElement.cs @@ -2,7 +2,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - internal class SecurityElement : UmbracoConfigurationElement, ISecuritySection + internal class SecurityElement : UmbracoConfigurationElement, ISecuritySettings { [ConfigurationProperty("keepUserLoggedIn")] internal InnerTextConfigurationElement KeepUserLoggedIn => GetOptionalTextElement("keepUserLoggedIn", true); @@ -38,14 +38,14 @@ namespace Umbraco.Core.Configuration.UmbracoSettings [ConfigurationProperty("memberPasswordConfiguration")] public MemberPasswordConfigurationElement MemberPasswordConfiguration => (MemberPasswordConfigurationElement)this["memberPasswordConfiguration"]; - bool ISecuritySection.KeepUserLoggedIn => KeepUserLoggedIn; + bool ISecuritySettings.KeepUserLoggedIn => KeepUserLoggedIn; - bool ISecuritySection.HideDisabledUsersInBackoffice => HideDisabledUsersInBackoffice; + bool ISecuritySettings.HideDisabledUsersInBackoffice => HideDisabledUsersInBackoffice; /// /// Used to enable/disable the forgot password functionality on the back office login screen /// - bool ISecuritySection.AllowPasswordReset => AllowPasswordReset; + bool ISecuritySettings.AllowPasswordReset => AllowPasswordReset; /// /// A boolean indicating that by default the email address will be the username @@ -54,14 +54,10 @@ namespace Umbraco.Core.Configuration.UmbracoSettings /// Even if this is true and the username is different from the email in the database, the username field will still be shown. /// When this is false, the username and email fields will be shown in the user section. /// - bool ISecuritySection.UsernameIsEmail => UsernameIsEmail; + bool ISecuritySettings.UsernameIsEmail => UsernameIsEmail; - string ISecuritySection.AuthCookieName => AuthCookieName; + string ISecuritySettings.AuthCookieName => AuthCookieName; - string ISecuritySection.AuthCookieDomain => AuthCookieDomain; - - IUserPasswordConfigurationSection ISecuritySection.UserPasswordConfiguration => UserPasswordConfiguration; - - IMemberPasswordConfigurationSection ISecuritySection.MemberPasswordConfiguration => MemberPasswordConfiguration; + string ISecuritySettings.AuthCookieDomain => AuthCookieDomain; } } diff --git a/src/Umbraco.Configuration/UmbracoSettings/UmbracoSettingsSection.cs b/src/Umbraco.Configuration/UmbracoSettings/UmbracoSettingsSection.cs index 2b980d133c..4860d3138e 100644 --- a/src/Umbraco.Configuration/UmbracoSettings/UmbracoSettingsSection.cs +++ b/src/Umbraco.Configuration/UmbracoSettings/UmbracoSettingsSection.cs @@ -26,7 +26,5 @@ namespace Umbraco.Core.Configuration.UmbracoSettings internal KeepAliveElement KeepAlive => (KeepAliveElement)this["keepAlive"]; IContentSection IUmbracoSettingsSection.Content => Content; - - ISecuritySection IUmbracoSettingsSection.Security => Security; } } diff --git a/src/Umbraco.Configuration/UmbracoSettings/UserPasswordConfigurationElement.cs b/src/Umbraco.Configuration/UmbracoSettings/UserPasswordConfigurationElement.cs index 8128f3d8e7..a1d2aa8842 100644 --- a/src/Umbraco.Configuration/UmbracoSettings/UserPasswordConfigurationElement.cs +++ b/src/Umbraco.Configuration/UmbracoSettings/UserPasswordConfigurationElement.cs @@ -1,6 +1,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - internal class UserPasswordConfigurationElement : PasswordConfigurationElement, IUserPasswordConfigurationSection + internal class UserPasswordConfigurationElement : PasswordConfigurationElement, IUserPasswordConfiguration { } } diff --git a/src/Umbraco.Core/Configuration/ConfigsExtensions.cs b/src/Umbraco.Core/Configuration/ConfigsExtensions.cs index b800b42454..6d4c8e1b91 100644 --- a/src/Umbraco.Core/Configuration/ConfigsExtensions.cs +++ b/src/Umbraco.Core/Configuration/ConfigsExtensions.cs @@ -28,6 +28,15 @@ namespace Umbraco.Core public static IUmbracoSettingsSection Settings(this Configs configs) => configs.GetConfig(); + public static ISecuritySettings Security(this Configs configs) + => configs.GetConfig(); + + + public static IUserPasswordConfiguration UserPasswordConfiguration(this Configs configs) + => configs.GetConfig(); + public static IMemberPasswordConfiguration MemberPasswordConfiguration(this Configs configs) + => configs.GetConfig(); + public static IRequestHandlerSettings RequestHandler(this Configs configs) => configs.GetConfig(); @@ -43,24 +52,6 @@ namespace Umbraco.Core public static ICoreDebug CoreDebug(this Configs configs) => configs.GetConfig(); - public static IUserPasswordConfiguration UserPasswordConfiguration(this Configs configs) - => configs.GetConfig(); - - public static IMemberPasswordConfiguration MemberPasswordConfiguration(this Configs configs) - => configs.GetConfig(); - - public static void AddPasswordConfigurations(this Configs configs) - { - configs.Add(() => - { - return new UserPasswordConfiguration(configs.Settings().Security.UserPasswordConfiguration); - }); - configs.Add(() => - { - return new MemberPasswordConfiguration(configs.Settings().Security.MemberPasswordConfiguration); - }); - } - public static void AddCoreConfigs(this Configs configs, IIOHelper ioHelper) { var configDir = new DirectoryInfo(ioHelper.MapPath(Constants.SystemDirectories.Config)); diff --git a/src/Umbraco.Core/Configuration/MemberPasswordConfiguration.cs b/src/Umbraco.Core/Configuration/MemberPasswordConfiguration.cs index 58c907c31f..8e7cd97f35 100644 --- a/src/Umbraco.Core/Configuration/MemberPasswordConfiguration.cs +++ b/src/Umbraco.Core/Configuration/MemberPasswordConfiguration.cs @@ -7,9 +7,9 @@ namespace Umbraco.Core.Configuration /// public class MemberPasswordConfiguration : PasswordConfiguration, IMemberPasswordConfiguration { - public MemberPasswordConfiguration(IMemberPasswordConfigurationSection configSection) - : base(configSection) - { + public MemberPasswordConfiguration(IMemberPasswordConfiguration configSettings) + : base(configSettings) + { } } } diff --git a/src/Umbraco.Core/Configuration/PasswordConfiguration.cs b/src/Umbraco.Core/Configuration/PasswordConfiguration.cs index 9edf1a462e..6827695b35 100644 --- a/src/Umbraco.Core/Configuration/PasswordConfiguration.cs +++ b/src/Umbraco.Core/Configuration/PasswordConfiguration.cs @@ -5,21 +5,21 @@ namespace Umbraco.Core.Configuration { public abstract class PasswordConfiguration : IPasswordConfiguration { - protected PasswordConfiguration(IPasswordConfigurationSection configSection) + protected PasswordConfiguration(IPasswordConfiguration configSettings) { - if (configSection == null) + if (configSettings == null) { - throw new ArgumentNullException(nameof(configSection)); + throw new ArgumentNullException(nameof(configSettings)); } - RequiredLength = configSection.RequiredLength; - RequireNonLetterOrDigit = configSection.RequireNonLetterOrDigit; - RequireDigit = configSection.RequireDigit; - RequireLowercase = configSection.RequireLowercase; - RequireUppercase = configSection.RequireUppercase; - UseLegacyEncoding = configSection.UseLegacyEncoding; - HashAlgorithmType = configSection.HashAlgorithmType; - MaxFailedAccessAttemptsBeforeLockout = configSection.MaxFailedAccessAttemptsBeforeLockout; + RequiredLength = configSettings.RequiredLength; + RequireNonLetterOrDigit = configSettings.RequireNonLetterOrDigit; + RequireDigit = configSettings.RequireDigit; + RequireLowercase = configSettings.RequireLowercase; + RequireUppercase = configSettings.RequireUppercase; + UseLegacyEncoding = configSettings.UseLegacyEncoding; + HashAlgorithmType = configSettings.HashAlgorithmType; + MaxFailedAccessAttemptsBeforeLockout = configSettings.MaxFailedAccessAttemptsBeforeLockout; } public int RequiredLength { get; } diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IMemberPasswordConfigurationSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IMemberPasswordConfigurationSection.cs deleted file mode 100644 index cbbb933857..0000000000 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IMemberPasswordConfigurationSection.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IMemberPasswordConfigurationSection : IPasswordConfigurationSection - { - } -} diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ISecuritySection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ISecuritySettings.cs similarity index 78% rename from src/Umbraco.Core/Configuration/UmbracoSettings/ISecuritySection.cs rename to src/Umbraco.Core/Configuration/UmbracoSettings/ISecuritySettings.cs index a6ed188713..6ab520fefd 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/ISecuritySection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ISecuritySettings.cs @@ -1,9 +1,9 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - public interface ISecuritySection : IUmbracoConfigurationSection + public interface ISecuritySettings : IUmbracoConfigurationSection { bool KeepUserLoggedIn { get; } - + bool HideDisabledUsersInBackoffice { get; } /// @@ -23,9 +23,5 @@ /// When this is false, the username and email fields will be shown in the user section. /// bool UsernameIsEmail { get; } - - IUserPasswordConfigurationSection UserPasswordConfiguration { get; } - - IMemberPasswordConfigurationSection MemberPasswordConfiguration { get; } } } diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs index 69715f8e46..3db70e6d42 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs @@ -5,7 +5,5 @@ namespace Umbraco.Core.Configuration.UmbracoSettings public interface IUmbracoSettingsSection : IUmbracoConfigurationSection { IContentSection Content { get; } - - ISecuritySection Security { get; } } } diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IUserPasswordConfigurationSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IUserPasswordConfigurationSection.cs deleted file mode 100644 index d80dd2b7e5..0000000000 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IUserPasswordConfigurationSection.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IUserPasswordConfigurationSection : IPasswordConfigurationSection - { - } -} diff --git a/src/Umbraco.Core/Configuration/UserPasswordConfiguration.cs b/src/Umbraco.Core/Configuration/UserPasswordConfiguration.cs index 4cfee5280b..07e6603cee 100644 --- a/src/Umbraco.Core/Configuration/UserPasswordConfiguration.cs +++ b/src/Umbraco.Core/Configuration/UserPasswordConfiguration.cs @@ -7,9 +7,9 @@ namespace Umbraco.Core.Configuration /// public class UserPasswordConfiguration : PasswordConfiguration, IUserPasswordConfiguration { - public UserPasswordConfiguration(IUserPasswordConfigurationSection configSection) - : base(configSection) - { + public UserPasswordConfiguration(IUserPasswordConfiguration configSettings) + : base(configSettings) + { } } } diff --git a/src/Umbraco.Infrastructure/Composing/CompositionExtensions/Configuration.cs b/src/Umbraco.Infrastructure/Composing/CompositionExtensions/Configuration.cs index 48e43156ae..e1bc079432 100644 --- a/src/Umbraco.Infrastructure/Composing/CompositionExtensions/Configuration.cs +++ b/src/Umbraco.Infrastructure/Composing/CompositionExtensions/Configuration.cs @@ -15,7 +15,6 @@ namespace Umbraco.Core.Composing.CompositionExtensions // register others composition.RegisterUnique(factory => factory.GetInstance().Content); - composition.RegisterUnique(factory => factory.GetInstance().Security); return composition; } diff --git a/src/Umbraco.Infrastructure/Models/ContentEditing/UserInvite.cs b/src/Umbraco.Infrastructure/Models/ContentEditing/UserInvite.cs index f1c6cf6c04..06e4d0748c 100644 --- a/src/Umbraco.Infrastructure/Models/ContentEditing/UserInvite.cs +++ b/src/Umbraco.Infrastructure/Models/ContentEditing/UserInvite.cs @@ -33,7 +33,7 @@ namespace Umbraco.Web.Models.ContentEditing if (UserGroups.Any() == false) yield return new ValidationResult("A user must be assigned to at least one group", new[] { nameof(UserGroups) }); - if (Current.Configs.Settings().Security.UsernameIsEmail == false && Username.IsNullOrWhiteSpace()) + if (Current.Configs.Security().UsernameIsEmail == false && Username.IsNullOrWhiteSpace()) yield return new ValidationResult("A username cannot be empty", new[] { nameof(Username) }); } } diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/SecurityElementTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/SecurityElementTests.cs index 9300c88a67..93f37a1e35 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/SecurityElementTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/SecurityElementTests.cs @@ -9,127 +9,127 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings [Test] public void KeepUserLoggedIn() { - Assert.IsTrue(SettingsSection.Security.KeepUserLoggedIn == true); + Assert.IsTrue(SecuritySettings.KeepUserLoggedIn == true); } [Test] public void HideDisabledUsersInBackoffice() { - Assert.IsTrue(SettingsSection.Security.HideDisabledUsersInBackoffice == false); + Assert.IsTrue(SecuritySettings.HideDisabledUsersInBackoffice == false); } [Test] public void AllowPasswordReset() { - Assert.IsTrue(SettingsSection.Security.AllowPasswordReset == true); + Assert.IsTrue(SecuritySettings.AllowPasswordReset == true); } [Test] public void AuthCookieDomain() { - Assert.IsTrue(SettingsSection.Security.AuthCookieDomain == null); + Assert.IsTrue(SecuritySettings.AuthCookieDomain == null); } [Test] public void AuthCookieName() { - Assert.IsTrue(SettingsSection.Security.AuthCookieName == "UMB_UCONTEXT"); + Assert.IsTrue(SecuritySettings.AuthCookieName == "UMB_UCONTEXT"); } [Test] public void UserPasswordConfiguration_RequiredLength() { - Assert.IsTrue(SettingsSection.Security.UserPasswordConfiguration.RequiredLength == 12); + Assert.IsTrue(UserPasswordConfiguration.RequiredLength == 12); } [Test] public void UserPasswordConfiguration_RequireNonLetterOrDigit() { - Assert.IsTrue(SettingsSection.Security.UserPasswordConfiguration.RequireNonLetterOrDigit == false); + Assert.IsTrue(UserPasswordConfiguration.RequireNonLetterOrDigit == false); } [Test] public void UserPasswordConfiguration_RequireDigit() { - Assert.IsTrue(SettingsSection.Security.UserPasswordConfiguration.RequireDigit == false); + Assert.IsTrue(UserPasswordConfiguration.RequireDigit == false); } [Test] public void UserPasswordConfiguration_RequireLowercase() { - Assert.IsTrue(SettingsSection.Security.UserPasswordConfiguration.RequireLowercase == false); + Assert.IsTrue(UserPasswordConfiguration.RequireLowercase == false); } [Test] public void UserPasswordConfiguration_RequireUppercase() { - Assert.IsTrue(SettingsSection.Security.UserPasswordConfiguration.RequireUppercase == false); + Assert.IsTrue(UserPasswordConfiguration.RequireUppercase == false); } [Test] public void UserPasswordConfiguration_UseLegacyEncoding() { - Assert.IsTrue(SettingsSection.Security.UserPasswordConfiguration.UseLegacyEncoding == false); + Assert.IsTrue(UserPasswordConfiguration.UseLegacyEncoding == false); } [Test] public void UserPasswordConfiguration_HashAlgorithmType() { - Assert.IsTrue(SettingsSection.Security.UserPasswordConfiguration.HashAlgorithmType == "HMACSHA256"); + Assert.IsTrue(UserPasswordConfiguration.HashAlgorithmType == "HMACSHA256"); } [Test] public void UserPasswordConfiguration_MaxFailedAccessAttemptsBeforeLockout() { - Assert.IsTrue(SettingsSection.Security.UserPasswordConfiguration.MaxFailedAccessAttemptsBeforeLockout == 5); + Assert.IsTrue(UserPasswordConfiguration.MaxFailedAccessAttemptsBeforeLockout == 5); } [Test] public void MemberPasswordConfiguration_RequiredLength() { - Assert.IsTrue(SettingsSection.Security.MemberPasswordConfiguration.RequiredLength == 12); + Assert.IsTrue(MemberPasswordConfiguration.RequiredLength == 12); } [Test] public void MemberPasswordConfiguration_RequireNonLetterOrDigit() { - Assert.IsTrue(SettingsSection.Security.MemberPasswordConfiguration.RequireNonLetterOrDigit == false); + Assert.IsTrue(MemberPasswordConfiguration.RequireNonLetterOrDigit == false); } [Test] public void MemberPasswordConfiguration_RequireDigit() { - Assert.IsTrue(SettingsSection.Security.MemberPasswordConfiguration.RequireDigit == false); + Assert.IsTrue(MemberPasswordConfiguration.RequireDigit == false); } [Test] public void MemberPasswordConfiguration_RequireLowercase() { - Assert.IsTrue(SettingsSection.Security.MemberPasswordConfiguration.RequireLowercase == false); + Assert.IsTrue(MemberPasswordConfiguration.RequireLowercase == false); } [Test] public void MemberPasswordConfiguration_RequireUppercase() { - Assert.IsTrue(SettingsSection.Security.MemberPasswordConfiguration.RequireUppercase == false); + Assert.IsTrue(MemberPasswordConfiguration.RequireUppercase == false); } [Test] public void MemberPasswordConfiguration_UseLegacyEncoding() { - Assert.IsTrue(SettingsSection.Security.MemberPasswordConfiguration.UseLegacyEncoding == false); + Assert.IsTrue(MemberPasswordConfiguration.UseLegacyEncoding == false); } [Test] public void MemberPasswordConfiguration_HashAlgorithmType() { - Assert.IsTrue(SettingsSection.Security.MemberPasswordConfiguration.HashAlgorithmType == "HMACSHA256"); + Assert.IsTrue(MemberPasswordConfiguration.HashAlgorithmType == "HMACSHA256"); } [Test] public void MemberPasswordConfiguration_MaxFailedAccessAttemptsBeforeLockout() { - Assert.IsTrue(SettingsSection.Security.MemberPasswordConfiguration.MaxFailedAccessAttemptsBeforeLockout == 5); + Assert.IsTrue(MemberPasswordConfiguration.MaxFailedAccessAttemptsBeforeLockout == 5); } } } diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/UmbracoSettingsTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/UmbracoSettingsTests.cs index b19e4d522a..364c30eecc 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/UmbracoSettingsTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/UmbracoSettingsTests.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.IO; using NUnit.Framework; +using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Tests.TestHelpers; @@ -38,5 +39,8 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings protected ILoggingSettings LoggingSettings => Settings.Logging; protected IWebRoutingSettings WebRoutingSettings => Settings.WebRouting; protected IRequestHandlerSettings RequestHandlerSettings => Settings.RequestHandler; + protected ISecuritySettings SecuritySettings => Settings.Security; + protected IUserPasswordConfiguration UserPasswordConfiguration => Settings.Security.UserPasswordConfiguration; + protected IMemberPasswordConfiguration MemberPasswordConfiguration => Settings.Security.MemberPasswordConfiguration; } } diff --git a/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs b/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs index f3d03e1ea4..e3c9657b34 100644 --- a/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs +++ b/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs @@ -45,18 +45,8 @@ namespace Umbraco.Tests.TestHelpers var settings = new Mock(); var content = new Mock(); - var security = new Mock(); - var requestHandler = new Mock(); - var logging = new Mock(); - var routing = new Mock(); - - var userPasswordConfig = new Mock(); - var memberPasswordConfig = new Mock(); - security.Setup(x => x.UserPasswordConfiguration).Returns(userPasswordConfig.Object); - security.Setup(x => x.MemberPasswordConfiguration).Returns(memberPasswordConfig.Object); settings.Setup(x => x.Content).Returns(content.Object); - settings.Setup(x => x.Security).Returns(security.Object); //Now configure some defaults - the defaults in the config section classes do NOT pertain to the mocked data!! settings.Setup(x => x.Content.ImageAutoFillProperties).Returns(ContentImagingElement.GetDefaultImageAutoFillProperties()); @@ -185,5 +175,26 @@ namespace Umbraco.Tests.TestHelpers return mock.Object; } + + public static ISecuritySettings GenerateMockSecuritySettings() + { + var security = new Mock(); + + return security.Object; + } + + public static IUserPasswordConfiguration GenerateMockUserPasswordConfiguration() + { + var mock = new Mock(); + + return mock.Object; + } + + public static IMemberPasswordConfiguration GenerateMockMemberPasswordConfiguration() + { + var mock = new Mock(); + + return mock.Object; + } } } diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index 6b177c16cc..10449ac785 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -416,6 +416,9 @@ namespace Umbraco.Tests.Testing Composition.Configs.Add(SettingsForTests.GetDefaultHostingSettings); Composition.Configs.Add(SettingsForTests.GenerateMockRequestHandlerSettings); Composition.Configs.Add(SettingsForTests.GenerateMockWebRoutingSettings); + Composition.Configs.Add(SettingsForTests.GenerateMockSecuritySettings); + Composition.Configs.Add(SettingsForTests.GenerateMockUserPasswordConfiguration); + Composition.Configs.Add(SettingsForTests.GenerateMockMemberPasswordConfiguration); //Composition.Configs.Add(() => new DefaultUserPasswordConfig()); } diff --git a/src/Umbraco.Tests/Web/Controllers/AuthenticationControllerTests.cs b/src/Umbraco.Tests/Web/Controllers/AuthenticationControllerTests.cs index 9ecedee056..0e757bbc6d 100644 --- a/src/Umbraco.Tests/Web/Controllers/AuthenticationControllerTests.cs +++ b/src/Umbraco.Tests/Web/Controllers/AuthenticationControllerTests.cs @@ -88,7 +88,7 @@ namespace Umbraco.Tests.Web.Controllers Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), - Factory.GetInstance(), + Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance() ); diff --git a/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs b/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs index d914ef2c39..e23a4db1dc 100644 --- a/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs +++ b/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs @@ -92,7 +92,8 @@ namespace Umbraco.Tests.Web.Controllers Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), - Factory.GetInstance() + Factory.GetInstance(), + Factory.GetInstance() ); return usersController; @@ -164,7 +165,8 @@ namespace Umbraco.Tests.Web.Controllers Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), - Factory.GetInstance() + Factory.GetInstance(), + Factory.GetInstance() ); return usersController; } @@ -206,7 +208,8 @@ namespace Umbraco.Tests.Web.Controllers Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), - Factory.GetInstance() + Factory.GetInstance(), + Factory.GetInstance() ); return usersController; } @@ -283,7 +286,8 @@ namespace Umbraco.Tests.Web.Controllers Factory.GetInstance(), Factory.GetInstance(), Factory.GetInstance(), - Factory.GetInstance() + Factory.GetInstance(), + Factory.GetInstance() ); return usersController; } diff --git a/src/Umbraco.Web.UI/Umbraco/Views/AuthorizeUpgrade.cshtml b/src/Umbraco.Web.UI/Umbraco/Views/AuthorizeUpgrade.cshtml index e41185d74b..35654e7a41 100644 --- a/src/Umbraco.Web.UI/Umbraco/Views/AuthorizeUpgrade.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/Views/AuthorizeUpgrade.cshtml @@ -48,7 +48,7 @@ redirectUrl = Url.Action("AuthorizeUpgrade", "BackOffice") }); } - @Html.BareMinimumServerVariablesScript(Url, externalLoginUrl, Model.Features, Model.GlobalSettings, Model.UmbracoVersion, Model.UmbracoSettingsSection, Model.IOHelper, Model.TreeCollection, Model.HttpContextAccessor, Model.HostingEnvironment, Model.RuntimeSettings) + @Html.BareMinimumServerVariablesScript(Url, externalLoginUrl, Model.Features, Model.GlobalSettings, Model.UmbracoVersion, Model.UmbracoSettingsSection, Model.IOHelper, Model.TreeCollection, Model.HttpContextAccessor, Model.HostingEnvironment, Model.RuntimeSettings, Model.SecuritySettings)