From a0eba5a9d82f565e8f087853898da11b80c1e65d Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 23 Oct 2018 15:02:56 +0200 Subject: [PATCH 01/53] wip testing ideas --- .../content/umbtabbedcontent.directive.js | 8 ++++++++ .../property/umbpropertyeditor.directive.js | 2 +- .../content/umb-tabbed-content.html | 19 ++++++++++++++++++- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbtabbedcontent.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbtabbedcontent.directive.js index 015255c577..e00b333a2f 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbtabbedcontent.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbtabbedcontent.directive.js @@ -13,6 +13,14 @@ //expose the property/methods for other directives to use this.content = $scope.content; + $scope.activeVariant = _.find(this.content.variants, variant => { + return variant.active; + }); + + $scope.unlockInvariantValue = function(property) { + property.unlockInvariantValue = !property.unlockInvariantValue; + }; + $scope.$watch("tabbedContentForm.$dirty", function (newValue, oldValue) { if (newValue === true) { diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbpropertyeditor.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbpropertyeditor.directive.js index 0aa2dc02c3..32cbbb31ec 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbpropertyeditor.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbpropertyeditor.directive.js @@ -12,7 +12,7 @@ function umbPropEditor(umbPropEditorHelper) { scope: { model: "=", isPreValue: "@", - preview: "@" + preview: "<" }, require: "^^form", diff --git a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html index b125457ab0..68d5d3d41c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html @@ -9,7 +9,24 @@
- + +
+ +
+
Unlock to change on all languages
+
Changes to {{property.label}} will be copied to all languages
+
+ +
+ Changes to {{property.label}} will be copied to all languages +
+
+ + + +
From 60b575825d8a464a636e75155945457b5000cd1a Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Tue, 23 Oct 2018 14:22:17 +0100 Subject: [PATCH 02/53] Adds new WIP attribute only based directive disable-tabindex - which sets all child input elements to use a tabindex of -1 --- .../util/disabletabindex.directive.js | 28 +++++++++++++++++++ .../views/components/umb-groups-builder.html | 13 +++++---- 2 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js new file mode 100644 index 0000000000..7f9a0fd2b3 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js @@ -0,0 +1,28 @@ +angular.module("umbraco.directives") + .directive('disableTabindex', function ($window, $timeout, windowResizeListener) { + + return { + restrict: 'A', //Can only be used as an attribute + link: function (scope, element, attrs) { + + //When the current element DOM subtree is modified + element.on('DOMSubtreeModified', function(e){ + //Check if any child items in e.target contain an input + var jqLiteEl = angular.element(e.target); + var childInputs = jqLiteEl.find('input'); + + console.log('jQLite childInputs', childInputs); + + //For each item in childInputs - override or set HTML attribute tabindex="-1" + angular.forEach(childInputs, function(element){ + console.log('item in loop', element); + + //TODO: Note we updating way too many times from the DOMSubtreeModified event - is this expensive? + angular.element(element).attr('tabindex', '-1'); + }); + + }); + + } + }; +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-groups-builder.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-groups-builder.html index 2a5bc4a572..8732da91e7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-groups-builder.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-groups-builder.html @@ -157,7 +157,7 @@
{{propertyTypeForm.groupName.errorMsg}}
- +
@@ -229,11 +229,12 @@
- - + + From 515f183b28f3e935250f879ca131290ee909d276 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 23 Oct 2018 16:00:53 +0200 Subject: [PATCH 03/53] make default language bold in selectors --- src/Umbraco.Web.UI.Client/src/less/components/editor.less | 1 - .../src/views/components/application/umb-navigation.html | 2 +- .../src/views/components/editor/umb-editor-content-header.html | 2 +- src/Umbraco.Web.UI.Client/src/views/languages/overview.html | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/editor.less b/src/Umbraco.Web.UI.Client/src/less/components/editor.less index f045b0adca..d699193c24 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/editor.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/editor.less @@ -201,7 +201,6 @@ a.umb-variant-switcher__toggle { .umb-variant-switcher__name { display: block; - font-weight: bold; } .umb-variant-switcher__state { diff --git a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html index 570ea3990b..ddb0396f34 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html @@ -11,7 +11,7 @@   diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-content-header.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-content-header.html index 149fccd00a..999176f608 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-content-header.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-content-header.html @@ -41,7 +41,7 @@ - {{variant.language.name}} + {{variant.language.name}}
Open in split view
diff --git a/src/Umbraco.Web.UI.Client/src/views/languages/overview.html b/src/Umbraco.Web.UI.Client/src/views/languages/overview.html index dfec56fbc0..c55ce83417 100644 --- a/src/Umbraco.Web.UI.Client/src/views/languages/overview.html +++ b/src/Umbraco.Web.UI.Client/src/views/languages/overview.html @@ -41,7 +41,7 @@ - {{ language.name }} + {{ language.name }} {{ language.culture }} From a8ac9cf8f28f99f0ebf76e5676b5e1697876e06e Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Tue, 23 Oct 2018 15:13:35 +0100 Subject: [PATCH 04/53] MDN JS Docs suggested to use MutationObsorver as oppsoed to deprecated DOMSubtreeModified event --- .../util/disabletabindex.directive.js | 54 ++++++++++++++----- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js index 7f9a0fd2b3..64edcf5955 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js @@ -5,22 +5,52 @@ angular.module("umbraco.directives") restrict: 'A', //Can only be used as an attribute link: function (scope, element, attrs) { - //When the current element DOM subtree is modified - element.on('DOMSubtreeModified', function(e){ - //Check if any child items in e.target contain an input - var jqLiteEl = angular.element(e.target); - var childInputs = jqLiteEl.find('input'); + //Use DOM Mutation Observer + //Select the node that will be observed for mutations (native DOM element not jQLite version) + var targetNode = element[0]; - console.log('jQLite childInputs', childInputs); + // Options for the observer (which mutations to observe) + var config = { attributes: false, childList: true, subtree: false }; - //For each item in childInputs - override or set HTML attribute tabindex="-1" - angular.forEach(childInputs, function(element){ - console.log('item in loop', element); + // Callback function to execute when mutations are observed + var callback = function(mutationsList, observer) { + for(var mutation of mutationsList) { - //TODO: Note we updating way too many times from the DOMSubtreeModified event - is this expensive? - angular.element(element).attr('tabindex', '-1'); - }); + console.log('mutation', mutation); + //DOM items have been added or removed + if (mutation.type == 'childList') { + + //Check if any child items in mutation.target contain an input + var jqLiteEl = angular.element(mutation.target); + var childInputs = jqLiteEl.find('input'); + + //For each item in childInputs - override or set HTML attribute tabindex="-1" + angular.forEach(childInputs, function(element){ + console.log('item in loop', element); + + //TODO: Note we updating way too many times from the DOMSubtreeModified event - is this expensive? + angular.element(element).attr('tabindex', '-1'); + }); + } + } + }; + + // Create an observer instance linked to the callback function + var observer = new MutationObserver(callback); + + // Start observing the target node for configured mutations + //GO GO GO + observer.observe(targetNode, config); + + + //TODO: Unsure if we need to do this - to ensure the browser not trying to notify us still + //When we browse away from the page + element.on('$destroy', function(e){ + console.log('element with disable-tabindex attribute is destoryed'); + + //Remove/stop the observer + observer.disconnect(); }); } From b20acebe6a4e48f5d7c96b2c833d28442d47aa2e Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Tue, 23 Oct 2018 15:23:36 +0100 Subject: [PATCH 05/53] Cleanup unused DI stuff in this directive --- .../src/common/directives/util/disabletabindex.directive.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js index 64edcf5955..624404c641 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js @@ -1,5 +1,5 @@ angular.module("umbraco.directives") - .directive('disableTabindex', function ($window, $timeout, windowResizeListener) { + .directive('disableTabindex', function () { return { restrict: 'A', //Can only be used as an attribute From 79e3a35869fcc8bb591b9dd4b148e5a8c1d9e98d Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Tue, 23 Oct 2018 16:03:49 +0100 Subject: [PATCH 06/53] WIP - Ensures the default language variant is always first then the rest of the variants are organised by A-Z --- .../Mapping/ContentItemDisplayVariationResolver.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs index a627eab184..562797e3e3 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs @@ -52,6 +52,18 @@ namespace Umbraco.Web.Models.Mapping variant.Name = source.GetCultureName(x.IsoCode); } + //Put the default language first in the list & then sort rest by a-z + var defaultLang = variants.SingleOrDefault(x => x.Language.IsDefault); + + //Remove the default lang from the list for now + variants.Remove(defaultLang); + + //Sort the remaining languages a-z + variants.OrderBy(x => x.Language.Name); + + //Insert the default lang as the first item + variants.Insert(0, defaultLang); + return variants; } return result; From 9704c124eda16a4c2ffff869007c54b1ec498802 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 24 Oct 2018 15:20:28 +1100 Subject: [PATCH 07/53] Removes all old deprecated drop down editors, we only have one drop down editor now and it doesn't store IDs either in the db or in the cache, it just stores what is selected --- src/Umbraco.Core/Constants-PropertyEditors.cs | 20 --- .../Migrations/Install/DatabaseDataCreator.cs | 2 +- .../Migrations/Upgrade/UmbracoPlan.cs | 2 + .../Upgrade/V_8_0_0/DataTypeMigration.cs | 7 +- .../DropDownPropertyEditorsMigration.cs | 145 ++++++++++++++++++ .../Upgrade/V_8_0_0/DropPreValueTable.cs | 15 ++ .../DropdownListMultipleValueConverter.cs | 33 ---- ...pdownListMultipleWithKeysValueConverter.cs | 34 ---- .../DropdownListValueConverter.cs | 23 --- .../DropdownListWithKeysValueConverter.cs | 27 ---- src/Umbraco.Core/Umbraco.Core.csproj | 6 +- .../MultiValuePropertyEditorTests.cs | 27 +--- .../PropertyEditorValueConverterTests.cs | 41 ++--- .../Entities/MockedContentTypes.cs | 11 +- .../CheckBoxListPropertyEditor.cs | 2 +- .../DropDownFlexiblePropertyEditor.cs | 2 +- .../DropDownMultipleConfigurationEditor.cs | 73 --------- .../DropDownMultiplePropertyEditor.cs | 30 ---- .../DropDownMultipleWithKeysPropertyEditor.cs | 37 ----- .../PropertyEditors/DropDownPropertyEditor.cs | 37 ----- .../DropDownWithKeysPropertyEditor.cs | 36 ----- .../PublishValueValueEditor.cs | 57 ------- .../PublishValuesMultipleValueEditor.cs | 55 +------ .../RadioButtonsPropertyEditor.cs | 21 ++- .../FlexibleDropdownPropertyValueConverter.cs | 4 +- src/Umbraco.Web/Umbraco.Web.csproj | 6 - 26 files changed, 225 insertions(+), 528 deletions(-) create mode 100644 src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs create mode 100644 src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropPreValueTable.cs delete mode 100644 src/Umbraco.Core/PropertyEditors/ValueConverters/DropdownListMultipleValueConverter.cs delete mode 100644 src/Umbraco.Core/PropertyEditors/ValueConverters/DropdownListMultipleWithKeysValueConverter.cs delete mode 100644 src/Umbraco.Core/PropertyEditors/ValueConverters/DropdownListValueConverter.cs delete mode 100644 src/Umbraco.Core/PropertyEditors/ValueConverters/DropdownListWithKeysValueConverter.cs delete mode 100644 src/Umbraco.Web/PropertyEditors/DropDownMultipleConfigurationEditor.cs delete mode 100644 src/Umbraco.Web/PropertyEditors/DropDownMultiplePropertyEditor.cs delete mode 100644 src/Umbraco.Web/PropertyEditors/DropDownMultipleWithKeysPropertyEditor.cs delete mode 100644 src/Umbraco.Web/PropertyEditors/DropDownPropertyEditor.cs delete mode 100644 src/Umbraco.Web/PropertyEditors/DropDownWithKeysPropertyEditor.cs delete mode 100644 src/Umbraco.Web/PropertyEditors/PublishValueValueEditor.cs diff --git a/src/Umbraco.Core/Constants-PropertyEditors.cs b/src/Umbraco.Core/Constants-PropertyEditors.cs index 126613cdb3..b09987ad90 100644 --- a/src/Umbraco.Core/Constants-PropertyEditors.cs +++ b/src/Umbraco.Core/Constants-PropertyEditors.cs @@ -44,26 +44,6 @@ namespace Umbraco.Core /// public const string DateTime = "Umbraco.DateTime"; - /// - /// DropDown List. - /// - public const string DropDownList = "Umbraco.DropDown"; - - /// - /// DropDown List, Publish Keys. - /// - public const string DropdownlistPublishKeys = "Umbraco.DropdownlistPublishingKeys"; - - /// - /// DropDown List Multiple. - /// - public const string DropDownListMultiple = "Umbraco.DropDownMultiple"; - - /// - /// DropDown List Multiple, Publish Keys. - /// - public const string DropdownlistMultiplePublishKeys = "Umbraco.DropdownlistMultiplePublishKeys"; - /// /// DropDown List. /// diff --git a/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs b/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs index c4ebfb664b..eb7cafcb01 100644 --- a/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs +++ b/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs @@ -117,7 +117,7 @@ namespace Umbraco.Core.Migrations.Install _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Constants.DataTypes.DropDownMultiple, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Constants.DataTypes.DropDownMultiple}", SortOrder = 2, UniqueId = new Guid("0b6a45e7-44ba-430d-9da5-4e46060b9e03"), Text = "Dropdown", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = -41, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,-41", SortOrder = 2, UniqueId = new Guid("5046194e-4237-453c-a547-15db3a07c4e1"), Text = "Date Picker", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = -40, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,-40", SortOrder = 2, UniqueId = new Guid("bb5f57c9-ce2b-4bb9-b697-4caca783a805"), Text = "Radiobox", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = -39, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,-39", SortOrder = 2, UniqueId = new Guid("f38f0ac7-1d27-439c-9f3f-089cd8825a53"), Text = "Dropdown multiple", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Constants.DataTypes.DropDownSingle, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Constants.DataTypes.DropDownSingle}", SortOrder = 2, UniqueId = new Guid("f38f0ac7-1d27-439c-9f3f-089cd8825a53"), Text = "Dropdown multiple", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = -37, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,-37", SortOrder = 2, UniqueId = new Guid("0225af17-b302-49cb-9176-b9f35cab9c17"), Text = "Approved Color", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = -36, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,-36", SortOrder = 2, UniqueId = new Guid("e4d66c0f-b935-4200-81f0-025f7256b89a"), Text = "Date Picker with time", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); _database.Insert(Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Constants.DataTypes.DefaultContentListView, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Constants.DataTypes.DefaultContentListView}", SortOrder = 2, UniqueId = new Guid("C0808DD3-8133-4E4B-8CE8-E2BEA84A96A4"), Text = Constants.Conventions.DataTypes.ListViewPrefix + "Content", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs index 5b0838573e..9ad7ccb48e 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs @@ -138,6 +138,8 @@ namespace Umbraco.Core.Migrations.Upgrade Chain("{6A2C7C1B-A9DB-4EA9-B6AB-78E7D5B722A7}"); Chain("{77874C77-93E5-4488-A404-A630907CEEF0}"); + Chain("{23275462-446E-44C7-8C2C-3B8C1127B07D}"); + Chain("{6B251841-3069-4AD5-8AE9-861F9523E8DA}"); //FINAL diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DataTypeMigration.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DataTypeMigration.cs index eb39f37112..d7180385f0 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DataTypeMigration.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DataTypeMigration.cs @@ -7,10 +7,11 @@ using NPoco; using Umbraco.Core.Migrations.Install; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Dtos; -using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Persistence.Querying; namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 { + public class DataTypeMigration : MigrationBase { public DataTypeMigration(IMigrationContext context) @@ -79,10 +80,6 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 Database.Update(dataType); } - - // drop preValues table - // FIXME keep it around for now - //Delete.Table("cmsDataTypePreValues"); } [TableName("cmsDataTypePreValues")] diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs new file mode 100644 index 0000000000..c9eba205e8 --- /dev/null +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Dtos; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Logging; + +namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 +{ + public class DropDownPropertyEditorsMigration : MigrationBase + { + public DropDownPropertyEditorsMigration(IMigrationContext context) : base(context) + { + } + + public override void Migrate() + { + //need to convert the old drop down data types to use the new one + var oldDropDowns = Database.Fetch(Sql().Select().Where(x => x.EditorAlias.Contains(".DropDown"))); + foreach(var dd in oldDropDowns) + { + //nothing to change if there is no config + if (dd.Configuration.IsNullOrWhiteSpace()) continue; + + ValueListConfiguration config; + try + { + config = JsonConvert.DeserializeObject(dd.Configuration); + } + catch (Exception ex) + { + Logger.Error(ex, $"Invalid drop down configuration detected: \"{dd.Configuration}\", cannot convert editor, values will be cleared"); + dd.Configuration = null; + Database.Update(dd); + continue; + } + + var propDataSql = Sql().Select("*").From() + .InnerJoin().On(x => x.Id, x => x.PropertyTypeId) + .InnerJoin().On(x => x.NodeId, x => x.DataTypeId) + .Where(x => x.EditorAlias == dd.EditorAlias); + + var propDatas = Database.Query(propDataSql); + var toUpdate = new List(); + foreach (var propData in propDatas) + { + if (UpdatePropertyDataDto(propData, config)) + { + //update later, we are iterating all values right now so no SQL can be run inside of this iteration (i.e. Query) + toUpdate.Add(propData); + } + } + + //run the property data updates + foreach(var propData in toUpdate) + { + Database.Update(propData); + } + + var requiresCacheRebuild = false; + switch (dd.EditorAlias) + { + case "Umbraco.DropDown": + UpdateDataType(dd, config, false); + break; + case "Umbraco.DropdownlistPublishingKeys": + UpdateDataType(dd, config, false); + requiresCacheRebuild = true; + break; + case "Umbraco.DropDownMultiple": + UpdateDataType(dd, config, false); + break; + case "Umbraco.DropdownlistMultiplePublishKeys": + UpdateDataType(dd, config, true); + requiresCacheRebuild = true; + break; + } + + if (requiresCacheRebuild) + { + //TODO: How to force rebuild the cache? + } + } + } + + private void UpdateDataType(DataTypeDto dataType, ValueListConfiguration config, bool isMultiple) + { + dataType.EditorAlias = Constants.PropertyEditors.Aliases.DropDownListFlexible; + var flexConfig = new + { + multiple = isMultiple, + items = config.Items + }; + dataType.Configuration = JsonConvert.SerializeObject(flexConfig); + } + + private bool UpdatePropertyDataDto(PropertyDataDto propData, ValueListConfiguration config) + { + //Get the INT ids stored for this property/drop down + IEnumerable ids = null; + if (!propData.VarcharValue.IsNullOrWhiteSpace()) + { + ids = ConvertStringValues(propData.VarcharValue); + } + else if (!propData.TextValue.IsNullOrWhiteSpace()) + { + ids = ConvertStringValues(propData.TextValue); + } + else if (propData.IntegerValue.HasValue) + { + ids = new[] { propData.IntegerValue.Value }; + } + + //if there are INT ids, convert them to values based on the configured pre-values + if (ids != null) + { + //map the ids to values + var vals = new List(); + foreach (var id in ids) + { + var val = config.Items.FirstOrDefault(x => x.Id == id); + if (val != null) + vals.Add(val.Value); + } + + propData.VarcharValue = string.Join(",", vals); + propData.TextValue = null; + propData.IntegerValue = null; + return true; + } + + return false; + } + + private IEnumerable ConvertStringValues(string val) + { + return val.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) + .Select(x => int.TryParse(x, out var i) ? i : int.MinValue) + .Where(x => x != int.MinValue); + } + + } +} diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropPreValueTable.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropPreValueTable.cs new file mode 100644 index 0000000000..26402cc477 --- /dev/null +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropPreValueTable.cs @@ -0,0 +1,15 @@ +namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 +{ + public class DropPreValueTable : MigrationBase + { + public DropPreValueTable(IMigrationContext context) : base(context) + { + } + + public override void Migrate() + { + // drop preValues table + Delete.Table("cmsDataTypePreValues").Do(); + } + } +} diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/DropdownListMultipleValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/DropdownListMultipleValueConverter.cs deleted file mode 100644 index d91f45292c..0000000000 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/DropdownListMultipleValueConverter.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Umbraco.Core.Models.PublishedContent; - -namespace Umbraco.Core.PropertyEditors.ValueConverters -{ - [DefaultPropertyValueConverter] - public class DropdownListMultipleValueConverter : PropertyValueConverterBase - { - public override bool IsConverter(PublishedPropertyType propertyType) - => propertyType.EditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.DropDownListMultiple); - - public override Type GetPropertyValueType(PublishedPropertyType propertyType) - => typeof (IEnumerable); - - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) - => PropertyCacheLevel.Element; - - public override object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel cacheLevel, object source, bool preview) - { - var sourceString = (source ?? "").ToString(); - - if (string.IsNullOrEmpty(sourceString)) - return Enumerable.Empty(); - - var values = - sourceString.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(v => v.Trim()); - - return values; - } - } -} diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/DropdownListMultipleWithKeysValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/DropdownListMultipleWithKeysValueConverter.cs deleted file mode 100644 index bceebc232b..0000000000 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/DropdownListMultipleWithKeysValueConverter.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Umbraco.Core.Models.PublishedContent; - -namespace Umbraco.Core.PropertyEditors.ValueConverters -{ - [DefaultPropertyValueConverter] - public class DropdownListMultipleWithKeysValueConverter : PropertyValueConverterBase - { - public override bool IsConverter(PublishedPropertyType propertyType) - => propertyType.EditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.DropdownlistMultiplePublishKeys); - - public override Type GetPropertyValueType(PublishedPropertyType propertyType) - => typeof (IEnumerable); - - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) - => PropertyCacheLevel.Element; - - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) - { - if (source == null) - return new int[] { }; - - var prevalueIds = source.ToString() - .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) - .Select(p => p.Trim()) - .Select(int.Parse) - .ToArray(); - - return prevalueIds; - } - } -} diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/DropdownListValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/DropdownListValueConverter.cs deleted file mode 100644 index 5fe1967f32..0000000000 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/DropdownListValueConverter.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using Umbraco.Core.Models.PublishedContent; - -namespace Umbraco.Core.PropertyEditors.ValueConverters -{ - [DefaultPropertyValueConverter] - public class DropdownListValueConverter : PropertyValueConverterBase - { - public override bool IsConverter(PublishedPropertyType propertyType) - => propertyType.EditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.DropDownList); - - public override Type GetPropertyValueType(PublishedPropertyType propertyType) - => typeof (string); - - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) - => PropertyCacheLevel.Element; - - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) - { - return source?.ToString() ?? string.Empty; - } - } -} diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/DropdownListWithKeysValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/DropdownListWithKeysValueConverter.cs deleted file mode 100644 index 960cd4afa6..0000000000 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/DropdownListWithKeysValueConverter.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using Umbraco.Core.Models.PublishedContent; - -namespace Umbraco.Core.PropertyEditors.ValueConverters -{ - [DefaultPropertyValueConverter] - public class DropdownListWithKeysValueConverter : PropertyValueConverterBase - { - public override bool IsConverter(PublishedPropertyType propertyType) - => propertyType.EditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.DropdownlistPublishKeys); - - public override Type GetPropertyValueType(PublishedPropertyType propertyType) - => typeof (int); - - public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) - => PropertyCacheLevel.Element; - - public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) - { - var intAttempt = source.TryConvertTo(); - if (intAttempt.Success) - return intAttempt.Result; - - return null; - } - } -} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 6951a9e17a..4d4bbb32e2 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -356,6 +356,8 @@ + + @@ -1239,10 +1241,6 @@ - - - - diff --git a/src/Umbraco.Tests/PropertyEditors/MultiValuePropertyEditorTests.cs b/src/Umbraco.Tests/PropertyEditors/MultiValuePropertyEditorTests.cs index 9ba5ccf6f2..129116bf61 100644 --- a/src/Umbraco.Tests/PropertyEditors/MultiValuePropertyEditorTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/MultiValuePropertyEditorTests.cs @@ -21,28 +21,15 @@ namespace Umbraco.Tests.PropertyEditors /// Tests for the base classes of ValueEditors and PreValueEditors that are used for Property Editors that edit /// multiple values such as the drop down list, check box list, color picker, etc.... /// + /// + /// Mostly this used to test the we'd store INT Ids in the Db but publish STRING values or sometimes the INT values + /// to cache. Now we always just deal with strings and we'll keep the tests that show that. + /// [TestFixture] public class MultiValuePropertyEditorTests { - //TODO: Test the other formatting methods for the drop down classes - [Test] - public void DropDownMultipleValueEditor_With_Keys_Format_Data_For_Cache() - { - var dataTypeServiceMock = new Mock(); - var editor = new PublishValuesMultipleValueEditor(true, Mock.Of(), new DataEditorAttribute("key", "nam", "view")); - - var dataType = new DataType(new CheckBoxListPropertyEditor(Mock.Of(), Mock.Of())); - var prop = new Property(1, new PropertyType(dataType)); - prop.SetValue("1234,4567,8910"); - - var result = editor.ConvertDbToString(prop.PropertyType, prop.GetValue(), new Mock().Object); - - Assert.AreEqual("1234,4567,8910", result); - } - - [Test] - public void DropDownMultipleValueEditor_No_Keys_Format_Data_For_Cache() + public void DropDownMultipleValueEditor_Format_Data_For_Cache() { var dataType = new DataType(new CheckBoxListPropertyEditor(Mock.Of(), Mock.Of())) { @@ -61,7 +48,7 @@ namespace Umbraco.Tests.PropertyEditors var dataTypeService = new TestObjects.TestDataTypeService(dataType); var prop = new Property(1, new PropertyType(dataType)); - prop.SetValue("1234,4567,8910"); + prop.SetValue("Value 1,Value 2,Value 3"); var valueEditor = dataType.Editor.GetValueEditor(); ((DataValueEditor) valueEditor).Configuration = dataType.Configuration; @@ -90,7 +77,7 @@ namespace Umbraco.Tests.PropertyEditors var dataTypeService = new TestObjects.TestDataTypeService(dataType); var prop = new Property(1, new PropertyType(dataType)); - prop.SetValue("1234"); + prop.SetValue("Value 2"); var result = dataType.Editor.GetValueEditor().ConvertDbToString(prop.PropertyType, prop.GetValue(), dataTypeService); diff --git a/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs b/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs index 2c3a2d1583..7ec23158f6 100644 --- a/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs @@ -1,8 +1,14 @@ using System; using System.Collections.Generic; +using System.Linq; +using Moq; using NUnit.Framework; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.ValueConverters; +using Umbraco.Web.PropertyEditors; +using Umbraco.Web.PropertyEditors.ValueConverters; namespace Umbraco.Tests.PropertyEditors { @@ -79,27 +85,26 @@ namespace Umbraco.Tests.PropertyEditors [TestCase(null, new string[] { })] public void CanConvertDropdownListMultiplePropertyEditor(object value, IEnumerable expected) { - var converter = new DropdownListMultipleValueConverter(); - var inter = converter.ConvertSourceToIntermediate(null, null, value, false); - var result = converter.ConvertIntermediateToObject(null, null, PropertyCacheLevel.Unknown, inter, false); + var mockPublishedContentTypeFactory = new Mock(); + mockPublishedContentTypeFactory.Setup(x => x.GetDataType(123)) + .Returns(new PublishedDataType(123, "test", new Lazy(() => new DropDownFlexibleConfiguration + { + Multiple = true + }))); + + var publishedPropType = new PublishedPropertyType( + new PublishedContentType(1234, "test", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing), + new PropertyType("test", ValueStorageType.Nvarchar) { DataTypeId = 123 }, + new PropertyValueConverterCollection(Enumerable.Empty()), + Mock.Of(), mockPublishedContentTypeFactory.Object); + + var converter = new FlexibleDropdownPropertyValueConverter(); + var inter = converter.ConvertSourceToIntermediate(null, publishedPropType, value, false); + var result = converter.ConvertIntermediateToObject(null, publishedPropType, PropertyCacheLevel.Unknown, inter, false); Assert.AreEqual(expected, result); } - - [TestCase("100", new[] { 100 })] - [TestCase("100,200", new[] { 100, 200 })] - [TestCase("100 , 200, 300 ", new[] { 100, 200, 300 })] - [TestCase("", new int[] { })] - [TestCase(null, new int[] { })] - public void CanConvertDropdownListMultipleWithKeysPropertyEditor(object value, IEnumerable expected) - { - var converter = new DropdownListMultipleWithKeysValueConverter(); - var inter = converter.ConvertSourceToIntermediate(null, null, value, false); - var result = converter.ConvertIntermediateToObject(null, null, PropertyCacheLevel.Unknown, inter, false); - - Assert.AreEqual(expected, result); - } - + [TestCase("1", 1)] [TestCase("1", 1)] [TestCase("0", 0)] diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs index 752d0ac97e..42beea7df3 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs @@ -362,12 +362,10 @@ namespace Umbraco.Tests.TestHelpers.Entities contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.NoEdit, ValueStorageType.Nvarchar) { Alias = "label", Name = "Label", Mandatory = false, SortOrder = 7, DataTypeId = -92 }); contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.DateTime, ValueStorageType.Date) { Alias = "dateTime", Name = "Date Time", Mandatory = false, SortOrder = 8, DataTypeId = -36 }); contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.ColorPicker, ValueStorageType.Nvarchar) { Alias = "colorPicker", Name = "Color Picker", Mandatory = false, SortOrder = 9, DataTypeId = -37 }); - //that one is gone in 7.4 - //contentCollection.Add(new PropertyType(Constants.PropertyEditors.FolderBrowserAlias, DataTypeDatabaseType.Nvarchar) { Alias = "folderBrowser", Name = "Folder Browser", Mandatory = false, SortOrder = 10, DataTypeDefinitionId = -38 }); - contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.DropDownListMultiple, ValueStorageType.Nvarchar) { Alias = "ddlMultiple", Name = "Dropdown List Multiple", Mandatory = false, SortOrder = 11, DataTypeId = -39 }); + contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.DropDownListFlexible, ValueStorageType.Nvarchar) { Alias = "ddlMultiple", Name = "Dropdown List Multiple", Mandatory = false, SortOrder = 11, DataTypeId = -39 }); contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.RadioButtonList, ValueStorageType.Nvarchar) { Alias = "rbList", Name = "Radio Button List", Mandatory = false, SortOrder = 12, DataTypeId = -40 }); contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Date, ValueStorageType.Date) { Alias = "date", Name = "Date", Mandatory = false, SortOrder = 13, DataTypeId = -41 }); - contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.DropDownList, ValueStorageType.Integer) { Alias = "ddl", Name = "Dropdown List", Mandatory = false, SortOrder = 14, DataTypeId = -42 }); + contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.DropDownListFlexible, ValueStorageType.Integer) { Alias = "ddl", Name = "Dropdown List", Mandatory = false, SortOrder = 14, DataTypeId = -42 }); contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.CheckBoxList, ValueStorageType.Nvarchar) { Alias = "chklist", Name = "Checkbox List", Mandatory = false, SortOrder = 15, DataTypeId = -43 }); contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.ContentPicker, ValueStorageType.Integer) { Alias = "contentPicker", Name = "Content Picker", Mandatory = false, SortOrder = 16, DataTypeId = 1046 }); contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.MediaPicker, ValueStorageType.Integer) { Alias = "mediaPicker", Name = "Media Picker", Mandatory = false, SortOrder = 17, DataTypeId = 1048 }); @@ -375,11 +373,6 @@ namespace Umbraco.Tests.TestHelpers.Entities contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.RelatedLinks, ValueStorageType.Ntext) { Alias = "relatedLinks", Name = "Related Links", Mandatory = false, SortOrder = 21, DataTypeId = 1050 }); contentCollection.Add(new PropertyType(Constants.PropertyEditors.Aliases.Tags, ValueStorageType.Ntext) { Alias = "tags", Name = "Tags", Mandatory = false, SortOrder = 22, DataTypeId = 1041 }); - //contentCollection.Add(new PropertyType(Constants.PropertyEditors.UltraSimpleEditorAlias, DataTypeDatabaseType.Ntext) { Alias = "simpleEditor", Name = "Ultra Simple Editor", Mandatory = false, SortOrder = 19, DataTypeDefinitionId = 1038 }); - //contentCollection.Add(new PropertyType(Constants.PropertyEditors.UltimatePickerAlias, DataTypeDatabaseType.Ntext) { Alias = "ultimatePicker", Name = "Ultimate Picker", Mandatory = false, SortOrder = 20, DataTypeDefinitionId = 1039 }); - //contentCollection.Add(new PropertyType(Constants.PropertyEditors.MacroContainerAlias, DataTypeDatabaseType.Ntext) { Alias = "macroContainer", Name = "Macro Container", Mandatory = false, SortOrder = 23, DataTypeDefinitionId = 1042 }); - //contentCollection.Add(new PropertyType(Constants.PropertyEditors.ImageCropperAlias, DataTypeDatabaseType.Ntext) { Alias = "imgCropper", Name = "Image Cropper", Mandatory = false, SortOrder = 24, DataTypeDefinitionId = 1043 }); - contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Content", SortOrder = 1 }); return contentType; diff --git a/src/Umbraco.Web/PropertyEditors/CheckBoxListPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/CheckBoxListPropertyEditor.cs index 53de8460f2..7f8dc3f03a 100644 --- a/src/Umbraco.Web/PropertyEditors/CheckBoxListPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/CheckBoxListPropertyEditor.cs @@ -31,6 +31,6 @@ namespace Umbraco.Web.PropertyEditors protected override IConfigurationEditor CreateConfigurationEditor() => new ValueListConfigurationEditor(_textService); /// - protected override IDataValueEditor CreateValueEditor() => new PublishValuesMultipleValueEditor(false, Attribute); + protected override IDataValueEditor CreateValueEditor() => new PublishValuesMultipleValueEditor(Logger, Attribute); } } diff --git a/src/Umbraco.Web/PropertyEditors/DropDownFlexiblePropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/DropDownFlexiblePropertyEditor.cs index 6acbedef63..4424e1d245 100644 --- a/src/Umbraco.Web/PropertyEditors/DropDownFlexiblePropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/DropDownFlexiblePropertyEditor.cs @@ -18,7 +18,7 @@ namespace Umbraco.Web.PropertyEditors protected override IDataValueEditor CreateValueEditor() { - return new PublishValuesMultipleValueEditor(false, Attribute); + return new PublishValuesMultipleValueEditor(Logger, Attribute); } protected override IConfigurationEditor CreateConfigurationEditor() => new DropDownFlexibleConfigurationEditor(_textService); diff --git a/src/Umbraco.Web/PropertyEditors/DropDownMultipleConfigurationEditor.cs b/src/Umbraco.Web/PropertyEditors/DropDownMultipleConfigurationEditor.cs deleted file mode 100644 index a8956f9c4f..0000000000 --- a/src/Umbraco.Web/PropertyEditors/DropDownMultipleConfigurationEditor.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System.Collections.Generic; -using Umbraco.Core.PropertyEditors; -using Umbraco.Core.Services; - -namespace Umbraco.Web.PropertyEditors -{ - /// - /// Represents a configuration editor for the "drop down list multiple" property editor. - /// - /// - /// Ensures that 'multiple' is saved for the config in the db but is not a configuration field. - /// This is mostly to maintain backwards compatibility with old property editors. Devs can now simply - /// use the "drop down" property editor and check the "multiple" configuration checkbox - /// fixme what is multiple exactly?! - /// - internal class DropDownMultipleConfigurationEditor : ValueListConfigurationEditor - { - public DropDownMultipleConfigurationEditor(ILocalizedTextService textService) - : base(textService) - { - Fields.Add(new ConfigurationField - { - Key = "multiple", - Name = "multiple", - View = "hidden", // so it does not show in the configuration editor - HideLabel = true - }); - } - - // editor... - // - // receives: - // "preValues":[ - // { - // "label":"Add prevalue", - // "description":"Add and remove values for the list", - // "hideLabel":false, - // "view":"multivalues", - // "config":{}, - // "key":"items", - // "value":{"169":{"value":"a","sortOrder":1},"170":{"value":"b","sortOrder":2},"171":{"value":"c","sortOrder":3}} - // }, - // { - // "label":"multiple", - // "description":null, - // "hideLabel":true, - // "view":"hidden", - // "config":{}, - // "key":"multiple", - // "value":"1" - // }] - // - // posts ('d' being a new value): - // [{key: "items", value: [{value: "a", sortOrder: 1, id: "169"}, {value: "c", sortOrder: 3, id: "171"}, {value: "d"}]}, {key: "multiple", value: "1"}] - // - // the 'multiple' thing never goes to DB - // values go to DB with alias 0, 1, 2 + their ID + value - // the sort order that comes back makes no sense - // - // FromEditor can totally ignore 'multiple' - - /// - public override Dictionary ToConfigurationEditor(ValueListConfiguration configuration) - { - var dictionary = base.ToConfigurationEditor(configuration); - - // always add the multiple field, as 'true' - dictionary["multiple"] = 1; - - return dictionary; - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Web/PropertyEditors/DropDownMultiplePropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/DropDownMultiplePropertyEditor.cs deleted file mode 100644 index 5992090a62..0000000000 --- a/src/Umbraco.Web/PropertyEditors/DropDownMultiplePropertyEditor.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Collections.Generic; -using Newtonsoft.Json; -using Umbraco.Core; -using Umbraco.Core.Logging; -using Umbraco.Core.PropertyEditors; -using Umbraco.Core.Services; - -namespace Umbraco.Web.PropertyEditors -{ - /// - /// A property editor to allow multiple selection of pre-defined items - /// - /// - /// Due to maintaining backwards compatibility this data type stores the value as a string which is a comma separated value of the - /// ids of the individual items so we have logic in here to deal with that. - /// - [DataEditor(Constants.PropertyEditors.Aliases.DropDownListMultiple, "Dropdown list multiple", "dropdown", Group = "lists", Icon="icon-bulleted-list", IsDeprecated = true)] - public class DropDownMultiplePropertyEditor : DropDownMultipleWithKeysPropertyEditor - { - /// - /// The constructor will setup the property editor based on the attribute if one is found - /// - public DropDownMultiplePropertyEditor(ILogger logger, ILocalizedTextService textService) - : base(logger, textService) - { } - - /// - protected override IDataValueEditor CreateValueEditor() => new PublishValuesMultipleValueEditor(false, Attribute); - } -} diff --git a/src/Umbraco.Web/PropertyEditors/DropDownMultipleWithKeysPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/DropDownMultipleWithKeysPropertyEditor.cs deleted file mode 100644 index 0b882c6b8d..0000000000 --- a/src/Umbraco.Web/PropertyEditors/DropDownMultipleWithKeysPropertyEditor.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Collections.Generic; -using Umbraco.Core; -using Umbraco.Core.Logging; -using Umbraco.Core.Models; -using Umbraco.Core.PropertyEditors; -using Umbraco.Core.Services; - -namespace Umbraco.Web.PropertyEditors -{ - /// - /// Represents a property editor that allows multiple selection of pre-defined items. - /// - /// - /// Due to backwards compatibility, this editor stores the value as a CSV string listing - /// the ids of individual items. - /// - [DataEditor(Constants.PropertyEditors.Aliases.DropdownlistMultiplePublishKeys, "Dropdown list multiple, publish keys", "dropdown", Group = "lists", Icon = "icon-bulleted-list", IsDeprecated = true)] - public class DropDownMultipleWithKeysPropertyEditor : DropDownPropertyEditor - { - private readonly ILocalizedTextService _textService; - - /// - /// Initializes a new instance of the class. - /// - public DropDownMultipleWithKeysPropertyEditor(ILogger logger, ILocalizedTextService textService) - : base(logger, textService) - { - _textService = textService; - } - - /// - protected override IDataValueEditor CreateValueEditor() => new PublishValuesMultipleValueEditor(true, Attribute); - - /// - protected override IConfigurationEditor CreateConfigurationEditor() => new DropDownMultipleConfigurationEditor(_textService); - } -} diff --git a/src/Umbraco.Web/PropertyEditors/DropDownPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/DropDownPropertyEditor.cs deleted file mode 100644 index f4736ab63f..0000000000 --- a/src/Umbraco.Web/PropertyEditors/DropDownPropertyEditor.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using Umbraco.Core; -using Umbraco.Core.Logging; -using Umbraco.Core.PropertyEditors; -using umbraco; -using ClientDependency.Core; -using Umbraco.Core.Services; -using Constants = Umbraco.Core.Constants; - -namespace Umbraco.Web.PropertyEditors -{ - /// - /// A property editor to allow the individual selection of pre-defined items. - /// - /// - /// Due to remaining backwards compatible, this stores the id of the drop down item in the database which is why it is marked - /// as INT and we have logic in here to ensure it is formatted correctly including ensuring that the string value is published - /// in cache and not the int ID. - /// - [DataEditor(Constants.PropertyEditors.Aliases.DropDownList, "Dropdown list", "dropdown", ValueType = ValueTypes.String, Group = "lists", Icon = "icon-indent", IsDeprecated = true)] - public class DropDownPropertyEditor : DropDownWithKeysPropertyEditor - { - /// - /// The constructor will setup the property editor based on the attribute if one is found - /// - public DropDownPropertyEditor(ILogger logger, ILocalizedTextService textService) - : base(logger, textService) - { } - - /// - /// We need to override the value editor so that we can ensure the string value is published in cache and not the integer ID value. - /// - /// - protected override IDataValueEditor CreateValueEditor() => new PublishValueValueEditor(Attribute, Logger); - } -} diff --git a/src/Umbraco.Web/PropertyEditors/DropDownWithKeysPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/DropDownWithKeysPropertyEditor.cs deleted file mode 100644 index 72d4b8c95f..0000000000 --- a/src/Umbraco.Web/PropertyEditors/DropDownWithKeysPropertyEditor.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Umbraco.Core; -using Umbraco.Core.Logging; -using Umbraco.Core.PropertyEditors; -using Umbraco.Core.Services; - -namespace Umbraco.Web.PropertyEditors -{ - /// - /// A property editor to allow the individual selection of pre-defined items. - /// - /// - /// Due to remaining backwards compatible, this stores the id of the drop down item in the database which is why it is marked - /// as INT and we have logic in here to ensure it is formatted correctly including ensuring that the INT ID value is published - /// in cache and not the string value. - /// - [DataEditor(Constants.PropertyEditors.Aliases.DropdownlistPublishKeys, "Dropdown list, publishing keys", "dropdown", ValueType = ValueTypes.Integer, Group = "lists", Icon = "icon-indent", IsDeprecated = true)] - public class DropDownWithKeysPropertyEditor : DataEditor - { - private readonly ILocalizedTextService _textService; - - /// - /// The constructor will setup the property editor based on the attribute if one is found - /// - public DropDownWithKeysPropertyEditor(ILogger logger, ILocalizedTextService textService) - : base(logger) - { - _textService = textService; - } - - /// - /// Return a custom pre-value editor - /// - /// - protected override IConfigurationEditor CreateConfigurationEditor() => new ValueListConfigurationEditor(_textService); - } -} diff --git a/src/Umbraco.Web/PropertyEditors/PublishValueValueEditor.cs b/src/Umbraco.Web/PropertyEditors/PublishValueValueEditor.cs deleted file mode 100644 index 49ed34ffef..0000000000 --- a/src/Umbraco.Web/PropertyEditors/PublishValueValueEditor.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Umbraco.Core; -using Umbraco.Core.Logging; -using Umbraco.Core.Models; -using Umbraco.Core.PropertyEditors; -using Umbraco.Core.Services; -using Umbraco.Web.Composing; - -namespace Umbraco.Web.PropertyEditors -{ - /// - /// A custom value editor for any property editor that stores a pre-value int id so that we can ensure that the 'value' not the ID get's put into cache - /// - /// - /// This is required for legacy/backwards compatibility, otherwise we'd just store the string version and cache the string version without - /// needing additional lookups. - /// - internal class PublishValueValueEditor : DataValueEditor - { - private readonly ILogger _logger; - - internal PublishValueValueEditor(DataEditorAttribute attribute, ILogger logger) - : base(attribute) - { - _logger = logger; - } - - /// - public override string ConvertDbToString(PropertyType propertyType, object value, IDataTypeService dataTypeService) - { - if (value == null) - return null; - - // get the configuration items - // if none, fallback to base - var configuration = dataTypeService.GetDataType(propertyType.DataTypeId).ConfigurationAs(); - if (configuration == null) - return base.ConvertDbToString(propertyType, value, dataTypeService); - - var items = configuration.Items; - - var idAttempt = value.TryConvertTo(); - if (idAttempt.Success) - { - var itemId = idAttempt.Result; - var item = items.FirstOrDefault(x => x.Id == itemId); - if (item != null) return item.Value; - - _logger.Warn("Could not find a configuration item with ID " + itemId + " for property alias " + propertyType.Alias); - } - - // fallback to default - return base.ConvertDbToString(propertyType, value, dataTypeService); - } - } -} diff --git a/src/Umbraco.Web/PropertyEditors/PublishValuesMultipleValueEditor.cs b/src/Umbraco.Web/PropertyEditors/PublishValuesMultipleValueEditor.cs index 0276bdc0a4..6aea6edba5 100644 --- a/src/Umbraco.Web/PropertyEditors/PublishValuesMultipleValueEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/PublishValuesMultipleValueEditor.cs @@ -11,60 +11,21 @@ using Umbraco.Web.Composing; namespace Umbraco.Web.PropertyEditors { /// - /// Custom value editor to handle posted json data and to return json data for the multiple selected items as well as ensuring - /// that the multiple selected int IDs are published to cache as a delimited string (values) + /// Custom value editor to handle posted json data and to return json data for the multiple selected items /// /// /// This is re-used by editors such as the multiple drop down list or check box list /// - internal class PublishValuesMultipleValueEditor : PublishValueValueEditor + internal class PublishValuesMultipleValueEditor : DataValueEditor { - private readonly bool _publishIds; + private readonly ILogger _logger; - internal PublishValuesMultipleValueEditor(bool publishIds, ILogger logger, DataEditorAttribute attribute) - : base(attribute, logger) + internal PublishValuesMultipleValueEditor(ILogger logger, DataEditorAttribute attribute) + : base(attribute) { - _publishIds = publishIds; + _logger = logger; } - - public PublishValuesMultipleValueEditor(bool publishIds, DataEditorAttribute attribute) - : this(publishIds, Current.Logger, attribute) - { } - - /// - /// If publishing ids, we don't need to do anything, otherwise we need to look up the pre-values and get the string values - /// - /// - /// - /// - /// - public override string ConvertDbToString(PropertyType propertyType, object propertyValue, IDataTypeService dataTypeService) - { - if (propertyValue == null) - return null; - - //publishing ids, so just need to return the value as-is - if (_publishIds) - { - return propertyValue.ToString(); - } - - // get the multiple ids - // if none, fallback to base - var selectedIds = propertyValue.ToString().Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries); - if (selectedIds.Any() == false) - return base.ConvertDbToString(propertyType, propertyValue, dataTypeService); - - // get the configuration items - // if none, fallback to base - var configuration = dataTypeService.GetDataType(propertyType.DataTypeId).ConfigurationAs(); - if (configuration == null) - return base.ConvertDbToString(propertyType, propertyValue, dataTypeService); - - var items = configuration.Items.Where(x => selectedIds.Contains(x.Id.ToInvariantString())).Select(x => x.Value); - return string.Join(",", items); - } - + /// /// Override so that we can return a json array to the editor for multi-select values /// @@ -81,7 +42,7 @@ namespace Umbraco.Web.PropertyEditors /// /// When multiple values are selected a json array will be posted back so we need to format for storage in - /// the database which is a comma separated ID value + /// the database which is a comma separated string value /// /// /// diff --git a/src/Umbraco.Web/PropertyEditors/RadioButtonsPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/RadioButtonsPropertyEditor.cs index e5d9e4d4e8..46069fec79 100644 --- a/src/Umbraco.Web/PropertyEditors/RadioButtonsPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/RadioButtonsPropertyEditor.cs @@ -8,19 +8,24 @@ namespace Umbraco.Web.PropertyEditors /// /// A property editor to allow the individual selection of pre-defined items. /// - /// - /// Due to remaining backwards compatible, this stores the id of the item in the database which is why it is marked - /// as INT and we have logic in here to ensure it is formatted correctly including ensuring that the INT ID value is published - /// in cache and not the string value. - /// [DataEditor(Constants.PropertyEditors.Aliases.RadioButtonList, "Radio button list", "radiobuttons", ValueType = ValueTypes.Integer, Group="lists", Icon="icon-target")] - public class RadioButtonsPropertyEditor : DropDownWithKeysPropertyEditor + public class RadioButtonsPropertyEditor : DataEditor { + private readonly ILocalizedTextService _textService; + /// /// The constructor will setup the property editor based on the attribute if one is found /// public RadioButtonsPropertyEditor(ILogger logger, ILocalizedTextService textService) - : base(logger, textService) - { } + : base(logger) + { + _textService = textService; + } + + /// + /// Return a custom pre-value editor + /// + /// + protected override IConfigurationEditor CreateConfigurationEditor() => new ValueListConfigurationEditor(_textService); } } diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/FlexibleDropdownPropertyValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/FlexibleDropdownPropertyValueConverter.cs index 521817a7de..d470f54662 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/FlexibleDropdownPropertyValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/FlexibleDropdownPropertyValueConverter.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using Umbraco.Core; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; @@ -16,7 +17,8 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) { - return source?.ToString().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + return source?.ToString().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(v => v.Trim()).ToArray() + ?? Enumerable.Empty(); } public override object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 6f122719d0..313b82de29 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -360,7 +360,6 @@ - @@ -814,11 +813,9 @@ - - @@ -828,9 +825,6 @@ - - - From f332e9a07e13b9a27cb4516cc5efefadb1909a74 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 24 Oct 2018 15:45:24 +1100 Subject: [PATCH 08/53] Fix sql and table check --- .../Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs | 7 +++++-- .../Migrations/Upgrade/V_8_0_0/DropPreValueTable.cs | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs index c9eba205e8..2f50b9cd72 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs @@ -18,7 +18,10 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 public override void Migrate() { //need to convert the old drop down data types to use the new one - var oldDropDowns = Database.Fetch(Sql().Select().Where(x => x.EditorAlias.Contains(".DropDown"))); + var oldDropDowns = Database.Fetch(Sql() + .Select() + .From() + .Where(x => x.EditorAlias.Contains(".DropDown"))); foreach(var dd in oldDropDowns) { //nothing to change if there is no config @@ -37,7 +40,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 continue; } - var propDataSql = Sql().Select("*").From() + var propDataSql = Sql().Select().From() .InnerJoin().On(x => x.Id, x => x.PropertyTypeId) .InnerJoin().On(x => x.NodeId, x => x.DataTypeId) .Where(x => x.EditorAlias == dd.EditorAlias); diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropPreValueTable.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropPreValueTable.cs index 26402cc477..fa6e47fac7 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropPreValueTable.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropPreValueTable.cs @@ -9,7 +9,8 @@ public override void Migrate() { // drop preValues table - Delete.Table("cmsDataTypePreValues").Do(); + if (TableExists("cmsDataTypePreValues")) + Delete.Table("cmsDataTypePreValues").Do(); } } } From df7623e6a7be9d6dfa5056b1b29c81b07ff3cd7c Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 24 Oct 2018 16:32:13 +1100 Subject: [PATCH 09/53] Fixes migration, ensure's that if it runs again it won't corrupt data, fixes flexible drop down angular editor to post string vals, not the int ids, removes old angular drop down prop editor --- .../DropDownPropertyEditorsMigration.cs | 49 +++++++++---- .../dropdown/dropdown.controller.js | 70 ------------------- .../propertyeditors/dropdown/dropdown.html | 20 ------ .../dropdownFlexible.controller.js | 2 +- .../dropdownFlexible/dropdownFlexible.html | 4 +- .../PublishValuesMultipleValueEditor.cs | 2 +- 6 files changed, 38 insertions(+), 109 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdown/dropdown.controller.js delete mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdown/dropdown.html diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs index 2f50b9cd72..accb755020 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs @@ -22,7 +22,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 .Select() .From() .Where(x => x.EditorAlias.Contains(".DropDown"))); - foreach(var dd in oldDropDowns) + foreach (var dd in oldDropDowns) { //nothing to change if there is no config if (dd.Configuration.IsNullOrWhiteSpace()) continue; @@ -30,7 +30,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 ValueListConfiguration config; try { - config = JsonConvert.DeserializeObject(dd.Configuration); + config = JsonConvert.DeserializeObject(dd.Configuration); } catch (Exception ex) { @@ -43,7 +43,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 var propDataSql = Sql().Select().From() .InnerJoin().On(x => x.Id, x => x.PropertyTypeId) .InnerJoin().On(x => x.NodeId, x => x.DataTypeId) - .Where(x => x.EditorAlias == dd.EditorAlias); + .Where(x => x.DataTypeId == dd.NodeId); var propDatas = Database.Query(propDataSql); var toUpdate = new List(); @@ -57,7 +57,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 } //run the property data updates - foreach(var propData in toUpdate) + foreach (var propData in toUpdate) { Database.Update(propData); } @@ -97,12 +97,13 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 items = config.Items }; dataType.Configuration = JsonConvert.SerializeObject(flexConfig); + Database.Update(dataType); } private bool UpdatePropertyDataDto(PropertyDataDto propData, ValueListConfiguration config) { //Get the INT ids stored for this property/drop down - IEnumerable ids = null; + int[] ids = null; if (!propData.VarcharValue.IsNullOrWhiteSpace()) { ids = ConvertStringValues(propData.VarcharValue); @@ -117,31 +118,49 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 } //if there are INT ids, convert them to values based on the configured pre-values - if (ids != null) + if (ids != null && ids.Length > 0) { //map the ids to values var vals = new List(); + var canConvert = true; foreach (var id in ids) { var val = config.Items.FirstOrDefault(x => x.Id == id); if (val != null) vals.Add(val.Value); + else + { + Logger.Warn($"Could not find associated data type configuration for stored Id {id}"); + canConvert = false; + } } - - propData.VarcharValue = string.Join(",", vals); - propData.TextValue = null; - propData.IntegerValue = null; - return true; + if (canConvert) + { + propData.VarcharValue = string.Join(",", vals); + propData.TextValue = null; + propData.IntegerValue = null; + return true; + } + } return false; } - private IEnumerable ConvertStringValues(string val) + private int[] ConvertStringValues(string val) { - return val.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) - .Select(x => int.TryParse(x, out var i) ? i : int.MinValue) - .Where(x => x != int.MinValue); + var splitVals = val.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + + var intVals = splitVals + .Select(x => int.TryParse(x, out var i) ? i : int.MinValue) + .Where(x => x != int.MinValue) + .ToArray(); + + //only return if the number of values are the same (i.e. All INTs) + if (splitVals.Length == intVals.Length) + return intVals; + + return null; } } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdown/dropdown.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdown/dropdown.controller.js deleted file mode 100644 index bcbd13f860..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdown/dropdown.controller.js +++ /dev/null @@ -1,70 +0,0 @@ -angular.module("umbraco").controller("Umbraco.PropertyEditors.DropdownController", - function($scope) { - - //setup the default config - var config = { - items: [], - multiple: false - }; - - //map the user config - angular.extend(config, $scope.model.config); - - //map back to the model - $scope.model.config = config; - - function convertArrayToDictionaryArray(model){ - //now we need to format the items in the dictionary because we always want to have an array - var newItems = []; - for (var i = 0; i < model.length; i++) { - newItems.push({ id: model[i], sortOrder: 0, value: model[i] }); - } - - return newItems; - } - - - function convertObjectToDictionaryArray(model){ - //now we need to format the items in the dictionary because we always want to have an array - var newItems = []; - var vals = _.values($scope.model.config.items); - var keys = _.keys($scope.model.config.items); - - for (var i = 0; i < vals.length; i++) { - var label = vals[i].value ? vals[i].value : vals[i]; - newItems.push({ id: keys[i], sortOrder: vals[i].sortOrder, value: label }); - } - - return newItems; - } - - if (angular.isArray($scope.model.config.items)) { - //PP: I dont think this will happen, but we have tests that expect it to happen.. - //if array is simple values, convert to array of objects - if(!angular.isObject($scope.model.config.items[0])){ - $scope.model.config.items = convertArrayToDictionaryArray($scope.model.config.items); - } - } - else if (angular.isObject($scope.model.config.items)) { - $scope.model.config.items = convertObjectToDictionaryArray($scope.model.config.items); - } - else { - throw "The items property must be either an array or a dictionary"; - } - - - //sort the values - $scope.model.config.items.sort(function (a, b) { return (a.sortOrder > b.sortOrder) ? 1 : ((b.sortOrder > a.sortOrder) ? -1 : 0); }); - - //now we need to check if the value is null/undefined, if it is we need to set it to "" so that any value that is set - // to "" gets selected by default - if ($scope.model.value === null || $scope.model.value === undefined) { - if ($scope.model.config.multiple) { - $scope.model.value = []; - } - else { - $scope.model.value = ""; - } - } - - }); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdown/dropdown.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdown/dropdown.html deleted file mode 100644 index 663373c8eb..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdown/dropdown.html +++ /dev/null @@ -1,20 +0,0 @@ -
- - - - - - -
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.controller.js index c7a472d80e..1ea77ee035 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.controller.js @@ -74,7 +74,7 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.DropdownFlexibleCo // if we run in single mode we'll store the value in a local variable // so we can pass an array as the model as our PropertyValueEditor expects that $scope.model.singleDropdownValue = ""; - if ($scope.model.config.multiple === "0") { + if (Object.toBoolean($scope.model.config.multiple)) { $scope.model.singleDropdownValue = Array.isArray($scope.model.value) ? $scope.model.value[0] : $scope.model.value; } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.html index 5edbab4f30..7c55f0bda9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.html @@ -5,7 +5,7 @@ ng-switch-default ng-change="updateSingleDropdownValue()" ng-model="model.singleDropdownValue" - ng-options="item.id as item.value for item in model.config.items"> + ng-options="item.value as item.value for item in model.config.items"> @@ -15,5 +15,5 @@ ng-switch-when="1" multiple ng-model="model.value" - ng-options="item.id as item.value for item in model.config.items"> + ng-options="item.value as item.value for item in model.config.items"> diff --git a/src/Umbraco.Web/PropertyEditors/PublishValuesMultipleValueEditor.cs b/src/Umbraco.Web/PropertyEditors/PublishValuesMultipleValueEditor.cs index 6aea6edba5..8e0737dedd 100644 --- a/src/Umbraco.Web/PropertyEditors/PublishValuesMultipleValueEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/PublishValuesMultipleValueEditor.cs @@ -25,7 +25,7 @@ namespace Umbraco.Web.PropertyEditors { _logger = logger; } - + /// /// Override so that we can return a json array to the editor for multi-select values /// From b199b7346b107c77ad180c691cfed7ffe52d15e0 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 24 Oct 2018 16:35:30 +1100 Subject: [PATCH 10/53] Fixes multiple selection with drop down --- .../dropdownFlexible/dropdownFlexible.controller.js | 5 ++++- .../propertyeditors/dropdownFlexible/dropdownFlexible.html | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.controller.js index 1ea77ee035..23bd2c9a42 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.controller.js @@ -12,7 +12,10 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.DropdownFlexibleCo //map back to the model $scope.model.config = config; - + + //ensure this is a bool, old data could store zeros/ones or string versions + $scope.model.config.multiple = Object.toBoolean($scope.model.config.multiple); + function convertArrayToDictionaryArray(model){ //now we need to format the items in the dictionary because we always want to have an array var newItems = []; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.html index 7c55f0bda9..3239e64acc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.html @@ -12,7 +12,7 @@ From 9347e6205fe5be3a8d1638d5a9626db5555dc416 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 24 Oct 2018 16:36:13 +1100 Subject: [PATCH 11/53] fixes test --- src/Umbraco.Tests/Composing/TypeLoaderTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs index 9b23ec3d6b..1199b37cdb 100644 --- a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs @@ -279,7 +279,7 @@ AnotherContentFinder public void GetDataEditors() { var types = _typeLoader.GetDataEditors(); - Assert.AreEqual(43, types.Count()); + Assert.AreEqual(39, types.Count()); } /// From b95a994f94339a22701867565f8855d9fae1f262 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 24 Oct 2018 09:12:09 +0200 Subject: [PATCH 12/53] set opacity and disabled cursor when property is locked --- src/Umbraco.Web.UI.Client/src/less/belle.less | 1 + .../src/less/utilities/theme/_opacity.less | 19 +++++++++++++++++++ .../content/umb-tabbed-content.html | 10 ++++++---- 3 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/less/utilities/theme/_opacity.less diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index ac8279e3ab..f5a05c20ee 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -168,6 +168,7 @@ // Utilities @import "utilities/layout/_display.less"; +@import "utilities/theme/_opacity.less"; @import "utilities/typography/_text-decoration.less"; @import "utilities/typography/_white-space.less"; @import "utilities/_flexbox.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/utilities/theme/_opacity.less b/src/Umbraco.Web.UI.Client/src/less/utilities/theme/_opacity.less new file mode 100644 index 0000000000..4550827cdc --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/utilities/theme/_opacity.less @@ -0,0 +1,19 @@ +/* + + Opacity + +*/ + +.o-100 { opacity: 1; } +.o-90 { opacity: 0.9; } +.o-80 { opacity: 0.8; } +.o-70 { opacity: 0.7; } +.o-60 { opacity: 0.6; } +.o-50 { opacity: 0.5; } +.o-40 { opacity: 0.4; } +.o-30 { opacity: 0.3; } +.o-20 { opacity: 0.2; } +.o-10 { opacity: 0.1; } +.o-05 { opacity: 0.05; } +.o-025 { opacity: 0.025; } +.o-0 { opacity: 0; } \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html index 68d5d3d41c..9ab8c509fd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html @@ -22,10 +22,12 @@ - - +
+ + +
From c94775608aaaec61955e614c07af77e4c277d71e Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Wed, 24 Oct 2018 09:18:26 +0100 Subject: [PATCH 13/53] When returning a collection of languages - such as through the GetAllLanguages API Controller call the model mapping ensures that rather than order by name we, also put the default lang first then order a-z --- .../Models/Mapping/LanguageMapperProfile.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Models/Mapping/LanguageMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/LanguageMapperProfile.cs index b305ee2824..6dd6135329 100644 --- a/src/Umbraco.Web/Models/Mapping/LanguageMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/LanguageMapperProfile.cs @@ -28,7 +28,21 @@ namespace Umbraco.Web.Models.Mapping { public IEnumerable Convert(IEnumerable source, IEnumerable destination, ResolutionContext context) { - return source.Select(x => context.Mapper.Map(x, null, context)).OrderBy(x => x.Name); + var langs = source.Select(x => context.Mapper.Map(x, null, context)).ToList(); + + //Put the default language first in the list & then sort rest by a-z + var defaultLang = langs.SingleOrDefault(x => x.IsDefault); + + //Remove the default lang from the list for now + langs.Remove(defaultLang); + + //Sort the remaining languages a-z + langs.OrderBy(x => x.Name); + + //Insert the default lang as the first item + langs.Insert(0, defaultLang); + + return langs; } } } From 30c312c4b09f19c23a34f73dd7467d3ab2b8f87c Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Wed, 24 Oct 2018 10:20:10 +0100 Subject: [PATCH 14/53] CSS & DOM element for overlay to prevent double click through to undelying input element --- .../src/less/components/umb-property-editor.less | 15 +++++++++++++++ .../components/property/umb-property-editor.html | 1 + 2 files changed, 16 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-property-editor.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-property-editor.less index cbea6987e7..5c681d78a8 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-property-editor.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-property-editor.less @@ -1,3 +1,18 @@ .umb-property-editor.-not-clickable { pointer-events: none; } + +.umb-property-editor { + position:relative; +} + +.umb-property-editor .overlay { + background: rgba(255,0,0,0.5); + display: block; + position: absolute; + top:0; + left:0; + height:100%; + width:100%; + cursor: not-allowed; +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-editor.html b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-editor.html index 13a5491184..22d5764da4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-editor.html @@ -1,3 +1,4 @@
+
From ba0d96ce2a2e8d6e235b931a66fe450e7d68e99b Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Wed, 24 Oct 2018 10:36:45 +0100 Subject: [PATCH 15/53] WIP for CSS overlay click through prevention stuff with Z-indexes --- .../src/less/components/umb-property-editor.less | 13 ++++++++++--- .../components/property/umb-property-editor.html | 4 ++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-property-editor.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-property-editor.less index 5c681d78a8..c1268b3410 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-property-editor.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-property-editor.less @@ -1,13 +1,18 @@ .umb-property-editor.-not-clickable { - pointer-events: none; + // pointer-events: none; } .umb-property-editor { position:relative; } -.umb-property-editor .overlay { - background: rgba(255,0,0,0.5); +.umb-property-editor__view{ + z-index:1; + position:relative; +} + +.umb-property-editor__overlay { + background: rgba(255,0,255,0.5); display: block; position: absolute; top:0; @@ -15,4 +20,6 @@ height:100%; width:100%; cursor: not-allowed; + user-select: none; + z-index:2; } \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-editor.html b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-editor.html index 22d5764da4..4b43d9634c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-editor.html @@ -1,4 +1,4 @@
-
-
+
+
From 62716fb417217ad86c1c21086c95976d165b5a52 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Wed, 24 Oct 2018 11:03:41 +0100 Subject: [PATCH 16/53] WIP disable tab index directive --- .../util/disabletabindex.directive.js | 85 ++++++++++++------- .../less/components/umb-property-editor.less | 1 - .../content/umb-tabbed-content.html | 10 +-- .../property/umb-property-editor.html | 6 +- .../views/components/umb-groups-builder.html | 3 +- 5 files changed, 64 insertions(+), 41 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js index 624404c641..8a32f3b089 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js @@ -3,55 +3,78 @@ angular.module("umbraco.directives") return { restrict: 'A', //Can only be used as an attribute + scope: { disableTabindex: "<"}, link: function (scope, element, attrs) { - //Use DOM Mutation Observer - //Select the node that will be observed for mutations (native DOM element not jQLite version) - var targetNode = element[0]; + function enableTest(){ + //Add in observer code - // Options for the observer (which mutations to observe) - var config = { attributes: false, childList: true, subtree: false }; + //Use DOM Mutation Observer + //Select the node that will be observed for mutations (native DOM element not jQLite version) + var targetNode = element[0]; - // Callback function to execute when mutations are observed - var callback = function(mutationsList, observer) { - for(var mutation of mutationsList) { + // Options for the observer (which mutations to observe) + var config = { attributes: false, childList: true, subtree: false }; - console.log('mutation', mutation); + // Callback function to execute when mutations are observed + var callback = function(mutationsList, observer) { + for(var mutation of mutationsList) { - //DOM items have been added or removed - if (mutation.type == 'childList') { + console.log('mutation', mutation); - //Check if any child items in mutation.target contain an input - var jqLiteEl = angular.element(mutation.target); - var childInputs = jqLiteEl.find('input'); + //DOM items have been added or removed + if (mutation.type == 'childList') { - //For each item in childInputs - override or set HTML attribute tabindex="-1" - angular.forEach(childInputs, function(element){ - console.log('item in loop', element); + //Check if any child items in mutation.target contain an input + var jqLiteEl = angular.element(mutation.target); + var childInputs = jqLiteEl.find('input'); - //TODO: Note we updating way too many times from the DOMSubtreeModified event - is this expensive? - angular.element(element).attr('tabindex', '-1'); - }); + //For each item in childInputs - override or set HTML attribute tabindex="-1" + angular.forEach(childInputs, function(element){ + console.log('item in loop', element); + + //TODO: Get existing element & it's tabindex (if any set) + + + //TODO: Note we updating way too many times from the DOMSubtreeModified event - is this expensive? + angular.element(element).attr('tabindex', '-1'); + }); + } } - } + }; + + // Create an observer instance linked to the callback function + var observer = new MutationObserver(callback); + + // Start observing the target node for configured mutations + //GO GO GO + observer.observe(targetNode, config); }; - // Create an observer instance linked to the callback function - var observer = new MutationObserver(callback); - // Start observing the target node for configured mutations - //GO GO GO - observer.observe(targetNode, config); + scope.$watch('disableTabindex',(newVal, oldVal) =>{ + console.log('new val', newVal); + + if(newVal === true){ + enableTest(); + }else{ + + } + }); + + + + //TODO: Unsure if we need to do this - to ensure the browser not trying to notify us still //When we browse away from the page - element.on('$destroy', function(e){ - console.log('element with disable-tabindex attribute is destoryed'); + // element.on('$destroy', function(e){ + // console.log('element with disable-tabindex attribute is destoryed'); - //Remove/stop the observer - observer.disconnect(); - }); + // //Remove/stop the observer + // observer.disconnect(); + // }); } }; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-property-editor.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-property-editor.less index c1268b3410..b019892a9f 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-property-editor.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-property-editor.less @@ -12,7 +12,6 @@ } .umb-property-editor__overlay { - background: rgba(255,0,255,0.5); display: block; position: absolute; top:0; diff --git a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html index 9ab8c509fd..7663aa8997 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html @@ -9,9 +9,9 @@
- -
- + +
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-editor.html b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-editor.html index 4b43d9634c..8674e4f6d3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-editor.html @@ -1,4 +1,6 @@
-
-
+
+
+
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-groups-builder.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-groups-builder.html index 8732da91e7..c976899cb8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-groups-builder.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-groups-builder.html @@ -232,8 +232,7 @@ + preview="true"> From a5b7e8b0ae1240d3d01a2b6453a216e80c6f45c4 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Wed, 24 Oct 2018 11:57:02 +0100 Subject: [PATCH 17/53] WIP put the tabindex back to what it was before --- .../util/disabletabindex.directive.js | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js index 8a32f3b089..71d9ff0e5d 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js @@ -6,6 +6,8 @@ angular.module("umbraco.directives") scope: { disableTabindex: "<"}, link: function (scope, element, attrs) { + var tabIndexesToRollback = []; + function enableTest(){ //Add in observer code @@ -34,7 +36,17 @@ angular.module("umbraco.directives") console.log('item in loop', element); //TODO: Get existing element & it's tabindex (if any set) + //Check if the element has an existing tab index + //If so store in a collection (that when this directive is disabled/toggled) + //The tabindex is returned back to normal + var currentTabIndex = angular.element(element).attr('tabindex'); + console.log('currentTabIndex', currentTabIndex); + if(currentTabIndex){ + //A value has been set - need to track it + var itemToRevert = { dom: element, tabindex: currentTabIndex }; + tabIndexesToRollback.push(itemToRevert); + } //TODO: Note we updating way too many times from the DOMSubtreeModified event - is this expensive? angular.element(element).attr('tabindex', '-1'); @@ -58,6 +70,15 @@ angular.module("umbraco.directives") if(newVal === true){ enableTest(); }else{ + console.log('what do I need to revert', tabIndexesToRollback); + + //Stop observation? + //TODO: Will it refire the observer?! + + angular.forEach(tabIndexesToRollback, function(rollbackItem){ + console.log('item in rollback', rollbackItem); + angular.element(rollbackItem.dom).attr('tabindex', rollbackItem.tabindex); + }); } }); From 5b84702c2f10c764a3e87d8fb1c0539d11dc8b18 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Wed, 24 Oct 2018 13:37:33 +0100 Subject: [PATCH 18/53] Cleanup of disable tab directive & adds in tabbable 3rd party lib (wrapped as an AngularJS service) --- .../util/disabletabindex.directive.js | 105 ++------- .../src/common/services/tabbable.service.js | 223 ++++++++++++++++++ .../property/umb-property-editor.html | 8 +- 3 files changed, 248 insertions(+), 88 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/common/services/tabbable.service.js diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js index 71d9ff0e5d..35b49d0ddf 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js @@ -1,101 +1,40 @@ angular.module("umbraco.directives") - .directive('disableTabindex', function () { + .directive('disableTabindex', function (tabbableService) { return { restrict: 'A', //Can only be used as an attribute - scope: { disableTabindex: "<"}, link: function (scope, element, attrs) { - var tabIndexesToRollback = []; + //Select the node that will be observed for mutations (native DOM element not jQLite version) + var targetNode = element[0]; - function enableTest(){ - //Add in observer code + //Watch for DOM changes - so when the property editor subview loads in + //We can be notified its updated the child elements inside the DIV we are watching + var observer = new MutationObserver(domChange); - //Use DOM Mutation Observer - //Select the node that will be observed for mutations (native DOM element not jQLite version) - var targetNode = element[0]; + // Options for the observer (which mutations to observe) + var config = { attributes: true, childList: true, subtree: false }; - // Options for the observer (which mutations to observe) - var config = { attributes: false, childList: true, subtree: false }; + function domChange(mutationsList, observer){ + for(var mutation of mutationsList) { - // Callback function to execute when mutations are observed - var callback = function(mutationsList, observer) { - for(var mutation of mutationsList) { + //DOM items have been added or removed + if (mutation.type == 'childList') { - console.log('mutation', mutation); + //Check if any child items in mutation.target contain an input + var childInputs = tabbableService.tabbable(mutation.target); - //DOM items have been added or removed - if (mutation.type == 'childList') { - - //Check if any child items in mutation.target contain an input - var jqLiteEl = angular.element(mutation.target); - var childInputs = jqLiteEl.find('input'); - - //For each item in childInputs - override or set HTML attribute tabindex="-1" - angular.forEach(childInputs, function(element){ - console.log('item in loop', element); - - //TODO: Get existing element & it's tabindex (if any set) - //Check if the element has an existing tab index - //If so store in a collection (that when this directive is disabled/toggled) - //The tabindex is returned back to normal - var currentTabIndex = angular.element(element).attr('tabindex'); - console.log('currentTabIndex', currentTabIndex); - - if(currentTabIndex){ - //A value has been set - need to track it - var itemToRevert = { dom: element, tabindex: currentTabIndex }; - tabIndexesToRollback.push(itemToRevert); - } - - //TODO: Note we updating way too many times from the DOMSubtreeModified event - is this expensive? - angular.element(element).attr('tabindex', '-1'); - }); - } + //For each item in childInputs - override or set HTML attribute tabindex="-1" + angular.forEach(childInputs, function(element){ + angular.element(element).attr('tabindex', '-1'); + }); } - }; - - // Create an observer instance linked to the callback function - var observer = new MutationObserver(callback); - - // Start observing the target node for configured mutations - //GO GO GO - observer.observe(targetNode, config); - }; - - - scope.$watch('disableTabindex',(newVal, oldVal) =>{ - console.log('new val', newVal); - - if(newVal === true){ - enableTest(); - }else{ - console.log('what do I need to revert', tabIndexesToRollback); - - //Stop observation? - //TODO: Will it refire the observer?! - - angular.forEach(tabIndexesToRollback, function(rollbackItem){ - console.log('item in rollback', rollbackItem); - angular.element(rollbackItem.dom).attr('tabindex', rollbackItem.tabindex); - }); - } - }); + } - - - - - - //TODO: Unsure if we need to do this - to ensure the browser not trying to notify us still - //When we browse away from the page - // element.on('$destroy', function(e){ - // console.log('element with disable-tabindex attribute is destoryed'); - - // //Remove/stop the observer - // observer.disconnect(); - // }); + // Start observing the target node for configured mutations + //GO GO GO + observer.observe(targetNode, config); } }; diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tabbable.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tabbable.service.js new file mode 100644 index 0000000000..4d8d5f68f3 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/services/tabbable.service.js @@ -0,0 +1,223 @@ +//tabbable JS Lib (Wrapped in angular service) +//https://github.com/davidtheclark/tabbable + +(function() { + 'use strict'; + + function tabbableService() { + + var candidateSelectors = [ + 'input', + 'select', + 'textarea', + 'a[href]', + 'button', + '[tabindex]', + 'audio[controls]', + 'video[controls]', + '[contenteditable]:not([contenteditable="false"])' + ]; + var candidateSelector = candidateSelectors.join(','); + + var matches = typeof Element === 'undefined' + ? function () {} + : Element.prototype.matches || Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector; + + function tabbable(el, options) { + options = options || {}; + + var elementDocument = el.ownerDocument || el; + var regularTabbables = []; + var orderedTabbables = []; + + var untouchabilityChecker = new UntouchabilityChecker(elementDocument); + var candidates = el.querySelectorAll(candidateSelector); + + if (options.includeContainer) { + if (matches.call(el, candidateSelector)) { + candidates = Array.prototype.slice.apply(candidates); + candidates.unshift(el); + } + } + + var i, candidate, candidateTabindex; + for (i = 0; i < candidates.length; i++) { + candidate = candidates[i]; + + if (!isNodeMatchingSelectorTabbable(candidate, untouchabilityChecker)) continue; + + candidateTabindex = getTabindex(candidate); + if (candidateTabindex === 0) { + regularTabbables.push(candidate); + } else { + orderedTabbables.push({ + documentOrder: i, + tabIndex: candidateTabindex, + node: candidate + }); + } + } + + var tabbableNodes = orderedTabbables + .sort(sortOrderedTabbables) + .map(function(a) { return a.node }) + .concat(regularTabbables); + + return tabbableNodes; + } + + tabbable.isTabbable = isTabbable; + tabbable.isFocusable = isFocusable; + + function isNodeMatchingSelectorTabbable(node, untouchabilityChecker) { + if ( + !isNodeMatchingSelectorFocusable(node, untouchabilityChecker) + || isNonTabbableRadio(node) + || getTabindex(node) < 0 + ) { + return false; + } + return true; + } + + function isTabbable(node, untouchabilityChecker) { + if (!node) throw new Error('No node provided'); + if (matches.call(node, candidateSelector) === false) return false; + return isNodeMatchingSelectorTabbable(node, untouchabilityChecker); + } + + function isNodeMatchingSelectorFocusable(node, untouchabilityChecker) { + untouchabilityChecker = untouchabilityChecker || new UntouchabilityChecker(node.ownerDocument || node); + if ( + node.disabled + || isHiddenInput(node) + || untouchabilityChecker.isUntouchable(node) + ) { + return false; + } + return true; + } + + var focusableCandidateSelector = candidateSelectors.concat('iframe').join(','); + function isFocusable(node, untouchabilityChecker) { + if (!node) throw new Error('No node provided'); + if (matches.call(node, focusableCandidateSelector) === false) return false; + return isNodeMatchingSelectorFocusable(node, untouchabilityChecker); + } + + function getTabindex(node) { + var tabindexAttr = parseInt(node.getAttribute('tabindex'), 10); + if (!isNaN(tabindexAttr)) return tabindexAttr; + // Browsers do not return `tabIndex` correctly for contentEditable nodes; + // so if they don't have a tabindex attribute specifically set, assume it's 0. + if (isContentEditable(node)) return 0; + return node.tabIndex; + } + + function sortOrderedTabbables(a, b) { + return a.tabIndex === b.tabIndex ? a.documentOrder - b.documentOrder : a.tabIndex - b.tabIndex; + } + + // Array.prototype.find not available in IE. + function find(list, predicate) { + for (var i = 0, length = list.length; i < length; i++) { + if (predicate(list[i])) return list[i]; + } + } + + function isContentEditable(node) { + return node.contentEditable === 'true'; + } + + function isInput(node) { + return node.tagName === 'INPUT'; + } + + function isHiddenInput(node) { + return isInput(node) && node.type === 'hidden'; + } + + function isRadio(node) { + return isInput(node) && node.type === 'radio'; + } + + function isNonTabbableRadio(node) { + return isRadio(node) && !isTabbableRadio(node); + } + + function getCheckedRadio(nodes) { + for (var i = 0; i < nodes.length; i++) { + if (nodes[i].checked) { + return nodes[i]; + } + } + } + + function isTabbableRadio(node) { + if (!node.name) return true; + // This won't account for the edge case where you have radio groups with the same + // in separate forms on the same page. + var radioSet = node.ownerDocument.querySelectorAll('input[type="radio"][name="' + node.name + '"]'); + var checked = getCheckedRadio(radioSet); + return !checked || checked === node; + } + + // An element is "untouchable" if *it or one of its ancestors* has + // `visibility: hidden` or `display: none`. + function UntouchabilityChecker(elementDocument) { + this.doc = elementDocument; + // Node cache must be refreshed on every check, in case + // the content of the element has changed. The cache contains tuples + // mapping nodes to their boolean result. + this.cache = []; + } + + // getComputedStyle accurately reflects `visibility: hidden` of ancestors + // but not `display: none`, so we need to recursively check parents. + UntouchabilityChecker.prototype.hasDisplayNone = function hasDisplayNone(node, nodeComputedStyle) { + if (node === this.doc.documentElement) return false; + + // Search for a cached result. + var cached = find(this.cache, function(item) { + return item === node; + }); + if (cached) return cached[1]; + + nodeComputedStyle = nodeComputedStyle || this.doc.defaultView.getComputedStyle(node); + + var result = false; + + if (nodeComputedStyle.display === 'none') { + result = true; + } else if (node.parentNode) { + result = this.hasDisplayNone(node.parentNode); + } + + this.cache.push([node, result]); + + return result; + } + + UntouchabilityChecker.prototype.isUntouchable = function isUntouchable(node) { + if (node === this.doc.documentElement) return false; + var computedStyle = this.doc.defaultView.getComputedStyle(node); + if (this.hasDisplayNone(node, computedStyle)) return true; + return computedStyle.visibility === 'hidden'; + } + + //module.exports = tabbable; + + //////////// + + var service = { + tabbable: tabbable, + isTabbable: isTabbable, + isFocusable: isFocusable + }; + + return service; + } + + angular.module('umbraco.services').factory('tabbableService', tabbableService); + + })(); diff --git a/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-editor.html b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-editor.html index 8674e4f6d3..cb82a83168 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-editor.html @@ -1,6 +1,4 @@ -
-
-
-
-
+
+
+
From e2557697bcee66cc4075a071a5c573eae4d1d659 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Wed, 24 Oct 2018 14:08:15 +0100 Subject: [PATCH 19/53] Emit a single event if the language has been created or been saved - send through a new property in args to determine if it was new or not Then event listener updates the tree languages dropdown only when a new language has been added or deleted OR when an existing language has been saved/updated to be set as the new default language (as this now at top of list) --- .../src/controllers/navigation.controller.js | 21 ++++++++++++++----- .../src/views/languages/edit.controller.js | 12 +++++------ 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/controllers/navigation.controller.js b/src/Umbraco.Web.UI.Client/src/controllers/navigation.controller.js index 2912755ce7..5d8f46c6db 100644 --- a/src/Umbraco.Web.UI.Client/src/controllers/navigation.controller.js +++ b/src/Umbraco.Web.UI.Client/src/controllers/navigation.controller.js @@ -194,7 +194,7 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar $rootScope.emptySection = false; } } - + })); //Listen for section state changes @@ -222,10 +222,21 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar }); })); - evts.push(eventsService.on("editors.languages.languageCreated", function (e, args) { - loadLanguages().then(function (languages) { - $scope.languages = languages; - }); + //Emitted when a language is created or an existing one saved/edited + evts.push(eventsService.on("editors.languages.languageSaved", function (e, args) { + console.log('lang event listen args', args); + if(args.isNew){ + //A new language has been created - reload languages for tree + loadLanguages().then(function (languages) { + $scope.languages = languages; + }); + } + else if(args.language.isDefault){ + //A language was saved and was set to be the new default (refresh the tree, so its at the top) + loadLanguages().then(function (languages) { + $scope.languages = languages; + }); + } })); //This reacts to clicks passed to the body element which emits a global call to close all dialogs diff --git a/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js index 961470b03a..a11aa8bff8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js @@ -113,11 +113,9 @@ notificationsService.success(value); }); - // emit event when language is created - if($routeParams.create) { - var args = { language: lang }; - eventsService.emit("editors.languages.languageCreated", args); - } + // emit event when language is created or updated/saved + var args = { language: lang, isNew: $routeParams.create ? true : false }; + eventsService.emit("editors.languages.languageSaved", args); back(); @@ -129,7 +127,7 @@ }); } - + } function back() { @@ -145,7 +143,7 @@ } function toggleDefault() { - + // it shouldn't be possible to uncheck the default language if(vm.initIsDefault) { return; From 9a1a13ba81f601919f87e4a24dc63d6183ee77c8 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Wed, 24 Oct 2018 14:55:01 +0100 Subject: [PATCH 20/53] Fix the ordering logic - it helps if you actually assign the ordered items back to the list *facepalm* --- .../Models/Mapping/ContentItemDisplayVariationResolver.cs | 2 +- src/Umbraco.Web/Models/Mapping/LanguageMapperProfile.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs index 562797e3e3..7db491ad2e 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentItemDisplayVariationResolver.cs @@ -59,7 +59,7 @@ namespace Umbraco.Web.Models.Mapping variants.Remove(defaultLang); //Sort the remaining languages a-z - variants.OrderBy(x => x.Language.Name); + variants = variants.OrderBy(x => x.Name).ToList(); //Insert the default lang as the first item variants.Insert(0, defaultLang); diff --git a/src/Umbraco.Web/Models/Mapping/LanguageMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/LanguageMapperProfile.cs index 6dd6135329..f820d5ae54 100644 --- a/src/Umbraco.Web/Models/Mapping/LanguageMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/LanguageMapperProfile.cs @@ -37,7 +37,7 @@ namespace Umbraco.Web.Models.Mapping langs.Remove(defaultLang); //Sort the remaining languages a-z - langs.OrderBy(x => x.Name); + langs = langs.OrderBy(x => x.Name).ToList(); //Insert the default lang as the first item langs.Insert(0, defaultLang); From 4420f101d2a8654c6201f58d0144a2e6abbe3db0 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 24 Oct 2018 20:41:58 +0200 Subject: [PATCH 21/53] add toggle to disable tabindex directive --- .../util/disabletabindex.directive.js | 49 ++++++++++--------- .../property/umb-property-editor.html | 8 +-- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js index 35b49d0ddf..800efb8c28 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js @@ -2,39 +2,44 @@ angular.module("umbraco.directives") .directive('disableTabindex', function (tabbableService) { return { - restrict: 'A', //Can only be used as an attribute + restrict: 'A', //Can only be used as an attribute, + scope: { + "disableTabindex": "<" + }, link: function (scope, element, attrs) { - //Select the node that will be observed for mutations (native DOM element not jQLite version) - var targetNode = element[0]; + if(scope.disableTabindex) { + //Select the node that will be observed for mutations (native DOM element not jQLite version) + var targetNode = element[0]; - //Watch for DOM changes - so when the property editor subview loads in - //We can be notified its updated the child elements inside the DIV we are watching - var observer = new MutationObserver(domChange); + //Watch for DOM changes - so when the property editor subview loads in + //We can be notified its updated the child elements inside the DIV we are watching + var observer = new MutationObserver(domChange); - // Options for the observer (which mutations to observe) - var config = { attributes: true, childList: true, subtree: false }; + // Options for the observer (which mutations to observe) + var config = { attributes: true, childList: true, subtree: false }; - function domChange(mutationsList, observer){ - for(var mutation of mutationsList) { + function domChange(mutationsList, observer){ + for(var mutation of mutationsList) { - //DOM items have been added or removed - if (mutation.type == 'childList') { + //DOM items have been added or removed + if (mutation.type == 'childList') { - //Check if any child items in mutation.target contain an input - var childInputs = tabbableService.tabbable(mutation.target); + //Check if any child items in mutation.target contain an input + var childInputs = tabbableService.tabbable(mutation.target); - //For each item in childInputs - override or set HTML attribute tabindex="-1" - angular.forEach(childInputs, function(element){ - angular.element(element).attr('tabindex', '-1'); - }); + //For each item in childInputs - override or set HTML attribute tabindex="-1" + angular.forEach(childInputs, function(element){ + angular.element(element).attr('tabindex', '-1'); + }); + } } } - } - // Start observing the target node for configured mutations - //GO GO GO - observer.observe(targetNode, config); + // Start observing the target node for configured mutations + //GO GO GO + observer.observe(targetNode, config); + } } }; diff --git a/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-editor.html b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-editor.html index cb82a83168..8674e4f6d3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-editor.html @@ -1,4 +1,6 @@ -
-
-
+
+
+
+
+
From 2137c1026b1bcfa674cb14ef3940980af144068e Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 24 Oct 2018 20:44:07 +0200 Subject: [PATCH 22/53] css cleanup --- .../less/components/umb-property-editor.less | 21 +++++++------------ .../property/umb-property-editor.html | 2 +- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-property-editor.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-property-editor.less index b019892a9f..a3e094d9be 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-property-editor.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-property-editor.less @@ -1,24 +1,19 @@ -.umb-property-editor.-not-clickable { - // pointer-events: none; -} - .umb-property-editor { - position:relative; + position: relative; } .umb-property-editor__view{ - z-index:1; - position:relative; + z-index: 1; + position: relative; } .umb-property-editor__overlay { display: block; position: absolute; - top:0; - left:0; - height:100%; - width:100%; - cursor: not-allowed; + top: 0; + left: 0; + height: 100%; + width: 100%; user-select: none; - z-index:2; + z-index: 2; } \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-editor.html b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-editor.html index 8674e4f6d3..820721df9e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property-editor.html @@ -1,4 +1,4 @@ -
+
From b63b25414c4f52bed57b8929c06fdbdd06a02375 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 25 Oct 2018 09:38:46 +0200 Subject: [PATCH 23/53] show inherted from label --- .../content/umbtabbedcontent.directive.js | 4 ++++ .../property/umbproperty.directive.js | 4 +++- .../content/umb-tabbed-content.html | 19 ++++++------------- .../components/property/umb-property.html | 4 ++++ 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbtabbedcontent.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbtabbedcontent.directive.js index 5176f65801..f83f441d66 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbtabbedcontent.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbtabbedcontent.directive.js @@ -17,6 +17,10 @@ return variant.active; }); + $scope.defaultVariant = _.find(this.content.variants, variant => { + return variant.language.isDefault; + }); + $scope.unlockInvariantValue = function(property) { property.unlockInvariantValue = !property.unlockInvariantValue; }; diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js index 69457a6f10..302378b8c0 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js @@ -7,7 +7,9 @@ angular.module("umbraco.directives") .directive('umbProperty', function (umbPropEditorHelper, userService) { return { scope: { - property: "=" + property: "=", + showInherit: "<", + inheritsFrom: "<" }, transclude: true, restrict: 'E', diff --git a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html index 7663aa8997..cdacea7cf1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html @@ -8,19 +8,12 @@
- - - +
/// - public void ExecuteLegacyJs(string jsToExecute) + public void ExecuteJsMethod(string jsToExecute) { SetJsAction(jsToExecute); } @@ -201,7 +207,7 @@ namespace Umbraco.Web.Models.Trees } } } - + #endregion } diff --git a/src/Umbraco.Web/Models/Trees/MenuItemList.cs b/src/Umbraco.Web/Models/Trees/MenuItemList.cs index 170042b151..1a8a2dd88c 100644 --- a/src/Umbraco.Web/Models/Trees/MenuItemList.cs +++ b/src/Umbraco.Web/Models/Trees/MenuItemList.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; using Umbraco.Core; +using Umbraco.Core.Services; +using Umbraco.Web.Actions; using Umbraco.Web.Composing; -using Umbraco.Web._Legacy.Actions; + namespace Umbraco.Web.Models.Trees { @@ -24,7 +26,7 @@ namespace Umbraco.Web.Models.Trees } /// - /// Adds a menu item + /// Adds a menu item based on a /// /// /// The text to display for the menu item, will default to the IAction alias if not specified @@ -32,65 +34,30 @@ namespace Umbraco.Web.Models.Trees { var item = new MenuItem(action, name); - DetectLegacyActionMenu(action.GetType(), item); - Add(item); return item; } /// - /// Adds a menu item - /// - /// - /// - /// - /// The text to display for the menu item, will default to the IAction alias if not specified - /// - /// - public TMenuItem Add(string name, bool hasSeparator = false, IDictionary additionalData = null) - where TAction : IAction - where TMenuItem : MenuItem, new() - { - var item = CreateMenuItem(name, hasSeparator, additionalData); - if (item == null) return null; - - var customMenuItem = new TMenuItem - { - Name = item.Name, - Alias = item.Alias, - SeperatorBefore = hasSeparator, - Icon = item.Icon, - Action = item.Action - }; - - Add(customMenuItem); - - return customMenuItem; - } - - /// - /// Adds a menu item + /// Adds a menu item based on an /// /// The text to display for the menu item, will default to the IAction alias if not specified /// public MenuItem Add(string name) where T : IAction { - return Add(name, false, null); + return Add(name, false); } /// - /// Adds a menu item with a key value pair which is merged to the AdditionalData bag + /// Adds a menu item based on an /// + /// The used to localize the action name based on it's alias /// - /// - /// - /// The text to display for the menu item, will default to the IAction alias if not specified - /// - public MenuItem Add(string name, string key, string value, bool hasSeparator = false) + public MenuItem Add(ILocalizedTextService textService) where T : IAction { - return Add(name, hasSeparator, new Dictionary { { key, value } }); + return Add(textService, false); } /// @@ -98,12 +65,11 @@ namespace Umbraco.Web.Models.Trees /// /// /// - /// /// The text to display for the menu item, will default to the IAction alias if not specified - /// - public MenuItem Add(string name, bool hasSeparator = false, IDictionary additionalData = null) + /// The text to display for the menu item, will default to the IAction alias if not specified + public MenuItem Add(string name, bool hasSeparator = false) where T : IAction { - var item = CreateMenuItem(name, hasSeparator, additionalData); + var item = CreateMenuItem(name, hasSeparator); if (item != null) { Add(item); @@ -113,69 +79,49 @@ namespace Umbraco.Web.Models.Trees } /// - /// + /// Adds a menu item with a dictionary which is merged to the AdditionalData bag /// /// /// - /// The text to display for the menu item, will default to the IAction alias if not specified - /// - /// - internal MenuItem CreateMenuItem(string name, bool hasSeparator = false, IDictionary additionalData = null) + /// The used to localize the action name based on it's alias + public MenuItem Add(ILocalizedTextService textService, bool hasSeparator = false) where T : IAction { - var item = Current.Actions.GetAction(); + var item = CreateMenuItem(textService, hasSeparator); if (item != null) { - var menuItem = new MenuItem(item, name) - { - SeperatorBefore = hasSeparator - }; - - if (additionalData != null) - { - foreach (var i in additionalData) - { - menuItem.AdditionalData[i.Key] = i.Value; - } - } - - DetectLegacyActionMenu(typeof(T), menuItem); - - //TODO: Once we implement 'real' menu items, not just IActions we can implement this since - // people may need to pass specific data to their menu items - - ////validate the data in the meta data bag - //item.ValidateRequiredData(AdditionalData); - - return menuItem; + Add(item); + return item; } return null; } - - /// - /// Checks if the IAction type passed in is attributed with LegacyActionMenuItemAttribute and if so - /// ensures that the correct action metadata is added. - /// - /// - /// - private void DetectLegacyActionMenu(Type actionType, MenuItem menuItem) + + internal MenuItem CreateMenuItem(string name, bool hasSeparator = false) + where T : IAction { - //This checks for legacy IActions that have the LegacyActionMenuItemAttribute which is a legacy hack - // to make old IAction actions work in v7 by mapping to the JS used by the new menu items - var attribute = actionType.GetCustomAttribute(false); - if (attribute != null) + var item = Current.Actions.GetAction(); + if (item == null) return null; + var menuItem = new MenuItem(item, name) { - //add the current type to the metadata - if (attribute.MethodName.IsNullOrWhiteSpace()) - { - //if no method name is supplied we will assume that the menu action is the type name of the current menu class - menuItem.AdditionalData.Add(MenuItem.JsActionKey, string.Format("{0}.{1}", attribute.ServiceName, this.GetType().Name)); - } - else - { - menuItem.AdditionalData.Add(MenuItem.JsActionKey, string.Format("{0}.{1}", attribute.ServiceName, attribute.MethodName)); - } - } + SeperatorBefore = hasSeparator + }; + + return menuItem; } + + internal MenuItem CreateMenuItem(ILocalizedTextService textService, bool hasSeparator = false) + where T : IAction + { + var item = Current.Actions.GetAction(); + if (item == null) return null; + + var menuItem = new MenuItem(item, textService.Localize($"actions/{item.Alias}")) + { + SeperatorBefore = hasSeparator + }; + + return menuItem; + } + } } diff --git a/src/Umbraco.Web/Models/Trees/RefreshNode.cs b/src/Umbraco.Web/Models/Trees/RefreshNode.cs index 7f20562962..587c51b887 100644 --- a/src/Umbraco.Web/Models/Trees/RefreshNode.cs +++ b/src/Umbraco.Web/Models/Trees/RefreshNode.cs @@ -1,10 +1,29 @@ -namespace Umbraco.Web.Models.Trees +using Umbraco.Core.Services; + +namespace Umbraco.Web.Models.Trees { + /// /// /// Represents the refresh node menu item /// - [ActionMenuItem("umbracoMenuActions")] public sealed class RefreshNode : ActionMenuItem { + public override string AngularServiceName => "umbracoMenuActions"; + + public RefreshNode(string name, bool seperatorBefore = false) + { + Alias = "refreshNode"; + Icon = "refresh"; + Name = name; + SeperatorBefore = seperatorBefore; + } + + public RefreshNode(ILocalizedTextService textService, bool seperatorBefore = false) + { + Alias = "refreshNode"; + Icon = "refresh"; + Name = textService.Localize($"actions/{Alias}"); + SeperatorBefore = seperatorBefore; + } } } diff --git a/src/Umbraco.Web/NotificationServiceExtensions.cs b/src/Umbraco.Web/NotificationServiceExtensions.cs index 1fb229a66e..4be7b4f091 100644 --- a/src/Umbraco.Web/NotificationServiceExtensions.cs +++ b/src/Umbraco.Web/NotificationServiceExtensions.cs @@ -6,9 +6,10 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models.Membership; using Umbraco.Core.Services; using Umbraco.Core.Models; -using Umbraco.Web._Legacy.Actions; + using System.Collections.Generic; using Umbraco.Core.Models.Entities; +using Umbraco.Web.Actions; using Umbraco.Web.Composing; namespace Umbraco.Web diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs index 028f1bf728..1af24db636 100644 --- a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs +++ b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs @@ -31,6 +31,7 @@ using Umbraco.Core.PropertyEditors.ValueConverters; using Umbraco.Core.Runtime; using Umbraco.Core.Services; using Umbraco.Examine; +using Umbraco.Web.Actions; using Umbraco.Web.Cache; using Umbraco.Web.Composing.CompositionRoots; using Umbraco.Web.ContentApps; @@ -53,7 +54,7 @@ using Umbraco.Web.Tour; using Umbraco.Web.Trees; using Umbraco.Web.UI.JavaScript; using Umbraco.Web.WebApi; -using Umbraco.Web._Legacy.Actions; + using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Web.Runtime @@ -139,7 +140,7 @@ namespace Umbraco.Web.Runtime Current.DefaultRenderMvcControllerType = typeof(RenderMvcController); // fixme WRONG! composition.Container.RegisterCollectionBuilder() - .SetProducer(() => typeLoader.GetActions()); + .Add(() => typeLoader.GetTypes()); var surfaceControllerTypes = new SurfaceControllerTypeCollection(typeLoader.GetSurfaceControllers()); composition.Container.RegisterInstance(surfaceControllerTypes); diff --git a/src/Umbraco.Web/Trees/ContentBlueprintTreeController.cs b/src/Umbraco.Web/Trees/ContentBlueprintTreeController.cs index 5b45888dca..01df73f4af 100644 --- a/src/Umbraco.Web/Trees/ContentBlueprintTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentBlueprintTreeController.cs @@ -4,10 +4,11 @@ using Umbraco.Core; using Umbraco.Core.Services; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; +using Umbraco.Web.Actions; using Umbraco.Web.Models.Trees; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; -using Umbraco.Web._Legacy.Actions; + using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Trees @@ -92,9 +93,8 @@ namespace Umbraco.Web.Trees if (id == Constants.System.Root.ToInvariantString()) { // root actions - menu.Items.Add(Services.TextService.Localize($"actions/{ActionNew.Instance.Alias}")); - menu.Items.Add(Services.TextService.Localize( - $"actions/{ActionRefresh.Instance.Alias}"), true); + menu.Items.Add(Services.TextService.Localize($"actions/{ActionNew.ActionAlias}")); + menu.Items.Add(new RefreshNode(Services.TextService, true)); return menu; } var cte = Services.EntityService.Get(int.Parse(id), UmbracoObjectTypes.DocumentType); @@ -102,15 +102,15 @@ namespace Umbraco.Web.Trees if (cte != null) { var ct = Services.ContentTypeService.Get(cte.Id); - var createItem = menu.Items.Add(Services.TextService.Localize($"actions/{ActionCreateBlueprintFromContent.Instance.Alias}")); + var createItem = menu.Items.Add(Services.TextService); createItem.NavigateToRoute("/settings/contentBlueprints/edit/-1?create=true&doctype=" + ct.Alias); - menu.Items.Add(Services.TextService.Localize($"actions/{ActionRefresh.Instance.Alias}"), true); + menu.Items.Add(new RefreshNode(Services.TextService, true)); return menu; } - menu.Items.Add(Services.TextService.Localize($"actions/{ActionDelete.Instance.Alias}")); + menu.Items.Add(Services.TextService); return menu; } diff --git a/src/Umbraco.Web/Trees/ContentTreeController.cs b/src/Umbraco.Web/Trees/ContentTreeController.cs index 4a0589cce4..a759382854 100644 --- a/src/Umbraco.Web/Trees/ContentTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentTreeController.cs @@ -8,11 +8,12 @@ using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Core.Services; +using Umbraco.Web.Actions; using Umbraco.Web.Composing; using Umbraco.Web.Models.Trees; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; -using Umbraco.Web._Legacy.Actions; + using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Search; using Constants = Umbraco.Core.Constants; @@ -114,23 +115,22 @@ namespace Umbraco.Web.Trees // if the user's start node is not the root then the only menu item to display is refresh if (UserStartNodes.Contains(Constants.System.Root) == false) { - menu.Items.Add( - Services.TextService.Localize(string.Concat("actions/", ActionRefresh.Instance.Alias)), - true); + menu.Items.Add(new RefreshNode(Services.TextService, true)); return menu; } //set the default to create - menu.DefaultMenuAlias = ActionNew.Instance.Alias; + menu.DefaultMenuAlias = ActionNew.ActionAlias; // we need to get the default permissions as you can't set permissions on the very root node var permission = Services.UserService.GetPermissions(Security.CurrentUser, Constants.System.Root).First(); - var nodeActions = global::Umbraco.Web._Legacy.Actions.Action.FromEntityPermission(permission) + var nodeActions = Current.Actions.FromEntityPermission(permission) .Select(x => new MenuItem(x)); //these two are the standard items - menu.Items.Add(Services.TextService.Localize("actions", ActionNew.Instance.Alias)); - menu.Items.Add(Services.TextService.Localize("actions", ActionSort.Instance.Alias), true); + //fixme: inject + menu.Items.Add(Services.TextService.Localize("actions", ActionNew.ActionAlias)); + menu.Items.Add(Services.TextService.Localize("actions", Current.Actions.GetAction().Alias), true); //filter the standard items FilterUserAllowedMenuItems(menu, nodeActions); @@ -143,7 +143,7 @@ namespace Umbraco.Web.Trees // add default actions for *all* users // fixme - temp disable RePublish as the page itself (republish.aspx) has been temp disabled //menu.Items.Add(Services.TextService.Localize("actions", ActionRePublish.Instance.Alias)).ConvertLegacyMenuItem(null, "content", "content"); - menu.Items.Add(Services.TextService.Localize("actions", ActionRefresh.Instance.Alias), true); + menu.Items.Add(new RefreshNode(Services.TextService, true)); return menu; } @@ -165,9 +165,7 @@ namespace Umbraco.Web.Trees if (Security.CurrentUser.HasPathAccess(item, Services.EntityService, RecycleBinId) == false) { var menu = new MenuItemCollection(); - menu.Items.Add( - Services.TextService.Localize(string.Concat("actions/", ActionRefresh.Instance.Alias)), - true); + menu.Items.Add(new RefreshNode(Services.TextService, true)); return menu; } @@ -182,7 +180,7 @@ namespace Umbraco.Web.Trees else { //set the default to create - nodeMenu.DefaultMenuAlias = ActionNew.Instance.Alias; + nodeMenu.DefaultMenuAlias = ActionNew.ActionAlias; } var allowedMenuItems = GetAllowedUserMenuItemsForNode(item); @@ -239,14 +237,13 @@ namespace Umbraco.Web.Trees AddActionNode(item, menu, true); - AddActionNode(item, menu, convert: true); AddActionNode(item, menu); - AddActionNode(item, menu, convert: true); + AddActionNode(item, menu); AddActionNode(item, menu, true, true); AddActionNode(item, menu, true); - AddActionNode(item, menu, true); + menu.Items.Add(new RefreshNode(Services.TextService, true)); return menu; } @@ -259,10 +256,10 @@ namespace Umbraco.Web.Trees protected MenuItemCollection GetNodeMenuItemsForDeletedContent(IUmbracoEntity item) { var menu = new MenuItemCollection(); - menu.Items.Add(Services.TextService.Localize("actions", ActionRestore.Instance.Alias)); - menu.Items.Add(Services.TextService.Localize("actions", ActionDelete.Instance.Alias)); + menu.Items.Add(Services.TextService.Localize("actions", ActionRestore.ActionAlias)); + menu.Items.Add(Services.TextService.Localize("actions", ActionDelete.ActionAlias)); - menu.Items.Add(Services.TextService.Localize("actions", ActionRefresh.Instance.Alias), true); + menu.Items.Add(new RefreshNode(Services.TextService, true)); return menu; } @@ -306,20 +303,24 @@ namespace Umbraco.Web.Trees entity.Name = "[[" + entity.Id + "]]"; } + //fixme: Remove the need for converting to legacy private void AddActionNode(IUmbracoEntity item, MenuItemCollection menu, bool hasSeparator = false, bool convert = false) where TAction : IAction { + //fixme: Inject var menuItem = menu.Items.Add(Services.TextService.Localize("actions", Current.Actions.GetAction().Alias), hasSeparator); if (convert) menuItem.ConvertLegacyMenuItem(item, "content", "content"); } - private void AddActionNode(IUmbracoEntity item, MenuItemCollection menu, bool hasSeparator = false, bool convert = false) - where TItem : MenuItem, new() - where TAction : IAction - { - var menuItem = menu.Items.Add(Services.TextService.Localize("actions", Current.Actions.GetAction().Alias), hasSeparator); - if (convert) menuItem.ConvertLegacyMenuItem(item, "content", "content"); - } + ////fixme: Remove the need for converting to legacy + //private void AddActionNode(IUmbracoEntity item, MenuItemCollection menu, bool hasSeparator = false, bool convert = false) + // where TItem : MenuItem, new() + // where TAction : IAction + //{ + // //fixme: Inject + // var menuItem = menu.Items.Add(Services.TextService.Localize("actions", Current.Actions.GetAction().Alias), hasSeparator); + // if (convert) menuItem.ConvertLegacyMenuItem(item, "content", "content"); + //} public IEnumerable Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null) { diff --git a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs index 4d4f1be483..c02f691f14 100644 --- a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs @@ -14,7 +14,9 @@ using Umbraco.Web.Models.Trees; using Umbraco.Web.WebApi.Filters; using System.Globalization; using Umbraco.Core.Models.Entities; -using Umbraco.Web._Legacy.Actions; +using Umbraco.Web.Actions; +using Umbraco.Web.Composing; + namespace Umbraco.Web.Trees { @@ -348,7 +350,7 @@ namespace Umbraco.Web.Trees { var menu = new MenuItemCollection(); menu.Items.Add(Services.TextService.Localize("actions/emptyTrashcan")); - menu.Items.Add(Services.TextService.Localize("actions", ActionRefresh.Instance.Alias), true); + menu.Items.Add(new RefreshNode(Services.TextService, true)); return menu; } @@ -381,13 +383,16 @@ namespace Umbraco.Web.Trees internal IEnumerable GetAllowedUserMenuItemsForNode(IUmbracoEntity dd) { var permission = Services.UserService.GetPermissions(Security.CurrentUser, dd.Path); - var actions = global::Umbraco.Web._Legacy.Actions.Action.FromEntityPermission(permission) + //fixme: inject + var actions = Current.Actions.FromEntityPermission(permission) .ToList(); + var actionDelete = Current.Actions.GetAction(); + // A user is allowed to delete their own stuff var tryGetCurrentUserId = Security.GetUserId(); - if (tryGetCurrentUserId && dd.CreatorId == tryGetCurrentUserId.Result && actions.Contains(ActionDelete.Instance) == false) - actions.Add(ActionDelete.Instance); + if (tryGetCurrentUserId && dd.CreatorId == tryGetCurrentUserId.Result && actions.Contains(actionDelete) == false) + actions.Add(actionDelete); return actions.Select(x => new MenuItem(x)); } diff --git a/src/Umbraco.Web/Trees/ContentTypeTreeController.cs b/src/Umbraco.Web/Trees/ContentTypeTreeController.cs index a2179be75f..673c5f4168 100644 --- a/src/Umbraco.Web/Trees/ContentTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentTypeTreeController.cs @@ -7,7 +7,8 @@ using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Models; using Umbraco.Core.Services; -using Umbraco.Web._Legacy.Actions; +using Umbraco.Web.Actions; + using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Models.Trees; using Umbraco.Web.WebApi.Filters; @@ -77,12 +78,13 @@ namespace Umbraco.Web.Trees if (id == Constants.System.Root.ToInvariantString()) { //set the default to create - menu.DefaultMenuAlias = ActionNew.Instance.Alias; + menu.DefaultMenuAlias = ActionNew.ActionAlias; // root actions - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias))); - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionImport.Instance.Alias)), true); - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true); + menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService, true); + menu.Items.Add(new RefreshNode(Services.TextService, true)); + return menu; } @@ -90,11 +92,11 @@ namespace Umbraco.Web.Trees if (container != null) { //set the default to create - menu.DefaultMenuAlias = ActionNew.Instance.Alias; + menu.DefaultMenuAlias = ActionNew.ActionAlias; - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias))); + menu.Items.Add(Services.TextService); - menu.Items.Add(new MenuItem("rename", Services.TextService.Localize(String.Format("actions/{0}", "rename"))) + menu.Items.Add(new MenuItem("rename", Services.TextService.Localize("actions/rename")) { Icon = "icon icon-edit" }); @@ -102,11 +104,9 @@ namespace Umbraco.Web.Trees if (container.HasChildren == false) { //can delete doc type - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias)), true); + menu.Items.Add(Services.TextService, true); } - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true); - - + menu.Items.Add(new RefreshNode(Services.TextService, true)); } else { @@ -115,18 +115,18 @@ namespace Umbraco.Web.Trees if (enableInheritedDocumentTypes) { - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias))); + menu.Items.Add(Services.TextService); } //no move action if this is a child doc type if (parent == null) { - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionMove.Instance.Alias)), true); + menu.Items.Add(Services.TextService, true); } - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionCopy.Instance.Alias))); - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionExport.Instance.Alias)), true); - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias)), true); + menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService, true); + menu.Items.Add(Services.TextService, true); if (enableInheritedDocumentTypes) - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true); + menu.Items.Add(new RefreshNode(Services.TextService, true)); } return menu; diff --git a/src/Umbraco.Web/Trees/DataTypeTreeController.cs b/src/Umbraco.Web/Trees/DataTypeTreeController.cs index 3087808f10..4f9f93f110 100644 --- a/src/Umbraco.Web/Trees/DataTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/DataTypeTreeController.cs @@ -9,9 +9,10 @@ using Umbraco.Web.Models.Trees; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; using Umbraco.Core.Services; +using Umbraco.Web.Actions; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Search; -using Umbraco.Web._Legacy.Actions; + using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Trees @@ -100,11 +101,11 @@ namespace Umbraco.Web.Trees if (id == Constants.System.Root.ToInvariantString()) { //set the default to create - menu.DefaultMenuAlias = ActionNew.Instance.Alias; + menu.DefaultMenuAlias = ActionNew.ActionAlias; // root actions - menu.Items.Add(Services.TextService.Localize($"actions/{ActionNew.Instance.Alias}")); - menu.Items.Add(Services.TextService.Localize("actions", ActionRefresh.Instance.Alias), true); + menu.Items.Add(Services.TextService); + menu.Items.Add(new RefreshNode(Services.TextService, true)); return menu; } @@ -112,9 +113,9 @@ namespace Umbraco.Web.Trees if (container != null) { //set the default to create - menu.DefaultMenuAlias = ActionNew.Instance.Alias; + menu.DefaultMenuAlias = ActionNew.ActionAlias; - menu.Items.Add(Services.TextService.Localize($"actions/{ActionNew.Instance.Alias}")); + menu.Items.Add(Services.TextService); menu.Items.Add(new MenuItem("rename", Services.TextService.Localize("actions/rename")) { @@ -124,19 +125,18 @@ namespace Umbraco.Web.Trees if (container.HasChildren == false) { //can delete data type - menu.Items.Add(Services.TextService.Localize($"actions/{ActionDelete.Instance.Alias}")); + menu.Items.Add(Services.TextService); } - menu.Items.Add(Services.TextService.Localize( - $"actions/{ActionRefresh.Instance.Alias}"), hasSeparator: true); + menu.Items.Add(new RefreshNode(Services.TextService, true)); } else { var nonDeletableSystemDataTypeIds = GetNonDeletableSystemDataTypeIds(); if (nonDeletableSystemDataTypeIds.Contains(int.Parse(id)) == false) - menu.Items.Add(Services.TextService.Localize($"actions/{ActionDelete.Instance.Alias}")); + menu.Items.Add(Services.TextService); - menu.Items.Add(Services.TextService.Localize($"actions/{ActionMove.Instance.Alias}"), hasSeparator: true); + menu.Items.Add(Services.TextService, hasSeparator: true); } return menu; diff --git a/src/Umbraco.Web/Trees/DictionaryTreeController.cs b/src/Umbraco.Web/Trees/DictionaryTreeController.cs index 3043377d65..a0510acd3e 100644 --- a/src/Umbraco.Web/Trees/DictionaryTreeController.cs +++ b/src/Umbraco.Web/Trees/DictionaryTreeController.cs @@ -3,7 +3,8 @@ using System.Linq; using System.Net.Http.Formatting; using Umbraco.Core; using Umbraco.Core.Services; -using Umbraco.Web._Legacy.Actions; +using Umbraco.Web.Actions; + using Umbraco.Web.Models.Trees; using Umbraco.Web.WebApi.Filters; @@ -95,14 +96,12 @@ namespace Umbraco.Web.Trees { var menu = new MenuItemCollection(); - menu.Items.Add(Services.TextService.Localize($"actions/{ActionNew.Instance.Alias}")); + menu.Items.Add(Services.TextService); if (id != Constants.System.Root.ToInvariantString()) - menu.Items.Add(Services.TextService.Localize( - $"actions/{ActionDelete.Instance.Alias}"), true); + menu.Items.Add(Services.TextService, true); - menu.Items.Add(Services.TextService.Localize( - $"actions/{ActionRefresh.Instance.Alias}"), true); + menu.Items.Add(new RefreshNode(Services.TextService, true)); return menu; } diff --git a/src/Umbraco.Web/Trees/FileSystemTreeController.cs b/src/Umbraco.Web/Trees/FileSystemTreeController.cs index 21e81a58af..a517cd45c2 100644 --- a/src/Umbraco.Web/Trees/FileSystemTreeController.cs +++ b/src/Umbraco.Web/Trees/FileSystemTreeController.cs @@ -7,8 +7,9 @@ using System.Web; using Umbraco.Core; using Umbraco.Core.IO; using Umbraco.Core.Services; +using Umbraco.Web.Actions; using Umbraco.Web.Models.Trees; -using Umbraco.Web._Legacy.Actions; + using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Trees @@ -79,11 +80,11 @@ namespace Umbraco.Web.Trees var menu = new MenuItemCollection(); //set the default to create - menu.DefaultMenuAlias = ActionNew.Instance.Alias; + menu.DefaultMenuAlias = ActionNew.ActionAlias; //create action - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias))); + menu.Items.Add(Services.TextService); //refresh action - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true); + menu.Items.Add(new RefreshNode(Services.TextService, true)); return menu; } @@ -93,9 +94,9 @@ namespace Umbraco.Web.Trees var menu = new MenuItemCollection(); //set the default to create - menu.DefaultMenuAlias = ActionNew.Instance.Alias; + menu.DefaultMenuAlias = ActionNew.ActionAlias; //create action - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias))); + menu.Items.Add(Services.TextService); var hasChildren = FileSystem.GetFiles(path).Any() || FileSystem.GetDirectories(path).Any(); @@ -103,11 +104,11 @@ namespace Umbraco.Web.Trees if (hasChildren == false) { //delete action - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias)), true); + menu.Items.Add(Services.TextService, true); } //refresh action - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true); + menu.Items.Add(new RefreshNode(Services.TextService, true)); return menu; } @@ -117,7 +118,7 @@ namespace Umbraco.Web.Trees var menu = new MenuItemCollection(); //if it's not a directory then we only allow to delete the item - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias))); + menu.Items.Add(Services.TextService); return menu; } diff --git a/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs b/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs index 131c7954c8..d0bfa360fa 100644 --- a/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs +++ b/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs @@ -6,7 +6,8 @@ using System.Web.Http.Routing; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Services; -using Umbraco.Web._Legacy.Actions; +using Umbraco.Web.Actions; + using Umbraco.Web.Composing; using Umbraco.Web.Models.Trees; @@ -17,6 +18,8 @@ namespace Umbraco.Web.Trees /// internal class LegacyTreeDataConverter { + //fixme: remove this whole class when everything is angularized + /// /// This will look at the legacy IAction's JsFunctionName and convert it to a confirmation dialog view if possible /// @@ -24,14 +27,9 @@ namespace Umbraco.Web.Trees /// internal static Attempt GetLegacyConfirmView(IAction action) { - if (action.JsFunctionName.IsNullOrWhiteSpace()) + switch (action) { - return Attempt.Fail(); - } - - switch (action.JsFunctionName) - { - case "UmbClientMgr.appActions().actionDelete()": + case ActionDelete actionDelete: return Attempt.Succeed( UmbracoConfig.For.GlobalSettings().Path.EnsureEndsWith('/') + "views/common/dialogs/legacydelete.html"); } @@ -49,64 +47,18 @@ namespace Umbraco.Web.Trees /// internal static Attempt GetUrlAndTitleFromLegacyAction(IAction action, string nodeId, string nodeType, string nodeName, string currentSection) { - if (action.JsFunctionName.IsNullOrWhiteSpace()) + switch (action) { - return Attempt.Fail(); - } - - switch (action.JsFunctionName) - { - case "UmbClientMgr.appActions().actionNew()": + case ActionNew actionNew: return Attempt.Succeed( new LegacyUrlAction( "create.aspx?nodeId=" + nodeId + "&nodeType=" + nodeType + "&nodeName=" + nodeName + "&rnd=" + DateTime.UtcNow.Ticks, Current.Services.TextService.Localize("actions/create"))); - case "UmbClientMgr.appActions().actionNewFolder()": - return Attempt.Succeed( - new LegacyUrlAction( - "createFolder.aspx?nodeId=" + nodeId + "&nodeType=" + nodeType + "&nodeName=" + nodeName + "&rnd=" + DateTime.UtcNow.Ticks, - Current.Services.TextService.Localize("actions/create"))); - case "UmbClientMgr.appActions().actionProtect()": + case ActionProtect actionProtect: return Attempt.Succeed( new LegacyUrlAction( "dialogs/protectPage.aspx?mode=cut&nodeId=" + nodeId + "&rnd=" + DateTime.UtcNow.Ticks, Current.Services.TextService.Localize("actions/protect"))); - case "UmbClientMgr.appActions().actionRollback()": - return Attempt.Succeed( - new LegacyUrlAction( - "dialogs/rollback.aspx?nodeId=" + nodeId + "&rnd=" + DateTime.UtcNow.Ticks, - Current.Services.TextService.Localize("actions/rollback"))); - case "UmbClientMgr.appActions().actionNotify()": - return Attempt.Succeed( - new LegacyUrlAction( - "dialogs/notifications.aspx?id=" + nodeId + "&rnd=" + DateTime.UtcNow.Ticks, - Current.Services.TextService.Localize("actions/notify"))); - case "UmbClientMgr.appActions().actionPublish()": - return Attempt.Succeed( - new LegacyUrlAction( - "dialogs/publish.aspx?id=" + nodeId + "&rnd=" + DateTime.UtcNow.Ticks, - Current.Services.TextService.Localize("actions/publish"))); - case "UmbClientMgr.appActions().actionChangeDocType()": - return Attempt.Succeed( - new LegacyUrlAction( - "dialogs/ChangeDocType.aspx?id=" + nodeId + "&rnd=" + DateTime.UtcNow.Ticks, - Current.Services.TextService.Localize("actions/changeDocType"))); - case "UmbClientMgr.appActions().actionToPublish()": - return Attempt.Succeed( - new LegacyUrlAction( - "dialogs/SendPublish.aspx?id=" + nodeId + "&rnd=" + DateTime.UtcNow.Ticks, - Current.Services.TextService.Localize("actions/sendtopublish"))); - case "UmbClientMgr.appActions().actionRePublish()": - return Attempt.Succeed( - new LegacyUrlAction( - "dialogs/republish.aspx?rnd=" + DateTime.UtcNow.Ticks, - Current.Services.TextService.Localize("actions/republish"))); - case "UmbClientMgr.appActions().actionSendToTranslate()": - return Attempt.Succeed( - new LegacyUrlAction( - "dialogs/sendToTranslation.aspx?id=" + nodeId + "&rnd=" + DateTime.UtcNow.Ticks, - Current.Services.TextService.Localize("actions/sendToTranslate"))); - } return Attempt.Fail(); } diff --git a/src/Umbraco.Web/Trees/MacrosTreeController.cs b/src/Umbraco.Web/Trees/MacrosTreeController.cs index a4f486661e..0faa1ce75d 100644 --- a/src/Umbraco.Web/Trees/MacrosTreeController.cs +++ b/src/Umbraco.Web/Trees/MacrosTreeController.cs @@ -8,7 +8,8 @@ using Umbraco.Web.Models.Trees; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; using Umbraco.Core.Services; -using Umbraco.Web._Legacy.Actions; +using Umbraco.Web.Actions; + using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Trees @@ -58,13 +59,13 @@ namespace Umbraco.Web.Trees if (id == Constants.System.Root.ToInvariantString()) { //Create the normal create action - menu.Items.Add(Services.TextService.Localize("actions", ActionNew.Instance.Alias)) + menu.Items.Add(Services.TextService) //Since we haven't implemented anything for macros in angular, this needs to be converted to //use the legacy format .ConvertLegacyMenuItem(null, "initmacros", queryStrings.GetValue("application")); //refresh action - menu.Items.Add(Services.TextService.Localize("actions", ActionRefresh.Instance.Alias), true); + menu.Items.Add(new RefreshNode(Services.TextService, true)); return menu; } @@ -74,7 +75,7 @@ namespace Umbraco.Web.Trees if (macro == null) return new MenuItemCollection(); //add delete option for all macros - menu.Items.Add(Services.TextService.Localize("actions", ActionDelete.Instance.Alias)) + menu.Items.Add(Services.TextService) //Since we haven't implemented anything for macros in angular, this needs to be converted to //use the legacy format .ConvertLegacyMenuItem(new EntitySlim diff --git a/src/Umbraco.Web/Trees/MediaTreeController.cs b/src/Umbraco.Web/Trees/MediaTreeController.cs index 8eb37e6224..d260253218 100644 --- a/src/Umbraco.Web/Trees/MediaTreeController.cs +++ b/src/Umbraco.Web/Trees/MediaTreeController.cs @@ -8,10 +8,12 @@ using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Core.Services; +using Umbraco.Web.Actions; +using Umbraco.Web.Composing; using Umbraco.Web.Models.Trees; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; -using Umbraco.Web._Legacy.Actions; + using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Search; using Constants = Umbraco.Core.Constants; @@ -81,28 +83,25 @@ namespace Umbraco.Web.Trees var menu = new MenuItemCollection(); //set the default - menu.DefaultMenuAlias = ActionNew.Instance.Alias; + menu.DefaultMenuAlias = ActionNew.ActionAlias; if (id == Constants.System.Root.ToInvariantString()) { // if the user's start node is not the root then the only menu item to display is refresh if (UserStartNodes.Contains(Constants.System.Root) == false) { - menu.Items.Add( - Services.TextService.Localize(string.Concat("actions/", ActionRefresh.Instance.Alias)), - true); + menu.Items.Add(new RefreshNode(Services.TextService, true)); return menu; } // root actions - menu.Items.Add(Services.TextService.Localize("actions", ActionNew.Instance.Alias)); - menu.Items.Add(Services.TextService.Localize("actions", ActionSort.Instance.Alias), true); - menu.Items.Add(Services.TextService.Localize("actions", ActionRefresh.Instance.Alias), true); + menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService, true); + menu.Items.Add(new RefreshNode(Services.TextService, true)); return menu; } - int iid; - if (int.TryParse(id, out iid) == false) + if (int.TryParse(id, out var iid) == false) { throw new HttpResponseException(HttpStatusCode.NotFound); } @@ -115,29 +114,27 @@ namespace Umbraco.Web.Trees //if the user has no path access for this node, all they can do is refresh if (Security.CurrentUser.HasPathAccess(item, Services.EntityService, RecycleBinId) == false) { - menu.Items.Add( - Services.TextService.Localize(string.Concat("actions/", ActionRefresh.Instance.Alias)), - true); + menu.Items.Add(new RefreshNode(Services.TextService, true)); return menu; } //return a normal node menu: - menu.Items.Add(Services.TextService.Localize("actions", ActionNew.Instance.Alias)); - menu.Items.Add(Services.TextService.Localize("actions", ActionMove.Instance.Alias)); - menu.Items.Add(Services.TextService.Localize("actions", ActionDelete.Instance.Alias)); - menu.Items.Add(Services.TextService.Localize("actions", ActionSort.Instance.Alias)); - menu.Items.Add(Services.TextService.Localize("actions", ActionRefresh.Instance.Alias), true); + menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService); + menu.Items.Add(new RefreshNode(Services.TextService, true)); //if the media item is in the recycle bin, don't have a default menu, just show the regular menu if (item.Path.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).Contains(RecycleBinId.ToInvariantString())) { menu.DefaultMenuAlias = null; - menu.Items.Insert(2, new MenuItem(ActionRestore.Instance, Services.TextService.Localize("actions", ActionRestore.Instance.Alias))); + menu.Items.Insert(2, new MenuItem(Current.Actions.GetAction(), Services.TextService.Localize("actions", ActionRestore.ActionAlias))); } else { //set the default to create - menu.DefaultMenuAlias = ActionNew.Instance.Alias; + menu.DefaultMenuAlias = ActionNew.ActionAlias; } return menu; diff --git a/src/Umbraco.Web/Trees/MediaTypeTreeController.cs b/src/Umbraco.Web/Trees/MediaTypeTreeController.cs index 7b346c6871..a679cefa32 100644 --- a/src/Umbraco.Web/Trees/MediaTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/MediaTypeTreeController.cs @@ -9,8 +9,9 @@ using Umbraco.Core.Models; using Umbraco.Web.Models.Trees; using Umbraco.Web.WebApi.Filters; using Umbraco.Core.Services; +using Umbraco.Web.Actions; using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web._Legacy.Actions; + using Umbraco.Web.Search; namespace Umbraco.Web.Trees @@ -70,12 +71,11 @@ namespace Umbraco.Web.Trees if (id == Constants.System.Root.ToInvariantString()) { //set the default to create - menu.DefaultMenuAlias = ActionNew.Instance.Alias; + menu.DefaultMenuAlias = ActionNew.ActionAlias; // root actions - menu.Items.Add(Services.TextService.Localize($"actions/{ActionNew.Instance.Alias}")); - menu.Items.Add(Services.TextService.Localize( - $"actions/{ActionRefresh.Instance.Alias}")); + menu.Items.Add(Services.TextService); + menu.Items.Add(new RefreshNode(Services.TextService)); return menu; } @@ -83,9 +83,9 @@ namespace Umbraco.Web.Trees if (container != null) { //set the default to create - menu.DefaultMenuAlias = ActionNew.Instance.Alias; + menu.DefaultMenuAlias = ActionNew.ActionAlias; - menu.Items.Add(Services.TextService.Localize($"actions/{ActionNew.Instance.Alias}")); + menu.Items.Add(Services.TextService); menu.Items.Add(new MenuItem("rename", Services.TextService.Localize("actions/rename")) { @@ -95,10 +95,9 @@ namespace Umbraco.Web.Trees if (container.HasChildren == false) { //can delete doc type - menu.Items.Add(Services.TextService.Localize($"actions/{ActionDelete.Instance.Alias}")); + menu.Items.Add(Services.TextService); } - menu.Items.Add(Services.TextService.Localize( - $"actions/{ActionRefresh.Instance.Alias}"), hasSeparator: true); + menu.Items.Add(new RefreshNode(Services.TextService, true)); } else { @@ -107,29 +106,28 @@ namespace Umbraco.Web.Trees if (enableInheritedMediaTypes) { - menu.Items.Add(Services.TextService.Localize($"actions/{ActionNew.Instance.Alias}")); + menu.Items.Add(Services.TextService); //no move action if this is a child doc type if (parent == null) { - menu.Items.Add(Services.TextService.Localize($"actions/{ActionMove.Instance.Alias}"), true); + menu.Items.Add(Services.TextService, true); } } else { - menu.Items.Add(Services.TextService.Localize($"actions/{ActionMove.Instance.Alias}")); + menu.Items.Add(Services.TextService); //no move action if this is a child doc type if (parent == null) { - menu.Items.Add(Services.TextService.Localize($"actions/{ActionMove.Instance.Alias}"), true); + menu.Items.Add(Services.TextService, true); } } - menu.Items.Add(Services.TextService.Localize($"actions/{ActionCopy.Instance.Alias}")); - menu.Items.Add(Services.TextService.Localize($"actions/{ActionDelete.Instance.Alias}")); + menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService); if (enableInheritedMediaTypes) - menu.Items.Add(Services.TextService.Localize( - $"actions/{ActionRefresh.Instance.Alias}"), true); + menu.Items.Add(new RefreshNode(Services.TextService, true)); } return menu; diff --git a/src/Umbraco.Web/Trees/MemberTreeController.cs b/src/Umbraco.Web/Trees/MemberTreeController.cs index f3ca0c9a02..f8bcb47b56 100644 --- a/src/Umbraco.Web/Trees/MemberTreeController.cs +++ b/src/Umbraco.Web/Trees/MemberTreeController.cs @@ -10,10 +10,11 @@ using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Core.Security; +using Umbraco.Web.Actions; using Umbraco.Web.Models.Trees; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; -using Umbraco.Web._Legacy.Actions; + using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Search; using Constants = Umbraco.Core.Constants; @@ -155,27 +156,32 @@ namespace Umbraco.Web.Trees if (_provider.IsUmbracoMembershipProvider()) { //set default - menu.DefaultMenuAlias = ActionNew.Instance.Alias; + menu.DefaultMenuAlias = ActionNew.ActionAlias; //Create the normal create action - menu.Items.Add(Services.TextService.Localize("actions", ActionNew.Instance.Alias)); + menu.Items.Add(Services.TextService); } else { //Create a custom create action - this does not launch a dialog, it just navigates to the create screen // we'll create it based on the ActionNew so it maintains the same icon properties, name, etc... - var createMenuItem = new MenuItem(ActionNew.Instance); + var createMenuItem = new MenuItem + { + Name = Services.TextService.Localize($"actions/{ActionNew.ActionAlias}"), + Alias = ActionNew.ActionAlias, + Icon = "add" + }; //we want to go to this route: /member/member/edit/-1?create=true createMenuItem.NavigateToRoute("/member/member/edit/-1?create=true"); menu.Items.Add(createMenuItem); } - menu.Items.Add(Services.TextService.Localize("actions", ActionRefresh.Instance.Alias), true); + menu.Items.Add(new RefreshNode(Services.TextService, true)); return menu; } //add delete option for all members - menu.Items.Add(Services.TextService.Localize("actions", ActionDelete.Instance.Alias)); + menu.Items.Add(Services.TextService); if (Security.CurrentUser.HasAccessToSensitiveData()) { diff --git a/src/Umbraco.Web/Trees/MemberTypeAndGroupTreeControllerBase.cs b/src/Umbraco.Web/Trees/MemberTypeAndGroupTreeControllerBase.cs index a710b52595..484ea1e2a9 100644 --- a/src/Umbraco.Web/Trees/MemberTypeAndGroupTreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/MemberTypeAndGroupTreeControllerBase.cs @@ -2,7 +2,8 @@ using System.Net.Http.Formatting; using Umbraco.Core; using Umbraco.Core.Services; -using Umbraco.Web._Legacy.Actions; +using Umbraco.Web.Actions; + using Umbraco.Web.Models.Trees; namespace Umbraco.Web.Trees @@ -25,14 +26,14 @@ namespace Umbraco.Web.Trees if (id == Constants.System.Root.ToInvariantString()) { // root actions - menu.Items.Add(Services.TextService.Localize("actions", ActionNew.Instance.Alias)); - menu.Items.Add(Services.TextService.Localize("actions", ActionRefresh.Instance.Alias), true); + menu.Items.Add(new CreateChildEntity(Services.TextService)); + menu.Items.Add(new RefreshNode(Services.TextService, true)); return menu; } else { //delete member type/group - menu.Items.Add(Services.TextService.Localize("actions", ActionDelete.Instance.Alias)); + menu.Items.Add(Services.TextService); } return menu; diff --git a/src/Umbraco.Web/Trees/PackagesTreeController.cs b/src/Umbraco.Web/Trees/PackagesTreeController.cs index 31f577ad85..8f62047941 100644 --- a/src/Umbraco.Web/Trees/PackagesTreeController.cs +++ b/src/Umbraco.Web/Trees/PackagesTreeController.cs @@ -8,7 +8,8 @@ using Umbraco.Web.WebApi.Filters; using umbraco; using umbraco.cms.businesslogic.packager; using Umbraco.Core.Services; -using Umbraco.Web._Legacy.Actions; +using Umbraco.Web.Actions; + using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Trees @@ -83,21 +84,20 @@ namespace Umbraco.Web.Trees // Root actions if (id == "-1") { - menu.Items.Add(Services.TextService.Localize($"actions/{ActionNew.Instance.Alias}")) + menu.Items.Add(Services.TextService) .ConvertLegacyMenuItem(null, Constants.Trees.Packages, queryStrings.GetValue("application")); } else if (id == "created") { - menu.Items.Add(Services.TextService.Localize($"actions/{ActionNew.Instance.Alias}")) + menu.Items.Add(Services.TextService) .ConvertLegacyMenuItem(null, Constants.Trees.Packages, queryStrings.GetValue("application")); - menu.Items.Add( - Services.TextService.Localize($"actions/{ActionRefresh.Instance.Alias}"), true); + menu.Items.Add(new RefreshNode(Services.TextService, true)); } else { //it's a package node - menu.Items.Add(Services.TextService.Localize("actions", ActionDelete.Instance.Alias)); + menu.Items.Add(Services.TextService); } return menu; diff --git a/src/Umbraco.Web/Trees/RelationTypeTreeController.cs b/src/Umbraco.Web/Trees/RelationTypeTreeController.cs index 4930b4dc5e..ab6e9e2dd7 100644 --- a/src/Umbraco.Web/Trees/RelationTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/RelationTypeTreeController.cs @@ -4,10 +4,11 @@ using System.Net.Http.Formatting; using Umbraco.Web.Models.Trees; using Umbraco.Web.WebApi.Filters; using Umbraco.Core; -using Umbraco.Web._Legacy.Actions; + using Umbraco.Core.Services; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; +using Umbraco.Web.Actions; namespace Umbraco.Web.Trees { @@ -24,12 +25,10 @@ namespace Umbraco.Web.Trees if (id == Constants.System.Root.ToInvariantString()) { //Create the normal create action - menu.Items.Add(Services.TextService.Localize("actions", ActionNew.Instance.Alias)) - //Since we haven't implemented anything for relationtypes in angular, this needs to be converted to - //use the legacy format - .ConvertLegacyMenuItem(null, "initrelationTypes", queryStrings.GetValue("application")); + var addMenuItem = menu.Items.Add(Services.TextService); + addMenuItem.LaunchDialogUrl("developer/RelationTypes/NewRelationType.aspx", "Create New RelationType"); //refresh action - menu.Items.Add(Services.TextService.Localize("actions", ActionRefresh.Instance.Alias), true); + menu.Items.Add(new RefreshNode(Services.TextService, true)); return menu; } @@ -38,7 +37,7 @@ namespace Umbraco.Web.Trees if (relationType == null) return new MenuItemCollection(); //add delete option for all macros - menu.Items.Add(Services.TextService.Localize("actions", ActionDelete.Instance.Alias)) + menu.Items.Add(Services.TextService) //Since we haven't implemented anything for relationtypes in angular, this needs to be converted to //use the legacy format .ConvertLegacyMenuItem(new EntitySlim diff --git a/src/Umbraco.Web/Trees/TemplatesTreeController.cs b/src/Umbraco.Web/Trees/TemplatesTreeController.cs index 768768f888..3ae45a072e 100644 --- a/src/Umbraco.Web/Trees/TemplatesTreeController.cs +++ b/src/Umbraco.Web/Trees/TemplatesTreeController.cs @@ -8,12 +8,13 @@ using Umbraco.Core; using Umbraco.Core.Services; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; +using Umbraco.Web.Actions; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Models.Trees; using Umbraco.Web.Mvc; using Umbraco.Web.Search; using Umbraco.Web.WebApi.Filters; -using Umbraco.Web._Legacy.Actions; + using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Trees @@ -69,13 +70,13 @@ namespace Umbraco.Web.Trees var menu = new MenuItemCollection(); //Create the normal create action - var item = menu.Items.Add(Services.TextService.Localize("actions", ActionNew.Instance.Alias)); + var item = menu.Items.Add(Services.TextService); item.NavigateToRoute($"{queryStrings.GetValue("application")}/templates/edit/{id}?create=true"); if (id == Constants.System.Root.ToInvariantString()) { //refresh action - menu.Items.Add(Services.TextService.Localize("actions", ActionRefresh.Instance.Alias), true); + menu.Items.Add(new RefreshNode(Services.TextService, true)); return menu; } @@ -88,11 +89,11 @@ namespace Umbraco.Web.Trees if (template.IsMasterTemplate == false) { //add delete option if it doesn't have children - menu.Items.Add(Services.TextService.Localize("actions", ActionDelete.Instance.Alias), true); + menu.Items.Add(Services.TextService, true); } //add refresh - menu.Items.Add(Services.TextService.Localize("actions", ActionRefresh.Instance.Alias), true); + menu.Items.Add(new RefreshNode(Services.TextService, true)); return menu; diff --git a/src/Umbraco.Web/Trees/UserPermissionsTreeController.cs b/src/Umbraco.Web/Trees/UserPermissionsTreeController.cs index e381c7ec0f..479cc20083 100644 --- a/src/Umbraco.Web/Trees/UserPermissionsTreeController.cs +++ b/src/Umbraco.Web/Trees/UserPermissionsTreeController.cs @@ -3,10 +3,11 @@ using System.Linq; using System.Net.Http.Formatting; using Umbraco.Core; using Umbraco.Core.Services; +using Umbraco.Web.Actions; using Umbraco.Web.Models.Trees; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; -using Umbraco.Web._Legacy.Actions; + using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Trees @@ -43,7 +44,7 @@ namespace Umbraco.Web.Trees if (id == Constants.System.Root.ToInvariantString()) { // root actions - menu.Items.Add(Services.TextService.Localize("actions", ActionRefresh.Instance.Alias), true); + menu.Items.Add(new RefreshNode(Services.TextService, true)); return menu; } diff --git a/src/Umbraco.Web/Trees/UserTreeController.cs b/src/Umbraco.Web/Trees/UserTreeController.cs index 8ae5b002c6..f029a929de 100644 --- a/src/Umbraco.Web/Trees/UserTreeController.cs +++ b/src/Umbraco.Web/Trees/UserTreeController.cs @@ -4,7 +4,7 @@ using Umbraco.Core.Services; using Umbraco.Web.Models.Trees; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; -using Umbraco.Web._Legacy.Actions; + using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Trees diff --git a/src/Umbraco.Web/TypeLoaderExtensions.cs b/src/Umbraco.Web/TypeLoaderExtensions.cs index 710c342115..4d09783ca9 100644 --- a/src/Umbraco.Web/TypeLoaderExtensions.cs +++ b/src/Umbraco.Web/TypeLoaderExtensions.cs @@ -5,7 +5,7 @@ using Umbraco.Web.Mvc; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; using Umbraco.Core.Composing; -using Umbraco.Web._Legacy.Actions; + namespace Umbraco.Web { @@ -14,15 +14,6 @@ namespace Umbraco.Web /// public static class TypeLoaderExtensions { - /// - /// Returns all available IAction in application - /// - /// - internal static IEnumerable GetActions(this TypeLoader mgr) - { - return mgr.GetTypes(); - } - /// /// Returns all available TreeApiController's in application that are attribute with TreeAttribute /// @@ -43,15 +34,5 @@ namespace Umbraco.Web return mgr.GetTypes(); } - /// - /// Returns all available ISearchableTrees in application - /// - /// - /// - internal static IEnumerable GetSearchableTrees(this TypeLoader mgr) - { - return mgr.GetTypes(); - } - } } diff --git a/src/Umbraco.Web/UI/Pages/UmbracoEnsuredPage.cs b/src/Umbraco.Web/UI/Pages/UmbracoEnsuredPage.cs index 30adf2cc3d..9f13939bac 100644 --- a/src/Umbraco.Web/UI/Pages/UmbracoEnsuredPage.cs +++ b/src/Umbraco.Web/UI/Pages/UmbracoEnsuredPage.cs @@ -11,9 +11,10 @@ using Umbraco.Core.Exceptions; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Core.Security; +using Umbraco.Web.Actions; using Umbraco.Web.Composing; using Umbraco.Web.Security; -using Umbraco.Web._Legacy.Actions; + namespace Umbraco.Web.UI.Pages { diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 656bbd29c5..f7317939dd 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -428,7 +428,6 @@ - @@ -531,39 +530,32 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -953,7 +945,6 @@ - @@ -1269,12 +1260,6 @@ NewRelationType.aspx - - RelationTypesWebService.asmx - Component - - - insertMasterpageContent.aspx ASPXCodeBehind @@ -1378,7 +1363,6 @@ ASPXCodeBehind - ASPXCodeBehind diff --git a/src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForContentAttribute.cs b/src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForContentAttribute.cs index 4d9a6db129..5d71992b74 100644 --- a/src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForContentAttribute.cs +++ b/src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForContentAttribute.cs @@ -5,9 +5,10 @@ using System.Web.Http.Filters; using Umbraco.Core.Exceptions; using Umbraco.Web.Composing; using Umbraco.Web.Editors; -using Umbraco.Web._Legacy.Actions; + using Umbraco.Core; using Umbraco.Core.Models; +using Umbraco.Web.Actions; namespace Umbraco.Web.WebApi.Filters { @@ -39,7 +40,7 @@ namespace Umbraco.Web.WebApi.Filters { if (string.IsNullOrEmpty(paramName)) throw new ArgumentNullOrEmptyException(nameof(paramName)); _paramName = paramName; - _permissionToCheck = ActionBrowse.Instance.Letter; + _permissionToCheck = ActionBrowse.ActionLetter; } public EnsureUserPermissionForContentAttribute(string paramName, char permissionToCheck) diff --git a/src/Umbraco.Web/WebApi/Filters/FilterAllowedOutgoingContentAttribute.cs b/src/Umbraco.Web/WebApi/Filters/FilterAllowedOutgoingContentAttribute.cs index 748e0e8a7d..31e0b22ce1 100644 --- a/src/Umbraco.Web/WebApi/Filters/FilterAllowedOutgoingContentAttribute.cs +++ b/src/Umbraco.Web/WebApi/Filters/FilterAllowedOutgoingContentAttribute.cs @@ -8,7 +8,8 @@ using Umbraco.Core.Services; using Umbraco.Core; using Umbraco.Web.Composing; using Umbraco.Core.Models; -using Umbraco.Web._Legacy.Actions; +using Umbraco.Web.Actions; + namespace Umbraco.Web.WebApi.Filters { @@ -25,7 +26,7 @@ namespace Umbraco.Web.WebApi.Filters public FilterAllowedOutgoingContentAttribute(Type outgoingType) : this(outgoingType, Current.Services.UserService, Current.Services.EntityService) { - _permissionToCheck = ActionBrowse.Instance.Letter; + _permissionToCheck = ActionBrowse.ActionLetter; } public FilterAllowedOutgoingContentAttribute(Type outgoingType, char permissionToCheck) @@ -37,7 +38,7 @@ namespace Umbraco.Web.WebApi.Filters public FilterAllowedOutgoingContentAttribute(Type outgoingType, string propertyName) : this(outgoingType, propertyName, Current.Services.UserService, Current.Services.EntityService) { - _permissionToCheck = ActionBrowse.Instance.Letter; + _permissionToCheck = ActionBrowse.ActionLetter; } public FilterAllowedOutgoingContentAttribute(Type outgoingType, IUserService userService, IEntityService entityService) @@ -45,7 +46,7 @@ namespace Umbraco.Web.WebApi.Filters { _userService = userService ?? throw new ArgumentNullException(nameof(userService)); _entityService = entityService ?? throw new ArgumentNullException(nameof(entityService)); - _permissionToCheck = ActionBrowse.Instance.Letter; + _permissionToCheck = ActionBrowse.ActionLetter; } public FilterAllowedOutgoingContentAttribute(Type outgoingType, char permissionToCheck, IUserService userService, IEntityService entityService) @@ -65,7 +66,7 @@ namespace Umbraco.Web.WebApi.Filters _entityService = entityService ?? throw new ArgumentNullException(nameof(entityService)); _userService = userService; _entityService = entityService; - _permissionToCheck = ActionBrowse.Instance.Letter; + _permissionToCheck = ActionBrowse.ActionLetter; } protected override void FilterItems(IUser user, IList items) diff --git a/src/Umbraco.Web/_Legacy/Actions/Action.cs b/src/Umbraco.Web/_Legacy/Actions/Action.cs deleted file mode 100644 index 388a5735fd..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/Action.cs +++ /dev/null @@ -1,191 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text.RegularExpressions; -using Umbraco.Core.Logging; -using Umbraco.Core.Models.Membership; -using Umbraco.Web.Composing; -using Umbraco.Core.Services; - -namespace Umbraco.Web._Legacy.Actions -{ - /// - /// Actions and Actionhandlers are a key concept to umbraco and a developer whom wish to apply - /// businessrules whenever data is changed within umbraco, by implementing the IActionHandler - /// interface it's possible to invoke methods (foreign to umbraco) - this can be used whenever - /// there is a specific rule which needs to be applied to content. - /// - /// The Action class itself has responsibility for registering actions and actionhandlers, - /// and contains methods which will be invoked whenever a change is made to ex. a document, media or member - /// - /// An action/actionhandler will automatically be registered, using reflection - /// which is enabling thirdparty developers to extend the core functionality of - /// umbraco without changing the codebase. - /// - [Obsolete("Actions and ActionHandlers are obsolete and should no longer be used")] - public class Action - { - private static readonly Dictionary ActionJs = new Dictionary(); - - /// - /// Jacascript for the contextmenu - /// Suggestion: this method should be moved to the presentation layer. - /// - /// - /// String representation - public string ReturnJavascript(string language) - { - return findActions(language); - } - - /// - /// Returns a list of JavaScript file paths. - /// - /// - public static List GetJavaScriptFileReferences() - { - return Current.Actions - .Where(x => !string.IsNullOrWhiteSpace(x.JsSource)) - .Select(x => x.JsSource).ToList(); - //return ActionJsReference; - } - - /// - /// Javascript menuitems - tree contextmenu - /// Umbraco console - /// - /// Suggestion: this method should be moved to the presentation layer. - /// - /// - /// - private static string findActions(string language) - { - if (!ActionJs.ContainsKey(language)) - { - string _actionJsList = ""; - - foreach (IAction action in Current.Actions) - { - // Adding try/catch so this rutine doesn't fail if one of the actions fail - // Add to language JsList - try - { - // NH: Add support for css sprites - string icon = action.Icon; - if (!string.IsNullOrEmpty(icon) && icon.StartsWith(".")) - icon = icon.Substring(1, icon.Length - 1); - else - icon = "images/" + icon; - - _actionJsList += string.Format(",\n\tmenuItem(\"{0}\", \"{1}\", \"{2}\", \"{3}\")", - action.Letter, icon, Current.Services.TextService.Localize("actions/"+ action.Alias, new[] { language }), action.JsFunctionName); - } - catch (Exception ex) - { - Current.Logger.Error(ex, "Error registrering action to javascript"); - } - } - - if (_actionJsList.Length > 0) - _actionJsList = _actionJsList.Substring(2, _actionJsList.Length - 2); - - _actionJsList = "\nvar menuMethods = new Array(\n" + _actionJsList + "\n)\n"; - ActionJs.Add(language, _actionJsList); - } - - return ActionJs[language]; - - } - - internal static List FromEntityPermission(EntityPermission entityPermission) - { - List list = new List(); - foreach (var c in entityPermission.AssignedPermissions.Where(x => x.Length == 1).Select(x => x.ToCharArray()[0])) - { - IAction action = Current.Actions.ToList().Find( - delegate (IAction a) - { - return a.Letter == c; - } - ); - if (action != null) - list.Add(action); - } - return list; - } - - /// - /// Returns a list of IActions that are permission assignable - /// - /// - public static List GetPermissionAssignable() - { - return Current.Actions.ToList().FindAll(x => x.CanBePermissionAssigned); - } - - /// - /// Check if the current IAction is using legacy javascript methods - /// - /// - /// false if the Iaction is incompatible with 4.5 - public static bool ValidateActionJs(IAction action) - { - return !action.JsFunctionName.Contains("+"); - } - - /// - /// Method to convert the old modal calls to the new ones - /// - /// - /// - public static string ConvertLegacyJs(string javascript) - { - MatchCollection tags = - Regex.Matches(javascript, "openModal[^;]*;", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); - foreach (Match tag in tags) - { - string[] function = tag.Value.Split(','); - if (function.Length > 0) - { - string newFunction = "UmbClientMgr.openModalWindow" + function[0].Substring(9).Replace("parent.nodeID", "UmbClientMgr.mainTree().getActionNode().nodeId").Replace("nodeID", "UmbClientMgr.mainTree().getActionNode().nodeId").Replace("parent.returnRandom()", "'" + Guid.NewGuid().ToString() + "'"); - newFunction += ", " + function[1]; - newFunction += ", true"; - newFunction += ", " + function[2]; - newFunction += ", " + function[3]; - javascript = javascript.Replace(tag.Value, newFunction); - } - } - - return javascript; - } - } - - /// - /// This class is used to manipulate IActions that are implemented in a wrong way - /// For instance incompatible trees with 4.0 vs 4.5 - /// - public class PlaceboAction : IAction - { - public char Letter { get; set; } - public bool ShowInNotifier { get; set; } - public bool CanBePermissionAssigned { get; set; } - public string Icon { get; set; } - public string Alias { get; set; } - public string JsFunctionName { get; set; } - public string JsSource { get; set; } - - public PlaceboAction() { } - public PlaceboAction(IAction legacyAction) - { - Letter = legacyAction.Letter; - ShowInNotifier = legacyAction.ShowInNotifier; - CanBePermissionAssigned = legacyAction.CanBePermissionAssigned; - Icon = legacyAction.Icon; - Alias = legacyAction.Alias; - JsFunctionName = legacyAction.JsFunctionName; - JsSource = legacyAction.JsSource; - } - } - -} diff --git a/src/Umbraco.Web/_Legacy/Actions/ActionAssignDomain.cs b/src/Umbraco.Web/_Legacy/Actions/ActionAssignDomain.cs deleted file mode 100644 index 37de1f8e0f..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/ActionAssignDomain.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using Umbraco.Web.UI.Pages; -using Umbraco.Core; -using Umbraco.Core.CodeAnnotations; - -namespace Umbraco.Web._Legacy.Actions -{ - /// - /// This action is invoked when a domain is being assigned to a document - /// - [ActionMetadata(Constants.Conventions.PermissionCategories.AdministrationCategory)] - public class ActionAssignDomain : IAction - { - public static ActionAssignDomain Instance { get; } = new ActionAssignDomain(); - - #region IAction Members - - public char Letter - { - get - { - return 'I'; - } - } - - public string JsFunctionName - { - get - { - return null; - } - } - - public string JsSource - { - get - { - return null; - } - } - - public string Alias - { - get - { - return "assignDomain"; - } - } - - public string Icon - { - get - { - return "home"; - } - } - - public bool ShowInNotifier - { - get - { - return false; - } - } - public bool CanBePermissionAssigned - { - get - { - return true; - } - } - #endregion - } -} diff --git a/src/Umbraco.Web/_Legacy/Actions/ActionBrowse.cs b/src/Umbraco.Web/_Legacy/Actions/ActionBrowse.cs deleted file mode 100644 index 1425b27917..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/ActionBrowse.cs +++ /dev/null @@ -1,65 +0,0 @@ -using Umbraco.Core; -using Umbraco.Core.CodeAnnotations; - -namespace Umbraco.Web._Legacy.Actions -{ - /// - /// This action is used as a security constraint that grants a user the ability to view nodes in a tree - /// that has permissions applied to it. - /// - /// - /// This action should not be invoked. It is used as the minimum required permission to view nodes in the content tree. By - /// granting a user this permission, the user is able to see the node in the tree but not edit the document. This may be used by other trees - /// that support permissions in the future. - /// - [ActionMetadata(Constants.Conventions.PermissionCategories.ContentCategory)] - public class ActionBrowse : IAction - { - //create singleton - private static readonly ActionBrowse instance = new ActionBrowse(); - private ActionBrowse() { } - public static ActionBrowse Instance - { - get { return instance; } - } - - #region IAction Members - - public char Letter - { - get { return 'F'; } - } - - public bool ShowInNotifier - { - get { return false; } - } - - public bool CanBePermissionAssigned - { - get { return true; } - } - - public string Icon - { - get { return ""; } - } - - public string Alias - { - get { return "browse"; } - } - - public string JsFunctionName - { - get { return ""; } - } - - public string JsSource - { - get { return ""; } - } - - #endregion - } -} diff --git a/src/Umbraco.Web/_Legacy/Actions/ActionChangeDocType.cs b/src/Umbraco.Web/_Legacy/Actions/ActionChangeDocType.cs deleted file mode 100644 index 9c31c172ab..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/ActionChangeDocType.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using Umbraco.Web.UI.Pages; -using Umbraco.Core; -using Umbraco.Core.CodeAnnotations; - -namespace Umbraco.Web._Legacy.Actions -{ - /// - /// This action is invoked when the document type of a piece of content is changed - /// - [ActionMetadata(Constants.Conventions.PermissionCategories.AdministrationCategory)] - public class ActionChangeDocType : IAction - { - //create singleton -#pragma warning disable 612,618 - private static readonly ActionChangeDocType m_instance = new ActionChangeDocType(); -#pragma warning restore 612,618 - - public static ActionChangeDocType Instance - { - get { return m_instance; } - } - - #region IAction Members - - public char Letter - { - get - { - - return '7'; - } - } - - public string JsFunctionName - { - get - { - return string.Format("{0}.actionChangeDocType()", ClientTools.Scripts.GetAppActions); - } - } - - public string JsSource - { - get - { - - return null; - } - } - - public string Alias - { - get - { - - return "changeDocType"; - } - } - - public string Icon - { - get - { - - return "axis-rotation-2"; - } - } - - public bool ShowInNotifier - { - get - { - - return true; - } - } - public bool CanBePermissionAssigned - { - get - { - - return true; - } - } - #endregion - } -} diff --git a/src/Umbraco.Web/_Legacy/Actions/ActionCollectionBuilder.cs b/src/Umbraco.Web/_Legacy/Actions/ActionCollectionBuilder.cs deleted file mode 100644 index 39058d6836..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/ActionCollectionBuilder.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using LightInject; -using Umbraco.Core.Composing; - -namespace Umbraco.Web._Legacy.Actions -{ - internal class ActionCollectionBuilder : ICollectionBuilder - { - private static Func> _producer; - - // for tests only - does not register the collection - public ActionCollectionBuilder() - { } - - public ActionCollectionBuilder(IServiceContainer container) - { - var collectionLifetime = CollectionLifetime; - - // register the collection - special lifetime - // the lifetime here is custom ResettablePerContainerLifetime which will manage one - // single instance of the collection (much alike PerContainerLifetime) but can be resetted - // to force a new collection to be created. - // this is needed because of the weird things we do during install, where we'd use the - // infamous DirtyBackdoorToConfiguration to reset the ActionResolver way after Resolution - // had frozen. This has been replaced by the possibility here to set the producer at any - // time - but the builder is internal - and all this will be gone eventually. - container.Register(factory => factory.GetInstance().CreateCollection(), collectionLifetime); - } - - public ActionCollection CreateCollection() - { - var actions = new List(); - foreach (var type in _producer()) - { - var getter = type.GetProperty("Instance", BindingFlags.Public | BindingFlags.Static); - var instance = getter == null - ? Activator.CreateInstance(type) as IAction - : getter.GetValue(null, null) as IAction; - if (instance == null) continue; - actions.Add(instance); - } - return new ActionCollection(actions); - } - - public void SetProducer(Func> producer) - { - _producer = producer; - CollectionLifetime.Reset(); - } - - private ResettablePerContainerLifetime CollectionLifetime { get; } = new ResettablePerContainerLifetime(); - - private class ResettablePerContainerLifetime : ILifetime - { - private object _instance; - - public object GetInstance(Func createInstance, Scope scope) - { - // not dealing with disposable instances, actions are not disposable - return _instance ?? (_instance = createInstance()); - } - - public void Reset() - { - _instance = null; - } - } - } -} diff --git a/src/Umbraco.Web/_Legacy/Actions/ActionCopy.cs b/src/Umbraco.Web/_Legacy/Actions/ActionCopy.cs deleted file mode 100644 index a489f1d280..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/ActionCopy.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using Umbraco.Web.UI.Pages; -using Umbraco.Core; -using Umbraco.Core.CodeAnnotations; - -namespace Umbraco.Web._Legacy.Actions -{ - /// - /// This action is invoked when copying a document, media, member - /// - [ActionMetadata(Constants.Conventions.PermissionCategories.StructureCategory)] - public class ActionCopy : IAction - { - //create singleton -#pragma warning disable 612,618 - private static readonly ActionCopy m_instance = new ActionCopy(); -#pragma warning restore 612,618 - - public static ActionCopy Instance - { - get { return m_instance; } - } - - #region IAction Members - - public char Letter - { - get - { - - return 'O'; - } - } - - public string JsFunctionName - { - get - { - return string.Format("{0}.actionCopy()", ClientTools.Scripts.GetAppActions); - } - } - - public string JsSource - { - get - { - - return null; - } - } - - public string Alias - { - get - { - - return "copy"; - } - } - - public string Icon - { - get - { - - return "documents"; - } - } - - public bool ShowInNotifier - { - get - { - - return true; - } - } - public bool CanBePermissionAssigned - { - get - { - - return true; - } - } - #endregion - } -} diff --git a/src/Umbraco.Web/_Legacy/Actions/ActionCreateBlueprintFromContent.cs b/src/Umbraco.Web/_Legacy/Actions/ActionCreateBlueprintFromContent.cs deleted file mode 100644 index e00de39aea..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/ActionCreateBlueprintFromContent.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Umbraco.Core; -using Umbraco.Core.CodeAnnotations; - -namespace Umbraco.Web._Legacy.Actions -{ - [ActionMetadata(Constants.Conventions.PermissionCategories.ContentCategory)] - public class ActionCreateBlueprintFromContent : IAction - { - private static readonly ActionCreateBlueprintFromContent instance = new ActionCreateBlueprintFromContent(); - - public static ActionCreateBlueprintFromContent Instance - { - get { return instance; } - } - - public char Letter { get; private set; } - public bool ShowInNotifier { get; private set; } - public bool CanBePermissionAssigned { get; private set; } - public string Icon { get; private set; } - public string Alias { get; private set; } - public string JsFunctionName { get; private set; } - public string JsSource { get; private set; } - - public ActionCreateBlueprintFromContent() - { - Letter = 'ï'; - CanBePermissionAssigned = true; - Icon = "blueprint"; - Alias = "createblueprint"; - } - } -} diff --git a/src/Umbraco.Web/_Legacy/Actions/ActionDelete.cs b/src/Umbraco.Web/_Legacy/Actions/ActionDelete.cs deleted file mode 100644 index 09ce4d8602..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/ActionDelete.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using Umbraco.Web.UI.Pages; -using Umbraco.Core; -using Umbraco.Core.CodeAnnotations; - -namespace Umbraco.Web._Legacy.Actions -{ - /// - /// This action is invoked when a document, media, member is deleted - /// - [ActionMetadata(Constants.Conventions.PermissionCategories.ContentCategory)] - public class ActionDelete : IAction - { - //create singleton -#pragma warning disable 612,618 - private static readonly ActionDelete m_instance = new ActionDelete(); -#pragma warning restore 612,618 - - public static ActionDelete Instance - { - get { return m_instance; } - } - - #region IAction Members - - public char Letter - { - get - { - return 'D'; - } - } - - public string JsFunctionName - { - get - { - return string.Format("{0}.actionDelete()", ClientTools.Scripts.GetAppActions); - } - } - - public string JsSource - { - get - { - return null; - } - } - - public string Alias - { - get - { - return "delete"; - } - } - - public string Icon - { - get - { - return "delete"; - } - } - - public bool ShowInNotifier - { - get - { - return true; - } - } - public bool CanBePermissionAssigned - { - get - { - return true; - } - } - #endregion - } -} diff --git a/src/Umbraco.Web/_Legacy/Actions/ActionEmptyTranscan.cs b/src/Umbraco.Web/_Legacy/Actions/ActionEmptyTranscan.cs deleted file mode 100644 index 7f8dd6b03c..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/ActionEmptyTranscan.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; -using Umbraco.Web.UI.Pages; - -namespace Umbraco.Web._Legacy.Actions -{ - /// - /// This action is invoked when the trash can is emptied - /// - public class ActionEmptyTranscan : IAction - { - //create singleton - private static readonly ActionEmptyTranscan InnerInstance = new ActionEmptyTranscan(); - - public static ActionEmptyTranscan Instance - { - get { return InnerInstance; } - } - - #region IAction Members - - public char Letter - { - get - { - return 'N'; - } - } - - public string JsFunctionName - { - get - { - return string.Format("{0}.actionEmptyTranscan()", ClientTools.Scripts.GetAppActions); - } - } - - public string JsSource - { - get - { - return null; - } - } - - public string Alias - { - get - { - return "emptyRecycleBin"; - } - } - - public string Icon - { - get - { - return "trash"; - } - } - - public bool ShowInNotifier - { - get - { - return false; - } - } - public bool CanBePermissionAssigned - { - get - { - return false; - } - } - - #endregion - } -} diff --git a/src/Umbraco.Web/_Legacy/Actions/ActionExport.cs b/src/Umbraco.Web/_Legacy/Actions/ActionExport.cs deleted file mode 100644 index df78026ea0..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/ActionExport.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; - -namespace Umbraco.Web._Legacy.Actions -{ - /// - /// This action is invoked when exporting a document type - /// - public class ActionExport : IAction - { - //create singleton -#pragma warning disable 612,618 - private static readonly ActionExport m_instance = new ActionExport(); -#pragma warning restore 612,618 - - public static ActionExport Instance - { - get { return m_instance; } - } - - #region IAction Members - - public char Letter - { - get - { - return '9'; - } - } - - public string JsFunctionName - { - get { return ""; } - } - - public string JsSource - { - get - { - return null; - } - } - - public string Alias - { - get - { - return "export"; - } - } - - public string Icon - { - get - { - return "download-alt"; - } - } - - public bool ShowInNotifier - { - get - { - return false; - } - } - public bool CanBePermissionAssigned - { - get - { - return false; - } - } - - #endregion - } -} diff --git a/src/Umbraco.Web/_Legacy/Actions/ActionImport.cs b/src/Umbraco.Web/_Legacy/Actions/ActionImport.cs deleted file mode 100644 index 42947cf36e..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/ActionImport.cs +++ /dev/null @@ -1,77 +0,0 @@ -namespace Umbraco.Web._Legacy.Actions -{ - /// - /// This action is invoked when importing a document type - /// - public class ActionImport : IAction - { - //create singleton -#pragma warning disable 612,618 - private static readonly ActionImport m_instance = new ActionImport(); -#pragma warning restore 612,618 - - public static ActionImport Instance - { - get { return m_instance; } - } - - #region IAction Members - - public char Letter - { - get - { - return '8'; - } - } - - public string JsFunctionName - { - get - { - return ""; - } - } - - public string JsSource - { - get - { - return null; - } - } - - public string Alias - { - get - { - return "importDocumentType"; - } - } - - public string Icon - { - get - { - return "page-up"; - } - } - - public bool ShowInNotifier - { - get - { - return false; - } - } - public bool CanBePermissionAssigned - { - get - { - return false; - } - } - - #endregion - } -} diff --git a/src/Umbraco.Web/_Legacy/Actions/ActionMove.cs b/src/Umbraco.Web/_Legacy/Actions/ActionMove.cs deleted file mode 100644 index 80aff5736a..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/ActionMove.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using Umbraco.Web.UI.Pages; -using Umbraco.Core; -using Umbraco.Core.CodeAnnotations; - -namespace Umbraco.Web._Legacy.Actions -{ - /// - /// This action is invoked upon creation of a document, media, member - /// - [ActionMetadata(Constants.Conventions.PermissionCategories.StructureCategory)] - public class ActionMove : IAction - { - //create singleton -#pragma warning disable 612,618 - private static readonly ActionMove m_instance = new ActionMove(); -#pragma warning restore 612,618 - - public static ActionMove Instance - { - get { return m_instance; } - } - - #region IAction Members - - public char Letter - { - get - { - - return 'M'; - } - } - - public string JsFunctionName - { - get - { - return string.Format("{0}.actionMove()", ClientTools.Scripts.GetAppActions); - } - } - - public string JsSource - { - get - { - - return null; - } - } - - public string Alias - { - get - { - - return "move"; - } - } - - public string Icon - { - get - { - - return "enter"; - } - } - - public bool ShowInNotifier - { - get - { - - return true; - } - } - public bool CanBePermissionAssigned - { - get - { - - return true; - } - } - #endregion - } -} diff --git a/src/Umbraco.Web/_Legacy/Actions/ActionNew.cs b/src/Umbraco.Web/_Legacy/Actions/ActionNew.cs deleted file mode 100644 index 72e863e38b..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/ActionNew.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System; -using Umbraco.Web.UI.Pages; -using Umbraco.Core; -using Umbraco.Core.CodeAnnotations; - -namespace Umbraco.Web._Legacy.Actions -{ - /// - /// This action is invoked upon creation of a document - /// - [ActionMetadata(Constants.Conventions.PermissionCategories.ContentCategory)] - public class ActionNew : IAction - { - public static ActionNew Instance { get; } = new ActionNew(); - - #region IAction Members - - public char Letter - { - get - { - return 'C'; - } - } - - public string JsFunctionName - { - get - { - return string.Format("{0}.actionNew()", ClientTools.Scripts.GetAppActions); - } - } - - public string JsSource - { - get - { - return null; - } - } - - public string Alias - { - get - { - return "create"; - } - } - - public string Icon - { - get - { - return "add"; - } - } - - public bool ShowInNotifier - { - get - { - return true; - } - } - public bool CanBePermissionAssigned - { - get - { - return true; - } - } - - #endregion - } -} diff --git a/src/Umbraco.Web/_Legacy/Actions/ActionNotify.cs b/src/Umbraco.Web/_Legacy/Actions/ActionNotify.cs deleted file mode 100644 index ef281eecbe..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/ActionNotify.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using Umbraco.Web.UI.Pages; - -namespace Umbraco.Web._Legacy.Actions -{ - /// - /// This action is invoked when a notification is sent - /// - public class ActionNotify : IAction - { - //create singleton -#pragma warning disable 612,618 - private static readonly ActionNotify m_instance = new ActionNotify(); -#pragma warning restore 612,618 - - public static ActionNotify Instance - { - get { return m_instance; } - } - - #region IAction Members - - public char Letter - { - get - { - - return 'T'; - } - } - - public string JsFunctionName - { - get - { - return string.Format("{0}.actionNotify()", ClientTools.Scripts.GetAppActions); - } - } - - public string JsSource - { - get - { - - return null; - } - } - - public string Alias - { - get - { - return "notify"; - } - } - - public string Icon - { - get - { - return "megaphone"; - } - } - - public bool ShowInNotifier - { - get - { - return false; - } - } - public bool CanBePermissionAssigned - { - get - { - return false; - } - } - #endregion - } -} diff --git a/src/Umbraco.Web/_Legacy/Actions/ActionNull.cs b/src/Umbraco.Web/_Legacy/Actions/ActionNull.cs deleted file mode 100644 index 78c5175fb6..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/ActionNull.cs +++ /dev/null @@ -1,56 +0,0 @@ -namespace Umbraco.Web._Legacy.Actions -{ - /// - /// This permission is assigned to a node when there are no permissions assigned to the node. - /// This is used internally to assign no permissions to a node for a user and shouldn't be used in code. - /// - public class ActionNull : IAction - { - //create singleton - private static readonly ActionNull instance = new ActionNull(); - private ActionNull() { } - public static ActionNull Instance - { - get { return instance; } - } - - #region IAction Members - - public char Letter - { - get { return '-'; } - } - - public bool ShowInNotifier - { - get { return false; } - } - - public bool CanBePermissionAssigned - { - get { return false; } - } - - public string Icon - { - get { return string.Empty; } - } - - public string Alias - { - get { return string.Empty; } - } - - public string JsFunctionName - { - get { return string.Empty; } - } - - public string JsSource - { - get { return string.Empty; } - } - - #endregion - } -} diff --git a/src/Umbraco.Web/_Legacy/Actions/ActionPackage.cs b/src/Umbraco.Web/_Legacy/Actions/ActionPackage.cs deleted file mode 100644 index 832e691b48..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/ActionPackage.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System; -using Umbraco.Web.UI.Pages; - -namespace Umbraco.Web._Legacy.Actions -{ - /// - /// This action is invoked upon creation of a document, media, member - /// - public class ActionPackage : IAction - { - //create singleton -#pragma warning disable 612,618 - private static readonly ActionPackage m_instance = new ActionPackage(); -#pragma warning restore 612,618 - - public static ActionPackage Instance - { - get { return m_instance; } - } - - #region IAction Members - - public char Letter - { - get - { - return 'X'; - } - } - - public string JsFunctionName - { - get - { - return string.Format("{0}.actionPackage()", ClientTools.Scripts.GetAppActions); - } - } - - public string JsSource - { - get - { - return ""; - } - } - - public string Alias - { - get - { - return "importPackage"; - } - } - - public string Icon - { - get - { - return "gift"; - } - } - - public bool ShowInNotifier - { - get - { - return false; - } - } - public bool CanBePermissionAssigned - { - get - { - return false; - } - } - - #endregion - } -} diff --git a/src/Umbraco.Web/_Legacy/Actions/ActionPackageCreate.cs b/src/Umbraco.Web/_Legacy/Actions/ActionPackageCreate.cs deleted file mode 100644 index f0ccb03d8e..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/ActionPackageCreate.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System; -using Umbraco.Web.UI.Pages; - -namespace Umbraco.Web._Legacy.Actions -{ - /// - /// This action is invoked upon creation of a document, media, member - /// - public class ActionPackageCreate : IAction - { - //create singleton -#pragma warning disable 612,618 - private static readonly ActionPackageCreate m_instance = new ActionPackageCreate(); -#pragma warning restore 612,618 - - public static ActionPackageCreate Instance - { - get { return m_instance; } - } - - #region IAction Members - - public char Letter - { - get - { - return 'Y'; - } - } - - public string JsFunctionName - { - get - { - return string.Format("{0}.actionPackageCreate()", ClientTools.Scripts.GetAppActions); - } - } - - public string JsSource - { - get - { - return null; - } - } - - public string Alias - { - get - { - return "createPackage"; - } - } - - public string Icon - { - get - { - return "gift"; - } - } - - public bool ShowInNotifier - { - get - { - return false; - } - } - public bool CanBePermissionAssigned - { - get - { - return false; - } - } - - #endregion - } -} diff --git a/src/Umbraco.Web/_Legacy/Actions/ActionProtect.cs b/src/Umbraco.Web/_Legacy/Actions/ActionProtect.cs deleted file mode 100644 index 357dfe89a4..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/ActionProtect.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using Umbraco.Web.UI.Pages; -using Umbraco.Core; -using Umbraco.Core.CodeAnnotations; - -namespace Umbraco.Web._Legacy.Actions -{ - /// - /// This action is invoked when a document is protected or unprotected - /// - [ActionMetadata(Constants.Conventions.PermissionCategories.AdministrationCategory)] - public class ActionProtect : IAction - { - //create singleton -#pragma warning disable 612,618 - private static readonly ActionProtect m_instance = new ActionProtect(); -#pragma warning restore 612,618 - - public static ActionProtect Instance - { - get { return m_instance; } - } - - #region IAction Members - - public char Letter - { - get - { - - return 'P'; - } - } - - public string JsFunctionName - { - get - { - return string.Format("{0}.actionProtect()", ClientTools.Scripts.GetAppActions); - } - } - - public string JsSource - { - get - { - - return null; - } - } - - public string Alias - { - get - { - - return "protect"; - } - } - - public string Icon - { - get - { - - return "lock"; - } - } - - public bool ShowInNotifier - { - get - { - - return true; - } - } - public bool CanBePermissionAssigned - { - get - { - - return true; - } - } - #endregion - } -} diff --git a/src/Umbraco.Web/_Legacy/Actions/ActionPublish.cs b/src/Umbraco.Web/_Legacy/Actions/ActionPublish.cs deleted file mode 100644 index 6b54873c43..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/ActionPublish.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using Umbraco.Web.UI.Pages; -using Umbraco.Core; -using Umbraco.Core.CodeAnnotations; - -namespace Umbraco.Web._Legacy.Actions -{ - /// - /// This action is invoked when a document is being published - /// - [ActionMetadata(Constants.Conventions.PermissionCategories.ContentCategory)] - public class ActionPublish : IAction - { - //create singleton -#pragma warning disable 612,618 - private static readonly ActionPublish m_instance = new ActionPublish(); -#pragma warning restore 612,618 - - public static ActionPublish Instance - { - get { return m_instance; } - } - - #region IAction Members - - public char Letter - { - get - { - return 'U'; - } - } - - public string JsFunctionName - { - get - { - return string.Empty; - } - } - - public string JsSource - { - get - { - return null; - } - } - - public string Alias - { - get - { - return "publish"; - } - } - - public string Icon - { - get - { - return string.Empty; - } - } - - public bool ShowInNotifier - { - get - { - return true; - } - } - public bool CanBePermissionAssigned - { - get - { - return true; - } - } - #endregion - } -} diff --git a/src/Umbraco.Web/_Legacy/Actions/ActionRePublish.cs b/src/Umbraco.Web/_Legacy/Actions/ActionRePublish.cs deleted file mode 100644 index b78af779e4..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/ActionRePublish.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; -using Umbraco.Web.UI.Pages; - -namespace Umbraco.Web._Legacy.Actions -{ - /// - /// This action is invoked when all documents are being republished - /// - public class ActionRePublish : IAction - { - //create singleton -#pragma warning disable 612,618 - private static readonly ActionRePublish m_instance = new ActionRePublish(); -#pragma warning restore 612,618 - - public static ActionRePublish Instance - { - get { return m_instance; } - } - - #region IAction Members - - public char Letter - { - get - { - return 'B'; - } - } - - public string JsFunctionName - { - get - { - return string.Format("{0}.actionRePublish()", ClientTools.Scripts.GetAppActions); - } - } - - public string JsSource - { - get - { - return null; - } - } - - public string Alias - { - get - { - return "republish"; - } - } - - public string Icon - { - get - { - return "globe"; - } - } - - public bool ShowInNotifier - { - get - { - return false; - } - } - public bool CanBePermissionAssigned - { - get - { - return false; - } - } - #endregion - } -} diff --git a/src/Umbraco.Web/_Legacy/Actions/ActionRefresh.cs b/src/Umbraco.Web/_Legacy/Actions/ActionRefresh.cs deleted file mode 100644 index 07133b4030..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/ActionRefresh.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using Umbraco.Web.UI.Pages; - -namespace Umbraco.Web._Legacy.Actions -{ - /// - /// This action is invoked when a node reloads its children - /// Concerns only the tree itself and thus you should not handle - /// this action from without umbraco. - /// - [LegacyActionMenuItem("umbracoMenuActions", "RefreshNode")] - public class ActionRefresh : IAction - { - //create singleton - private static readonly ActionRefresh InnerInstance = new ActionRefresh(); - - public static ActionRefresh Instance - { - get { return InnerInstance; } - } - - #region IAction Members - - public char Letter - { - get - { - - return 'L'; - } - } - - public string JsFunctionName - { - get - { - return string.Format("{0}.actionRefresh()", ClientTools.Scripts.GetAppActions); - } - } - - public string JsSource - { - get - { - - return null; - } - } - - public string Alias - { - get - { - - return "refreshNode"; - } - } - - public string Icon - { - get - { - - return "refresh"; - } - } - - public bool ShowInNotifier - { - get - { - - return false; - } - } - public bool CanBePermissionAssigned - { - get - { - - return false; - } - } - #endregion - } -} diff --git a/src/Umbraco.Web/_Legacy/Actions/ActionRestore.cs b/src/Umbraco.Web/_Legacy/Actions/ActionRestore.cs deleted file mode 100644 index da70eb1409..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/ActionRestore.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace Umbraco.Web._Legacy.Actions -{ - /// - /// This action is invoked when the content/media item is to be restored from the recycle bin - /// - public class ActionRestore : IAction - { - //create singleton - - private ActionRestore() { } - - public static ActionRestore Instance { get; } = new ActionRestore(); - - #region IAction Members - - public char Letter => 'V'; - - public string JsFunctionName => null; - - public string JsSource => null; - - public string Alias => "restore"; - - public string Icon => "undo"; - - public bool ShowInNotifier => true; - - public bool CanBePermissionAssigned => false; - - #endregion - } -} diff --git a/src/Umbraco.Web/_Legacy/Actions/ActionRights.cs b/src/Umbraco.Web/_Legacy/Actions/ActionRights.cs deleted file mode 100644 index e1ee74e61c..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/ActionRights.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using Umbraco.Web.UI.Pages; -using Umbraco.Core; -using Umbraco.Core.CodeAnnotations; - -namespace Umbraco.Web._Legacy.Actions -{ - /// - /// This action is invoked when rights are changed on a document - /// - [ActionMetadata(Constants.Conventions.PermissionCategories.ContentCategory)] - public class ActionRights : IAction - { - //create singleton -#pragma warning disable 612,618 - private static readonly ActionRights m_instance = new ActionRights(); -#pragma warning restore 612,618 - - public static ActionRights Instance - { - get { return m_instance; } - } - - #region IAction Members - - public char Letter - { - get - { - - return 'R'; - } - } - - public string JsFunctionName - { - get - { - return string.Format("{0}.actionRights()", ClientTools.Scripts.GetAppActions); - } - } - - public string JsSource - { - get - { - - return null; - } - } - - public string Alias - { - get - { - - return "rights"; - } - } - - public string Icon - { - get - { - - return "vcard"; - } - } - - public bool ShowInNotifier - { - get - { - - return true; - } - } - public bool CanBePermissionAssigned - { - get - { - - return true; - } - } - #endregion - } -} diff --git a/src/Umbraco.Web/_Legacy/Actions/ActionRollback.cs b/src/Umbraco.Web/_Legacy/Actions/ActionRollback.cs deleted file mode 100644 index 59044666f7..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/ActionRollback.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System; -using Umbraco.Web.UI.Pages; -using Umbraco.Core; -using Umbraco.Core.CodeAnnotations; - -namespace Umbraco.Web._Legacy.Actions -{ - /// - /// This action is invoked when copying a document is being rolled back - /// - [ActionMetadata(Constants.Conventions.PermissionCategories.AdministrationCategory)] - public class ActionRollback : IAction - { - //create singleton -#pragma warning disable 612,618 - private static readonly ActionRollback m_instance = new ActionRollback(); -#pragma warning restore 612,618 - - public static ActionRollback Instance - { - get { return m_instance; } - } - - #region IAction Members - - public char Letter - { - get - { - - return 'K'; - } - } - - public string JsFunctionName - { - get - { - return string.Format("{0}.actionRollback()", ClientTools.Scripts.GetAppActions); - } - } - - public string JsSource - { - get - { - return ""; - } - } - - public string Alias - { - get - { - - return "rollback"; - } - } - - public string Icon - { - get - { - - return "undo"; - } - } - - public bool ShowInNotifier - { - get - { - - return true; - } - } - public bool CanBePermissionAssigned - { - get - { - - return true; - } - } - #endregion - } -} diff --git a/src/Umbraco.Web/_Legacy/Actions/ActionSort.cs b/src/Umbraco.Web/_Legacy/Actions/ActionSort.cs deleted file mode 100644 index b813dcbc8c..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/ActionSort.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using Umbraco.Web.UI.Pages; -using Umbraco.Core; -using Umbraco.Core.CodeAnnotations; - -namespace Umbraco.Web._Legacy.Actions -{ - /// - /// This action is invoked when children to a document, media, member is being sorted - /// - [ActionMetadata(Constants.Conventions.PermissionCategories.StructureCategory)] - public class ActionSort : IAction - { - //create singleton -#pragma warning disable 612,618 - private static readonly ActionSort m_instance = new ActionSort(); -#pragma warning restore 612,618 - - public static ActionSort Instance - { - get { return m_instance; } - } - - #region IAction Members - - public char Letter - { - get - { - - return 'S'; - } - } - - public string JsFunctionName - { - get - { - return null; - } - } - - public string JsSource - { - get - { - - return null; - } - } - - public string Alias - { - get - { - - return "sort"; - } - } - - public string Icon - { - get - { - - return "navigation-vertical"; - } - } - - public bool ShowInNotifier - { - get - { - - return true; - } - } - public bool CanBePermissionAssigned - { - get - { - - return true; - } - } - #endregion - } -} diff --git a/src/Umbraco.Web/_Legacy/Actions/ActionToPublish.cs b/src/Umbraco.Web/_Legacy/Actions/ActionToPublish.cs deleted file mode 100644 index ff471bc198..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/ActionToPublish.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using Umbraco.Web.UI.Pages; -using Umbraco.Core; -using Umbraco.Core.CodeAnnotations; - -namespace Umbraco.Web._Legacy.Actions -{ - /// - /// This action is invoked when children to a document is being sent to published (by an editor without publishrights) - /// - [ActionMetadata(Constants.Conventions.PermissionCategories.ContentCategory)] - public class ActionToPublish : IAction - { - //create singleton -#pragma warning disable 612,618 - private static readonly ActionToPublish m_instance = new ActionToPublish(); -#pragma warning restore 612,618 - - public static ActionToPublish Instance - { - get { return m_instance; } - } - - #region IAction Members - - public char Letter - { - get - { - return 'H'; - } - } - - public string JsFunctionName - { - get - { - return string.Format("{0}.actionToPublish()", ClientTools.Scripts.GetAppActions); - } - } - - public string JsSource - { - get - { - return null; - } - } - - public string Alias - { - get - { - return "sendtopublish"; - } - } - - public string Icon - { - get - { - return "outbox"; - } - } - - public bool ShowInNotifier - { - get - { - return true; - } - } - public bool CanBePermissionAssigned - { - get - { - //SD: Changed this to true so that any user may be able to perform this action, not just a writer - return true; - } - } - #endregion - } -} diff --git a/src/Umbraco.Web/_Legacy/Actions/ActionTranslate.cs b/src/Umbraco.Web/_Legacy/Actions/ActionTranslate.cs deleted file mode 100644 index 0cc5120fd0..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/ActionTranslate.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using Umbraco.Core; -using Umbraco.Core.CodeAnnotations; - -namespace Umbraco.Web._Legacy.Actions -{ - /// - /// This action is invoked when a translation occurs - /// - [ActionMetadata(Constants.Conventions.PermissionCategories.AdministrationCategory)] - public class ActionTranslate : IAction - { - //create singleton -#pragma warning disable 612,618 - private static readonly ActionTranslate m_instance = new ActionTranslate(); -#pragma warning restore 612,618 - - public static ActionTranslate Instance - { - get { return m_instance; } - } - - #region IAction Members - - public char Letter - { - get - { - return '4'; - } - } - - public string JsFunctionName - { - get - { - return ""; - } - } - - public string JsSource - { - get - { - return null; - } - } - - public string Alias - { - get - { - return "translate"; - } - } - - public string Icon - { - get - { - return "comments"; - } - } - - public bool ShowInNotifier - { - get - { - return true; - } - } - public bool CanBePermissionAssigned - { - get - { - return true; - } - } - - #endregion - } -} diff --git a/src/Umbraco.Web/_Legacy/Actions/ActionUnPublish.cs b/src/Umbraco.Web/_Legacy/Actions/ActionUnPublish.cs deleted file mode 100644 index a61b805859..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/ActionUnPublish.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using Umbraco.Core; -using Umbraco.Core.CodeAnnotations; - -namespace Umbraco.Web._Legacy.Actions -{ - - /// - /// This action is invoked when a document is being unpublished - /// - [ActionMetadata(Constants.Conventions.PermissionCategories.ContentCategory)] - public class ActionUnpublish : IAction - { - //create singleton -#pragma warning disable 612,618 - private static readonly ActionUnpublish m_instance = new ActionUnpublish(); -#pragma warning restore 612,618 - - public static ActionUnpublish Instance => m_instance; - - public char Letter => 'Z'; - public string JsFunctionName => ""; - public string JsSource => null; - public string Alias => "unpublish"; - public string Icon => "circle-dotted"; - public bool ShowInNotifier => false; - public bool CanBePermissionAssigned => true; - } - -} diff --git a/src/Umbraco.Web/_Legacy/Actions/ActionUpdate.cs b/src/Umbraco.Web/_Legacy/Actions/ActionUpdate.cs deleted file mode 100644 index 15458e83ad..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/ActionUpdate.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using Umbraco.Web.UI.Pages; -using Umbraco.Core; -using Umbraco.Core.CodeAnnotations; - -namespace Umbraco.Web._Legacy.Actions -{ - /// - /// This action is invoked when copying a document or media - /// - [ActionMetadata(Constants.Conventions.PermissionCategories.ContentCategory)] - public class ActionUpdate : IAction - { - //create singleton -#pragma warning disable 612,618 - private static readonly ActionUpdate m_instance = new ActionUpdate(); -#pragma warning restore 612,618 - - public static ActionUpdate Instance - { - get { return m_instance; } - } - - #region IAction Members - - public char Letter - { - get - { - return 'A'; - } - } - - public string JsFunctionName - { - get - { - return string.Format("{0}.actionUpdate()", ClientTools.Scripts.GetAppActions); - } - } - - public string JsSource - { - get - { - return null; - } - } - - public string Alias - { - get - { - return "update"; - } - } - - public string Icon - { - get - { - return "save"; - } - } - - public bool ShowInNotifier - { - get - { - return true; - } - } - public bool CanBePermissionAssigned - { - get - { - return true; - } - } - #endregion - } -} diff --git a/src/Umbraco.Web/_Legacy/Actions/ContextMenuSeperator.cs b/src/Umbraco.Web/_Legacy/Actions/ContextMenuSeperator.cs deleted file mode 100644 index 2c66932a04..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/ContextMenuSeperator.cs +++ /dev/null @@ -1,51 +0,0 @@ -namespace Umbraco.Web._Legacy.Actions -{ - /// - /// Used simply to define context menu seperator items. - /// This should not be used directly in any code except for creating menus. - /// - public class ContextMenuSeperator : IAction - { - //create singleton - private static readonly ContextMenuSeperator instance = new ContextMenuSeperator(); - private ContextMenuSeperator() { } - public static ContextMenuSeperator Instance - { - get { return instance; } - } - - #region IAction Members - - public char Letter - { - get { return ','; } - } - - public string JsFunctionName - { - get { return string.Empty; } - } - public string JsSource - { - get { return string.Empty; } - } - public string Alias - { - get { return string.Empty; } - } - public string Icon - { - get { return string.Empty; } - } - public bool ShowInNotifier - { - get { return false; } - } - public bool CanBePermissionAssigned - { - get { return false; } - } - - #endregion - } -} diff --git a/src/Umbraco.Web/_Legacy/Actions/IAction.cs b/src/Umbraco.Web/_Legacy/Actions/IAction.cs deleted file mode 100644 index 410a407517..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/IAction.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Umbraco.Core.Composing; - -namespace Umbraco.Web._Legacy.Actions -{ - public interface IAction : IDiscoverable - { - char Letter { get; } - bool ShowInNotifier { get; } - bool CanBePermissionAssigned { get; } - string Icon { get; } - string Alias { get; } - string JsFunctionName { get; } - /// - /// A path to a supporting JavaScript file for the IAction. A script tag will be rendered out with the reference to the JavaScript file. - /// - string JsSource { get; } - } -} diff --git a/src/Umbraco.Web/_Legacy/Actions/LegacyActionMenuItemAttribute.cs b/src/Umbraco.Web/_Legacy/Actions/LegacyActionMenuItemAttribute.cs deleted file mode 100644 index ca9ace9630..0000000000 --- a/src/Umbraco.Web/_Legacy/Actions/LegacyActionMenuItemAttribute.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using Umbraco.Core; -using Umbraco.Core.Exceptions; - -namespace Umbraco.Web._Legacy.Actions -{ - /// - /// The attribute to assign to any IAction objects. - /// - /// - /// This is purely used for compatibility reasons for old IActions used in v7 that haven't been upgraded to - /// the new format. - /// - [AttributeUsage(AttributeTargets.Class)] - internal sealed class LegacyActionMenuItemAttribute : Attribute - { - /// - /// This constructor defines both the angular service and method name to use - /// - /// - /// - public LegacyActionMenuItemAttribute(string serviceName, string methodName) - { - if (string.IsNullOrEmpty(serviceName)) throw new ArgumentNullOrEmptyException(nameof(serviceName)); - if (string.IsNullOrEmpty(methodName)) throw new ArgumentNullOrEmptyException(nameof(methodName)); - - MethodName = methodName; - ServiceName = serviceName; - } - - /// - /// This constructor will assume that the method name equals the type name of the action menu class - /// - /// - public LegacyActionMenuItemAttribute(string serviceName) - { - if (string.IsNullOrEmpty(serviceName)) throw new ArgumentNullOrEmptyException(nameof(serviceName)); - - MethodName = ""; - ServiceName = serviceName; - } - - public string MethodName { get; } - public string ServiceName { get; } - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/RelationTypesWebService.asmx b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/RelationTypesWebService.asmx deleted file mode 100644 index 160745ca2d..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/RelationTypesWebService.asmx +++ /dev/null @@ -1 +0,0 @@ -<%@ WebService Language="C#" CodeBehind="RelationTypesWebService.asmx.cs" Class="umbraco.cms.presentation.developer.RelationTypes.RelationTypesWebService" %> diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/RelationTypesWebService.asmx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/RelationTypesWebService.asmx.cs deleted file mode 100644 index 0f8ca29c94..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/RelationTypesWebService.asmx.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Web.Services; -using Umbraco.Core; -using Umbraco.Core.Models; -using Umbraco.Web; -using Umbraco.Web.Composing; - -namespace umbraco.cms.presentation.developer.RelationTypes -{ - /// - /// Webservice to delete relation types, this allows deletion via a javacscript call hooked into the tree UI - /// - [WebService(Namespace = "http://tempuri.org/")] - [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] - [System.ComponentModel.ToolboxItem(false)] - [System.Web.Script.Services.ScriptService] // Allows this Web Service to be called from script, using ASP.NET AJAX - public class RelationTypesWebService : WebService - { - /// - /// Delete an Umbraco RelationType and all it's associated Relations - /// - /// database id of the relation type to delete - [WebMethod] - public void DeleteRelationType(int relationTypeId) - { - var user = UmbracoContext.Current.Security.CurrentUser; - - if (user.IsAdmin()) - { - var relationService = Current.Services.RelationService; - var relationType = relationService.GetRelationTypeById(relationTypeId); - relationService.Delete(relationType); - } - } - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/TreeMenu/ActionDeleteRelationType.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/TreeMenu/ActionDeleteRelationType.cs deleted file mode 100644 index cf39b17e55..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/TreeMenu/ActionDeleteRelationType.cs +++ /dev/null @@ -1,83 +0,0 @@ -using Umbraco.Web._Legacy.Actions; - -namespace umbraco.cms.presentation.developer.RelationTypes.TreeMenu -{ - /// - /// Delete a Relation Type - an Umbraco tree context menu action - /// - public class ActionDeleteRelationType : IAction - { - /// - /// Private field for the singleton instance - /// - private static readonly ActionDeleteRelationType instance = new ActionDeleteRelationType(); - - /// - /// Gets a singleton instance of this action - /// - public static ActionDeleteRelationType Instance - { - get { return instance; } - } - - #region IAction Members - - /// - /// Gets a string alias used to identify this action - /// - public string Alias - { - get { return "delete"; } - } - - /// - /// Gets a unique char to associate with this action - /// - public char Letter - { - get { return '¤'; } - } - - /// - /// Gets a value indicating whether the Umbraco notification area is used ? - /// - public bool ShowInNotifier - { - get { return false; } - } - - /// - /// Gets a value indicating whether this action can be configured for use only by specific members - /// - public bool CanBePermissionAssigned - { - get { return false; } // Since this tree is in the developer section, no further granular permissions are required - } - - /// - /// Gets an icon to be used for the right click action - /// - public string Icon - { - get { return "delete"; } // delete refers to an existing sprite - } - - /// - /// Gets a string for the javascript source - /// - public string JsSource - { - get { return "developer/RelationTypes/TreeMenu/ActionDeleteRelationType.js"; } - } - - /// - /// Gets a javascript string to execute when this action is fired - /// - public string JsFunctionName - { - get { return "javascript:actionDeleteRelationType(UmbClientMgr.mainTree().getActionNode().nodeId,UmbClientMgr.mainTree().getActionNode().nodeName);"; } - } - - #endregion - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/TreeMenu/ActionNewRelationType.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/TreeMenu/ActionNewRelationType.cs deleted file mode 100644 index 6018539983..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/RelationTypes/TreeMenu/ActionNewRelationType.cs +++ /dev/null @@ -1,83 +0,0 @@ -using Umbraco.Web._Legacy.Actions; - -namespace umbraco.cms.presentation.developer.RelationTypes.TreeMenu -{ - /// - /// Create new Relation Type - an Umbraco tree context menu action - /// - public class ActionNewRelationType : IAction - { - /// - /// Private field for the singleton instance - /// - private static readonly ActionNewRelationType instance = new ActionNewRelationType(); - - /// - /// Gets a singleton instance of this action - /// - public static ActionNewRelationType Instance - { - get { return instance; } - } - - #region IAction Members - - /// - /// Gets a string alias used to identify this action - /// - public string Alias - { - get { return "create"; } - } - - /// - /// Gets a unique char to associate with this action - /// - public char Letter - { - get { return '®'; } - } - - /// - /// Gets a value indicating whether the Umbraco notification area is used ? - /// - public bool ShowInNotifier - { - get { return false; } - } - - /// - /// Gets a value indicating whether this action can be configured for use only by specific members - /// - public bool CanBePermissionAssigned - { - get { return false; } // Since this tree is in the developer section, no further granular permissions are required - } - - /// - /// Gets an icon to be used for the right click action - /// - public string Icon - { - get { return "add"; } // add refers to an existing sprite - } - - /// - /// Gets a string for the javascript source - /// - public string JsSource - { - get { return "developer/RelationTypes/TreeMenu/ActionNewRelationType.js"; } - } - - /// - /// Gets a javascript string to execute when this action is fired - /// - public string JsFunctionName - { - get { return "javascript:actionNewRelationType();"; } - } - - #endregion - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/SendPublish.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/SendPublish.aspx.cs index 727af897ca..1f4ba277b5 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/SendPublish.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/SendPublish.aspx.cs @@ -1,7 +1,9 @@ using System; using Umbraco.Web; using Umbraco.Core; -using Umbraco.Web._Legacy.Actions; +using Umbraco.Web.Actions; +using Umbraco.Web.Composing; + namespace umbraco.dialogs { @@ -25,7 +27,7 @@ namespace umbraco.dialogs { //send notifications! TODO: This should be put somewhere centralized instead of hard coded directly here Services.NotificationService.SendNotification( - Services.ContentService.GetById(docId), ActionToPublish.Instance); + Services.ContentService.GetById(docId), Current.Actions.GetAction()); } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Item.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Item.cs index 89a03a1437..8473341ca8 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Item.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Item.cs @@ -11,9 +11,10 @@ using Umbraco.Core.Services; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Web; +using Umbraco.Web.Actions; using Umbraco.Web.Composing; using Umbraco.Web.Macros; -using Umbraco.Web._Legacy.Actions; + namespace umbraco.presentation.templateControls { @@ -276,7 +277,7 @@ namespace umbraco.presentation.templateControls if (u == null) return false; var permission = Current.Services.UserService.GetPermissions(u, PageElements["path"].ToString()); - return permission.AssignedPermissions.Contains(ActionUpdate.Instance.Letter.ToString(CultureInfo.InvariantCulture), StringComparer.Ordinal); + return permission.AssignedPermissions.Contains(ActionUpdate.ActionLetter.ToString(CultureInfo.InvariantCulture), StringComparer.Ordinal); } #endregion diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx.cs index f82587a413..5e0cb289f0 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx.cs @@ -9,8 +9,9 @@ using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Web; +using Umbraco.Web.Actions; using Umbraco.Web.Composing; -using Umbraco.Web._Legacy.Actions; + namespace umbraco.presentation.webservices { @@ -186,7 +187,7 @@ namespace umbraco.presentation.webservices //send notifications! TODO: This should be put somewhere centralized instead of hard coded directly here if (parentId > 0) { - Services.NotificationService.SendNotification(contentService.GetById(parentId), ActionSort.Instance, UmbracoContext, Services.TextService, GlobalSettings); + Services.NotificationService.SendNotification(contentService.GetById(parentId), Current.Actions.GetAction(), UmbracoContext, Services.TextService, GlobalSettings); } } From f4b970e88741fac418aec9692971cae199f30e78 Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 29 Oct 2018 11:21:30 +0100 Subject: [PATCH 27/53] Fix timing-dependent test --- src/Umbraco.Tests/Persistence/LocksTests.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Tests/Persistence/LocksTests.cs b/src/Umbraco.Tests/Persistence/LocksTests.cs index 819dbc89ed..56779ace0f 100644 --- a/src/Umbraco.Tests/Persistence/LocksTests.cs +++ b/src/Umbraco.Tests/Persistence/LocksTests.cs @@ -203,7 +203,14 @@ namespace Umbraco.Tests.Persistence Assert.IsNotNull(e1); Assert.IsInstanceOf(e1); - Assert.IsNull(e2); + // the assertion below depends on timing conditions - on a fast enough environment, + // thread1 dies (deadlock) and frees thread2, which succeeds - however on a slow + // environment (CI) both threads can end up dying due to deadlock - so, cannot test + // that e2 is null - but if it's not, can test that it's a timeout + // + //Assert.IsNull(e2); + if (e2 != null) + Assert.IsInstanceOf(e2); } private void DeadLockTestThread(int id1, int id2, EventWaitHandle myEv, WaitHandle otherEv, ref Exception exception) From 777d88ed8ff234386e8a5c69172ffb00b687592f Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 29 Oct 2018 12:38:46 +0100 Subject: [PATCH 28/53] Fixed unit test --- src/Umbraco.Tests/UI/LegacyDialogTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Tests/UI/LegacyDialogTests.cs b/src/Umbraco.Tests/UI/LegacyDialogTests.cs index 99391104ab..be9b0d4d7e 100644 --- a/src/Umbraco.Tests/UI/LegacyDialogTests.cs +++ b/src/Umbraco.Tests/UI/LegacyDialogTests.cs @@ -23,7 +23,7 @@ namespace Umbraco.Tests.UI } } - [TestCase(typeof(macroTasks), Constants.Applications.Packages)] + [TestCase(typeof(macroTasks), Constants.Applications.Settings)] [TestCase(typeof(CreatedPackageTasks), Constants.Applications.Packages)] public void Check_Assigned_Apps_For_Tasks(Type taskType, string app) { From cb9843b023ce9ef3afbd58b3973ed76c0bc73b35 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 29 Oct 2018 23:23:21 +1100 Subject: [PATCH 29/53] Removes IActions that shouldn't be IActions --- src/Umbraco.Web/Actions/ActionCollection.cs | 2 +- .../Actions/ActionCollectionBuilder.cs | 13 +++++++++++ .../Actions/ActionEmptyTranscan.cs | 16 ------------- src/Umbraco.Web/Actions/ActionExport.cs | 15 ------------ src/Umbraco.Web/Actions/ActionImport.cs | 15 ------------ src/Umbraco.Web/Actions/ActionNotify.cs | 16 ------------- src/Umbraco.Web/Actions/ActionRePublish.cs | 16 ------------- src/Umbraco.Web/Actions/ActionRefresh.cs | 23 ------------------- .../Models/Trees/ActionMenuItem.cs | 15 +++++++++--- .../Models/Trees/CreateChildEntity.cs | 5 ++-- src/Umbraco.Web/Models/Trees/ExportMember.cs | 9 +++++++- src/Umbraco.Web/Models/Trees/MenuItem.cs | 8 +++++++ src/Umbraco.Web/Models/Trees/MenuItemList.cs | 22 ------------------ src/Umbraco.Web/Models/Trees/RefreshNode.cs | 6 ++--- .../Trees/ContentTreeController.cs | 8 +++++-- .../Trees/ContentTreeControllerBase.cs | 5 +++- .../Trees/ContentTypeTreeController.cs | 12 ++++++++-- src/Umbraco.Web/Trees/MemberTreeController.cs | 7 +----- src/Umbraco.Web/Umbraco.Web.csproj | 6 ----- 19 files changed, 67 insertions(+), 152 deletions(-) delete mode 100644 src/Umbraco.Web/Actions/ActionEmptyTranscan.cs delete mode 100644 src/Umbraco.Web/Actions/ActionExport.cs delete mode 100644 src/Umbraco.Web/Actions/ActionImport.cs delete mode 100644 src/Umbraco.Web/Actions/ActionNotify.cs delete mode 100644 src/Umbraco.Web/Actions/ActionRePublish.cs delete mode 100644 src/Umbraco.Web/Actions/ActionRefresh.cs diff --git a/src/Umbraco.Web/Actions/ActionCollection.cs b/src/Umbraco.Web/Actions/ActionCollection.cs index 70a3b67b02..64cf950c60 100644 --- a/src/Umbraco.Web/Actions/ActionCollection.cs +++ b/src/Umbraco.Web/Actions/ActionCollection.cs @@ -17,7 +17,7 @@ namespace Umbraco.Web.Actions internal T GetAction() where T : IAction { - return this.OfType().SingleOrDefault(); + return this.OfType().FirstOrDefault(); } internal IEnumerable GetByLetters(IEnumerable letters) diff --git a/src/Umbraco.Web/Actions/ActionCollectionBuilder.cs b/src/Umbraco.Web/Actions/ActionCollectionBuilder.cs index 71b1ef48a5..6002c8d2b0 100644 --- a/src/Umbraco.Web/Actions/ActionCollectionBuilder.cs +++ b/src/Umbraco.Web/Actions/ActionCollectionBuilder.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; using LightInject; using Umbraco.Core.Composing; @@ -14,5 +15,17 @@ namespace Umbraco.Web.Actions { } protected override ActionCollectionBuilder This => this; + + protected override IEnumerable CreateItems(params object[] args) + { + var items = base.CreateItems(args).ToList(); + //validate the items, no actions should exist that do not either expose notifications or permissions + var invalid = items.Where(x => !x.CanBePermissionAssigned && !x.ShowInNotifier).ToList(); + if (invalid.Count > 0) + { + throw new InvalidOperationException($"Invalid actions '{string.Join(", ", invalid.Select(x => x.Alias))}'. All {typeof(IAction)} implementations must be true for either {nameof(IAction.CanBePermissionAssigned)} or {nameof(IAction.ShowInNotifier)}"); + } + return items; + } } } diff --git a/src/Umbraco.Web/Actions/ActionEmptyTranscan.cs b/src/Umbraco.Web/Actions/ActionEmptyTranscan.cs deleted file mode 100644 index db70e2d2b2..0000000000 --- a/src/Umbraco.Web/Actions/ActionEmptyTranscan.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Umbraco.Web.UI.Pages; - - -namespace Umbraco.Web.Actions -{ - //fixme: not needed, remove this - public class ActionEmptyTranscan : IAction - { - public char Letter => 'N'; - public string Alias => "emptyRecycleBin"; - public string Category => null; - public string Icon => "trash"; - public bool ShowInNotifier => false; - public bool CanBePermissionAssigned => false; - } -} diff --git a/src/Umbraco.Web/Actions/ActionExport.cs b/src/Umbraco.Web/Actions/ActionExport.cs deleted file mode 100644 index 8843bee5ea..0000000000 --- a/src/Umbraco.Web/Actions/ActionExport.cs +++ /dev/null @@ -1,15 +0,0 @@ - - -namespace Umbraco.Web.Actions -{ - //fixme: not needed, remove this - public class ActionExport : IAction - { - public char Letter => '9'; - public string Alias => "export"; - public string Category => null; - public string Icon => "download-alt"; - public bool ShowInNotifier => false; - public bool CanBePermissionAssigned => false; - } -} diff --git a/src/Umbraco.Web/Actions/ActionImport.cs b/src/Umbraco.Web/Actions/ActionImport.cs deleted file mode 100644 index 1cd8682735..0000000000 --- a/src/Umbraco.Web/Actions/ActionImport.cs +++ /dev/null @@ -1,15 +0,0 @@ - - -namespace Umbraco.Web.Actions -{ - //fixme: not needed, remove this - public class ActionImport : IAction - { - public char Letter => '8'; - public string Alias => "importDocumentType"; - public string Category => null; - public string Icon => "page-up"; - public bool ShowInNotifier => false; - public bool CanBePermissionAssigned => false; - } -} diff --git a/src/Umbraco.Web/Actions/ActionNotify.cs b/src/Umbraco.Web/Actions/ActionNotify.cs deleted file mode 100644 index 218345a678..0000000000 --- a/src/Umbraco.Web/Actions/ActionNotify.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Umbraco.Web.UI.Pages; - - -namespace Umbraco.Web.Actions -{ - //fixme: not needed, remove this - public class ActionNotify : IAction - { - public char Letter => 'T'; - public string Alias => "notify"; - public string Category => null; - public string Icon => "megaphone"; - public bool ShowInNotifier => false; - public bool CanBePermissionAssigned => false; - } -} diff --git a/src/Umbraco.Web/Actions/ActionRePublish.cs b/src/Umbraco.Web/Actions/ActionRePublish.cs deleted file mode 100644 index c0af369370..0000000000 --- a/src/Umbraco.Web/Actions/ActionRePublish.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Umbraco.Web.UI.Pages; - - -namespace Umbraco.Web.Actions -{ - //fixme: not needed, remove this - public class ActionRePublish : IAction - { - public char Letter => 'B'; - public string Alias => "republish"; - public string Category => null; - public string Icon => "globe"; - public bool ShowInNotifier => false; - public bool CanBePermissionAssigned => false; - } -} diff --git a/src/Umbraco.Web/Actions/ActionRefresh.cs b/src/Umbraco.Web/Actions/ActionRefresh.cs deleted file mode 100644 index 38247f4e4b..0000000000 --- a/src/Umbraco.Web/Actions/ActionRefresh.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Umbraco.Web.Models.Trees; -using Umbraco.Web.UI.Pages; - - -namespace Umbraco.Web.Actions -{ - ///// - ///// This action is invoked when a node reloads its children - ///// Concerns only the tree itself and thus you should not handle - ///// this action from without umbraco. - ///// - //public class ActionRefresh : IAction - //{ - // public static string ActionAlias => "refreshNode"; - - // public char Letter => 'L'; - // public string Alias => ActionAlias; - // public string Icon => "refresh"; - // public bool ShowInNotifier => false; - // public bool CanBePermissionAssigned => false; - // public string Category => null; - //} -} diff --git a/src/Umbraco.Web/Models/Trees/ActionMenuItem.cs b/src/Umbraco.Web/Models/Trees/ActionMenuItem.cs index ed15b82b16..9354417155 100644 --- a/src/Umbraco.Web/Models/Trees/ActionMenuItem.cs +++ b/src/Umbraco.Web/Models/Trees/ActionMenuItem.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics.CodeAnalysis; using Umbraco.Core; +using Umbraco.Core.Services; namespace Umbraco.Web.Models.Trees { @@ -28,9 +29,17 @@ namespace Umbraco.Web.Models.Trees /// public virtual string AngularServiceMethodName { get; } = null; - [SuppressMessage("ReSharper", "VirtualMemberCallInConstructor")] - protected ActionMenuItem() - : base() + protected ActionMenuItem(string alias, string name) : base(alias, name) + { + Initialize(); + } + + protected ActionMenuItem(string alias, ILocalizedTextService textService) : base(alias, textService) + { + Initialize(); + } + + private void Initialize() { //add the current type to the metadata if (AngularServiceMethodName.IsNullOrWhiteSpace()) diff --git a/src/Umbraco.Web/Models/Trees/CreateChildEntity.cs b/src/Umbraco.Web/Models/Trees/CreateChildEntity.cs index 90d0e9e1cb..022056c35d 100644 --- a/src/Umbraco.Web/Models/Trees/CreateChildEntity.cs +++ b/src/Umbraco.Web/Models/Trees/CreateChildEntity.cs @@ -11,17 +11,16 @@ namespace Umbraco.Web.Models.Trees public override string AngularServiceName => "umbracoMenuActions"; public CreateChildEntity(string name, bool seperatorBefore = false) + : base(ActionNew.ActionAlias, name) { - Alias = ActionNew.ActionAlias; Icon = "add"; Name = name; SeperatorBefore = seperatorBefore; } public CreateChildEntity(ILocalizedTextService textService, bool seperatorBefore = false) + : base(ActionNew.ActionAlias, textService) { - Alias = ActionNew.ActionAlias; Icon = "add"; - Name = textService.Localize($"actions/{Alias}"); SeperatorBefore = seperatorBefore; } } diff --git a/src/Umbraco.Web/Models/Trees/ExportMember.cs b/src/Umbraco.Web/Models/Trees/ExportMember.cs index 5f3eaebd45..558f7c1fa1 100644 --- a/src/Umbraco.Web/Models/Trees/ExportMember.cs +++ b/src/Umbraco.Web/Models/Trees/ExportMember.cs @@ -1,4 +1,6 @@ -namespace Umbraco.Web.Models.Trees +using Umbraco.Core.Services; + +namespace Umbraco.Web.Models.Trees { /// /// Represents the export member menu item @@ -6,5 +8,10 @@ public sealed class ExportMember : ActionMenuItem { public override string AngularServiceName => "umbracoMenuActions"; + + public ExportMember(ILocalizedTextService textService) : base("export", textService) + { + Icon = "download-alt"; + } } } diff --git a/src/Umbraco.Web/Models/Trees/MenuItem.cs b/src/Umbraco.Web/Models/Trees/MenuItem.cs index fb586e02d6..3e8a43e03e 100644 --- a/src/Umbraco.Web/Models/Trees/MenuItem.cs +++ b/src/Umbraco.Web/Models/Trees/MenuItem.cs @@ -31,6 +31,14 @@ namespace Umbraco.Web.Models.Trees Name = name; } + + public MenuItem(string alias, ILocalizedTextService textService) + : this() + { + Alias = alias; + Name = textService.Localize($"actions/{Alias}"); + } + /// /// Create a menu item based on an definition /// diff --git a/src/Umbraco.Web/Models/Trees/MenuItemList.cs b/src/Umbraco.Web/Models/Trees/MenuItemList.cs index 1a8a2dd88c..672750825c 100644 --- a/src/Umbraco.Web/Models/Trees/MenuItemList.cs +++ b/src/Umbraco.Web/Models/Trees/MenuItemList.cs @@ -38,28 +38,6 @@ namespace Umbraco.Web.Models.Trees return item; } - /// - /// Adds a menu item based on an - /// - /// The text to display for the menu item, will default to the IAction alias if not specified - /// - public MenuItem Add(string name) - where T : IAction - { - return Add(name, false); - } - - /// - /// Adds a menu item based on an - /// - /// The used to localize the action name based on it's alias - /// - public MenuItem Add(ILocalizedTextService textService) - where T : IAction - { - return Add(textService, false); - } - /// /// Adds a menu item with a dictionary which is merged to the AdditionalData bag /// diff --git a/src/Umbraco.Web/Models/Trees/RefreshNode.cs b/src/Umbraco.Web/Models/Trees/RefreshNode.cs index 587c51b887..2641baa34f 100644 --- a/src/Umbraco.Web/Models/Trees/RefreshNode.cs +++ b/src/Umbraco.Web/Models/Trees/RefreshNode.cs @@ -11,18 +11,16 @@ namespace Umbraco.Web.Models.Trees public override string AngularServiceName => "umbracoMenuActions"; public RefreshNode(string name, bool seperatorBefore = false) + : base("refreshNode", name) { - Alias = "refreshNode"; Icon = "refresh"; - Name = name; SeperatorBefore = seperatorBefore; } public RefreshNode(ILocalizedTextService textService, bool seperatorBefore = false) + : base("refreshNode", textService) { - Alias = "refreshNode"; Icon = "refresh"; - Name = textService.Localize($"actions/{Alias}"); SeperatorBefore = seperatorBefore; } } diff --git a/src/Umbraco.Web/Trees/ContentTreeController.cs b/src/Umbraco.Web/Trees/ContentTreeController.cs index a759382854..a7598e5d3a 100644 --- a/src/Umbraco.Web/Trees/ContentTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentTreeController.cs @@ -240,8 +240,12 @@ namespace Umbraco.Web.Trees AddActionNode(item, menu); AddActionNode(item, menu); AddActionNode(item, menu, true, true); - - AddActionNode(item, menu, true); + + menu.Items.Add(new MenuItem("notify", Services.TextService) + { + Icon = "megaphone", + SeperatorBefore = true + }); menu.Items.Add(new RefreshNode(Services.TextService, true)); diff --git a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs index c02f691f14..1398903cfc 100644 --- a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs @@ -349,7 +349,10 @@ namespace Umbraco.Web.Trees if (RecycleBinId.ToInvariantString() == id) { var menu = new MenuItemCollection(); - menu.Items.Add(Services.TextService.Localize("actions/emptyTrashcan")); + menu.Items.Add(new MenuItem("emptyRecycleBin", Services.TextService) + { + Icon = "trash" + }); menu.Items.Add(new RefreshNode(Services.TextService, true)); return menu; } diff --git a/src/Umbraco.Web/Trees/ContentTypeTreeController.cs b/src/Umbraco.Web/Trees/ContentTypeTreeController.cs index 673c5f4168..32367ad02c 100644 --- a/src/Umbraco.Web/Trees/ContentTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentTypeTreeController.cs @@ -82,7 +82,11 @@ namespace Umbraco.Web.Trees // root actions menu.Items.Add(Services.TextService); - menu.Items.Add(Services.TextService, true); + menu.Items.Add(new MenuItem("importDocumentType", Services.TextService) + { + Icon = "page-up", + SeperatorBefore = true + }); menu.Items.Add(new RefreshNode(Services.TextService, true)); return menu; @@ -123,7 +127,11 @@ namespace Umbraco.Web.Trees menu.Items.Add(Services.TextService, true); } menu.Items.Add(Services.TextService); - menu.Items.Add(Services.TextService, true); + menu.Items.Add(new MenuItem("export", Services.TextService) + { + Icon = "download-alt", + SeperatorBefore = true + }); menu.Items.Add(Services.TextService, true); if (enableInheritedDocumentTypes) menu.Items.Add(new RefreshNode(Services.TextService, true)); diff --git a/src/Umbraco.Web/Trees/MemberTreeController.cs b/src/Umbraco.Web/Trees/MemberTreeController.cs index f8bcb47b56..24fc624110 100644 --- a/src/Umbraco.Web/Trees/MemberTreeController.cs +++ b/src/Umbraco.Web/Trees/MemberTreeController.cs @@ -185,12 +185,7 @@ namespace Umbraco.Web.Trees if (Security.CurrentUser.HasAccessToSensitiveData()) { - menu.Items.Add(new ExportMember - { - Name = Services.TextService.Localize("actions/export"), - Icon = "download-alt", - Alias = "export" - }); + menu.Items.Add(new ExportMember(Services.TextService)); } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index f7317939dd..ff48b7ab9e 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -538,16 +538,10 @@ - - - - - - From 77f4f0bbad2622a495c6674cb7983ae6a2d56753 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 30 Oct 2018 00:34:43 +1100 Subject: [PATCH 30/53] Refactors how the 'OpensDialog' gets assigned to menu items, fixes permission check on rollback --- src/Umbraco.Web/Actions/ActionRollback.cs | 4 +- src/Umbraco.Web/Editors/ContentController.cs | 1 + src/Umbraco.Web/Models/Trees/MenuItem.cs | 4 +- src/Umbraco.Web/Models/Trees/MenuItemList.cs | 8 ++-- .../Trees/ContentBlueprintTreeController.cs | 6 +-- .../Trees/ContentTreeController.cs | 37 +++++++++---------- .../Trees/ContentTreeControllerBase.cs | 3 +- .../Trees/ContentTypeTreeController.cs | 22 ++++++----- .../Trees/DataTypeTreeController.cs | 10 ++--- .../Trees/DictionaryTreeController.cs | 4 +- .../Trees/FileSystemTreeController.cs | 8 ++-- src/Umbraco.Web/Trees/MacrosTreeController.cs | 4 +- src/Umbraco.Web/Trees/MediaTreeController.cs | 13 ++++--- .../Trees/MediaTypeTreeController.cs | 18 ++++----- src/Umbraco.Web/Trees/MemberTreeController.cs | 11 +++--- .../MemberTypeAndGroupTreeControllerBase.cs | 2 +- .../Trees/PackagesTreeController.cs | 6 +-- .../Trees/RelationTypeTreeController.cs | 4 +- .../Trees/TemplatesTreeController.cs | 4 +- 19 files changed, 89 insertions(+), 80 deletions(-) diff --git a/src/Umbraco.Web/Actions/ActionRollback.cs b/src/Umbraco.Web/Actions/ActionRollback.cs index adfdd752cf..96ce1e7767 100644 --- a/src/Umbraco.Web/Actions/ActionRollback.cs +++ b/src/Umbraco.Web/Actions/ActionRollback.cs @@ -10,7 +10,9 @@ namespace Umbraco.Web.Actions /// public class ActionRollback : IAction { - public char Letter => 'K'; + public const char ActionLetter = 'K'; + + public char Letter => ActionLetter; public string Alias => "rollback"; public string Category => Constants.Conventions.PermissionCategories.AdministrationCategory; public string Icon => "undo"; diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 4a68ca1d91..c1ed76dbc8 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -1761,6 +1761,7 @@ namespace Umbraco.Web.Editors : content.Variants.FirstOrDefault(x => x.Language.IsoCode == culture); } + [EnsureUserPermissionForContent("contentId", ActionRollback.ActionLetter)] [HttpPost] public HttpResponseMessage PostRollbackContent(int contentId, int versionId, string culture = "*") { diff --git a/src/Umbraco.Web/Models/Trees/MenuItem.cs b/src/Umbraco.Web/Models/Trees/MenuItem.cs index dfafa7ce41..4170cdb73f 100644 --- a/src/Umbraco.Web/Models/Trees/MenuItem.cs +++ b/src/Umbraco.Web/Models/Trees/MenuItem.cs @@ -52,7 +52,6 @@ namespace Umbraco.Web.Models.Trees SeperatorBefore = false; Icon = action.Icon; Action = action; - OpensDialog = legacyMenu.OpensDialog; } #endregion @@ -87,6 +86,9 @@ namespace Umbraco.Web.Models.Trees [DataMember(Name = "cssclass")] public string Icon { get; set; } + /// + /// Used in the UI to inform the user that the menu item will open a dialog/confirmation + /// [DataMember(Name = "opensDialog")] public bool OpensDialog { get; set; } diff --git a/src/Umbraco.Web/Models/Trees/MenuItemList.cs b/src/Umbraco.Web/Models/Trees/MenuItemList.cs index 672750825c..b34f0b4444 100644 --- a/src/Umbraco.Web/Models/Trees/MenuItemList.cs +++ b/src/Umbraco.Web/Models/Trees/MenuItemList.cs @@ -62,7 +62,8 @@ namespace Umbraco.Web.Models.Trees /// /// /// The used to localize the action name based on it's alias - public MenuItem Add(ILocalizedTextService textService, bool hasSeparator = false) + /// + public MenuItem Add(ILocalizedTextService textService, bool hasSeparator = false, bool opensDialog = false) where T : IAction { var item = CreateMenuItem(textService, hasSeparator); @@ -87,7 +88,7 @@ namespace Umbraco.Web.Models.Trees return menuItem; } - internal MenuItem CreateMenuItem(ILocalizedTextService textService, bool hasSeparator = false) + internal MenuItem CreateMenuItem(ILocalizedTextService textService, bool hasSeparator = false, bool opensDialog = false) where T : IAction { var item = Current.Actions.GetAction(); @@ -95,7 +96,8 @@ namespace Umbraco.Web.Models.Trees var menuItem = new MenuItem(item, textService.Localize($"actions/{item.Alias}")) { - SeperatorBefore = hasSeparator + SeperatorBefore = hasSeparator, + OpensDialog = opensDialog }; return menuItem; diff --git a/src/Umbraco.Web/Trees/ContentBlueprintTreeController.cs b/src/Umbraco.Web/Trees/ContentBlueprintTreeController.cs index 01df73f4af..364c9c391f 100644 --- a/src/Umbraco.Web/Trees/ContentBlueprintTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentBlueprintTreeController.cs @@ -93,7 +93,7 @@ namespace Umbraco.Web.Trees if (id == Constants.System.Root.ToInvariantString()) { // root actions - menu.Items.Add(Services.TextService.Localize($"actions/{ActionNew.ActionAlias}")); + menu.Items.Add(Services.TextService, opensDialog: true); menu.Items.Add(new RefreshNode(Services.TextService, true)); return menu; } @@ -102,7 +102,7 @@ namespace Umbraco.Web.Trees if (cte != null) { var ct = Services.ContentTypeService.Get(cte.Id); - var createItem = menu.Items.Add(Services.TextService); + var createItem = menu.Items.Add(Services.TextService, opensDialog: true); createItem.NavigateToRoute("/settings/contentBlueprints/edit/-1?create=true&doctype=" + ct.Alias); menu.Items.Add(new RefreshNode(Services.TextService, true)); @@ -110,7 +110,7 @@ namespace Umbraco.Web.Trees return menu; } - menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService, opensDialog: true); return menu; } diff --git a/src/Umbraco.Web/Trees/ContentTreeController.cs b/src/Umbraco.Web/Trees/ContentTreeController.cs index a7598e5d3a..1667299436 100644 --- a/src/Umbraco.Web/Trees/ContentTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentTreeController.cs @@ -128,9 +128,8 @@ namespace Umbraco.Web.Trees .Select(x => new MenuItem(x)); //these two are the standard items - //fixme: inject - menu.Items.Add(Services.TextService.Localize("actions", ActionNew.ActionAlias)); - menu.Items.Add(Services.TextService.Localize("actions", Current.Actions.GetAction().Alias), true); + menu.Items.Add(Services.TextService, opensDialog: true); + menu.Items.Add(Services.TextService, true); //filter the standard items FilterUserAllowedMenuItems(menu, nodeActions); @@ -226,25 +225,22 @@ namespace Umbraco.Web.Trees protected MenuItemCollection GetAllNodeMenuItems(IUmbracoEntity item) { var menu = new MenuItemCollection(); - AddActionNode(item, menu); - AddActionNode(item, menu); - - AddActionNode(item, menu); - - //need to ensure some of these are converted to the legacy system - until we upgrade them all to be angularized. - AddActionNode(item, menu, true); - AddActionNode(item, menu); - + AddActionNode(item, menu, opensDialog: true); + AddActionNode(item, menu, opensDialog: true); + AddActionNode(item, menu, opensDialog: true); + AddActionNode(item, menu, true, opensDialog: true); + AddActionNode(item, menu, opensDialog: true); AddActionNode(item, menu, true); - - AddActionNode(item, menu); - AddActionNode(item, menu); - AddActionNode(item, menu, true, true); + AddActionNode(item, menu, opensDialog: true); + AddActionNode(item, menu, opensDialog: true); + //fixme - conver this editor to angular + AddActionNode(item, menu, true, convert: true, opensDialog: true); menu.Items.Add(new MenuItem("notify", Services.TextService) { Icon = "megaphone", - SeperatorBefore = true + SeperatorBefore = true, + OpensDialog = true }); menu.Items.Add(new RefreshNode(Services.TextService, true)); @@ -260,8 +256,8 @@ namespace Umbraco.Web.Trees protected MenuItemCollection GetNodeMenuItemsForDeletedContent(IUmbracoEntity item) { var menu = new MenuItemCollection(); - menu.Items.Add(Services.TextService.Localize("actions", ActionRestore.ActionAlias)); - menu.Items.Add(Services.TextService.Localize("actions", ActionDelete.ActionAlias)); + menu.Items.Add(Services.TextService, opensDialog: true); + menu.Items.Add(Services.TextService, opensDialog: true); menu.Items.Add(new RefreshNode(Services.TextService, true)); @@ -308,12 +304,13 @@ namespace Umbraco.Web.Trees } //fixme: Remove the need for converting to legacy - private void AddActionNode(IUmbracoEntity item, MenuItemCollection menu, bool hasSeparator = false, bool convert = false) + private void AddActionNode(IUmbracoEntity item, MenuItemCollection menu, bool hasSeparator = false, bool convert = false, bool opensDialog = false) where TAction : IAction { //fixme: Inject var menuItem = menu.Items.Add(Services.TextService.Localize("actions", Current.Actions.GetAction().Alias), hasSeparator); if (convert) menuItem.ConvertLegacyMenuItem(item, "content", "content"); + menuItem.OpensDialog = opensDialog; } ////fixme: Remove the need for converting to legacy diff --git a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs index 1398903cfc..646f47068b 100644 --- a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs @@ -351,7 +351,8 @@ namespace Umbraco.Web.Trees var menu = new MenuItemCollection(); menu.Items.Add(new MenuItem("emptyRecycleBin", Services.TextService) { - Icon = "trash" + Icon = "trash", + OpensDialog = true }); menu.Items.Add(new RefreshNode(Services.TextService, true)); return menu; diff --git a/src/Umbraco.Web/Trees/ContentTypeTreeController.cs b/src/Umbraco.Web/Trees/ContentTypeTreeController.cs index 32367ad02c..43e5b03f2f 100644 --- a/src/Umbraco.Web/Trees/ContentTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentTypeTreeController.cs @@ -81,11 +81,12 @@ namespace Umbraco.Web.Trees menu.DefaultMenuAlias = ActionNew.ActionAlias; // root actions - menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService, opensDialog: true); menu.Items.Add(new MenuItem("importDocumentType", Services.TextService) { Icon = "page-up", - SeperatorBefore = true + SeperatorBefore = true, + OpensDialog = true }); menu.Items.Add(new RefreshNode(Services.TextService, true)); @@ -98,9 +99,9 @@ namespace Umbraco.Web.Trees //set the default to create menu.DefaultMenuAlias = ActionNew.ActionAlias; - menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService, opensDialog: true); - menu.Items.Add(new MenuItem("rename", Services.TextService.Localize("actions/rename")) + menu.Items.Add(new MenuItem("rename", Services.TextService) { Icon = "icon icon-edit" }); @@ -108,7 +109,7 @@ namespace Umbraco.Web.Trees if (container.HasChildren == false) { //can delete doc type - menu.Items.Add(Services.TextService, true); + menu.Items.Add(Services.TextService, true, opensDialog: true); } menu.Items.Add(new RefreshNode(Services.TextService, true)); } @@ -119,20 +120,21 @@ namespace Umbraco.Web.Trees if (enableInheritedDocumentTypes) { - menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService, opensDialog: true); } //no move action if this is a child doc type if (parent == null) { - menu.Items.Add(Services.TextService, true); + menu.Items.Add(Services.TextService, true, opensDialog: true); } - menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService, opensDialog: true); menu.Items.Add(new MenuItem("export", Services.TextService) { Icon = "download-alt", - SeperatorBefore = true + SeperatorBefore = true, + OpensDialog = true }); - menu.Items.Add(Services.TextService, true); + menu.Items.Add(Services.TextService, true, opensDialog: true); if (enableInheritedDocumentTypes) menu.Items.Add(new RefreshNode(Services.TextService, true)); } diff --git a/src/Umbraco.Web/Trees/DataTypeTreeController.cs b/src/Umbraco.Web/Trees/DataTypeTreeController.cs index 4f9f93f110..6c89f4a1dc 100644 --- a/src/Umbraco.Web/Trees/DataTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/DataTypeTreeController.cs @@ -104,7 +104,7 @@ namespace Umbraco.Web.Trees menu.DefaultMenuAlias = ActionNew.ActionAlias; // root actions - menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService, opensDialog: true); menu.Items.Add(new RefreshNode(Services.TextService, true)); return menu; } @@ -115,7 +115,7 @@ namespace Umbraco.Web.Trees //set the default to create menu.DefaultMenuAlias = ActionNew.ActionAlias; - menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService, opensDialog: true); menu.Items.Add(new MenuItem("rename", Services.TextService.Localize("actions/rename")) { @@ -125,7 +125,7 @@ namespace Umbraco.Web.Trees if (container.HasChildren == false) { //can delete data type - menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService, opensDialog: true); } menu.Items.Add(new RefreshNode(Services.TextService, true)); } @@ -134,9 +134,9 @@ namespace Umbraco.Web.Trees var nonDeletableSystemDataTypeIds = GetNonDeletableSystemDataTypeIds(); if (nonDeletableSystemDataTypeIds.Contains(int.Parse(id)) == false) - menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService, opensDialog: true); - menu.Items.Add(Services.TextService, hasSeparator: true); + menu.Items.Add(Services.TextService, hasSeparator: true, opensDialog: true); } return menu; diff --git a/src/Umbraco.Web/Trees/DictionaryTreeController.cs b/src/Umbraco.Web/Trees/DictionaryTreeController.cs index a0510acd3e..d0a7fce3ad 100644 --- a/src/Umbraco.Web/Trees/DictionaryTreeController.cs +++ b/src/Umbraco.Web/Trees/DictionaryTreeController.cs @@ -96,10 +96,10 @@ namespace Umbraco.Web.Trees { var menu = new MenuItemCollection(); - menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService, opensDialog: true); if (id != Constants.System.Root.ToInvariantString()) - menu.Items.Add(Services.TextService, true); + menu.Items.Add(Services.TextService, true, opensDialog: true); menu.Items.Add(new RefreshNode(Services.TextService, true)); diff --git a/src/Umbraco.Web/Trees/FileSystemTreeController.cs b/src/Umbraco.Web/Trees/FileSystemTreeController.cs index a517cd45c2..f1e53ed5d4 100644 --- a/src/Umbraco.Web/Trees/FileSystemTreeController.cs +++ b/src/Umbraco.Web/Trees/FileSystemTreeController.cs @@ -82,7 +82,7 @@ namespace Umbraco.Web.Trees //set the default to create menu.DefaultMenuAlias = ActionNew.ActionAlias; //create action - menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService, opensDialog: true); //refresh action menu.Items.Add(new RefreshNode(Services.TextService, true)); @@ -96,7 +96,7 @@ namespace Umbraco.Web.Trees //set the default to create menu.DefaultMenuAlias = ActionNew.ActionAlias; //create action - menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService, opensDialog: true); var hasChildren = FileSystem.GetFiles(path).Any() || FileSystem.GetDirectories(path).Any(); @@ -104,7 +104,7 @@ namespace Umbraco.Web.Trees if (hasChildren == false) { //delete action - menu.Items.Add(Services.TextService, true); + menu.Items.Add(Services.TextService, true, opensDialog: true); } //refresh action @@ -118,7 +118,7 @@ namespace Umbraco.Web.Trees var menu = new MenuItemCollection(); //if it's not a directory then we only allow to delete the item - menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService, opensDialog: true); return menu; } diff --git a/src/Umbraco.Web/Trees/MacrosTreeController.cs b/src/Umbraco.Web/Trees/MacrosTreeController.cs index 0faa1ce75d..3f925eef8d 100644 --- a/src/Umbraco.Web/Trees/MacrosTreeController.cs +++ b/src/Umbraco.Web/Trees/MacrosTreeController.cs @@ -59,7 +59,7 @@ namespace Umbraco.Web.Trees if (id == Constants.System.Root.ToInvariantString()) { //Create the normal create action - menu.Items.Add(Services.TextService) + menu.Items.Add(Services.TextService, opensDialog: true) //Since we haven't implemented anything for macros in angular, this needs to be converted to //use the legacy format .ConvertLegacyMenuItem(null, "initmacros", queryStrings.GetValue("application")); @@ -75,7 +75,7 @@ namespace Umbraco.Web.Trees if (macro == null) return new MenuItemCollection(); //add delete option for all macros - menu.Items.Add(Services.TextService) + menu.Items.Add(Services.TextService, opensDialog: true) //Since we haven't implemented anything for macros in angular, this needs to be converted to //use the legacy format .ConvertLegacyMenuItem(new EntitySlim diff --git a/src/Umbraco.Web/Trees/MediaTreeController.cs b/src/Umbraco.Web/Trees/MediaTreeController.cs index d260253218..0292a907fc 100644 --- a/src/Umbraco.Web/Trees/MediaTreeController.cs +++ b/src/Umbraco.Web/Trees/MediaTreeController.cs @@ -95,7 +95,7 @@ namespace Umbraco.Web.Trees } // root actions - menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService, opensDialog: true); menu.Items.Add(Services.TextService, true); menu.Items.Add(new RefreshNode(Services.TextService, true)); return menu; @@ -119,9 +119,9 @@ namespace Umbraco.Web.Trees } //return a normal node menu: - menu.Items.Add(Services.TextService); - menu.Items.Add(Services.TextService); - menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService, opensDialog: true); + menu.Items.Add(Services.TextService, opensDialog: true); + menu.Items.Add(Services.TextService, opensDialog: true); menu.Items.Add(Services.TextService); menu.Items.Add(new RefreshNode(Services.TextService, true)); @@ -129,7 +129,10 @@ namespace Umbraco.Web.Trees if (item.Path.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).Contains(RecycleBinId.ToInvariantString())) { menu.DefaultMenuAlias = null; - menu.Items.Insert(2, new MenuItem(Current.Actions.GetAction(), Services.TextService.Localize("actions", ActionRestore.ActionAlias))); + menu.Items.Insert(2, new MenuItem(ActionRestore.ActionAlias, Services.TextService) + { + OpensDialog = true + }); } else { diff --git a/src/Umbraco.Web/Trees/MediaTypeTreeController.cs b/src/Umbraco.Web/Trees/MediaTypeTreeController.cs index a679cefa32..547199676a 100644 --- a/src/Umbraco.Web/Trees/MediaTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/MediaTypeTreeController.cs @@ -74,7 +74,7 @@ namespace Umbraco.Web.Trees menu.DefaultMenuAlias = ActionNew.ActionAlias; // root actions - menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService, opensDialog: true); menu.Items.Add(new RefreshNode(Services.TextService)); return menu; } @@ -85,7 +85,7 @@ namespace Umbraco.Web.Trees //set the default to create menu.DefaultMenuAlias = ActionNew.ActionAlias; - menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService, opensDialog: true); menu.Items.Add(new MenuItem("rename", Services.TextService.Localize("actions/rename")) { @@ -95,7 +95,7 @@ namespace Umbraco.Web.Trees if (container.HasChildren == false) { //can delete doc type - menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService, opensDialog: true); } menu.Items.Add(new RefreshNode(Services.TextService, true)); } @@ -106,26 +106,26 @@ namespace Umbraco.Web.Trees if (enableInheritedMediaTypes) { - menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService, opensDialog: true); //no move action if this is a child doc type if (parent == null) { - menu.Items.Add(Services.TextService, true); + menu.Items.Add(Services.TextService, true, opensDialog: true); } } else { - menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService, opensDialog: true); //no move action if this is a child doc type if (parent == null) { - menu.Items.Add(Services.TextService, true); + menu.Items.Add(Services.TextService, true, opensDialog: true); } } - menu.Items.Add(Services.TextService); - menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService, opensDialog: true); + menu.Items.Add(Services.TextService, opensDialog: true); if (enableInheritedMediaTypes) menu.Items.Add(new RefreshNode(Services.TextService, true)); } diff --git a/src/Umbraco.Web/Trees/MemberTreeController.cs b/src/Umbraco.Web/Trees/MemberTreeController.cs index 24fc624110..68819351c0 100644 --- a/src/Umbraco.Web/Trees/MemberTreeController.cs +++ b/src/Umbraco.Web/Trees/MemberTreeController.cs @@ -159,17 +159,16 @@ namespace Umbraco.Web.Trees menu.DefaultMenuAlias = ActionNew.ActionAlias; //Create the normal create action - menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService, opensDialog: true); } else { //Create a custom create action - this does not launch a dialog, it just navigates to the create screen // we'll create it based on the ActionNew so it maintains the same icon properties, name, etc... - var createMenuItem = new MenuItem + var createMenuItem = new MenuItem(ActionNew.ActionAlias, Services.TextService) { - Name = Services.TextService.Localize($"actions/{ActionNew.ActionAlias}"), - Alias = ActionNew.ActionAlias, - Icon = "add" + Icon = "add", + OpensDialog = true }; //we want to go to this route: /member/member/edit/-1?create=true createMenuItem.NavigateToRoute("/member/member/edit/-1?create=true"); @@ -181,7 +180,7 @@ namespace Umbraco.Web.Trees } //add delete option for all members - menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService, opensDialog: true); if (Security.CurrentUser.HasAccessToSensitiveData()) { diff --git a/src/Umbraco.Web/Trees/MemberTypeAndGroupTreeControllerBase.cs b/src/Umbraco.Web/Trees/MemberTypeAndGroupTreeControllerBase.cs index 484ea1e2a9..9ea5908891 100644 --- a/src/Umbraco.Web/Trees/MemberTypeAndGroupTreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/MemberTypeAndGroupTreeControllerBase.cs @@ -33,7 +33,7 @@ namespace Umbraco.Web.Trees else { //delete member type/group - menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService, opensDialog: true); } return menu; diff --git a/src/Umbraco.Web/Trees/PackagesTreeController.cs b/src/Umbraco.Web/Trees/PackagesTreeController.cs index 8f62047941..8158b47985 100644 --- a/src/Umbraco.Web/Trees/PackagesTreeController.cs +++ b/src/Umbraco.Web/Trees/PackagesTreeController.cs @@ -84,12 +84,12 @@ namespace Umbraco.Web.Trees // Root actions if (id == "-1") { - menu.Items.Add(Services.TextService) + menu.Items.Add(Services.TextService, opensDialog: true) .ConvertLegacyMenuItem(null, Constants.Trees.Packages, queryStrings.GetValue("application")); } else if (id == "created") { - menu.Items.Add(Services.TextService) + menu.Items.Add(Services.TextService, opensDialog: true) .ConvertLegacyMenuItem(null, Constants.Trees.Packages, queryStrings.GetValue("application")); menu.Items.Add(new RefreshNode(Services.TextService, true)); @@ -97,7 +97,7 @@ namespace Umbraco.Web.Trees else { //it's a package node - menu.Items.Add(Services.TextService); + menu.Items.Add(Services.TextService, opensDialog: true); } return menu; diff --git a/src/Umbraco.Web/Trees/RelationTypeTreeController.cs b/src/Umbraco.Web/Trees/RelationTypeTreeController.cs index ab6e9e2dd7..33ccc152c5 100644 --- a/src/Umbraco.Web/Trees/RelationTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/RelationTypeTreeController.cs @@ -25,7 +25,7 @@ namespace Umbraco.Web.Trees if (id == Constants.System.Root.ToInvariantString()) { //Create the normal create action - var addMenuItem = menu.Items.Add(Services.TextService); + var addMenuItem = menu.Items.Add(Services.TextService, opensDialog: true); addMenuItem.LaunchDialogUrl("developer/RelationTypes/NewRelationType.aspx", "Create New RelationType"); //refresh action menu.Items.Add(new RefreshNode(Services.TextService, true)); @@ -37,7 +37,7 @@ namespace Umbraco.Web.Trees if (relationType == null) return new MenuItemCollection(); //add delete option for all macros - menu.Items.Add(Services.TextService) + menu.Items.Add(Services.TextService, opensDialog: true) //Since we haven't implemented anything for relationtypes in angular, this needs to be converted to //use the legacy format .ConvertLegacyMenuItem(new EntitySlim diff --git a/src/Umbraco.Web/Trees/TemplatesTreeController.cs b/src/Umbraco.Web/Trees/TemplatesTreeController.cs index 3ae45a072e..2339d92d96 100644 --- a/src/Umbraco.Web/Trees/TemplatesTreeController.cs +++ b/src/Umbraco.Web/Trees/TemplatesTreeController.cs @@ -70,7 +70,7 @@ namespace Umbraco.Web.Trees var menu = new MenuItemCollection(); //Create the normal create action - var item = menu.Items.Add(Services.TextService); + var item = menu.Items.Add(Services.TextService, opensDialog: true); item.NavigateToRoute($"{queryStrings.GetValue("application")}/templates/edit/{id}?create=true"); if (id == Constants.System.Root.ToInvariantString()) @@ -89,7 +89,7 @@ namespace Umbraco.Web.Trees if (template.IsMasterTemplate == false) { //add delete option if it doesn't have children - menu.Items.Add(Services.TextService, true); + menu.Items.Add(Services.TextService, true, opensDialog: true); } //add refresh From 372f77fde5a16641960fa92b9c9637e9a53b3199 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 30 Oct 2018 00:36:11 +1100 Subject: [PATCH 31/53] reverts accidental change --- src/Umbraco.Web/Editors/ContentController.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index c1ed76dbc8..53e2593fef 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -944,13 +944,13 @@ namespace Umbraco.Web.Editors /// /// [EnsureUserPermissionForContent("id", 'U')] - public HttpResponseMessage PostPublishById(ContentPublish model) + public HttpResponseMessage PostPublishById(int id) { - var foundContent = GetObjectFromRequest(() => Services.ContentService.GetById(model.Id)); + var foundContent = GetObjectFromRequest(() => Services.ContentService.GetById(id)); if (foundContent == null) { - return HandleContentNotFound(model.Id, false); + return HandleContentNotFound(id, false); } var publishResult = Services.ContentService.SavePublishing(foundContent, Security.GetUserId().ResultOr(0)); From 15eaa33e08be55cc43e1cee59b261a7e22b05083 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 30 Oct 2018 01:15:23 +1100 Subject: [PATCH 32/53] updates migration to invariant string check --- .../Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs index accb755020..64ee2f6b88 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs @@ -65,17 +65,17 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 var requiresCacheRebuild = false; switch (dd.EditorAlias) { - case "Umbraco.DropDown": + case string ea when ea.InvariantEquals("Umbraco.DropDown"): UpdateDataType(dd, config, false); break; - case "Umbraco.DropdownlistPublishingKeys": + case string ea when ea.InvariantEquals("Umbraco.DropdownlistPublishingKeys"): UpdateDataType(dd, config, false); requiresCacheRebuild = true; break; - case "Umbraco.DropDownMultiple": + case string ea when ea.InvariantEquals("Umbraco.DropDownMultiple"): UpdateDataType(dd, config, false); break; - case "Umbraco.DropdownlistMultiplePublishKeys": + case string ea when ea.InvariantEquals("Umbraco.DropdownlistMultiplePublishKeys"): UpdateDataType(dd, config, true); requiresCacheRebuild = true; break; From 0d0d26d3e994ad42ecfd9edb77319d15adc0c2dc Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 30 Oct 2018 02:05:43 +1100 Subject: [PATCH 33/53] Fixes converting the Umbraco.DropDownMultiple to a multiple drop down, fixes boolean logic on the js single drop down --- .../Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs | 2 +- .../dropdownFlexible/dropdownFlexible.controller.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs index 64ee2f6b88..87d7a8c571 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs @@ -73,7 +73,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 requiresCacheRebuild = true; break; case string ea when ea.InvariantEquals("Umbraco.DropDownMultiple"): - UpdateDataType(dd, config, false); + UpdateDataType(dd, config, true); break; case string ea when ea.InvariantEquals("Umbraco.DropdownlistMultiplePublishKeys"): UpdateDataType(dd, config, true); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.controller.js index 23bd2c9a42..f8e02a240a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdownFlexible/dropdownFlexible.controller.js @@ -77,7 +77,7 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.DropdownFlexibleCo // if we run in single mode we'll store the value in a local variable // so we can pass an array as the model as our PropertyValueEditor expects that $scope.model.singleDropdownValue = ""; - if (Object.toBoolean($scope.model.config.multiple)) { + if (!Object.toBoolean($scope.model.config.multiple)) { $scope.model.singleDropdownValue = Array.isArray($scope.model.value) ? $scope.model.value[0] : $scope.model.value; } From f123331d421b4fed7485a3146af10139541bd58c Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 30 Oct 2018 02:11:33 +1100 Subject: [PATCH 34/53] Fixes updating the storage type --- .../Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs index 87d7a8c571..600f952d6a 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs @@ -6,6 +6,7 @@ using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Logging; +using Umbraco.Core.Models; namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 { @@ -96,6 +97,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 multiple = isMultiple, items = config.Items }; + dataType.DbType = ValueStorageType.Nvarchar.ToString(); dataType.Configuration = JsonConvert.SerializeObject(flexConfig); Database.Update(dataType); } From 386dd2281ce321666ce07a1b39069dc7227c68ab Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 30 Oct 2018 02:18:07 +1100 Subject: [PATCH 35/53] Fixes log messages --- .../Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs index 600f952d6a..e5cda85168 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs @@ -35,7 +35,9 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 } catch (Exception ex) { - Logger.Error(ex, $"Invalid drop down configuration detected: \"{dd.Configuration}\", cannot convert editor, values will be cleared"); + Logger.Error( + ex, "Invalid drop down configuration detected: \"{Configuration}\", cannot convert editor, values will be cleared", + dd.Configuration); dd.Configuration = null; Database.Update(dd); continue; @@ -132,7 +134,8 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 vals.Add(val.Value); else { - Logger.Warn($"Could not find associated data type configuration for stored Id {id}"); + Logger.Warn( + "Could not find associated data type configuration for stored Id {DataTypeId}", id); canConvert = false; } } From fc39568415f43a1f2cc484fcbf8dd4bc697e207f Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 30 Oct 2018 14:49:25 +1100 Subject: [PATCH 36/53] Updated audit info to include full lang name --- .../Services/Implement/ContentService.cs | 20 ++++++++++++++----- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 6 +++--- .../Umbraco/config/lang/en_us.xml | 6 +++--- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index f14747cda3..3953e7a640 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -849,7 +849,7 @@ namespace Umbraco.Core.Services.Implement //track the cultures that have changed var culturesChanging = content.ContentType.VariesByCulture() - ? string.Join(",", content.CultureInfos.Where(x => x.Value.IsDirty()).Select(x => x.Key)) + ? content.CultureInfos.Where(x => x.Value.IsDirty()).Select(x => x.Key).ToList() : null; //TODO: Currently there's no way to change track which variant properties have changed, we only have change // tracking enabled on all values on the Property which doesn't allow us to know which variants have changed. @@ -867,7 +867,12 @@ namespace Umbraco.Core.Services.Implement scope.Events.Dispatch(TreeChanged, this, new TreeChange(content, changeType).ToEventArgs()); if (culturesChanging != null) - Audit(AuditType.SaveVariant, userId, content.Id, $"Saved cultures: {culturesChanging}", culturesChanging); + { + var langs = string.Join(", ", _languageRepository.GetMany() + .Where(x => culturesChanging.InvariantContains(x.IsoCode)) + .Select(x => x.CultureName)); + Audit(AuditType.SaveVariant, userId, content.Id, $"Saved languagues: {langs}", langs); + } else Audit(AuditType.Save, userId, content.Id); @@ -1057,7 +1062,7 @@ namespace Umbraco.Core.Services.Implement var publishing = content.PublishedState == PublishedState.Publishing; var unpublishing = content.PublishedState == PublishedState.Unpublishing; - string culturesChanging = null; + IEnumerable culturesChanging = null; using (var scope = ScopeProvider.CreateScope()) { @@ -1082,7 +1087,7 @@ namespace Umbraco.Core.Services.Implement } else { - culturesChanging = string.Join(",", content.PublishCultureInfos.Where(x => x.Value.IsDirty()).Select(x => x.Key)); + culturesChanging = content.PublishCultureInfos.Where(x => x.Value.IsDirty()).Select(x => x.Key).ToList(); } } @@ -1193,7 +1198,12 @@ namespace Umbraco.Core.Services.Implement } if (culturesChanging != null) - Audit(AuditType.PublishVariant, userId, content.Id, $"Published cultures: {culturesChanging}", culturesChanging); + { + var langs = string.Join(", ", _languageRepository.GetMany() + .Where(x => culturesChanging.InvariantContains(x.IsoCode)) + .Select(x => x.CultureName)); + Audit(AuditType.PublishVariant, userId, content.Id, $"Published languagues: {langs}", langs); + } else Audit(AuditType.Publish, userId, content.Id); diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 8bc9d190fa..6ee8fc2428 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -143,14 +143,14 @@ Content deleted Content unpublished Content saved and Published - Content cultures %0% saved and published + Content languages: %0% saved and published Content saved - Content cultures %0% saved + Content languages: %0% saved Content moved Content copied Content rolled back Content sent for publishing - Content cultures %0% sent for publishing + Content languages: %0% sent for publishing Sort child items performed by user Copy Publish 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 a2487b2dc8..a710abde5a 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -148,14 +148,14 @@ Content deleted Content unpublished Content saved and Published - Content cultures %0% saved and published + Content languages: %0% saved and published Content saved - Content cultures %0% saved + Content languages: %0% saved Content moved Content copied Content rolled back Content sent for publishing - Content cultures %0% sent for publishing + Content languages: %0% sent for publishing Sort child items performed by user Copy Publish From 9416c5f638d34ed36418a87974bd4b803f52d513 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 30 Oct 2018 14:57:54 +1100 Subject: [PATCH 37/53] fixes up localized text for audit trail --- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 6 +++--- src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 6ee8fc2428..4abfdff0fb 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -143,14 +143,14 @@ Content deleted Content unpublished Content saved and Published - Content languages: %0% saved and published + Content saved and published for languages: %0% Content saved - Content languages: %0% saved + Content saved for languages: %0% Content moved Content copied Content rolled back Content sent for publishing - Content languages: %0% sent for publishing + Content sent for publishing for languages: %0% Sort child items performed by user Copy Publish 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 a710abde5a..87097e0b5e 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -148,14 +148,14 @@ Content deleted Content unpublished Content saved and Published - Content languages: %0% saved and published + Content saved and published for languages: %0% Content saved - Content languages: %0% saved + Content saved for languages: %0% Content moved Content copied Content rolled back Content sent for publishing - Content languages: %0% sent for publishing + Content sent for publishing for languages: %0% Sort child items performed by user Copy Publish From a383309e46912e3f434c3852acf4c2b2d576d1b7 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 30 Oct 2018 17:32:27 +1100 Subject: [PATCH 38/53] Gets indexes updating based on content type changes --- .../Implement/DocumentRepository.cs | 2 +- src/Umbraco.Core/Services/IContentService.cs | 10 +- .../Services/Implement/ContentService.cs | 26 ++---- .../Services/ContentServiceTests.cs | 2 +- .../Services/PerformanceTests.cs | 34 ------- src/Umbraco.Web/Search/ExamineComponent.cs | 91 ++++++++++++++++++- 6 files changed, 105 insertions(+), 60 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs index f3afe99b28..b3a7c31e54 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs @@ -99,7 +99,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement private string VariantNameSqlExpression => SqlContext.VisitDto((ccv, node) => ccv.Name ?? node.Text, "ccv").Sql; - protected virtual Sql GetBaseQuery(QueryType queryType, bool current) + protected Sql GetBaseQuery(QueryType queryType, bool current) { var sql = SqlContext.Sql(); diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index 7371686c7c..0cfa0bb601 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -79,9 +79,15 @@ namespace Umbraco.Core.Services IEnumerable GetByIds(IEnumerable ids); /// - /// Gets documents of a given document type. + /// Gets paged documents of a content content /// - IEnumerable GetByType(int documentTypeId); + /// The page number. + /// The page number. + /// The page size. + /// Total number of documents. + /// Search text filter. + /// Ordering infos. + IEnumerable GetPagedOfType(int contentType, long pageIndex, int pageSize, out long totalRecords, IQuery filter = null, Ordering ordering = null); /// /// Gets documents at a given level. diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index 3953e7a640..64c6a23aff 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -405,28 +405,20 @@ namespace Umbraco.Core.Services.Implement } } - /// - /// Gets a collection of objects by the Id of the - /// - /// Id of the - /// An Enumerable list of objects - public IEnumerable GetByType(int id) + public IEnumerable GetPagedOfType(int contentTypeId, long pageIndex, int pageSize, out long totalRecords, IQuery filter, Ordering ordering = null) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - scope.ReadLock(Constants.Locks.ContentTree); - var query = Query().Where(x => x.ContentTypeId == id); - return _documentRepository.Get(query); - } - } + if(pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); + if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); + + if (ordering == null) + ordering = Ordering.By("sortOrder"); - internal IEnumerable GetPublishedContentOfContentType(int id) - { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { scope.ReadLock(Constants.Locks.ContentTree); - var query = Query().Where(x => x.ContentTypeId == id); - return _documentRepository.Get(query); + return _documentRepository.GetPage( + Query().Where(x => x.ContentTypeId == contentTypeId), + pageIndex, pageSize, out totalRecords, filter, ordering); } } diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 5aad7c4d90..5b06037f90 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -1738,7 +1738,7 @@ namespace Umbraco.Tests.Services // Act contentService.DeleteOfType(contentType.Id); var rootContent = contentService.GetRootContent(); - var contents = contentService.GetByType(contentType.Id); + var contents = contentService.GetPagedOfType(contentType.Id, 0, int.MaxValue, out var _); // Assert Assert.That(rootContent.Any(), Is.False); diff --git a/src/Umbraco.Tests/Services/PerformanceTests.cs b/src/Umbraco.Tests/Services/PerformanceTests.cs index 9b0117c266..900a466a1d 100644 --- a/src/Umbraco.Tests/Services/PerformanceTests.cs +++ b/src/Umbraco.Tests/Services/PerformanceTests.cs @@ -108,40 +108,6 @@ namespace Umbraco.Tests.Services } } - [Test] - public void Get_All_Published_Content_Of_Type() - { - var result = PrimeDbWithLotsOfContent(); - var contentSvc = (ContentService)ServiceContext.ContentService; - - var countOfPublished = result.Count(x => x.Published); - var contentTypeId = result.First().ContentTypeId; - - var proflog = GetTestProfilingLogger(); - using (proflog.DebugDuration("Getting published content of type normally")) - { - //do this 10x! - for (var i = 0; i < 10; i++) - { - - //get all content items that are published of this type - var published = contentSvc.GetByType(contentTypeId).Where(content => content.Published); - Assert.AreEqual(countOfPublished, published.Count(x => x.ContentTypeId == contentTypeId)); - } - } - - using (proflog.DebugDuration("Getting published content of type optimized")) - { - - //do this 10x! - for (var i = 0; i < 10; i++) - { - //get all content items that are published of this type - var published = contentSvc.GetPublishedContentOfContentType(contentTypeId); - Assert.AreEqual(countOfPublished, published.Count(x => x.ContentTypeId == contentTypeId)); - } - } - } [Test] public void Truncate_Insert_Vs_Update_Insert() diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index ff7d0c8dc4..530e8ba449 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -102,13 +102,10 @@ namespace Umbraco.Web.Search // bind to distributed cache events - this ensures that this logic occurs on ALL servers // that are taking part in a load balanced environment. ContentCacheRefresher.CacheUpdated += ContentCacheRefresherUpdated; + ContentTypeCacheRefresher.CacheUpdated += ContentTypeCacheRefresherUpdated; ; MediaCacheRefresher.CacheUpdated += MediaCacheRefresherUpdated; MemberCacheRefresher.CacheUpdated += MemberCacheRefresherUpdated; - // fixme - content type? - // events handling removed in ef013f9d3b945d0a48a306ff1afbd49c10c3fff8 - // because, could not make sense of it? - EnsureUnlocked(profilingLogger.Logger, examineManager); RebuildIndexesOnStartup(profilingLogger.Logger); @@ -317,6 +314,88 @@ namespace Umbraco.Web.Search } } + private void ContentTypeCacheRefresherUpdated(ContentTypeCacheRefresher sender, CacheRefresherEventArgs args) + { + + //before content type changes just caused full blown re-indexing: + // https://github.com/umbraco/Umbraco-CMS/commit/ef013f9d3b945d0a48a306ff1afbd49c10c3fff8 + + + if (Suspendable.ExamineEvents.CanIndex == false) + return; + + if (args.MessageType != MessageType.RefreshByPayload) + throw new NotSupportedException(); + + var contentService = _services.ContentService; + + var removedIds = new List(); + var refreshedIds = new List(); + + //TODO: What do we do about these? + var otherIds = new List(); + + foreach (var payload in (ContentTypeCacheRefresher.JsonPayload[])args.MessageObject) + { + if (payload.ChangeTypes.HasType(ContentTypeChangeTypes.Remove)) + removedIds.Add(payload.Id); + else if (payload.ChangeTypes.HasType(ContentTypeChangeTypes.RefreshMain)) + refreshedIds.Add(payload.Id); + else if (payload.ChangeTypes.HasType(ContentTypeChangeTypes.RefreshOther)) + otherIds.Add(payload.Id); + } + + const int pageSize = 500; + + //Re-index all content of these types + foreach(var id in refreshedIds) + { + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + var contentToRefresh = _services.ContentService.GetPagedOfType(id, page++, pageSize, out total); + foreach(var c in contentToRefresh) + { + //TODO: We might have to order by Path ascending or something since we're going to need to check + // contentService.IsPathPublished(content) but we don't want to make that check for every content item + IContent published = null; + if (c.Published && contentService.IsPathPublished(c)) + published = c; + + ReIndexForContent(c, published); + } + } + } + + //Delete all content of this content type that is in any content indexer by looking up matched examine docs + foreach(var id in removedIds) + { + foreach(var index in _examineManager.IndexProviders.Values.OfType()) + { + var searcher = index.GetSearcher(); + + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + //paging with examine, see https://shazwazza.com/post/paging-with-examine/ + var results = searcher.Search( + searcher.CreateCriteria().Field("nodeType", id).Compile(), + maxResults: pageSize * (page + 1)); + total = results.TotalItemCount; + var paged = results.Skip(page * pageSize); + + foreach(var item in paged) + if (int.TryParse(item.Id, out var contentId)) + DeleteIndexForEntity(contentId, false); + } + } + + + } + } + private void ContentCacheRefresherUpdated(ContentCacheRefresher sender, CacheRefresherEventArgs args) { if (Suspendable.ExamineEvents.CanIndex == false) @@ -340,6 +419,8 @@ namespace Umbraco.Web.Search // ExamineEvents does not support RefreshAll // just ignore that payload // so what?! + + //fixme: Rebuild the index at this point? } else // RefreshNode or RefreshBranch (maybe trashed) { @@ -355,7 +436,7 @@ namespace Umbraco.Web.Search } IContent published = null; - if (content.Published && ((ContentService)contentService).IsPathPublished(content)) + if (content.Published && contentService.IsPathPublished(content)) published = content; // just that content From 23860e58b93bfc9ecb1ce41a0b5ec3fddcd360e4 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 31 Oct 2018 16:33:10 +1100 Subject: [PATCH 39/53] Update re-indexing logic for content types including updating content types affected by composition changes --- src/Umbraco.Examine/UmbracoExamineIndexer.cs | 2 +- src/Umbraco.Web/Search/ExamineComponent.cs | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Examine/UmbracoExamineIndexer.cs b/src/Umbraco.Examine/UmbracoExamineIndexer.cs index 64fd7b5c71..a4c1fb4336 100644 --- a/src/Umbraco.Examine/UmbracoExamineIndexer.cs +++ b/src/Umbraco.Examine/UmbracoExamineIndexer.cs @@ -418,7 +418,7 @@ namespace Umbraco.Examine //icon if (e.IndexItem.ValueSet.Values.TryGetValue("icon", out var icon) && e.IndexItem.ValueSet.Values.ContainsKey(IconFieldName) == false) { - e.IndexItem.ValueSet.Values[IconFieldName] = new List { icon }; + e.IndexItem.ValueSet.Values[IconFieldName] = icon; } } diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index 530e8ba449..a1625206d1 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -317,7 +317,8 @@ namespace Umbraco.Web.Search private void ContentTypeCacheRefresherUpdated(ContentTypeCacheRefresher sender, CacheRefresherEventArgs args) { - //before content type changes just caused full blown re-indexing: + //before content type changes just didn't do anything for indexing, simply just updated the + //definitions to index: // https://github.com/umbraco/Umbraco-CMS/commit/ef013f9d3b945d0a48a306ff1afbd49c10c3fff8 @@ -331,8 +332,6 @@ namespace Umbraco.Web.Search var removedIds = new List(); var refreshedIds = new List(); - - //TODO: What do we do about these? var otherIds = new List(); foreach (var payload in (ContentTypeCacheRefresher.JsonPayload[])args.MessageObject) @@ -348,7 +347,7 @@ namespace Umbraco.Web.Search const int pageSize = 500; //Re-index all content of these types - foreach(var id in refreshedIds) + foreach(var id in refreshedIds.Concat(otherIds).Distinct()) { var page = 0; var total = long.MaxValue; From 18f6e7ba789702fcf2d9434fa0c4bbe1f3d73e21 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 31 Oct 2018 17:57:35 +1100 Subject: [PATCH 40/53] Updates ExamineComponent to page over descendants when caches are updated --- src/Umbraco.Web/Search/ExamineComponent.cs | 42 +++++++++++++--------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index a1625206d1..b1a302785d 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -314,14 +314,13 @@ namespace Umbraco.Web.Search } } + /// + /// Updates indexes based on content type changes + /// + /// + /// private void ContentTypeCacheRefresherUpdated(ContentTypeCacheRefresher sender, CacheRefresherEventArgs args) { - - //before content type changes just didn't do anything for indexing, simply just updated the - //definitions to index: - // https://github.com/umbraco/Umbraco-CMS/commit/ef013f9d3b945d0a48a306ff1afbd49c10c3fff8 - - if (Suspendable.ExamineEvents.CanIndex == false) return; @@ -395,6 +394,11 @@ namespace Umbraco.Web.Search } } + /// + /// Updates indexes based on content changes + /// + /// + /// private void ContentCacheRefresherUpdated(ContentCacheRefresher sender, CacheRefresherEventArgs args) { if (Suspendable.ExamineEvents.CanIndex == false) @@ -445,19 +449,25 @@ namespace Umbraco.Web.Search if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch)) { var masked = published == null ? null : new List(); - var descendants = contentService.GetDescendants(content); - foreach (var descendant in descendants) + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while(page * pageSize < total) { - published = null; - if (masked != null) // else everything is masked + var descendants = contentService.GetPagedDescendants(content.Id, page++, pageSize, out total); + foreach (var descendant in descendants) { - if (masked.Contains(descendant.ParentId) || !descendant.Published) - masked.Add(descendant.Id); - else - published = descendant; - } + published = null; + if (masked != null) // else everything is masked + { + if (masked.Contains(descendant.ParentId) || !descendant.Published) + masked.Add(descendant.Id); + else + published = descendant; + } - ReIndexForContent(descendant, published); + ReIndexForContent(descendant, published); + } } } } From 437bf978a1e67c95d09de629c527564157f0c643 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 31 Oct 2018 18:01:39 +1100 Subject: [PATCH 41/53] Removes non paged GetChildren and GetDescendants from content service --- src/Umbraco.Core/ContentExtensions.cs | 46 +--- .../Services/EntityXmlSerializer.cs | 31 ++- src/Umbraco.Core/Services/IContentService.cs | 21 +- .../Services/Implement/ContentService.cs | 214 ++++++++---------- .../Integration/ContentEventsTests.cs | 2 +- .../NPocoTests/PetaPocoCachesTest.cs | 4 - .../Services/ContentServicePerformanceTest.cs | 2 +- .../Services/ContentServiceTests.cs | 85 ++++--- .../Umbraco/dialogs/ChangeDocType.aspx.cs | 3 +- src/Umbraco.Web/Editors/ContentController.cs | 5 +- 10 files changed, 164 insertions(+), 249 deletions(-) diff --git a/src/Umbraco.Core/ContentExtensions.cs b/src/Umbraco.Core/ContentExtensions.cs index 4f88c2b803..f0fa6cdf17 100644 --- a/src/Umbraco.Core/ContentExtensions.cs +++ b/src/Umbraco.Core/ContentExtensions.cs @@ -48,28 +48,6 @@ namespace Umbraco.Core return contentService.GetAncestors(content); } - /// - /// Returns a list of the current contents children. - /// - /// Current content - /// - /// An enumerable list of objects - public static IEnumerable Children(this IContent content, IContentService contentService) - { - return contentService.GetChildren(content.Id); - } - - /// - /// Returns a list of the current contents descendants, not including the content itself. - /// - /// Current content - /// - /// An enumerable list of objects - public static IEnumerable Descendants(this IContent content, IContentService contentService) - { - return contentService.GetDescendants(content); - } - /// /// Returns the parent of the current content. /// @@ -179,29 +157,7 @@ namespace Umbraco.Core } return false; } - - /// - /// Returns the children for the content base item - /// - /// - /// - /// - /// - /// This is a bit of a hack because we need to type check! - /// - internal static IEnumerable Children(IContentBase content, ServiceContext services) - { - if (content is IContent) - { - return services.ContentService.GetChildren(content.Id); - } - if (content is IMedia) - { - return services.MediaService.GetChildren(content.Id); - } - return null; - } - + /// /// Returns properties that do not belong to a group /// diff --git a/src/Umbraco.Core/Services/EntityXmlSerializer.cs b/src/Umbraco.Core/Services/EntityXmlSerializer.cs index e418c8d3e6..ebb35a43ed 100644 --- a/src/Umbraco.Core/Services/EntityXmlSerializer.cs +++ b/src/Umbraco.Core/Services/EntityXmlSerializer.cs @@ -30,7 +30,7 @@ namespace Umbraco.Core.Services IEnumerable urlSegmentProviders, IContent content, bool published, - bool withDescendants = false) // fixme take care of usage! + bool withDescendants = false) //fixme take care of usage! only used for the packager { if (contentService == null) throw new ArgumentNullException(nameof(contentService)); if (dataTypeService == null) throw new ArgumentNullException(nameof(dataTypeService)); @@ -58,9 +58,15 @@ namespace Umbraco.Core.Services if (withDescendants) { - var descendants = contentService.GetDescendants(content).ToArray(); - var currentChildren = descendants.Where(x => x.ParentId == content.Id); - SerializeDescendants(contentService, dataTypeService, userService, localizationService, urlSegmentProviders, descendants, currentChildren, xml, published); + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while(page * pageSize < total) + { + var children = contentService.GetPagedChildren(content.Id, page++, pageSize, out total); + SerializeChildren(contentService, dataTypeService, userService, localizationService, urlSegmentProviders, children, xml, published); + } + } return xml; @@ -451,7 +457,7 @@ namespace Umbraco.Core.Services } // exports an IContent item descendants. - private static void SerializeDescendants(IContentService contentService, IDataTypeService dataTypeService, IUserService userService, ILocalizationService localizationService, IEnumerable urlSegmentProviders, IContent[] originalDescendants, IEnumerable children, XElement xml, bool published) + private static void SerializeChildren(IContentService contentService, IDataTypeService dataTypeService, IUserService userService, ILocalizationService localizationService, IEnumerable urlSegmentProviders, IEnumerable children, XElement xml, bool published) { foreach (var child in children) { @@ -459,12 +465,15 @@ namespace Umbraco.Core.Services var childXml = Serialize(contentService, dataTypeService, userService, localizationService, urlSegmentProviders, child, published); xml.Add(childXml); - // capture id (out of closure) and get the grandChildren (children of the child) - var parentId = child.Id; - var grandChildren = originalDescendants.Where(x => x.ParentId == parentId); - - // recurse - SerializeDescendants(contentService, dataTypeService, userService, localizationService, urlSegmentProviders, originalDescendants, grandChildren, childXml, published); + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while(page * pageSize < total) + { + var grandChildren = contentService.GetPagedChildren(child.Id, page++, pageSize, out total); + // recurse + SerializeChildren(contentService, dataTypeService, userService, localizationService, urlSegmentProviders, grandChildren, childXml, published); + } } } diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index 0cfa0bb601..b821ae1eb0 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -94,16 +94,6 @@ namespace Umbraco.Core.Services /// IEnumerable GetByLevel(int level); - /// - /// Gets child documents of a given parent. - /// - IEnumerable GetChildren(int parentId); - - /// - /// Gets child documents of a document, (partially) matching a name. - /// - IEnumerable GetChildren(int parentId, string name); - /// /// Gets the parent of a document. /// @@ -124,16 +114,6 @@ namespace Umbraco.Core.Services /// IEnumerable GetAncestors(IContent content); - /// - /// Gets descendant documents of a document. - /// - IEnumerable GetDescendants(int id); - - /// - /// Gets descendant documents of a document. - /// - IEnumerable GetDescendants(IContent content); - /// /// Gets all versions of a document. /// @@ -172,6 +152,7 @@ namespace Umbraco.Core.Services /// IEnumerable GetContentForRelease(); + //fixme: should be paged /// /// Gets documents in the recycle bin. /// diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index 64c6a23aff..1f28988fa6 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -528,21 +528,6 @@ namespace Umbraco.Core.Services.Implement } } - /// - /// Gets a collection of objects by Parent Id - /// - /// Id of the Parent to retrieve Children from - /// An Enumerable list of objects - public IEnumerable GetChildren(int id) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - scope.ReadLock(Constants.Locks.ContentTree); - var query = Query().Where(x => x.ParentId == id); - return _documentRepository.Get(query).OrderBy(x => x.SortOrder); - } - } - /// /// Gets a collection of published objects by Parent Id /// @@ -626,60 +611,6 @@ namespace Umbraco.Core.Services.Implement } } - /// - /// Gets a collection of objects by its name or partial name - /// - /// Id of the Parent to retrieve Children from - /// Full or partial name of the children - /// An Enumerable list of objects - public IEnumerable GetChildren(int parentId, string name) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - scope.ReadLock(Constants.Locks.ContentTree); - var query = Query().Where(x => x.ParentId == parentId && x.Name.Contains(name)); - return _documentRepository.Get(query); - } - } - - /// - /// Gets a collection of objects by Parent Id - /// - /// Id of the Parent to retrieve Descendants from - /// An Enumerable list of objects - public IEnumerable GetDescendants(int id) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - scope.ReadLock(Constants.Locks.ContentTree); - var content = GetById(id); - if (content == null) - { - scope.Complete(); // else causes rollback - return Enumerable.Empty(); - } - var pathMatch = content.Path + ","; - var query = Query().Where(x => x.Id != content.Id && x.Path.StartsWith(pathMatch)); - return _documentRepository.Get(query); - } - } - - /// - /// Gets a collection of objects by Parent Id - /// - /// item to retrieve Descendants from - /// An Enumerable list of objects - public IEnumerable GetDescendants(IContent content) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - scope.ReadLock(Constants.Locks.ContentTree); - var pathMatch = content.Path + ","; - var query = Query().Where(x => x.Id != content.Id && x.Path.StartsWith(pathMatch)); - return _documentRepository.Get(query); - } - } - /// /// Gets the parent of the current content as an item. /// @@ -1347,27 +1278,35 @@ namespace Umbraco.Core.Services.Implement // if one fails, abort its branch var exclude = new HashSet(); - //fixme: should be paged to not overwhelm the database (timeouts) - foreach (var d in GetDescendants(document)) + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) { - // if parent is excluded, exclude document and ignore - // if not forcing, and not publishing, exclude document and ignore - if (exclude.Contains(d.ParentId) || !force && !d.Published) + var descendants = GetPagedDescendants(document.Id, page++, pageSize, out total); + + foreach (var d in descendants) { + // if parent is excluded, exclude document and ignore + // if not forcing, and not publishing, exclude document and ignore + if (exclude.Contains(d.ParentId) || !force && !d.Published) + { + exclude.Add(d.Id); + continue; + } + + // no need to check path here, + // 1. because we know the parent is path-published (we just published it) + // 2. because it would not work as nothing's been written out to the db until the uow completes + result = SaveAndPublishBranchOne(scope, d, editing, publishCultures, false, publishedDocuments, evtMsgs, userId); + results.Add(result); + if (result.Success) continue; + + // abort branch exclude.Add(d.Id); - continue; } - - // no need to check path here, - // 1. because we know the parent is path-published (we just published it) - // 2. because it would not work as nothing's been written out to the db until the uow completes - result = SaveAndPublishBranchOne(scope, d, editing, publishCultures, false, publishedDocuments, evtMsgs, userId); - results.Add(result); - if (result.Success) continue; - - // abort branch - exclude.Add(d.Id); } + scope.Events.Dispatch(TreeChanged, this, new TreeChange(document, TreeChangeTypes.RefreshBranch).ToEventArgs()); scope.Events.Dispatch(Published, this, new PublishEventArgs(publishedDocuments, false, false), "Published"); @@ -1450,6 +1389,8 @@ namespace Umbraco.Core.Services.Implement private void DeleteLocked(IScope scope, IContent content) { + //TODO: Test this + // then recursively delete descendants, bottom-up // just repository.Delete + an event var stack = new Stack(); @@ -1458,14 +1399,37 @@ namespace Umbraco.Core.Services.Implement while (stack.Count > 0) { var c = stack.Peek(); - IContent[] cc; if (c.Level == level) - while ((cc = c.Children(this).ToArray()).Length > 0) + { + var hasChildren = true; + while (hasChildren) { - foreach (var ci in cc) - stack.Push(ci); - c = cc[cc.Length - 1]; + IContent last = null; + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + var countChildren = 0; + + //get all children of c in pages + while (page * pageSize < total) + { + var children = GetPagedChildren(c.Id, page++, pageSize, out total); + + foreach (var ci in children) + { + countChildren++; + stack.Push(ci); + last = ci; + } + + if (countChildren == 0) + hasChildren = false; //exit, there are no children left to load + else + c = last; //recurse with the last child + } } + } + c = stack.Pop(); level = c.Level; @@ -1686,9 +1650,6 @@ namespace Umbraco.Core.Services.Implement moves.Add(Tuple.Create(content, content.Path)); // capture original path - // get before moving, in case uow is immediate - var descendants = GetDescendants(content); - // these will be updated by the repo because we changed parentId //content.Path = (parent == null ? "-1" : parent.Path) + "," + content.Id; //content.SortOrder = ((ContentRepository) repository).NextChildSortOrder(parentId); @@ -1700,18 +1661,26 @@ namespace Umbraco.Core.Services.Implement //paths[content.Id] = content.Path; paths[content.Id] = (parent == null ? (parentId == Constants.System.RecycleBinContent ? "-1,-20" : "-1") : parent.Path) + "," + content.Id; - foreach (var descendant in descendants) + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while(page * pageSize < total) { - moves.Add(Tuple.Create(descendant, descendant.Path)); // capture original path + var descendants = GetPagedDescendants(content.Id, page++, pageSize, out total); + foreach (var descendant in descendants) + { + moves.Add(Tuple.Create(descendant, descendant.Path)); // capture original path - // update path and level since we do not update parentId - if (paths.ContainsKey(descendant.ParentId) == false) - Console.WriteLine("oops on " + descendant.ParentId + " for " + content.Path + " " + parent?.Path); - descendant.Path = paths[descendant.Id] = paths[descendant.ParentId] + "," + descendant.Id; - Console.WriteLine("path " + descendant.Id + " = " + paths[descendant.Id]); - descendant.Level += levelDelta; - PerformMoveContentLocked(descendant, userId, trash); + // update path and level since we do not update parentId + if (paths.ContainsKey(descendant.ParentId) == false) + Console.WriteLine("oops on " + descendant.ParentId + " for " + content.Path + " " + parent?.Path); + descendant.Path = paths[descendant.Id] = paths[descendant.ParentId] + "," + descendant.Id; + Console.WriteLine("path " + descendant.Id + " = " + paths[descendant.Id]); + descendant.Level += levelDelta; + PerformMoveContentLocked(descendant, userId, trash); + } } + } private void PerformMoveContentLocked(IContent content, int userId, bool? trash) @@ -1844,29 +1813,36 @@ namespace Umbraco.Core.Services.Implement if (recursive) // process descendants { - foreach (var descendant in GetDescendants(content)) + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while(page * pageSize < total) { - // if parent has not been copied, skip, else gets its copy id - if (idmap.TryGetValue(descendant.ParentId, out parentId) == false) continue; + var descendants = GetPagedDescendants(content.Id, page++, pageSize, out total); + foreach (var descendant in descendants) + { + // if parent has not been copied, skip, else gets its copy id + if (idmap.TryGetValue(descendant.ParentId, out parentId) == false) continue; - var descendantCopy = descendant.DeepCloneWithResetIdentities(); - descendantCopy.ParentId = parentId; + var descendantCopy = descendant.DeepCloneWithResetIdentities(); + descendantCopy.ParentId = parentId; - if (scope.Events.DispatchCancelable(Copying, this, new CopyEventArgs(descendant, descendantCopy, parentId))) - continue; + if (scope.Events.DispatchCancelable(Copying, this, new CopyEventArgs(descendant, descendantCopy, parentId))) + continue; - // a copy is not published (but not really unpublishing either) - // update the create author and last edit author - if (descendantCopy.Published) - ((Content) descendantCopy).Published = false; - descendantCopy.CreatorId = userId; - descendantCopy.WriterId = userId; + // a copy is not published (but not really unpublishing either) + // update the create author and last edit author + if (descendantCopy.Published) + ((Content)descendantCopy).Published = false; + descendantCopy.CreatorId = userId; + descendantCopy.WriterId = userId; - // save and flush (see above) - _documentRepository.Save(descendantCopy); + // save and flush (see above) + _documentRepository.Save(descendantCopy); - copies.Add(Tuple.Create(descendant, descendantCopy)); - idmap[descendant.Id] = descendantCopy.Id; + copies.Add(Tuple.Create(descendant, descendantCopy)); + idmap[descendant.Id] = descendantCopy.Id; + } } } diff --git a/src/Umbraco.Tests/Integration/ContentEventsTests.cs b/src/Umbraco.Tests/Integration/ContentEventsTests.cs index 4ca63e9e96..655d44e86c 100644 --- a/src/Umbraco.Tests/Integration/ContentEventsTests.cs +++ b/src/Umbraco.Tests/Integration/ContentEventsTests.cs @@ -459,7 +459,7 @@ namespace Umbraco.Tests.Integration #region Utils private IEnumerable Children(IContent content) - => ServiceContext.ContentService.GetChildren(content.Id); + => ServiceContext.ContentService.GetPagedChildren(content.Id, 0, int.MaxValue, out var total); #endregion diff --git a/src/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs b/src/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs index 21a75b2e24..b1ebee108a 100644 --- a/src/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs +++ b/src/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs @@ -125,10 +125,6 @@ namespace Umbraco.Tests.Persistence.NPocoTests contentService.GetByLevel(2); - contentService.GetChildren(id1); - - contentService.GetDescendants(id2); - contentService.GetVersions(id3); contentService.GetRootContent(); diff --git a/src/Umbraco.Tests/Services/ContentServicePerformanceTest.cs b/src/Umbraco.Tests/Services/ContentServicePerformanceTest.cs index 28021f1e22..4326eee273 100644 --- a/src/Umbraco.Tests/Services/ContentServicePerformanceTest.cs +++ b/src/Umbraco.Tests/Services/ContentServicePerformanceTest.cs @@ -105,7 +105,7 @@ namespace Umbraco.Tests.Services total.AddRange(ServiceContext.ContentService.GetRootContent()); foreach (var content in total.ToArray()) { - total.AddRange(ServiceContext.ContentService.GetDescendants(content)); + total.AddRange(ServiceContext.ContentService.GetPagedDescendants(content.Id, 0, int.MaxValue, out var _)); } TestProfiler.Disable(); Current.Logger.Info("Returned " + total.Count + " items"); diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 5b06037f90..952c8f9dc1 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -1038,37 +1038,6 @@ namespace Umbraco.Tests.Services Assert.That(contents.Count(), Is.GreaterThanOrEqualTo(2)); } - [Test] - public void Can_Get_Children_Of_Content_Id() - { - // Arrange - var contentService = ServiceContext.ContentService; - - // Act - var contents = contentService.GetChildren(NodeDto.NodeIdSeed + 2).ToList(); - - // Assert - Assert.That(contents, Is.Not.Null); - Assert.That(contents.Any(), Is.True); - Assert.That(contents.Count(), Is.GreaterThanOrEqualTo(2)); - } - - [Test] - public void Can_Get_Descendents_Of_Content() - { - // Arrange - var contentService = ServiceContext.ContentService; - var hierarchy = CreateContentHierarchy(); - contentService.Save(hierarchy, 0); - - // Act - var contents = contentService.GetDescendants(NodeDto.NodeIdSeed + 2).ToList(); - - // Assert - Assert.That(contents, Is.Not.Null); - Assert.That(contents.Any(), Is.True); - Assert.That(contents.Count(), Is.EqualTo(52)); - } [Test] public void Can_Get_All_Versions_Of_Content() @@ -1476,8 +1445,16 @@ namespace Umbraco.Tests.Services var parent = contentService.GetById(parentId); Console.WriteLine(" " + parent.Id); - foreach (var x in contentService.GetDescendants(parent)) - Console.WriteLine(" ".Substring(0, x.Level) + x.Id); + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while(page * pageSize < total) + { + var descendants = contentService.GetPagedDescendants(parent.Id, page++, pageSize, out total); + foreach (var x in descendants) + Console.WriteLine(" ".Substring(0, x.Level) + x.Id); + } + Console.WriteLine(); // publish parent & its branch @@ -1492,7 +1469,7 @@ namespace Umbraco.Tests.Services Assert.IsTrue(parentPublished.All(x => x.Success)); Assert.IsTrue(parent.Published); - var children = contentService.GetChildren(parentId); + var children = contentService.GetPagedChildren(parentId, 0, 500, out var totalChildren); //we only want the first so page size, etc.. is abitrary // children are published including ... that was released 5 mins ago Assert.IsTrue(children.First(x => x.Id == NodeDto.NodeIdSeed + 4).Published); @@ -1785,7 +1762,13 @@ namespace Umbraco.Tests.Services contentService.Save(subsubpage, 0); var content = contentService.GetById(NodeDto.NodeIdSeed + 2); - var descendants = contentService.GetDescendants(content).ToList(); + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + var descendants = new List(); + while(page * pageSize < total) + descendants.AddRange(contentService.GetPagedDescendants(content.Id, page++, pageSize, out total)); + Assert.AreNotEqual(-20, content.ParentId); Assert.IsFalse(content.Trashed); Assert.AreEqual(3, descendants.Count); @@ -1793,7 +1776,11 @@ namespace Umbraco.Tests.Services Assert.IsFalse(descendants.Any(x => x.Trashed)); contentService.MoveToRecycleBin(content, 0); - descendants = contentService.GetDescendants(content).ToList(); + + descendants.Clear(); + page = 0; + while (page * pageSize < total) + descendants.AddRange(contentService.GetPagedDescendants(content.Id, page++, pageSize, out total)); Assert.AreEqual(-20, content.ParentId); Assert.IsTrue(content.Trashed); @@ -1883,8 +1870,13 @@ namespace Umbraco.Tests.Services ServiceContext.ContentService.Save(childPage3); //Verify that the children have the inherited permissions - var descendants = ServiceContext.ContentService.GetDescendants(parentPage).ToArray(); - Assert.AreEqual(3, descendants.Length); + var descendants = new List(); + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while(page * pageSize < total) + descendants.AddRange(ServiceContext.ContentService.GetPagedDescendants(parentPage.Id, page++, pageSize, out total)); + Assert.AreEqual(3, descendants.Count); foreach (var descendant in descendants) { @@ -1902,8 +1894,11 @@ namespace Umbraco.Tests.Services //Now copy, what should happen is the child pages will now have permissions inherited from the new parent var copy = ServiceContext.ContentService.Copy(childPage1, parentPage2.Id, false, true); - descendants = ServiceContext.ContentService.GetDescendants(parentPage2).ToArray(); - Assert.AreEqual(3, descendants.Length); + descendants.Clear(); + page = 0; + while (page * pageSize < total) + descendants.AddRange(ServiceContext.ContentService.GetPagedDescendants(parentPage2.Id, page++, pageSize, out total)); + Assert.AreEqual(3, descendants.Count); foreach (var descendant in descendants) { @@ -2035,7 +2030,7 @@ namespace Umbraco.Tests.Services var contentService = ServiceContext.ContentService; var temp = contentService.GetById(NodeDto.NodeIdSeed + 2); Assert.AreEqual("Home", temp.Name); - Assert.AreEqual(2, temp.Children(contentService).Count()); + Assert.AreEqual(2, contentService.CountChildren(temp.Id)); // Act var copy = contentService.Copy(temp, temp.ParentId, false, true, 0); @@ -2045,10 +2040,10 @@ namespace Umbraco.Tests.Services Assert.That(copy, Is.Not.Null); Assert.That(copy.Id, Is.Not.EqualTo(content.Id)); Assert.AreNotSame(content, copy); - Assert.AreEqual(2, copy.Children(contentService).Count()); + Assert.AreEqual(2, contentService.CountChildren(copy.Id)); var child = contentService.GetById(NodeDto.NodeIdSeed + 3); - var childCopy = copy.Children(contentService).First(); + var childCopy = contentService.GetPagedChildren(copy.Id, 0, 500, out var total).First(); Assert.AreEqual(childCopy.Name, child.Name); Assert.AreNotEqual(childCopy.Id, child.Id); Assert.AreNotEqual(childCopy.Key, child.Key); @@ -2061,7 +2056,7 @@ namespace Umbraco.Tests.Services var contentService = ServiceContext.ContentService; var temp = contentService.GetById(NodeDto.NodeIdSeed + 2); Assert.AreEqual("Home", temp.Name); - Assert.AreEqual(2, temp.Children(contentService).Count()); + Assert.AreEqual(2, contentService.CountChildren(temp.Id)); // Act var copy = contentService.Copy(temp, temp.ParentId, false, false, 0); @@ -2071,7 +2066,7 @@ namespace Umbraco.Tests.Services Assert.That(copy, Is.Not.Null); Assert.That(copy.Id, Is.Not.EqualTo(content.Id)); Assert.AreNotSame(content, copy); - Assert.AreEqual(0, copy.Children(contentService).Count()); + Assert.AreEqual(0, contentService.CountChildren(copy.Id)); } [Test] diff --git a/src/Umbraco.Web.UI/Umbraco/dialogs/ChangeDocType.aspx.cs b/src/Umbraco.Web.UI/Umbraco/dialogs/ChangeDocType.aspx.cs index 2297ea1aa7..8111410edd 100644 --- a/src/Umbraco.Web.UI/Umbraco/dialogs/ChangeDocType.aspx.cs +++ b/src/Umbraco.Web.UI/Umbraco/dialogs/ChangeDocType.aspx.cs @@ -113,7 +113,8 @@ namespace Umbraco.Web.UI.Umbraco.Dialogs private IEnumerable RemoveInvalidByChildrenDocumentTypesFromAlternatives(IEnumerable documentTypes) { - var docTypeIdsOfChildren = _content.Children(Services.ContentService) + //fixme Should do proper paging here ... when this is refactored we will + var docTypeIdsOfChildren = Services.ContentService.GetPagedChildren(_content.Id, 0, int.MaxValue, out var total) .Select(x => x.ContentType.Id) .Distinct() .ToList(); diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 9681a79ed1..e5d9e8fc3c 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -456,7 +456,7 @@ namespace Umbraco.Web.Editors public PagedResult> GetChildren( int id, string includeProperties, - int pageNumber = 0, //TODO: This should be '1' as it's not the index + int pageNumber = 0, int pageSize = 0, string orderBy = "SortOrder", Direction orderDirection = Direction.Ascending, @@ -483,7 +483,8 @@ namespace Umbraco.Web.Editors } else { - children = Services.ContentService.GetChildren(id).ToList(); + //better to not use this without paging where possible, currently only the sort dialog does + children = Services.ContentService.GetPagedChildren(id, 0, int.MaxValue, out var total).ToList(); totalChildren = children.Count; } From 881ea22efe05fb26740a5e2dc5d41a78429494ef Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Wed, 31 Oct 2018 10:24:40 +0000 Subject: [PATCH 42/53] Fix lang typo in ContentService & fix up the breaking unit test --- .../Services/Implement/ContentService.cs | 2 +- src/Umbraco.Tests/Services/ContentServiceTests.cs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index 3953e7a640..9bb8e6720e 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -1202,7 +1202,7 @@ namespace Umbraco.Core.Services.Implement var langs = string.Join(", ", _languageRepository.GetMany() .Where(x => culturesChanging.InvariantContains(x.IsoCode)) .Select(x => x.CultureName)); - Audit(AuditType.PublishVariant, userId, content.Id, $"Published languagues: {langs}", langs); + Audit(AuditType.PublishVariant, userId, content.Id, $"Published languages: {langs}", langs); } else Audit(AuditType.Publish, userId, content.Id); diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 5aad7c4d90..c5bbe197ef 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -1293,11 +1293,11 @@ namespace Umbraco.Tests.Services { // Arrange - var langUk = new Language("en-UK") { IsDefault = true }; + var langGB = new Language("en-GB") { IsDefault = true }; var langFr = new Language("fr-FR"); ServiceContext.LocalizationService.Save(langFr); - ServiceContext.LocalizationService.Save(langUk); + ServiceContext.LocalizationService.Save(langGB); var contentType = MockedContentTypes.CreateBasicContentType(); contentType.Variations = ContentVariation.Culture; @@ -1309,16 +1309,16 @@ namespace Umbraco.Tests.Services var published = ServiceContext.ContentService.SavePublishing(content); //audit log will only show that french was published var lastLog = ServiceContext.AuditService.GetLogs(content.Id).Last(); - Assert.AreEqual($"Published cultures: fr-fr", lastLog.Comment); + Assert.AreEqual($"Published languages: French (France)", lastLog.Comment); //re-get content = ServiceContext.ContentService.GetById(content.Id); - content.SetCultureName("content-en", langUk.IsoCode); - content.PublishCulture(langUk.IsoCode); + content.SetCultureName("content-en", langGB.IsoCode); + content.PublishCulture(langGB.IsoCode); published = ServiceContext.ContentService.SavePublishing(content); //audit log will only show that english was published lastLog = ServiceContext.AuditService.GetLogs(content.Id).Last(); - Assert.AreEqual($"Published cultures: en-uk", lastLog.Comment); + Assert.AreEqual($"Published languages: English (United Kingdom)", lastLog.Comment); } [Test] From 40eafe57b1f489266e4f570aea0887b067f12b54 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 31 Oct 2018 22:38:58 +1100 Subject: [PATCH 43/53] Fixes tests --- src/Umbraco.Core/Services/IContentService.cs | 2 +- .../Services/Implement/ContentService.cs | 30 ++++- .../Integration/ContentEventsTests.cs | 113 +++++++++--------- .../Services/ContentServiceTests.cs | 23 ++-- 4 files changed, 93 insertions(+), 75 deletions(-) diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index b821ae1eb0..c6efe926c2 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -193,7 +193,7 @@ namespace Umbraco.Core.Services /// The ordering direction. /// Search text filter. IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords, - string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = ""); + string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = ""); /// /// Gets descendant documents of a given parent. diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index 1f28988fa6..d2a74ba37e 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -593,8 +593,6 @@ namespace Umbraco.Core.Services.Implement { scope.ReadLock(Constants.Locks.ContentTree); - var query = Query(); - //if the id is System Root, then just get all if (id != Constants.System.Root) { @@ -604,8 +602,24 @@ namespace Umbraco.Core.Services.Implement totalChildren = 0; return Enumerable.Empty(); } - query.Where(x => x.Path.SqlStartsWith($"{contentPath[0].Path},", TextColumnType.NVarchar)); + return GetPagedDescendants(contentPath[0].Path, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); } + return GetPagedDescendants(null, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); + } + } + + private IEnumerable GetPagedDescendants(string contentPath, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter) + { + if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); + if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); + + using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + { + scope.ReadLock(Constants.Locks.ContentTree); + + var query = Query(); + if (!contentPath.IsNullOrWhiteSpace()) + query.Where(x => x.Path.SqlStartsWith($"{contentPath},", TextColumnType.NVarchar)); return _documentRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField)); } @@ -794,7 +808,7 @@ namespace Umbraco.Core.Services.Implement var langs = string.Join(", ", _languageRepository.GetMany() .Where(x => culturesChanging.InvariantContains(x.IsoCode)) .Select(x => x.CultureName)); - Audit(AuditType.SaveVariant, userId, content.Id, $"Saved languagues: {langs}", langs); + Audit(AuditType.SaveVariant, userId, content.Id, $"Saved languages: {langs}", langs); } else Audit(AuditType.Save, userId, content.Id); @@ -1125,7 +1139,7 @@ namespace Umbraco.Core.Services.Implement var langs = string.Join(", ", _languageRepository.GetMany() .Where(x => culturesChanging.InvariantContains(x.IsoCode)) .Select(x => x.CultureName)); - Audit(AuditType.PublishVariant, userId, content.Id, $"Published languagues: {langs}", langs); + Audit(AuditType.PublishVariant, userId, content.Id, $"Published languages: {langs}", langs); } else Audit(AuditType.Publish, userId, content.Id); @@ -1650,6 +1664,9 @@ namespace Umbraco.Core.Services.Implement moves.Add(Tuple.Create(content, content.Path)); // capture original path + //need to store the original path to lookup descendants based on it below + var originalPath = content.Path; + // these will be updated by the repo because we changed parentId //content.Path = (parent == null ? "-1" : parent.Path) + "," + content.Id; //content.SortOrder = ((ContentRepository) repository).NextChildSortOrder(parentId); @@ -1666,7 +1683,7 @@ namespace Umbraco.Core.Services.Implement var total = long.MaxValue; while(page * pageSize < total) { - var descendants = GetPagedDescendants(content.Id, page++, pageSize, out total); + var descendants = GetPagedDescendants(originalPath, page++, pageSize, out total, "Path", Direction.Ascending, true, null); foreach (var descendant in descendants) { moves.Add(Tuple.Create(descendant, descendant.Path)); // capture original path @@ -1685,6 +1702,7 @@ namespace Umbraco.Core.Services.Implement private void PerformMoveContentLocked(IContent content, int userId, bool? trash) { + //fixme no casting if (trash.HasValue) ((ContentBase) content).Trashed = trash.Value; content.WriterId = userId; _documentRepository.Save(content); diff --git a/src/Umbraco.Tests/Integration/ContentEventsTests.cs b/src/Umbraco.Tests/Integration/ContentEventsTests.cs index 655d44e86c..af188c6a09 100644 --- a/src/Umbraco.Tests/Integration/ContentEventsTests.cs +++ b/src/Umbraco.Tests/Integration/ContentEventsTests.cs @@ -846,22 +846,18 @@ namespace Umbraco.Tests.Integration // force:true => all nodes are republished, refreshing all nodes - but only with changes - published w/out changes are not repub Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1.Id}.u+p", _events[i++].ToString()); - //Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[0].Id}.p+p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u+p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p+p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u+p", _events[i++].ToString()); - - // remember: ordered by level, sortOrder //Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[0].Id}.p+p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u+p", _events[i++].ToString()); - //Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p+p", _events[i++].ToString()); - //Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p+p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[1].Id}.u+p", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u+p", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u+p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[1].Id}.u+p", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p+p", _events[i++].ToString()); + //Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p+p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[1].Id}.u+p", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u+p", _events[i++].ToString()); + //Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p+p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[1].Id}.u+p", _events[i++].ToString()); - Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i++].ToString()); // repub content1 } @@ -1073,17 +1069,18 @@ namespace Umbraco.Tests.Integration Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1.Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[0].Id}.p=m", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=m", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[0].Id}.p=m", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=m", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=m", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content5C[1].Id}.u=u", _events[i++].ToString()); + m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i].ToString()); } @@ -1706,16 +1703,16 @@ namespace Umbraco.Tests.Integration var content5C = Children(content1C[3]).ToArray(); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1.Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[0].Id}.p=m", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=m", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[0].Id}.p=m", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=m", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=m", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content5C[1].Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i++].ToString()); @@ -1759,16 +1756,16 @@ namespace Umbraco.Tests.Integration var content5C = Children(content1C[3]).ToArray(); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1.Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[0].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[0].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=p", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content5C[1].Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i++].ToString()); @@ -1816,16 +1813,16 @@ namespace Umbraco.Tests.Integration var content5C = Children(content1C[3]).ToArray(); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1.Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[0].Id}.p=m", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=m", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[0].Id}.p=m", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=m", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=m", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content5C[1].Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i++].ToString()); @@ -1871,16 +1868,16 @@ namespace Umbraco.Tests.Integration var content5C = Children(content1C[3]).ToArray(); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1.Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[0].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[0].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=p", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content5C[1].Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i++].ToString()); @@ -1925,16 +1922,16 @@ namespace Umbraco.Tests.Integration var content5C = Children(content1C[3]).ToArray(); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1.Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[0].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[0].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=p", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content5C[1].Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i++].ToString()); @@ -1984,16 +1981,16 @@ namespace Umbraco.Tests.Integration var content5C = Children(content1C[3]).ToArray(); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1.Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[0].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[0].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=p", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content2C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content3C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[2].Id}.p=p", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[0].Id}.p=p", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content4C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content1C[3].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{content5C[0].Id}.p=m", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{content5C[1].Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{content1.Id}", _events[i++].ToString()); @@ -2098,16 +2095,16 @@ namespace Umbraco.Tests.Integration var m = 0; Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy.Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copyC[0].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copyC[1].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copyC[2].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copyC[3].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy2C[0].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy3C[0].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy4C[0].Id}.u=u", _events[i++].ToString()); - Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy5C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy2C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copyC[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy3C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy3C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copyC[2].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy4C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy4C[1].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copyC[3].Id}.u=u", _events[i++].ToString()); + Assert.AreEqual($"{m++:000}: ContentRepository/Refresh/{copy5C[0].Id}.u=u", _events[i++].ToString()); Assert.AreEqual($"{m:000}: ContentRepository/Refresh/{copy5C[1].Id}.u=u", _events[i++].ToString()); m++; Assert.AreEqual($"{m:000}: ContentCacheRefresher/RefreshBranch/{copy.Id}", _events[i].ToString()); diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 952c8f9dc1..1f99bd1127 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -35,7 +35,10 @@ namespace Umbraco.Tests.Services /// as well as configuration. /// [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, PublishedRepositoryEvents = true, WithApplication = true)] + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, + PublishedRepositoryEvents = true, + WithApplication = true, + Logger = UmbracoTestOptions.Logger.Console)] public class ContentServiceTests : TestWithSomeContentBase { //TODO Add test to verify there is only ONE newest document/content in {Constants.DatabaseSchema.Tables.Document} table after updating. @@ -1209,7 +1212,7 @@ namespace Umbraco.Tests.Services { // Arrange - var langUk = new Language("en-UK") { IsDefault = true }; + var langUk = new Language("en-GB") { IsDefault = true }; var langFr = new Language("fr-FR"); ServiceContext.LocalizationService.Save(langFr); @@ -1262,7 +1265,7 @@ namespace Umbraco.Tests.Services { // Arrange - var langUk = new Language("en-UK") { IsDefault = true }; + var langUk = new Language("en-GB") { IsDefault = true }; var langFr = new Language("fr-FR"); ServiceContext.LocalizationService.Save(langFr); @@ -1278,7 +1281,7 @@ namespace Umbraco.Tests.Services var published = ServiceContext.ContentService.SavePublishing(content); //audit log will only show that french was published var lastLog = ServiceContext.AuditService.GetLogs(content.Id).Last(); - Assert.AreEqual($"Published cultures: fr-fr", lastLog.Comment); + Assert.AreEqual($"Published languages: French (France)", lastLog.Comment); //re-get content = ServiceContext.ContentService.GetById(content.Id); @@ -1287,7 +1290,7 @@ namespace Umbraco.Tests.Services published = ServiceContext.ContentService.SavePublishing(content); //audit log will only show that english was published lastLog = ServiceContext.AuditService.GetLogs(content.Id).Last(); - Assert.AreEqual($"Published cultures: en-uk", lastLog.Comment); + Assert.AreEqual($"Published languages: English (United Kingdom)", lastLog.Comment); } [Test] @@ -2775,7 +2778,7 @@ namespace Umbraco.Tests.Services { var languageService = ServiceContext.LocalizationService; - var langUk = new Language("en-UK") { IsDefault = true }; + var langUk = new Language("en-GB") { IsDefault = true }; var langFr = new Language("fr-FR"); languageService.Save(langFr); @@ -2810,7 +2813,7 @@ namespace Umbraco.Tests.Services { var languageService = ServiceContext.LocalizationService; - var langUk = new Language("en-UK") { IsDefault = true }; + var langUk = new Language("en-GB") { IsDefault = true }; var langFr = new Language("fr-FR"); languageService.Save(langFr); @@ -2847,7 +2850,7 @@ namespace Umbraco.Tests.Services { var languageService = ServiceContext.LocalizationService; - var langUk = new Language("en-UK") { IsDefault = true }; + var langUk = new Language("en-GB") { IsDefault = true }; var langFr = new Language("fr-FR"); var langDa = new Language("da-DK"); @@ -2939,7 +2942,7 @@ namespace Umbraco.Tests.Services private void WriteList(List list) { foreach (var content in list) - Console.WriteLine("[{0}] {1} {2} {3} {4}", content.Id, content.Name, content.GetCultureName("en-UK"), content.GetCultureName("fr-FR"), content.GetCultureName("da-DK")); + Console.WriteLine("[{0}] {1} {2} {3} {4}", content.Id, content.Name, content.GetCultureName("en-GB"), content.GetCultureName("fr-FR"), content.GetCultureName("da-DK")); Console.WriteLine("-"); } @@ -2951,7 +2954,7 @@ namespace Umbraco.Tests.Services //var langFr = new Language("fr-FR") { IsDefaultVariantLanguage = true }; var langXx = new Language("pt-PT") { IsDefault = true }; var langFr = new Language("fr-FR"); - var langUk = new Language("en-UK"); + var langUk = new Language("en-GB"); var langDe = new Language("de-DE"); languageService.Save(langFr); From 25dcc3f2c185d84fe9b08f0e6c887f0a43ad3079 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 31 Oct 2018 23:11:37 +1100 Subject: [PATCH 44/53] Removes GetChildren and GetDescendants from IMediaService, paging is now required --- src/Umbraco.Core/ContentExtensions.cs | 73 ----------- .../Services/EntityXmlSerializer.cs | 28 +++-- src/Umbraco.Core/Services/IMediaService.cs | 23 +--- .../Services/Implement/ContentService.cs | 83 ++++--------- .../Services/Implement/MediaService.cs | 117 ++++++------------ src/Umbraco.Web/Editors/ContentController.cs | 2 +- src/Umbraco.Web/Editors/MediaController.cs | 29 ++++- .../Routing/UrlProviderExtensions.cs | 2 +- src/Umbraco.Web/Search/ExamineComponent.cs | 12 +- 9 files changed, 112 insertions(+), 257 deletions(-) diff --git a/src/Umbraco.Core/ContentExtensions.cs b/src/Umbraco.Core/ContentExtensions.cs index f0fa6cdf17..910717304c 100644 --- a/src/Umbraco.Core/ContentExtensions.cs +++ b/src/Umbraco.Core/ContentExtensions.cs @@ -37,79 +37,6 @@ namespace Umbraco.Core return dirty.WasPropertyDirty("Published") && entity.Published; } - /// - /// Returns a list of the current contents ancestors, not including the content itself. - /// - /// Current content - /// - /// An enumerable list of objects - public static IEnumerable Ancestors(this IContent content, IContentService contentService) - { - return contentService.GetAncestors(content); - } - - /// - /// Returns the parent of the current content. - /// - /// Current content - /// - /// An object - public static IContent Parent(this IContent content, IContentService contentService) - { - return contentService.GetById(content.ParentId); - } - - #endregion - - #region IMedia - - /// - /// Returns a list of the current medias ancestors, not including the media itself. - /// - /// Current media - /// - /// An enumerable list of objects - public static IEnumerable Ancestors(this IMedia media, IMediaService mediaService) - { - return mediaService.GetAncestors(media); - } - - - /// - /// Returns a list of the current medias children. - /// - /// Current media - /// - /// An enumerable list of objects - public static IEnumerable Children(this IMedia media, IMediaService mediaService) - { - return mediaService.GetChildren(media.Id); - } - - - /// - /// Returns a list of the current medias descendants, not including the media itself. - /// - /// Current media - /// - /// An enumerable list of objects - public static IEnumerable Descendants(this IMedia media, IMediaService mediaService) - { - return mediaService.GetDescendants(media); - } - - - /// - /// Returns the parent of the current media. - /// - /// Current media - /// - /// An object - public static IMedia Parent(this IMedia media, IMediaService mediaService) - { - return mediaService.GetById(media.ParentId); - } - #endregion /// diff --git a/src/Umbraco.Core/Services/EntityXmlSerializer.cs b/src/Umbraco.Core/Services/EntityXmlSerializer.cs index ebb35a43ed..5b64584dc6 100644 --- a/src/Umbraco.Core/Services/EntityXmlSerializer.cs +++ b/src/Umbraco.Core/Services/EntityXmlSerializer.cs @@ -109,9 +109,14 @@ namespace Umbraco.Core.Services if (withDescendants) { - var descendants = mediaService.GetDescendants(media).ToArray(); - var currentChildren = descendants.Where(x => x.ParentId == media.Id); - SerializeDescendants(mediaService, dataTypeService, userService, localizationService, urlSegmentProviders, descendants, currentChildren, xml); + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + var children = mediaService.GetPagedChildren(media.Id, page++, pageSize, out total); + SerializeChildren(mediaService, dataTypeService, userService, localizationService, urlSegmentProviders, children, xml); + } } return xml; @@ -478,7 +483,7 @@ namespace Umbraco.Core.Services } // exports an IMedia item descendants. - private static void SerializeDescendants(IMediaService mediaService, IDataTypeService dataTypeService, IUserService userService, ILocalizationService localizationService, IEnumerable urlSegmentProviders, IMedia[] originalDescendants, IEnumerable children, XElement xml) + private static void SerializeChildren(IMediaService mediaService, IDataTypeService dataTypeService, IUserService userService, ILocalizationService localizationService, IEnumerable urlSegmentProviders, IEnumerable children, XElement xml) { foreach (var child in children) { @@ -486,12 +491,15 @@ namespace Umbraco.Core.Services var childXml = Serialize(mediaService, dataTypeService, userService, localizationService, urlSegmentProviders, child); xml.Add(childXml); - // capture id (out of closure) and get the grandChildren (children of the child) - var parentId = child.Id; - var grandChildren = originalDescendants.Where(x => x.ParentId == parentId); - - // recurse - SerializeDescendants(mediaService, dataTypeService, userService, localizationService, urlSegmentProviders, originalDescendants, grandChildren, childXml); + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + var grandChildren = mediaService.GetPagedChildren(child.Id, page++, pageSize, out total); + // recurse + SerializeChildren(mediaService, dataTypeService, userService, localizationService, urlSegmentProviders, grandChildren, childXml); + } } } } diff --git a/src/Umbraco.Core/Services/IMediaService.cs b/src/Umbraco.Core/Services/IMediaService.cs index 31c2e74fd4..6976a09b76 100644 --- a/src/Umbraco.Core/Services/IMediaService.cs +++ b/src/Umbraco.Core/Services/IMediaService.cs @@ -78,13 +78,6 @@ namespace Umbraco.Core.Services /// IMedia GetById(int id); - /// - /// Gets a collection of objects by Parent Id - /// - /// Id of the Parent to retrieve Children from - /// An Enumerable list of objects - IEnumerable GetChildren(int id); - /// /// Gets a collection of objects by Parent Id /// @@ -158,14 +151,7 @@ namespace Umbraco.Core.Services /// An Enumerable list of objects IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter); - - /// - /// Gets descendants of a object by its Id - /// - /// Id of the Parent to retrieve descendants from - /// An Enumerable flat list of objects - IEnumerable GetDescendants(int id); - + /// /// Gets a collection of objects by the Id of the /// @@ -321,13 +307,6 @@ namespace Umbraco.Core.Services /// An Enumerable list of objects IEnumerable GetAncestors(IMedia media); - /// - /// Gets descendants of a object by its Id - /// - /// The Parent object to retrieve descendants from - /// An Enumerable flat list of objects - IEnumerable GetDescendants(IMedia media); - /// /// Gets the parent of the current media as an item. /// diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index d2a74ba37e..2c8e38777b 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -586,9 +586,6 @@ namespace Umbraco.Core.Services.Implement /// public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter) { - if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); - if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { scope.ReadLock(Constants.Locks.ContentTree); @@ -602,27 +599,22 @@ namespace Umbraco.Core.Services.Implement totalChildren = 0; return Enumerable.Empty(); } - return GetPagedDescendants(contentPath[0].Path, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); + return GetPagedDescendantsLocked(contentPath[0].Path, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); } - return GetPagedDescendants(null, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); + return GetPagedDescendantsLocked(null, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); } } - private IEnumerable GetPagedDescendants(string contentPath, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter) + private IEnumerable GetPagedDescendantsLocked(string contentPath, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter) { if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - scope.ReadLock(Constants.Locks.ContentTree); + var query = Query(); + if (!contentPath.IsNullOrWhiteSpace()) + query.Where(x => x.Path.SqlStartsWith($"{contentPath},", TextColumnType.NVarchar)); - var query = Query(); - if (!contentPath.IsNullOrWhiteSpace()) - query.Where(x => x.Path.SqlStartsWith($"{contentPath},", TextColumnType.NVarchar)); - - return _documentRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField)); - } + return _documentRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField)); } /// @@ -1403,50 +1395,8 @@ namespace Umbraco.Core.Services.Implement private void DeleteLocked(IScope scope, IContent content) { - //TODO: Test this - - // then recursively delete descendants, bottom-up - // just repository.Delete + an event - var stack = new Stack(); - stack.Push(content); - var level = 1; - while (stack.Count > 0) + void DoDelete(IContent c) { - var c = stack.Peek(); - if (c.Level == level) - { - var hasChildren = true; - while (hasChildren) - { - IContent last = null; - const int pageSize = 500; - var page = 0; - var total = long.MaxValue; - var countChildren = 0; - - //get all children of c in pages - while (page * pageSize < total) - { - var children = GetPagedChildren(c.Id, page++, pageSize, out total); - - foreach (var ci in children) - { - countChildren++; - stack.Push(ci); - last = ci; - } - - if (countChildren == 0) - hasChildren = false; //exit, there are no children left to load - else - c = last; //recurse with the last child - } - } - } - - c = stack.Pop(); - level = c.Level; - _documentRepository.Delete(c); var args = new DeleteEventArgs(c, false); // raise event & get flagged files scope.Events.Dispatch(Deleted, this, args, nameof(Deleted)); @@ -1455,6 +1405,18 @@ namespace Umbraco.Core.Services.Implement _mediaFileSystem.DeleteFiles(args.MediaFilesToDelete, // remove flagged files (file, e) => Logger.Error(e, "An error occurred while deleting file attached to nodes: {File}", file)); } + + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + //get descendants - ordered from deepest to shallowest + var descendants = GetPagedDescendants(content.Id, page, pageSize, out total, "Path", Direction.Descending); + foreach (var c in descendants) + DoDelete(c); + } + DoDelete(content); } //TODO: @@ -1683,16 +1645,13 @@ namespace Umbraco.Core.Services.Implement var total = long.MaxValue; while(page * pageSize < total) { - var descendants = GetPagedDescendants(originalPath, page++, pageSize, out total, "Path", Direction.Ascending, true, null); + var descendants = GetPagedDescendantsLocked(originalPath, page++, pageSize, out total, "Path", Direction.Ascending, true, null); foreach (var descendant in descendants) { moves.Add(Tuple.Create(descendant, descendant.Path)); // capture original path // update path and level since we do not update parentId - if (paths.ContainsKey(descendant.ParentId) == false) - Console.WriteLine("oops on " + descendant.ParentId + " for " + content.Path + " " + parent?.Path); descendant.Path = paths[descendant.Id] = paths[descendant.ParentId] + "," + descendant.Id; - Console.WriteLine("path " + descendant.Id + " = " + paths[descendant.Id]); descendant.Level += levelDelta; PerformMoveContentLocked(descendant, userId, trash); } diff --git a/src/Umbraco.Core/Services/Implement/MediaService.cs b/src/Umbraco.Core/Services/Implement/MediaService.cs index da04f41e18..25aa02befa 100644 --- a/src/Umbraco.Core/Services/Implement/MediaService.cs +++ b/src/Umbraco.Core/Services/Implement/MediaService.cs @@ -460,21 +460,6 @@ namespace Umbraco.Core.Services.Implement } } - /// - /// Gets a collection of objects by Parent Id - /// - /// Id of the Parent to retrieve Children from - /// An Enumerable list of objects - public IEnumerable GetChildren(int id) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - scope.ReadLock(Constants.Locks.MediaTree); - var query = Query().Where(x => x.ParentId == id); - return _mediaRepository.Get(query).OrderBy(x => x.SortOrder); - } - } - /// /// Gets a collection of objects by Parent Id /// @@ -594,15 +579,10 @@ namespace Umbraco.Core.Services.Implement /// An Enumerable list of objects public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter) { - if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); - if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { scope.ReadLock(Constants.Locks.MediaTree); - var query = Query(); - //if the id is System Root, then just get all if (id != Constants.System.Root) { @@ -612,47 +592,22 @@ namespace Umbraco.Core.Services.Implement totalChildren = 0; return Enumerable.Empty(); } - query.Where(x => x.Path.SqlStartsWith(mediaPath[0].Path + ",", TextColumnType.NVarchar)); + return GetPagedDescendantsLocked(mediaPath[0].Path, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); } - - return _mediaRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField)); + return GetPagedDescendantsLocked(null, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); } } - /// - /// Gets descendants of a object by its Id - /// - /// Id of the Parent to retrieve descendants from - /// An Enumerable flat list of objects - public IEnumerable GetDescendants(int id) + private IEnumerable GetPagedDescendantsLocked(string mediaPath, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - scope.ReadLock(Constants.Locks.MediaTree); - var media = GetById(id); - if (media == null) - return Enumerable.Empty(); + if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); + if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); - var pathMatch = media.Path + ","; - var query = Query().Where(x => x.Id != media.Id && x.Path.StartsWith(pathMatch)); - return _mediaRepository.Get(query); - } - } + var query = Query(); + if (!mediaPath.IsNullOrWhiteSpace()) + query.Where(x => x.Path.SqlStartsWith(mediaPath + ",", TextColumnType.NVarchar)); - /// - /// Gets descendants of a object by its Id - /// - /// The Parent object to retrieve descendants from - /// An Enumerable flat list of objects - public IEnumerable GetDescendants(IMedia media) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - scope.ReadLock(Constants.Locks.MediaTree); - var pathMatch = media.Path + ","; - var query = Query().Where(x => x.Id != media.Id && x.Path.StartsWith(pathMatch)); - return _mediaRepository.Get(query); - } + return _mediaRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField)); } /// @@ -865,25 +820,8 @@ namespace Umbraco.Core.Services.Implement private void DeleteLocked(IScope scope, IMedia media) { - // then recursively delete descendants, bottom-up - // just repository.Delete + an event - var stack = new Stack(); - stack.Push(media); - var level = 1; - while (stack.Count > 0) + void DoDelete(IMedia c) { - var c = stack.Peek(); - IMedia[] cc; - if (c.Level == level) - while ((cc = c.Children(this).ToArray()).Length > 0) - { - foreach (var ci in cc) - stack.Push(ci); - c = cc[cc.Length - 1]; - } - c = stack.Pop(); - level = c.Level; - _mediaRepository.Delete(c); var args = new DeleteEventArgs(c, false); // raise event & get flagged files scope.Events.Dispatch(Deleted, this, args); @@ -891,6 +829,18 @@ namespace Umbraco.Core.Services.Implement _mediaFileSystem.DeleteFiles(args.MediaFilesToDelete, // remove flagged files (file, e) => Logger.Error(e, "An error occurred while deleting file attached to nodes: {File}", file)); } + + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while(page * pageSize < total) + { + //get descendants - ordered from deepest to shallowest + var descendants = GetPagedDescendants(media.Id, page, pageSize, out total, "Path", Direction.Descending); + foreach (var c in descendants) + DoDelete(c); + } + DoDelete(media); } //TODO: @@ -1100,8 +1050,8 @@ namespace Umbraco.Core.Services.Implement moves.Add(Tuple.Create(media, media.Path)); // capture original path - // get before moving, in case uow is immediate - var descendants = GetDescendants(media); + //need to store the original path to lookup descendants based on it below + var originalPath = media.Path; // these will be updated by the repo because we changed parentId //media.Path = (parent == null ? "-1" : parent.Path) + "," + media.Id; @@ -1114,14 +1064,21 @@ namespace Umbraco.Core.Services.Implement //paths[media.Id] = media.Path; paths[media.Id] = (parent == null ? (parentId == Constants.System.RecycleBinMedia ? "-1,-21" : "-1") : parent.Path) + "," + media.Id; - foreach (var descendant in descendants) + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) { - moves.Add(Tuple.Create(descendant, descendant.Path)); // capture original path + var descendants = GetPagedDescendantsLocked(originalPath, page++, pageSize, out total, "Path", Direction.Ascending, true, null); + foreach (var descendant in descendants) + { + moves.Add(Tuple.Create(descendant, descendant.Path)); // capture original path - // update path and level since we do not update parentId - descendant.Path = paths[descendant.Id] = paths[descendant.ParentId] + "," + descendant.Id; - descendant.Level += levelDelta; - PerformMoveMediaLocked(descendant, userId, trash); + // update path and level since we do not update parentId + descendant.Path = paths[descendant.Id] = paths[descendant.ParentId] + "," + descendant.Id; + descendant.Level += levelDelta; + PerformMoveMediaLocked(descendant, userId, trash); + } } } diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index e5d9e8fc3c..ed87f770ca 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -1318,7 +1318,7 @@ namespace Umbraco.Web.Editors xnames.Add(xcontent.Name); if (xcontent.ParentId < -1) xnames.Add("Recycle Bin"); - xcontent = xcontent.Parent(Services.ContentService); + xcontent = Services.ContentService.GetParent(xcontent); } xnames.Reverse(); domainModel.Other = "/" + string.Join("/", xnames); diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index dc744ea361..3dfd435d8f 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -271,7 +271,7 @@ namespace Umbraco.Web.Editors // else proceed as usual long totalChildren; - IMedia[] children; + List children; if (pageNumber > 0 && pageSize > 0) { IQuery queryFilter = null; @@ -287,12 +287,13 @@ namespace Umbraco.Web.Editors id, (pageNumber - 1), pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, - queryFilter).ToArray(); + queryFilter).ToList(); } else { - children = Services.MediaService.GetChildren(id).ToArray(); - totalChildren = children.Length; + //better to not use this without paging where possible, currently only the sort dialog does + children = Services.MediaService.GetPagedChildren(id, 0, int.MaxValue, out var total).ToList(); + totalChildren = children.Count; } if (totalChildren == 0) @@ -663,7 +664,8 @@ namespace Umbraco.Web.Editors " returned null"); //look for matching folder - folderMediaItem = mediaRoot.Children(Services.MediaService).FirstOrDefault(x => x.Name == folderName && x.ContentType.Alias == Constants.Conventions.MediaTypes.Folder); + folderMediaItem = FindInChildren(mediaRoot.Id, folderName, Constants.Conventions.MediaTypes.Folder); + if (folderMediaItem == null) { //if null, create a folder @@ -753,6 +755,23 @@ namespace Umbraco.Web.Editors return Request.CreateResponse(HttpStatusCode.OK, tempFiles); } + private IMedia FindInChildren(int mediaId, string nameToFind, string contentTypeAlias) + { + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + var children = Services.MediaService.GetPagedChildren(mediaId, page, pageSize, out total); + foreach (var c in children) + { + if (c.Name == nameToFind && c.ContentType.Alias == contentTypeAlias) + return c; + } + } + return null; + } + /// /// Given a parent id which could be a GUID, UDI or an INT, this will resolve the INT /// diff --git a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs b/src/Umbraco.Web/Routing/UrlProviderExtensions.cs index 2be0dce096..14b9c0a465 100644 --- a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs +++ b/src/Umbraco.Web/Routing/UrlProviderExtensions.cs @@ -129,7 +129,7 @@ namespace Umbraco.Web.Routing var parent = content; do { - parent = parent.ParentId > 0 ? parent.Parent(contentService) : null; + parent = parent.ParentId > 0 ? contentService.GetParent(parent) : null; } while (parent != null && parent.Published && (!parent.ContentType.VariesByCulture() || parent.IsCulturePublished(culture))); diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index b1a302785d..05be969f92 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -304,10 +304,16 @@ namespace Umbraco.Web.Search // branch if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch)) { - var descendants = mediaService.GetDescendants(media); - foreach (var descendant in descendants) + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) { - ReIndexForMedia(descendant, descendant.Trashed == false); + var descendants = mediaService.GetPagedDescendants(media.Id, page++, pageSize, out total); + foreach (var descendant in descendants) + { + ReIndexForMedia(descendant, descendant.Trashed == false); + } } } } From b8d1dd7684a5dabb3359ba8fbfee926e2ae7126d Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 1 Nov 2018 00:05:17 +1100 Subject: [PATCH 45/53] Cleans up paged methods of IContentService to use the Ordering parameter for consistency --- src/Umbraco.Core/Services/IContentService.cs | 39 ++++++++++----- .../Services/Implement/ContentService.cs | 47 +++++++++++++++---- src/Umbraco.Examine/UmbracoContentIndexer.cs | 3 +- .../Services/ContentServiceTests.cs | 2 +- .../UmbracoExamine/IndexInitializer.cs | 4 +- .../UmbracoExamine/SearchTests.cs | 2 +- src/Umbraco.Web/Search/ExamineComponent.cs | 45 ++++++++++++++---- 7 files changed, 105 insertions(+), 37 deletions(-) diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index c6efe926c2..c609a7493d 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -78,17 +78,6 @@ namespace Umbraco.Core.Services /// IEnumerable GetByIds(IEnumerable ids); - /// - /// Gets paged documents of a content content - /// - /// The page number. - /// The page number. - /// The page size. - /// Total number of documents. - /// Search text filter. - /// Ordering infos. - IEnumerable GetPagedOfType(int contentType, long pageIndex, int pageSize, out long totalRecords, IQuery filter = null, Ordering ordering = null); - /// /// Gets documents at a given level. /// @@ -193,7 +182,7 @@ namespace Umbraco.Core.Services /// The ordering direction. /// Search text filter. IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords, - string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = ""); + string filter = null, Ordering ordering = null); /// /// Gets descendant documents of a given parent. @@ -207,7 +196,31 @@ namespace Umbraco.Core.Services /// A flag indicating whether the ordering field is a system field. /// Query filter. IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords, - string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter); + IQuery filter, Ordering ordering = null); + + /// + /// Gets paged documents of a content content + /// + /// The page number. + /// The page number. + /// The page size. + /// Total number of documents. + /// Search text filter. + /// Ordering infos. + IEnumerable GetPagedOfType(int contentTypeId, long pageIndex, int pageSize, out long totalRecords, + IQuery filter, Ordering ordering = null); + + /// + /// Gets paged documents for specified content types + /// + /// The page number. + /// The page number. + /// The page size. + /// Total number of documents. + /// Search text filter. + /// Ordering infos. + IEnumerable GetPagedOfTypes(int[] contentTypeIds, long pageIndex, int pageSize, out long totalRecords, + IQuery filter, Ordering ordering = null); /// /// Counts documents of a given document type. diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index 2c8e38777b..1c3f5203f9 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -405,6 +405,7 @@ namespace Umbraco.Core.Services.Implement } } + /// public IEnumerable GetPagedOfType(int contentTypeId, long pageIndex, int pageSize, out long totalRecords, IQuery filter, Ordering ordering = null) { if(pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); @@ -422,6 +423,24 @@ namespace Umbraco.Core.Services.Implement } } + /// + public IEnumerable GetPagedOfTypes(int[] contentTypeIds, long pageIndex, int pageSize, out long totalRecords, IQuery filter, Ordering ordering = null) + { + if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); + if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); + + if (ordering == null) + ordering = Ordering.By("sortOrder"); + + using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + { + scope.ReadLock(Constants.Locks.ContentTree); + return _documentRepository.GetPage( + Query().Where(x => contentTypeIds.Contains(x.ContentTypeId)), + pageIndex, pageSize, out totalRecords, filter, ordering); + } + } + /// /// Gets a collection of objects by Level /// @@ -574,18 +593,24 @@ namespace Umbraco.Core.Services.Implement } /// - public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "") + public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, + string filter = null, Ordering ordering = null) + //string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "") { var filterQuery = filter.IsNullOrWhiteSpace() ? null : Query().Where(x => x.Name.Contains(filter)); - return GetPagedDescendants(id, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, true, filterQuery); + return GetPagedDescendants(id, pageIndex, pageSize, out totalChildren, filterQuery, ordering); } /// - public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter) + public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, + IQuery filter, Ordering ordering = null) { + if (ordering == null) + ordering = Ordering.By("Path"); + using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { scope.ReadLock(Constants.Locks.ContentTree); @@ -599,22 +624,26 @@ namespace Umbraco.Core.Services.Implement totalChildren = 0; return Enumerable.Empty(); } - return GetPagedDescendantsLocked(contentPath[0].Path, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); + return GetPagedDescendantsLocked(contentPath[0].Path, pageIndex, pageSize, out totalChildren, filter, ordering); } - return GetPagedDescendantsLocked(null, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); + return GetPagedDescendantsLocked(null, pageIndex, pageSize, out totalChildren, filter, ordering); } } - private IEnumerable GetPagedDescendantsLocked(string contentPath, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter) + private IEnumerable GetPagedDescendantsLocked(string contentPath, long pageIndex, int pageSize, out long totalChildren, + IQuery filter, Ordering ordering) { if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); + if (filter == null) throw new ArgumentNullException(nameof(filter)); + + if (ordering == null) throw new ArgumentNullException(nameof(ordering)); var query = Query(); if (!contentPath.IsNullOrWhiteSpace()) query.Where(x => x.Path.SqlStartsWith($"{contentPath},", TextColumnType.NVarchar)); - return _documentRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField)); + return _documentRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, ordering); } /// @@ -1412,7 +1441,7 @@ namespace Umbraco.Core.Services.Implement while (page * pageSize < total) { //get descendants - ordered from deepest to shallowest - var descendants = GetPagedDescendants(content.Id, page, pageSize, out total, "Path", Direction.Descending); + var descendants = GetPagedDescendants(content.Id, page, pageSize, out total, ordering: Ordering.By("Path", Direction.Descending)); foreach (var c in descendants) DoDelete(c); } @@ -1645,7 +1674,7 @@ namespace Umbraco.Core.Services.Implement var total = long.MaxValue; while(page * pageSize < total) { - var descendants = GetPagedDescendantsLocked(originalPath, page++, pageSize, out total, "Path", Direction.Ascending, true, null); + var descendants = GetPagedDescendantsLocked(originalPath, page++, pageSize, out total, null, Ordering.By("Path", Direction.Ascending)); foreach (var descendant in descendants) { moves.Add(Tuple.Create(descendant, descendant.Path)); // capture original path diff --git a/src/Umbraco.Examine/UmbracoContentIndexer.cs b/src/Umbraco.Examine/UmbracoContentIndexer.cs index 94982c8591..fab9f226a4 100644 --- a/src/Umbraco.Examine/UmbracoContentIndexer.cs +++ b/src/Umbraco.Examine/UmbracoContentIndexer.cs @@ -258,7 +258,8 @@ namespace Umbraco.Examine else { //add the published filter - descendants = ContentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total, "Path", Direction.Ascending, true, _publishedQuery); + descendants = ContentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total, + _publishedQuery, Ordering.By("Path", Direction.Ascending)); } //if specific types are declared we need to post filter them diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 1f99bd1127..c2b1cdc52d 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -1718,7 +1718,7 @@ namespace Umbraco.Tests.Services // Act contentService.DeleteOfType(contentType.Id); var rootContent = contentService.GetRootContent(); - var contents = contentService.GetPagedOfType(contentType.Id, 0, int.MaxValue, out var _); + var contents = contentService.GetPagedOfType(contentType.Id, 0, int.MaxValue, out var _, null); // Assert Assert.That(rootContent.Any(), Is.False); diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index dedf04488c..b197cea5bc 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -71,11 +71,11 @@ namespace Umbraco.Tests.UmbracoExamine contentService = Mock.Of( x => x.GetPagedDescendants( - It.IsAny(), It.IsAny(), It.IsAny(), out longTotalRecs, It.IsAny(), It.IsAny(), It.IsAny()) + It.IsAny(), It.IsAny(), It.IsAny(), out longTotalRecs, It.IsAny(), It.IsAny()) == allRecs && x.GetPagedDescendants( - It.IsAny(), It.IsAny(), It.IsAny(), out longTotalRecs, It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>()) + It.IsAny(), It.IsAny(), It.IsAny(), out longTotalRecs, It.IsAny>(), It.IsAny()) == allRecs); } diff --git a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs index 2d440b8453..768d1c735c 100644 --- a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs +++ b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs @@ -49,7 +49,7 @@ namespace Umbraco.Tests.UmbracoExamine .ToArray(); var contentService = Mock.Of( x => x.GetPagedDescendants( - It.IsAny(), It.IsAny(), It.IsAny(), out totalRecs, It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>()) + It.IsAny(), It.IsAny(), It.IsAny(), out totalRecs, It.IsAny>(), It.IsAny()) == allRecs); diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index 05be969f92..b5cfac7e99 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -26,6 +26,7 @@ using Umbraco.Web.Cache; using Umbraco.Web.Composing; using Umbraco.Web.PropertyEditors; using Umbraco.Examine; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; namespace Umbraco.Web.Search { @@ -351,21 +352,42 @@ namespace Umbraco.Web.Search const int pageSize = 500; - //Re-index all content of these types - foreach(var id in refreshedIds.Concat(otherIds).Distinct()) - { + if (refreshedIds.Count > 0 || otherIds.Count > 0) + { var page = 0; var total = long.MaxValue; while (page * pageSize < total) { - var contentToRefresh = _services.ContentService.GetPagedOfType(id, page++, pageSize, out total); - foreach(var c in contentToRefresh) + var contentToRefresh = _services.ContentService.GetPagedOfTypes( + //Re-index all content of these types + refreshedIds.Concat(otherIds).Distinct().ToArray(), + page++, pageSize, out total, null, + //order by shallowest to deepest, this allows us to check it's published state without checking every item + Ordering.By("Path", Direction.Ascending)); + + //track which Ids have their paths are published + var publishChecked = new Dictionary(); + + foreach (var c in contentToRefresh) { - //TODO: We might have to order by Path ascending or something since we're going to need to check - // contentService.IsPathPublished(content) but we don't want to make that check for every content item IContent published = null; - if (c.Published && contentService.IsPathPublished(c)) - published = c; + if (c.Published) + { + if (publishChecked.TryGetValue(c.ParentId, out var isPublished)) + { + //if the parent's published path has already been verified then this is published + if (isPublished) + published = c; + } + else + { + //nothing by parent id, so query the service and cache the result for the next child to check against + isPublished = contentService.IsPathPublished(c); + publishChecked[c.Id] = isPublished; + if (isPublished) + published = c; + } + } ReIndexForContent(c, published); } @@ -460,7 +482,10 @@ namespace Umbraco.Web.Search var total = long.MaxValue; while(page * pageSize < total) { - var descendants = contentService.GetPagedDescendants(content.Id, page++, pageSize, out total); + var descendants = contentService.GetPagedDescendants(content.Id, page++, pageSize, out total, + //order by shallowest to deepest, this allows us to check it's published state without checking every item + ordering: Ordering.By("Path", Direction.Ascending)); + foreach (var descendant in descendants) { published = null; From ae5bda0910e98144cfde1624ca8cbd3ad1889a5d Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 1 Nov 2018 00:21:52 +1100 Subject: [PATCH 46/53] Streamlines IMediaService to be consistent with IContentService for paging and using the Ordering class --- src/Umbraco.Core/Services/IMediaService.cs | 26 ++----- .../Services/Implement/ContentService.cs | 2 - .../Services/Implement/MediaService.cs | 78 +++++++------------ .../Services/MediaServiceTests.cs | 8 +- .../UmbracoExamine/IndexInitializer.cs | 4 +- src/Umbraco.Web/Editors/MediaController.cs | 17 ++-- 6 files changed, 48 insertions(+), 87 deletions(-) diff --git a/src/Umbraco.Core/Services/IMediaService.cs b/src/Umbraco.Core/Services/IMediaService.cs index 6976a09b76..6fa211f4f3 100644 --- a/src/Umbraco.Core/Services/IMediaService.cs +++ b/src/Umbraco.Core/Services/IMediaService.cs @@ -90,7 +90,7 @@ namespace Umbraco.Core.Services /// Search text filter /// An Enumerable list of objects IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalRecords, - string orderBy = "SortOrder", Direction orderDirection = Direction.Ascending, string filter = ""); + string filter = null, Ordering ordering = null); /// /// Gets a collection of objects by Parent Id @@ -105,23 +105,7 @@ namespace Umbraco.Core.Services /// /// An Enumerable list of objects IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalRecords, - string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter); - - /// - /// Gets a collection of objects by Parent Id - /// - /// Id of the Parent to retrieve Children from - /// Page number - /// Page size - /// Total records query would return without paging - /// Field to order by - /// Direction to order by - /// Flag to indicate when ordering by system field - /// Search text filter - /// A list of content type Ids to filter the list by - /// An Enumerable list of objects - IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalRecords, - string orderBy, Direction orderDirection, bool orderBySystemField, string filter, int[] contentTypeFilter); + IQuery filter, Ordering ordering = null); /// /// Gets a collection of objects by Parent Id @@ -135,7 +119,7 @@ namespace Umbraco.Core.Services /// Search text filter /// An Enumerable list of objects IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords, - string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = ""); + string filter = null, Ordering ordering = null); /// /// Gets a collection of objects by Parent Id @@ -150,8 +134,8 @@ namespace Umbraco.Core.Services /// /// An Enumerable list of objects IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords, - string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter); - + IQuery filter, Ordering ordering = null); + /// /// Gets a collection of objects by the Id of the /// diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index 1c3f5203f9..debf7dcb34 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -595,7 +595,6 @@ namespace Umbraco.Core.Services.Implement /// public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string filter = null, Ordering ordering = null) - //string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "") { var filterQuery = filter.IsNullOrWhiteSpace() ? null @@ -636,7 +635,6 @@ namespace Umbraco.Core.Services.Implement if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); if (filter == null) throw new ArgumentNullException(nameof(filter)); - if (ordering == null) throw new ArgumentNullException(nameof(ordering)); var query = Query(); diff --git a/src/Umbraco.Core/Services/Implement/MediaService.cs b/src/Umbraco.Core/Services/Implement/MediaService.cs index 25aa02befa..a584521c33 100644 --- a/src/Umbraco.Core/Services/Implement/MediaService.cs +++ b/src/Umbraco.Core/Services/Implement/MediaService.cs @@ -471,13 +471,14 @@ namespace Umbraco.Core.Services.Implement /// Direction to order by /// Search text filter /// An Enumerable list of objects - public IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, string filter = "") + public IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalChildren, + string filter = null, Ordering ordering = null) { var filterQuery = filter.IsNullOrWhiteSpace() ? null : Query().Where(x => x.Name.Contains(filter)); - return GetPagedChildren(id, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, true, filterQuery); + return GetPagedChildren(id, pageIndex, pageSize, out totalChildren, filterQuery, ordering); } /// @@ -492,56 +493,21 @@ namespace Umbraco.Core.Services.Implement /// Flag to indicate when ordering by system field /// /// An Enumerable list of objects - public IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter) + public IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalChildren, + IQuery filter, Ordering ordering = null) { if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); + if (ordering == null) + ordering = Ordering.By("sortOrder"); + using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { scope.ReadLock(Constants.Locks.MediaTree); var query = Query().Where(x => x.ParentId == id); - return _mediaRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField)); - } - } - - /// - /// Gets a collection of objects by Parent Id - /// - /// Id of the Parent to retrieve Children from - /// Page number - /// Page size - /// Total records query would return without paging - /// Field to order by - /// Direction to order by - /// Flag to indicate when ordering by system field - /// Search text filter - /// A list of content type Ids to filter the list by - /// An Enumerable list of objects - public IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, string filter, int[] contentTypeFilter) - { - if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); - if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); - - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - scope.ReadLock(Constants.Locks.MediaTree); - - var query = Query(); - // always check for a parent - else it will also get decendants (and then you should use the GetPagedDescendants method) - - query.Where(x => x.ParentId == id); - - if (contentTypeFilter != null && contentTypeFilter.Length > 0) - { - query.Where(x => contentTypeFilter.Contains(x.ContentTypeId)); - } - - var filterQuery = filter.IsNullOrWhiteSpace() - ? null - : Query().Where(x => x.Name.Contains(filter)); - return _mediaRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filterQuery, Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField)); + return _mediaRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, ordering); } } @@ -556,13 +522,14 @@ namespace Umbraco.Core.Services.Implement /// Direction to order by /// Search text filter /// An Enumerable list of objects - public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "") + public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, + string filter = null, Ordering ordering = null) { var filterQuery = filter.IsNullOrWhiteSpace() ? null : Query().Where(x => x.Name.Contains(filter)); - return GetPagedDescendants(id, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, true, filterQuery); + return GetPagedDescendants(id, pageIndex, pageSize, out totalChildren, filterQuery, ordering); } /// @@ -577,8 +544,12 @@ namespace Umbraco.Core.Services.Implement /// Flag to indicate when ordering by system field /// /// An Enumerable list of objects - public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter) + public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, + IQuery filter, Ordering ordering = null) { + if (ordering == null) + ordering = Ordering.By("Path"); + using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { scope.ReadLock(Constants.Locks.MediaTree); @@ -592,22 +563,25 @@ namespace Umbraco.Core.Services.Implement totalChildren = 0; return Enumerable.Empty(); } - return GetPagedDescendantsLocked(mediaPath[0].Path, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); + return GetPagedDescendantsLocked(mediaPath[0].Path, pageIndex, pageSize, out totalChildren, filter, ordering); } - return GetPagedDescendantsLocked(null, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); + return GetPagedDescendantsLocked(null, pageIndex, pageSize, out totalChildren, filter, ordering); } } - private IEnumerable GetPagedDescendantsLocked(string mediaPath, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter) + private IEnumerable GetPagedDescendantsLocked(string mediaPath, long pageIndex, int pageSize, out long totalChildren, + IQuery filter, Ordering ordering) { if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); + if (filter == null) throw new ArgumentNullException(nameof(filter)); + if (ordering == null) throw new ArgumentNullException(nameof(ordering)); var query = Query(); if (!mediaPath.IsNullOrWhiteSpace()) query.Where(x => x.Path.SqlStartsWith(mediaPath + ",", TextColumnType.NVarchar)); - return _mediaRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField)); + return _mediaRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, ordering); } /// @@ -836,7 +810,7 @@ namespace Umbraco.Core.Services.Implement while(page * pageSize < total) { //get descendants - ordered from deepest to shallowest - var descendants = GetPagedDescendants(media.Id, page, pageSize, out total, "Path", Direction.Descending); + var descendants = GetPagedDescendants(media.Id, page, pageSize, out total, ordering: Ordering.By("Path", Direction.Descending)); foreach (var c in descendants) DoDelete(c); } @@ -1069,7 +1043,7 @@ namespace Umbraco.Core.Services.Implement var total = long.MaxValue; while (page * pageSize < total) { - var descendants = GetPagedDescendantsLocked(originalPath, page++, pageSize, out total, "Path", Direction.Ascending, true, null); + var descendants = GetPagedDescendantsLocked(originalPath, page++, pageSize, out total, null, Ordering.By("Path", Direction.Ascending)); foreach (var descendant in descendants) { moves.Add(Tuple.Create(descendant, descendant.Path)); // capture original path diff --git a/src/Umbraco.Tests/Services/MediaServiceTests.cs b/src/Umbraco.Tests/Services/MediaServiceTests.cs index 68fd2c3e11..b9e1fee0db 100644 --- a/src/Umbraco.Tests/Services/MediaServiceTests.cs +++ b/src/Umbraco.Tests/Services/MediaServiceTests.cs @@ -69,11 +69,15 @@ namespace Umbraco.Tests.Services } long total; - var result = ServiceContext.MediaService.GetPagedChildren(-1, 0, 11, out total, "SortOrder", Direction.Ascending, true, null, new[] { mediaType1.Id, mediaType2.Id }); + var result = ServiceContext.MediaService.GetPagedChildren(-1, 0, 11, out total, + SqlContext.Query().Where(x => new[] { mediaType1.Id, mediaType2.Id }.Contains(x.ContentTypeId)), + Ordering.By("SortOrder", Direction.Ascending)); Assert.AreEqual(11, result.Count()); Assert.AreEqual(20, total); - result = ServiceContext.MediaService.GetPagedChildren(-1, 1, 11, out total, "SortOrder", Direction.Ascending, true, null, new[] { mediaType1.Id, mediaType2.Id }); + result = ServiceContext.MediaService.GetPagedChildren(-1, 1, 11, out total, + SqlContext.Query().Where(x => new[] { mediaType1.Id, mediaType2.Id }.Contains(x.ContentTypeId)), + Ordering.By("SortOrder", Direction.Ascending)); Assert.AreEqual(9, result.Count()); Assert.AreEqual(20, total); } diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index b197cea5bc..8aec854441 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -116,12 +116,12 @@ namespace Umbraco.Tests.UmbracoExamine mediaServiceMock .Setup(x => x.GetPagedDescendants( - It.IsAny(), It.IsAny(), It.IsAny(), out totalRecs, It.IsAny(), It.IsAny(), It.IsAny()) + It.IsAny(), It.IsAny(), It.IsAny(), out totalRecs, It.IsAny(), It.IsAny()) ).Returns(() => allRecs); mediaServiceMock .Setup(x => x.GetPagedDescendants( - It.IsAny(), It.IsAny(), It.IsAny(), out totalRecs, It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>()) + It.IsAny(), It.IsAny(), It.IsAny(), out totalRecs, It.IsAny>(), It.IsAny()) ).Returns(() => allRecs); //mediaServiceMock.Setup(service => service.GetPagedXmlEntries(It.IsAny(), It.IsAny(), It.IsAny(), out longTotalRecs)) diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index 3dfd435d8f..f61cbc5952 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -209,7 +209,10 @@ namespace Umbraco.Web.Editors } long total; - var children = Services.MediaService.GetPagedChildren(id, pageNumber - 1, pageSize, out total, "Name", Direction.Ascending, true, null, folderTypes.ToArray()); + var children = Services.MediaService.GetPagedChildren(id, pageNumber - 1, pageSize, out total, + //lookup these content types + SqlContext.Query().Where(x => folderTypes.Contains(x.ContentTypeId)), + Ordering.By("Name", Direction.Ascending)); return new PagedResult>(total, pageNumber, pageSize) { @@ -286,8 +289,8 @@ namespace Umbraco.Web.Editors .GetPagedChildren( id, (pageNumber - 1), pageSize, out totalChildren, - orderBy, orderDirection, orderBySystemField, - queryFilter).ToList(); + queryFilter, + Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField)).ToList(); } else { @@ -762,12 +765,10 @@ namespace Umbraco.Web.Editors var total = long.MaxValue; while (page * pageSize < total) { - var children = Services.MediaService.GetPagedChildren(mediaId, page, pageSize, out total); + var children = Services.MediaService.GetPagedChildren(mediaId, page, pageSize, out total, + SqlContext.Query().Where(x => x.Name == nameToFind)); foreach (var c in children) - { - if (c.Name == nameToFind && c.ContentType.Alias == contentTypeAlias) - return c; - } + return c; //return first one if any are found } return null; } From b2c3944074294b062fc8eff33f6fa0fc7107a01e Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Wed, 31 Oct 2018 13:47:07 +0000 Subject: [PATCH 47/53] Remove a block of commented out code - that was forgotten --- src/Umbraco.Web/Trees/ContentTreeController.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/Umbraco.Web/Trees/ContentTreeController.cs b/src/Umbraco.Web/Trees/ContentTreeController.cs index 1667299436..04c356a4fd 100644 --- a/src/Umbraco.Web/Trees/ContentTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentTreeController.cs @@ -313,16 +313,6 @@ namespace Umbraco.Web.Trees menuItem.OpensDialog = opensDialog; } - ////fixme: Remove the need for converting to legacy - //private void AddActionNode(IUmbracoEntity item, MenuItemCollection menu, bool hasSeparator = false, bool convert = false) - // where TItem : MenuItem, new() - // where TAction : IAction - //{ - // //fixme: Inject - // var menuItem = menu.Items.Add(Services.TextService.Localize("actions", Current.Actions.GetAction().Alias), hasSeparator); - // if (convert) menuItem.ConvertLegacyMenuItem(item, "content", "content"); - //} - public IEnumerable Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null) { return _treeSearcher.ExamineSearch(Umbraco, query, UmbracoEntityTypes.Document, pageSize, pageIndex, out totalFound, searchFrom); From 6a1d6668c4e42651dc834483c6d980e8dc5c9a91 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 1 Nov 2018 01:01:50 +1100 Subject: [PATCH 48/53] Gets both media and member indexing working for when media/member types are changed --- src/Umbraco.Core/Services/IMediaService.cs | 25 ++- .../Services/Implement/MediaService.cs | 39 +++- .../Services/Implement/MemberService.cs | 6 +- src/Umbraco.Web/Search/ExamineComponent.cs | 191 ++++++++++++------ 4 files changed, 182 insertions(+), 79 deletions(-) diff --git a/src/Umbraco.Core/Services/IMediaService.cs b/src/Umbraco.Core/Services/IMediaService.cs index 6fa211f4f3..0c6e528dee 100644 --- a/src/Umbraco.Core/Services/IMediaService.cs +++ b/src/Umbraco.Core/Services/IMediaService.cs @@ -137,11 +137,28 @@ namespace Umbraco.Core.Services IQuery filter, Ordering ordering = null); /// - /// Gets a collection of objects by the Id of the + /// Gets paged documents of a content content /// - /// Id of the - /// An Enumerable list of objects - IEnumerable GetMediaOfMediaType(int id); + /// The page number. + /// The page number. + /// The page size. + /// Total number of documents. + /// Search text filter. + /// Ordering infos. + IEnumerable GetPagedOfType(int contentTypeId, long pageIndex, int pageSize, out long totalRecords, + IQuery filter, Ordering ordering = null); + + /// + /// Gets paged documents for specified content types + /// + /// The page number. + /// The page number. + /// The page size. + /// Total number of documents. + /// Search text filter. + /// Ordering infos. + IEnumerable GetPagedOfTypes(int[] contentTypeIds, long pageIndex, int pageSize, out long totalRecords, + IQuery filter, Ordering ordering = null); /// /// Gets a collection of objects, which reside at the first level / root diff --git a/src/Umbraco.Core/Services/Implement/MediaService.cs b/src/Umbraco.Core/Services/Implement/MediaService.cs index a584521c33..a69b39943b 100644 --- a/src/Umbraco.Core/Services/Implement/MediaService.cs +++ b/src/Umbraco.Core/Services/Implement/MediaService.cs @@ -364,18 +364,39 @@ namespace Umbraco.Core.Services.Implement } } - /// - /// Gets a collection of objects by the Id of the - /// - /// Id of the - /// An Enumerable list of objects - public IEnumerable GetMediaOfMediaType(int id) + /// + public IEnumerable GetPagedOfType(int contentTypeId, long pageIndex, int pageSize, out long totalRecords, IQuery filter, Ordering ordering = null) { + if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); + if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); + + if (ordering == null) + ordering = Ordering.By("sortOrder"); + using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { - scope.ReadLock(Constants.Locks.MediaTree); - var query = Query().Where(x => x.ContentTypeId == id); - return _mediaRepository.Get(query); + scope.ReadLock(Constants.Locks.ContentTree); + return _mediaRepository.GetPage( + Query().Where(x => x.ContentTypeId == contentTypeId), + pageIndex, pageSize, out totalRecords, filter, ordering); + } + } + + /// + public IEnumerable GetPagedOfTypes(int[] contentTypeIds, long pageIndex, int pageSize, out long totalRecords, IQuery filter, Ordering ordering = null) + { + if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); + if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); + + if (ordering == null) + ordering = Ordering.By("sortOrder"); + + using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + { + scope.ReadLock(Constants.Locks.ContentTree); + return _mediaRepository.GetPage( + Query().Where(x => contentTypeIds.Contains(x.ContentTypeId)), + pageIndex, pageSize, out totalRecords, filter, ordering); } } diff --git a/src/Umbraco.Core/Services/Implement/MemberService.cs b/src/Umbraco.Core/Services/Implement/MemberService.cs index 3fd714f974..5a644cfec1 100644 --- a/src/Umbraco.Core/Services/Implement/MemberService.cs +++ b/src/Umbraco.Core/Services/Implement/MemberService.cs @@ -393,12 +393,14 @@ namespace Umbraco.Core.Services.Implement // fixme get rid of string filter? - public IEnumerable GetAll(long pageIndex, int pageSize, out long totalRecords, string orderBy, Direction orderDirection, string memberTypeAlias = null, string filter = "") + public IEnumerable GetAll(long pageIndex, int pageSize, out long totalRecords, + string orderBy, Direction orderDirection, string memberTypeAlias = null, string filter = "") { return GetAll(pageIndex, pageSize, out totalRecords, orderBy, orderDirection, true, memberTypeAlias, filter); } - public IEnumerable GetAll(long pageIndex, int pageSize, out long totalRecords, string orderBy, Direction orderDirection, bool orderBySystemField, string memberTypeAlias, string filter) + public IEnumerable GetAll(long pageIndex, int pageSize, out long totalRecords, + string orderBy, Direction orderDirection, bool orderBySystemField, string memberTypeAlias, string filter) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index b5cfac7e99..7a036ef712 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -333,92 +333,155 @@ namespace Umbraco.Web.Search if (args.MessageType != MessageType.RefreshByPayload) throw new NotSupportedException(); - - var contentService = _services.ContentService; - - var removedIds = new List(); - var refreshedIds = new List(); - var otherIds = new List(); - + + var changedIds = new Dictionary removedIds, List refreshedIds, List otherIds)>(); + foreach (var payload in (ContentTypeCacheRefresher.JsonPayload[])args.MessageObject) { + if (!changedIds.TryGetValue(payload.ItemType, out var idLists)) + { + idLists = (removedIds: new List(), refreshedIds: new List(), otherIds: new List()); + changedIds.Add(payload.ItemType, idLists); + } + if (payload.ChangeTypes.HasType(ContentTypeChangeTypes.Remove)) - removedIds.Add(payload.Id); + idLists.removedIds.Add(payload.Id); else if (payload.ChangeTypes.HasType(ContentTypeChangeTypes.RefreshMain)) - refreshedIds.Add(payload.Id); + idLists.refreshedIds.Add(payload.Id); else if (payload.ChangeTypes.HasType(ContentTypeChangeTypes.RefreshOther)) - otherIds.Add(payload.Id); + idLists.otherIds.Add(payload.Id); } const int pageSize = 500; - if (refreshedIds.Count > 0 || otherIds.Count > 0) - { + foreach(var ci in changedIds) + { + if (ci.Value.refreshedIds.Count > 0 || ci.Value.otherIds.Count > 0) + { + switch(ci.Key) + { + case var itemType when itemType == typeof(IContentType).Name: + RefreshContentOfContentTypes(ci.Value.refreshedIds.Concat(ci.Value.otherIds).Distinct().ToArray()); + break; + case var itemType when itemType == typeof(IMediaType).Name: + RefreshMediaOfMediaTypes(ci.Value.refreshedIds.Concat(ci.Value.otherIds).Distinct().ToArray()); + break; + case var itemType when itemType == typeof(IMemberType).Name: + RefreshMemberOfMemberTypes(ci.Value.refreshedIds.Concat(ci.Value.otherIds).Distinct().ToArray()); + break; + } + } + + //Delete all content of this content/media/member type that is in any content indexer by looking up matched examine docs + foreach (var id in ci.Value.removedIds) + { + foreach (var index in _examineManager.IndexProviders.Values.OfType()) + { + var searcher = index.GetSearcher(); + + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + //paging with examine, see https://shazwazza.com/post/paging-with-examine/ + var results = searcher.Search( + searcher.CreateCriteria().Field("nodeType", id).Compile(), + maxResults: pageSize * (page + 1)); + total = results.TotalItemCount; + var paged = results.Skip(page * pageSize); + + foreach (var item in paged) + if (int.TryParse(item.Id, out var contentId)) + DeleteIndexForEntity(contentId, false); + } + } + } + } + } + + private void RefreshMemberOfMemberTypes(int[] memberTypeIds) + { + const int pageSize = 500; + + var memberTypes = _services.MemberTypeService.GetAll(memberTypeIds); + foreach(var memberType in memberTypes) + { var page = 0; var total = long.MaxValue; while (page * pageSize < total) { - var contentToRefresh = _services.ContentService.GetPagedOfTypes( - //Re-index all content of these types - refreshedIds.Concat(otherIds).Distinct().ToArray(), - page++, pageSize, out total, null, - //order by shallowest to deepest, this allows us to check it's published state without checking every item - Ordering.By("Path", Direction.Ascending)); + var memberToRefresh = _services.MemberService.GetAll( + page++, pageSize, out total, "LoginName", Direction.Ascending, + memberType.Alias); - //track which Ids have their paths are published - var publishChecked = new Dictionary(); - - foreach (var c in contentToRefresh) + foreach (var c in memberToRefresh) { - IContent published = null; - if (c.Published) - { - if (publishChecked.TryGetValue(c.ParentId, out var isPublished)) - { - //if the parent's published path has already been verified then this is published - if (isPublished) - published = c; - } - else - { - //nothing by parent id, so query the service and cache the result for the next child to check against - isPublished = contentService.IsPathPublished(c); - publishChecked[c.Id] = isPublished; - if (isPublished) - published = c; - } - } - - ReIndexForContent(c, published); + ReIndexForMember(c); } } } + } - //Delete all content of this content type that is in any content indexer by looking up matched examine docs - foreach(var id in removedIds) + private void RefreshMediaOfMediaTypes(int[] mediaTypeIds) + { + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) { - foreach(var index in _examineManager.IndexProviders.Values.OfType()) + var mediaToRefresh = _services.MediaService.GetPagedOfTypes( + //Re-index all content of these types + mediaTypeIds, + page++, pageSize, out total, null, + Ordering.By("Path", Direction.Ascending)); + + foreach (var c in mediaToRefresh) { - var searcher = index.GetSearcher(); - - var page = 0; - var total = long.MaxValue; - while (page * pageSize < total) - { - //paging with examine, see https://shazwazza.com/post/paging-with-examine/ - var results = searcher.Search( - searcher.CreateCriteria().Field("nodeType", id).Compile(), - maxResults: pageSize * (page + 1)); - total = results.TotalItemCount; - var paged = results.Skip(page * pageSize); - - foreach(var item in paged) - if (int.TryParse(item.Id, out var contentId)) - DeleteIndexForEntity(contentId, false); - } + ReIndexForMedia(c, c.Trashed == false); + } + } + } + + private void RefreshContentOfContentTypes(int[] contentTypeIds) + { + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + var contentToRefresh = _services.ContentService.GetPagedOfTypes( + //Re-index all content of these types + contentTypeIds, + page++, pageSize, out total, null, + //order by shallowest to deepest, this allows us to check it's published state without checking every item + Ordering.By("Path", Direction.Ascending)); + + //track which Ids have their paths are published + var publishChecked = new Dictionary(); + + foreach (var c in contentToRefresh) + { + IContent published = null; + if (c.Published) + { + if (publishChecked.TryGetValue(c.ParentId, out var isPublished)) + { + //if the parent's published path has already been verified then this is published + if (isPublished) + published = c; + } + else + { + //nothing by parent id, so query the service and cache the result for the next child to check against + isPublished = _services.ContentService.IsPathPublished(c); + publishChecked[c.Id] = isPublished; + if (isPublished) + published = c; + } + } + + ReIndexForContent(c, published); } - - } } From 082d860a5b2f97319d25db61afdaf0c34fca2f9b Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 1 Nov 2018 01:28:12 +1100 Subject: [PATCH 49/53] fixes tests --- src/Umbraco.Core/Services/Implement/ContentService.cs | 1 - src/Umbraco.Core/Services/Implement/MediaService.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index debf7dcb34..64e536b7e2 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -634,7 +634,6 @@ namespace Umbraco.Core.Services.Implement { if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); - if (filter == null) throw new ArgumentNullException(nameof(filter)); if (ordering == null) throw new ArgumentNullException(nameof(ordering)); var query = Query(); diff --git a/src/Umbraco.Core/Services/Implement/MediaService.cs b/src/Umbraco.Core/Services/Implement/MediaService.cs index a69b39943b..a41d12ac59 100644 --- a/src/Umbraco.Core/Services/Implement/MediaService.cs +++ b/src/Umbraco.Core/Services/Implement/MediaService.cs @@ -595,7 +595,6 @@ namespace Umbraco.Core.Services.Implement { if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); - if (filter == null) throw new ArgumentNullException(nameof(filter)); if (ordering == null) throw new ArgumentNullException(nameof(ordering)); var query = Query(); From f15d30b1b491e39fb8fd61a4a4a598ef775818aa Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Sun, 28 Oct 2018 12:51:17 +0100 Subject: [PATCH 50/53] Assign the current main culture to GetChildren lookup if none is defined, as this is required for variant data --- .../src/common/resources/content.resource.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js index 64c2b127e9..2475353d94 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js @@ -23,7 +23,7 @@ * **/ -function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { +function contentResource($q, $http, $routeParams, umbDataFormatter, umbRequestHelper) { /** internal method process the saving of data and post processing the result */ function saveContentItem(content, action, files, restApiUrl, showNotifications) { @@ -607,7 +607,10 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { else if (options.orderDirection === "desc") { options.orderDirection = "Descending"; } - + if (!options.cultureName) { + // must send a culture to the content API to handle variant data correctly + options.cultureName = $routeParams.mculture; + } //converts the value to a js bool function toBool(v) { if (angular.isNumber(v)) { From 76a8cd6dcf59ed61d4674d6385fb74ffa68cff82 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 1 Nov 2018 10:22:45 +1100 Subject: [PATCH 51/53] Changes recycle bin service queries to paged --- src/Umbraco.Core/Services/IContentService.cs | 4 ++-- src/Umbraco.Core/Services/IMediaService.cs | 3 ++- .../Services/Implement/ContentService.cs | 8 ++++++-- src/Umbraco.Core/Services/Implement/MediaService.cs | 13 +++++++------ .../Persistence/NPocoTests/PetaPocoCachesTest.cs | 2 -- src/Umbraco.Tests/Services/ContentServiceTests.cs | 8 ++++---- 6 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index c609a7493d..ccf88a7b1e 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -141,11 +141,11 @@ namespace Umbraco.Core.Services /// IEnumerable GetContentForRelease(); - //fixme: should be paged /// /// Gets documents in the recycle bin. /// - IEnumerable GetContentInRecycleBin(); + IEnumerable GetPagedContentInRecycleBin(long pageIndex, int pageSize, out long totalRecords, + IQuery filter = null, Ordering ordering = null); /// /// Gets child documents of a parent. diff --git a/src/Umbraco.Core/Services/IMediaService.cs b/src/Umbraco.Core/Services/IMediaService.cs index 0c6e528dee..2476af94dd 100644 --- a/src/Umbraco.Core/Services/IMediaService.cs +++ b/src/Umbraco.Core/Services/IMediaService.cs @@ -170,7 +170,8 @@ namespace Umbraco.Core.Services /// Gets a collection of an objects, which resides in the Recycle Bin /// /// An Enumerable list of objects - IEnumerable GetMediaInRecycleBin(); + IEnumerable GetPagedMediaInRecycleBin(long pageIndex, int pageSize, out long totalRecords, + IQuery filter = null, Ordering ordering = null); /// /// Moves an object to a new location diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index 64e536b7e2..63d6167f39 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -727,13 +727,17 @@ namespace Umbraco.Core.Services.Implement /// Gets a collection of an objects, which resides in the Recycle Bin /// /// An Enumerable list of objects - public IEnumerable GetContentInRecycleBin() + public IEnumerable GetPagedContentInRecycleBin(long pageIndex, int pageSize, out long totalRecords, + IQuery filter = null, Ordering ordering = null) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { + if (ordering == null) + ordering = Ordering.By("Path"); + scope.ReadLock(Constants.Locks.ContentTree); var query = Query().Where(x => x.Path.StartsWith(Constants.System.RecycleBinContentPathPrefix)); - return _documentRepository.Get(query); + return _documentRepository.GetPage(query, pageIndex, pageSize, out totalRecords, filter, ordering); } } diff --git a/src/Umbraco.Core/Services/Implement/MediaService.cs b/src/Umbraco.Core/Services/Implement/MediaService.cs index a41d12ac59..4be75487b7 100644 --- a/src/Umbraco.Core/Services/Implement/MediaService.cs +++ b/src/Umbraco.Core/Services/Implement/MediaService.cs @@ -643,17 +643,18 @@ namespace Umbraco.Core.Services.Implement } } - /// - /// Gets a collection of an objects, which resides in the Recycle Bin - /// - /// An Enumerable list of objects - public IEnumerable GetMediaInRecycleBin() + /// + public IEnumerable GetPagedMediaInRecycleBin(long pageIndex, int pageSize, out long totalRecords, + IQuery filter = null, Ordering ordering = null) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { + if (ordering == null) + ordering = Ordering.By("Path"); + scope.ReadLock(Constants.Locks.MediaTree); var query = Query().Where(x => x.Path.StartsWith(Constants.System.RecycleBinMediaPathPrefix)); - return _mediaRepository.Get(query); + return _mediaRepository.GetPage(query, pageIndex, pageSize, out totalRecords, filter, ordering); } } diff --git a/src/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs b/src/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs index b1ebee108a..570fbfb7e3 100644 --- a/src/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs +++ b/src/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs @@ -133,8 +133,6 @@ namespace Umbraco.Tests.Persistence.NPocoTests contentService.GetContentForRelease(); - contentService.GetContentInRecycleBin(); - ((ContentService)contentService).GetPublishedDescendants(new Content("Test", -1, new ContentType(-1)) { Id = id1, diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index c2b1cdc52d..b22a1dfd6a 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -1172,7 +1172,7 @@ namespace Umbraco.Tests.Services var contentService = ServiceContext.ContentService; // Act - var contents = contentService.GetContentInRecycleBin().ToList(); + var contents = contentService.GetPagedContentInRecycleBin(0, int.MaxValue, out var _).ToList(); // Assert Assert.That(contents, Is.Not.Null); @@ -1792,7 +1792,7 @@ namespace Umbraco.Tests.Services Assert.True(descendants.All(x => x.Trashed)); contentService.EmptyRecycleBin(); - var trashed = contentService.GetContentInRecycleBin(); + var trashed = contentService.GetPagedContentInRecycleBin(0, int.MaxValue, out var _).ToList(); Assert.IsEmpty(trashed); } @@ -1804,7 +1804,7 @@ namespace Umbraco.Tests.Services // Act contentService.EmptyRecycleBin(); - var contents = contentService.GetContentInRecycleBin(); + var contents = contentService.GetPagedContentInRecycleBin(0, int.MaxValue, out var _).ToList(); // Assert Assert.That(contents.Any(), Is.False); @@ -1982,7 +1982,7 @@ namespace Umbraco.Tests.Services // Act ServiceContext.ContentService.MoveToRecycleBin(content1); ServiceContext.ContentService.EmptyRecycleBin(); - var contents = ServiceContext.ContentService.GetContentInRecycleBin(); + var contents = ServiceContext.ContentService.GetPagedContentInRecycleBin(0, int.MaxValue, out var _).ToList(); // Assert Assert.That(contents.Any(), Is.False); From 047bce17bf3dc85ae9522b63ac2abe87586577b1 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 1 Nov 2018 10:28:53 +1100 Subject: [PATCH 52/53] Cleans up more IContentService and IMediaService and removes any string filtering methods --- src/Umbraco.Core/Services/IContentService.cs | 29 +------ src/Umbraco.Core/Services/IMediaService.cs | 36 +-------- .../Services/Implement/ContentService.cs | 29 +------ .../Services/Implement/MediaService.cs | 76 ++----------------- .../UmbracoExamine/IndexInitializer.cs | 9 --- 5 files changed, 16 insertions(+), 163 deletions(-) diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index ccf88a7b1e..6cfc923ebe 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -147,18 +147,6 @@ namespace Umbraco.Core.Services IEnumerable GetPagedContentInRecycleBin(long pageIndex, int pageSize, out long totalRecords, IQuery filter = null, Ordering ordering = null); - /// - /// Gets child documents of a parent. - /// - /// The parent identifier. - /// The page number. - /// The page size. - /// Total number of documents. - /// Search text filter. - /// Ordering infos. - IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalRecords, - string filter = null, Ordering ordering = null); - /// /// Gets child documents of a parent. /// @@ -169,20 +157,7 @@ namespace Umbraco.Core.Services /// Query filter. /// Ordering infos. IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalRecords, - IQuery filter, Ordering ordering = null); - - /// - /// Gets descendant documents of a given parent. - /// - /// The parent identifier. - /// The page number. - /// The page size. - /// Total number of documents. - /// A field to order by. - /// The ordering direction. - /// Search text filter. - IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords, - string filter = null, Ordering ordering = null); + IQuery filter = null, Ordering ordering = null); /// /// Gets descendant documents of a given parent. @@ -196,7 +171,7 @@ namespace Umbraco.Core.Services /// A flag indicating whether the ordering field is a system field. /// Query filter. IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords, - IQuery filter, Ordering ordering = null); + IQuery filter = null, Ordering ordering = null); /// /// Gets paged documents of a content content diff --git a/src/Umbraco.Core/Services/IMediaService.cs b/src/Umbraco.Core/Services/IMediaService.cs index 2476af94dd..9cc559ccd5 100644 --- a/src/Umbraco.Core/Services/IMediaService.cs +++ b/src/Umbraco.Core/Services/IMediaService.cs @@ -78,20 +78,6 @@ namespace Umbraco.Core.Services /// IMedia GetById(int id); - /// - /// Gets a collection of objects by Parent Id - /// - /// Id of the Parent to retrieve Children from - /// Page number - /// Page size - /// Total records query would return without paging - /// Field to order by - /// Direction to order by - /// Search text filter - /// An Enumerable list of objects - IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalRecords, - string filter = null, Ordering ordering = null); - /// /// Gets a collection of objects by Parent Id /// @@ -105,21 +91,7 @@ namespace Umbraco.Core.Services /// /// An Enumerable list of objects IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalRecords, - IQuery filter, Ordering ordering = null); - - /// - /// Gets a collection of objects by Parent Id - /// - /// Id of the Parent to retrieve Descendants from - /// Page number - /// Page size - /// Total records query would return without paging - /// Field to order by - /// Direction to order by - /// Search text filter - /// An Enumerable list of objects - IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords, - string filter = null, Ordering ordering = null); + IQuery filter = null, Ordering ordering = null); /// /// Gets a collection of objects by Parent Id @@ -134,7 +106,7 @@ namespace Umbraco.Core.Services /// /// An Enumerable list of objects IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords, - IQuery filter, Ordering ordering = null); + IQuery filter = null, Ordering ordering = null); /// /// Gets paged documents of a content content @@ -146,7 +118,7 @@ namespace Umbraco.Core.Services /// Search text filter. /// Ordering infos. IEnumerable GetPagedOfType(int contentTypeId, long pageIndex, int pageSize, out long totalRecords, - IQuery filter, Ordering ordering = null); + IQuery filter = null, Ordering ordering = null); /// /// Gets paged documents for specified content types @@ -158,7 +130,7 @@ namespace Umbraco.Core.Services /// Search text filter. /// Ordering infos. IEnumerable GetPagedOfTypes(int[] contentTypeIds, long pageIndex, int pageSize, out long totalRecords, - IQuery filter, Ordering ordering = null); + IQuery filter = null, Ordering ordering = null); /// /// Gets a collection of objects, which reside at the first level / root diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index 63d6167f39..386c76db88 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -406,7 +406,8 @@ namespace Umbraco.Core.Services.Implement } /// - public IEnumerable GetPagedOfType(int contentTypeId, long pageIndex, int pageSize, out long totalRecords, IQuery filter, Ordering ordering = null) + public IEnumerable GetPagedOfType(int contentTypeId, long pageIndex, int pageSize, out long totalRecords + , IQuery filter = null, Ordering ordering = null) { if(pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); @@ -564,18 +565,7 @@ namespace Umbraco.Core.Services.Implement /// public IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalChildren, - string filter = null, Ordering ordering = null) - { - var filterQuery = filter.IsNullOrWhiteSpace() - ? null - : Query().Where(x => x.Name.Contains(filter)); - - return GetPagedChildren(id, pageIndex, pageSize, out totalChildren, filterQuery, ordering); - } - - /// - public IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalChildren, - IQuery filter, Ordering ordering = null) + IQuery filter = null, Ordering ordering = null) { if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); @@ -594,18 +584,7 @@ namespace Umbraco.Core.Services.Implement /// public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, - string filter = null, Ordering ordering = null) - { - var filterQuery = filter.IsNullOrWhiteSpace() - ? null - : Query().Where(x => x.Name.Contains(filter)); - - return GetPagedDescendants(id, pageIndex, pageSize, out totalChildren, filterQuery, ordering); - } - - /// - public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, - IQuery filter, Ordering ordering = null) + IQuery filter = null, Ordering ordering = null) { if (ordering == null) ordering = Ordering.By("Path"); diff --git a/src/Umbraco.Core/Services/Implement/MediaService.cs b/src/Umbraco.Core/Services/Implement/MediaService.cs index 4be75487b7..5f35e35acf 100644 --- a/src/Umbraco.Core/Services/Implement/MediaService.cs +++ b/src/Umbraco.Core/Services/Implement/MediaService.cs @@ -365,7 +365,7 @@ namespace Umbraco.Core.Services.Implement } /// - public IEnumerable GetPagedOfType(int contentTypeId, long pageIndex, int pageSize, out long totalRecords, IQuery filter, Ordering ordering = null) + public IEnumerable GetPagedOfType(int contentTypeId, long pageIndex, int pageSize, out long totalRecords, IQuery filter = null, Ordering ordering = null) { if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); @@ -383,7 +383,7 @@ namespace Umbraco.Core.Services.Implement } /// - public IEnumerable GetPagedOfTypes(int[] contentTypeIds, long pageIndex, int pageSize, out long totalRecords, IQuery filter, Ordering ordering = null) + public IEnumerable GetPagedOfTypes(int[] contentTypeIds, long pageIndex, int pageSize, out long totalRecords, IQuery filter = null, Ordering ordering = null) { if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); @@ -481,41 +481,9 @@ namespace Umbraco.Core.Services.Implement } } - /// - /// Gets a collection of objects by Parent Id - /// - /// Id of the Parent to retrieve Children from - /// Page index (zero based) - /// Page size - /// Total records query would return without paging - /// Field to order by - /// Direction to order by - /// Search text filter - /// An Enumerable list of objects + /// public IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalChildren, - string filter = null, Ordering ordering = null) - { - var filterQuery = filter.IsNullOrWhiteSpace() - ? null - : Query().Where(x => x.Name.Contains(filter)); - - return GetPagedChildren(id, pageIndex, pageSize, out totalChildren, filterQuery, ordering); - } - - /// - /// Gets a collection of objects by Parent Id - /// - /// Id of the Parent to retrieve Children from - /// Page index (zero based) - /// Page size - /// Total records query would return without paging - /// Field to order by - /// Direction to order by - /// Flag to indicate when ordering by system field - /// - /// An Enumerable list of objects - public IEnumerable GetPagedChildren(int id, long pageIndex, int pageSize, out long totalChildren, - IQuery filter, Ordering ordering = null) + IQuery filter = null, Ordering ordering = null) { if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); @@ -532,41 +500,9 @@ namespace Umbraco.Core.Services.Implement } } - /// - /// Gets a collection of objects by Parent Id - /// - /// Id of the Parent to retrieve Descendants from - /// Page number - /// Page size - /// Total records query would return without paging - /// Field to order by - /// Direction to order by - /// Search text filter - /// An Enumerable list of objects + /// public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, - string filter = null, Ordering ordering = null) - { - var filterQuery = filter.IsNullOrWhiteSpace() - ? null - : Query().Where(x => x.Name.Contains(filter)); - - return GetPagedDescendants(id, pageIndex, pageSize, out totalChildren, filterQuery, ordering); - } - - /// - /// Gets a collection of objects by Parent Id - /// - /// Id of the Parent to retrieve Descendants from - /// Page number - /// Page size - /// Total records query would return without paging - /// Field to order by - /// Direction to order by - /// Flag to indicate when ordering by system field - /// - /// An Enumerable list of objects - public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, - IQuery filter, Ordering ordering = null) + IQuery filter = null, Ordering ordering = null) { if (ordering == null) ordering = Ordering.By("Path"); diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index 8aec854441..8b57e10849 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -71,10 +71,6 @@ namespace Umbraco.Tests.UmbracoExamine contentService = Mock.Of( x => x.GetPagedDescendants( - It.IsAny(), It.IsAny(), It.IsAny(), out longTotalRecs, It.IsAny(), It.IsAny()) - == - allRecs - && x.GetPagedDescendants( It.IsAny(), It.IsAny(), It.IsAny(), out longTotalRecs, It.IsAny>(), It.IsAny()) == allRecs); @@ -114,11 +110,6 @@ namespace Umbraco.Tests.UmbracoExamine // MOCK! var mediaServiceMock = new Mock(); - mediaServiceMock - .Setup(x => x.GetPagedDescendants( - It.IsAny(), It.IsAny(), It.IsAny(), out totalRecs, It.IsAny(), It.IsAny()) - ).Returns(() => allRecs); - mediaServiceMock .Setup(x => x.GetPagedDescendants( It.IsAny(), It.IsAny(), It.IsAny(), out totalRecs, It.IsAny>(), It.IsAny()) From 0e8bac0b0907456da24e517fa36497450a71f237 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 1 Nov 2018 11:24:03 +1100 Subject: [PATCH 53/53] fixes member indexing with list object --- src/Umbraco.Examine/UmbracoMemberIndexer.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Examine/UmbracoMemberIndexer.cs b/src/Umbraco.Examine/UmbracoMemberIndexer.cs index b7cbcc19bc..82bf3b9cf6 100644 --- a/src/Umbraco.Examine/UmbracoMemberIndexer.cs +++ b/src/Umbraco.Examine/UmbracoMemberIndexer.cs @@ -175,13 +175,17 @@ namespace Umbraco.Examine if (e.IndexItem.ValueSet.Values.TryGetValue("key", out var key) && e.IndexItem.ValueSet.Values.ContainsKey("__key") == false) { //double __ prefix means it will be indexed as culture invariant - e.IndexItem.ValueSet.Values["__key"] = new List { key }; + e.IndexItem.ValueSet.Values["__key"] = key; } if (e.IndexItem.ValueSet.Values.TryGetValue("email", out var email) && e.IndexItem.ValueSet.Values.ContainsKey("_searchEmail") == false) { - //will be indexed as full text (the default anaylyzer) - e.IndexItem.ValueSet.Values["_searchEmail"] = new List { email?.ToString().Replace(".", " ").Replace("@", " ") }; + if (email.Count > 0) + { + //will be indexed as full text (the default anaylyzer) + e.IndexItem.ValueSet.Values["_searchEmail"] = new List { email[0]?.ToString().Replace(".", " ").Replace("@", " ") }; + } + } }