diff --git a/src/Umbraco.Core/PropertyEditors/ContentPickerPropertyEditor.cs b/src/Umbraco.Core/PropertyEditors/ContentPickerPropertyEditor.cs index 6cd7645868..635b590ba4 100644 --- a/src/Umbraco.Core/PropertyEditors/ContentPickerPropertyEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/ContentPickerPropertyEditor.cs @@ -21,7 +21,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; "Content Picker", "contentpicker", ValueType = ValueTypes.String, - Group = Constants.PropertyEditors.Groups.Pickers)] + Group = Constants.PropertyEditors.Groups.Pickers, + ValueEditorIsReusable = true)] public class ContentPickerPropertyEditor : DataEditor { private readonly IEditorConfigurationParser _editorConfigurationParser; diff --git a/src/Umbraco.Core/PropertyEditors/DataEditor.cs b/src/Umbraco.Core/PropertyEditors/DataEditor.cs index b2b95f475b..3009e8af62 100644 --- a/src/Umbraco.Core/PropertyEditors/DataEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/DataEditor.cs @@ -20,6 +20,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; [DataContract] public class DataEditor : IDataEditor { + private readonly bool _canReuseValueEditor; + private IDataValueEditor? _reusableValueEditor; private IDictionary? _defaultConfiguration; /// @@ -48,6 +50,8 @@ public class DataEditor : IDataEditor Icon = Attribute.Icon; Group = Attribute.Group; IsDeprecated = Attribute.IsDeprecated; + + _canReuseValueEditor = Attribute.ValueEditorIsReusable; } /// @@ -118,18 +122,14 @@ public class DataEditor : IDataEditor /// instance is returned. Otherwise, a new instance is created by CreateValueEditor. /// /// - /// The instance created by CreateValueEditor is not cached, i.e. - /// a new instance is created each time the property value is retrieved. The - /// property editor is a singleton, and the value editor cannot be a singleton - /// since it depends on the datatype configuration. - /// - /// - /// Technically, it could be cached by datatype but let's keep things - /// simple enough for now. + /// The instance created by CreateValueEditor is cached if allowed by the DataEditor + /// attribute ( == true). /// /// - // TODO: point of that one? shouldn't we always configure? - public IDataValueEditor GetValueEditor() => ExplicitValueEditor ?? CreateValueEditor(); + public IDataValueEditor GetValueEditor() => ExplicitValueEditor + ?? (_canReuseValueEditor + ? _reusableValueEditor ??= CreateValueEditor() + : CreateValueEditor()); /// /// diff --git a/src/Umbraco.Core/PropertyEditors/DataEditorAttribute.cs b/src/Umbraco.Core/PropertyEditors/DataEditorAttribute.cs index ce15c66a80..d9164d07ab 100644 --- a/src/Umbraco.Core/PropertyEditors/DataEditorAttribute.cs +++ b/src/Umbraco.Core/PropertyEditors/DataEditorAttribute.cs @@ -178,4 +178,10 @@ public sealed class DataEditorAttribute : Attribute /// /// A deprecated editor is still supported but not proposed in the UI. public bool IsDeprecated { get; set; } + + /// + /// Gets or sets a value indicating whether the value editor can be reused (cached). + /// + /// While most value editors can be reused, complex editors (e.g. block based editors) might not be applicable for reuse. + public bool ValueEditorIsReusable { get; set; } } diff --git a/src/Umbraco.Core/PropertyEditors/DecimalPropertyEditor.cs b/src/Umbraco.Core/PropertyEditors/DecimalPropertyEditor.cs index a936a72512..a6fa0633d7 100644 --- a/src/Umbraco.Core/PropertyEditors/DecimalPropertyEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/DecimalPropertyEditor.cs @@ -11,7 +11,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; EditorType.PropertyValue | EditorType.MacroParameter, "Decimal", "decimal", - ValueType = ValueTypes.Decimal)] + ValueType = ValueTypes.Decimal, + ValueEditorIsReusable = true)] public class DecimalPropertyEditor : DataEditor { /// diff --git a/src/Umbraco.Core/PropertyEditors/EyeDropperColorPickerPropertyEditor.cs b/src/Umbraco.Core/PropertyEditors/EyeDropperColorPickerPropertyEditor.cs index 12b1b2c8ef..c9e8545b68 100644 --- a/src/Umbraco.Core/PropertyEditors/EyeDropperColorPickerPropertyEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/EyeDropperColorPickerPropertyEditor.cs @@ -11,7 +11,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; "Eye Dropper Color Picker", "eyedropper", Icon = "icon-colorpicker", - Group = Constants.PropertyEditors.Groups.Pickers)] + Group = Constants.PropertyEditors.Groups.Pickers, + ValueEditorIsReusable = true)] public class EyeDropperColorPickerPropertyEditor : DataEditor { private readonly IEditorConfigurationParser _editorConfigurationParser; diff --git a/src/Umbraco.Core/PropertyEditors/IntegerPropertyEditor.cs b/src/Umbraco.Core/PropertyEditors/IntegerPropertyEditor.cs index a504c7df31..6910912c51 100644 --- a/src/Umbraco.Core/PropertyEditors/IntegerPropertyEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/IntegerPropertyEditor.cs @@ -11,7 +11,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; EditorType.PropertyValue | EditorType.MacroParameter, "Numeric", "integer", - ValueType = ValueTypes.Integer)] + ValueType = ValueTypes.Integer, + ValueEditorIsReusable = true)] public class IntegerPropertyEditor : DataEditor { public IntegerPropertyEditor( diff --git a/src/Umbraco.Core/PropertyEditors/LabelPropertyEditor.cs b/src/Umbraco.Core/PropertyEditors/LabelPropertyEditor.cs index ae2f4c0897..eb4c96552f 100644 --- a/src/Umbraco.Core/PropertyEditors/LabelPropertyEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/LabelPropertyEditor.cs @@ -18,7 +18,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; Constants.PropertyEditors.Aliases.Label, "Label", "readonlyvalue", - Icon = "icon-readonly")] + Icon = "icon-readonly", + ValueEditorIsReusable = true)] public class LabelPropertyEditor : DataEditor { private readonly IEditorConfigurationParser _editorConfigurationParser; diff --git a/src/Umbraco.Core/PropertyEditors/MarkdownPropertyEditor.cs b/src/Umbraco.Core/PropertyEditors/MarkdownPropertyEditor.cs index aa6e881aa2..4bc17c8cfc 100644 --- a/src/Umbraco.Core/PropertyEditors/MarkdownPropertyEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/MarkdownPropertyEditor.cs @@ -17,7 +17,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; "markdowneditor", ValueType = ValueTypes.Text, Group = Constants.PropertyEditors.Groups.RichContent, - Icon = "icon-code")] + Icon = "icon-code", + ValueEditorIsReusable = true)] public class MarkdownPropertyEditor : DataEditor { private readonly IEditorConfigurationParser _editorConfigurationParser; diff --git a/src/Umbraco.Core/PropertyEditors/MemberGroupPickerPropertyEditor.cs b/src/Umbraco.Core/PropertyEditors/MemberGroupPickerPropertyEditor.cs index e839c0b527..dcb19624be 100644 --- a/src/Umbraco.Core/PropertyEditors/MemberGroupPickerPropertyEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/MemberGroupPickerPropertyEditor.cs @@ -6,7 +6,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; "membergrouppicker", ValueType = ValueTypes.Text, Group = Constants.PropertyEditors.Groups.People, - Icon = Constants.Icons.MemberGroup)] + Icon = Constants.Icons.MemberGroup, + ValueEditorIsReusable = true)] public class MemberGroupPickerPropertyEditor : DataEditor { public MemberGroupPickerPropertyEditor( diff --git a/src/Umbraco.Core/PropertyEditors/MemberPickerPropertyEditor.cs b/src/Umbraco.Core/PropertyEditors/MemberPickerPropertyEditor.cs index 241736737e..b16acaffb1 100644 --- a/src/Umbraco.Core/PropertyEditors/MemberPickerPropertyEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/MemberPickerPropertyEditor.cs @@ -6,7 +6,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; "memberpicker", ValueType = ValueTypes.String, Group = Constants.PropertyEditors.Groups.People, - Icon = Constants.Icons.Member)] + Icon = Constants.Icons.Member, + ValueEditorIsReusable = true)] public class MemberPickerPropertyEditor : DataEditor { public MemberPickerPropertyEditor( diff --git a/src/Umbraco.Core/PropertyEditors/UserPickerPropertyEditor.cs b/src/Umbraco.Core/PropertyEditors/UserPickerPropertyEditor.cs index 20bc2eb120..79f9c6795b 100644 --- a/src/Umbraco.Core/PropertyEditors/UserPickerPropertyEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/UserPickerPropertyEditor.cs @@ -6,7 +6,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; "userpicker", ValueType = ValueTypes.Integer, Group = Constants.PropertyEditors.Groups.People, - Icon = Constants.Icons.User)] + Icon = Constants.Icons.User, + ValueEditorIsReusable = true)] public class UserPickerPropertyEditor : DataEditor { public UserPickerPropertyEditor( diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs index b7d600ec7c..dd5b77abec 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs @@ -57,7 +57,7 @@ public static partial class UmbracoBuilderExtensions builder.Services.AddTransient(); builder.Services.AddUnique(); builder.Services.AddTransient(); - builder.Services.AddTransient(); + builder.Services.AddSingleton(); return builder; } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/BlockListPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/BlockListPropertyEditor.cs index 70a0aa35dc..f36d7b67ff 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/BlockListPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/BlockListPropertyEditor.cs @@ -17,7 +17,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; "blocklist", ValueType = ValueTypes.Json, Group = Constants.PropertyEditors.Groups.Lists, - Icon = "icon-thumbnail-list")] + Icon = "icon-thumbnail-list", + ValueEditorIsReusable = false)] public class BlockListPropertyEditor : BlockEditorPropertyEditor { private readonly IEditorConfigurationParser _editorConfigurationParser; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/CheckBoxListPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/CheckBoxListPropertyEditor.cs index 76a7fb5b6d..e64a7fe16c 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/CheckBoxListPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/CheckBoxListPropertyEditor.cs @@ -17,7 +17,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; "Checkbox list", "checkboxlist", Icon = "icon-bulleted-list", - Group = Constants.PropertyEditors.Groups.Lists)] + Group = Constants.PropertyEditors.Groups.Lists, + ValueEditorIsReusable = true)] public class CheckBoxListPropertyEditor : DataEditor { private readonly IEditorConfigurationParser _editorConfigurationParser; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ColorPickerPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ColorPickerPropertyEditor.cs index 1ff39654b1..1ce8ae4930 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ColorPickerPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ColorPickerPropertyEditor.cs @@ -14,7 +14,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; "Color Picker", "colorpicker", Icon = "icon-colorpicker", - Group = Constants.PropertyEditors.Groups.Pickers)] + Group = Constants.PropertyEditors.Groups.Pickers, + ValueEditorIsReusable = true)] public class ColorPickerPropertyEditor : DataEditor { private readonly IEditorConfigurationParser _editorConfigurationParser; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/DateTimePropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/DateTimePropertyEditor.cs index b6c55ebb6c..e4fedf37ea 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/DateTimePropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/DateTimePropertyEditor.cs @@ -18,7 +18,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; "Date/Time", "datepicker", ValueType = ValueTypes.DateTime, - Icon = "icon-time")] + Icon = "icon-time", + ValueEditorIsReusable = true)] public class DateTimePropertyEditor : DataEditor { private readonly IEditorConfigurationParser _editorConfigurationParser; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/DropDownFlexiblePropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/DropDownFlexiblePropertyEditor.cs index aca49d2f42..831f858fb8 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/DropDownFlexiblePropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/DropDownFlexiblePropertyEditor.cs @@ -14,7 +14,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; "Dropdown", "dropdownFlexible", Group = Constants.PropertyEditors.Groups.Lists, - Icon = "icon-indent")] + Icon = "icon-indent", + ValueEditorIsReusable = true)] public class DropDownFlexiblePropertyEditor : DataEditor { private readonly IEditorConfigurationParser _editorConfigurationParser; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/EmailAddressPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/EmailAddressPropertyEditor.cs index 1561c63e3c..6edcb61f4d 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/EmailAddressPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/EmailAddressPropertyEditor.cs @@ -12,7 +12,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; EditorType.PropertyValue | EditorType.MacroParameter, "Email address", "email", - Icon = "icon-message")] + Icon = "icon-message", + ValueEditorIsReusable = true)] public class EmailAddressPropertyEditor : DataEditor { private readonly IIOHelper _ioHelper; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs index 1e5972f41f..a2cf5ef6e9 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs @@ -21,7 +21,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; "File upload", "fileupload", Group = Constants.PropertyEditors.Groups.Media, - Icon = "icon-download-alt")] + Icon = "icon-download-alt", + ValueEditorIsReusable = true)] public class FileUploadPropertyEditor : DataEditor, IMediaUrlGenerator, INotificationHandler, INotificationHandler, INotificationHandler, INotificationHandler, diff --git a/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyEditor.cs index bd3f5423ee..d2281b0136 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyEditor.cs @@ -28,7 +28,8 @@ namespace Umbraco.Cms.Core.PropertyEditors HideLabel = true, ValueType = ValueTypes.Json, Icon = "icon-layout", - Group = Constants.PropertyEditors.Groups.RichContent)] + Group = Constants.PropertyEditors.Groups.RichContent, + ValueEditorIsReusable = false)] public class GridPropertyEditor : DataEditor { private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs index f7b966e3ad..c3390b3fc5 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs @@ -28,7 +28,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; ValueType = ValueTypes.Json, HideLabel = false, Group = Constants.PropertyEditors.Groups.Media, - Icon = "icon-crop")] + Icon = "icon-crop", + ValueEditorIsReusable = true)] public class ImageCropperPropertyEditor : DataEditor, IMediaUrlGenerator, INotificationHandler, INotificationHandler, INotificationHandler, INotificationHandler, diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ListViewPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ListViewPropertyEditor.cs index f027b9edd3..b1b7c5c034 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ListViewPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ListViewPropertyEditor.cs @@ -17,7 +17,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; "listview", HideLabel = true, Group = Constants.PropertyEditors.Groups.Lists, - Icon = Constants.Icons.ListView)] + Icon = Constants.Icons.ListView, + ValueEditorIsReusable = true)] public class ListViewPropertyEditor : DataEditor { private readonly IEditorConfigurationParser _editorConfigurationParser; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/MediaPicker3PropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MediaPicker3PropertyEditor.cs index 653c88f1c3..ed774f9215 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/MediaPicker3PropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MediaPicker3PropertyEditor.cs @@ -24,7 +24,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; "mediapicker3", ValueType = ValueTypes.Json, Group = Constants.PropertyEditors.Groups.Media, - Icon = Constants.Icons.MediaImage)] + Icon = Constants.Icons.MediaImage, + ValueEditorIsReusable = true)] public class MediaPicker3PropertyEditor : DataEditor { private readonly IEditorConfigurationParser _editorConfigurationParser; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/MediaPickerPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MediaPickerPropertyEditor.cs index ccc604ef72..5cbc8e91a0 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/MediaPickerPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MediaPickerPropertyEditor.cs @@ -26,7 +26,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; ValueType = ValueTypes.Text, Group = Constants.PropertyEditors.Groups.Media, Icon = Constants.Icons.MediaImage, - IsDeprecated = false)] + IsDeprecated = false, + ValueEditorIsReusable = true)] public class MediaPickerPropertyEditor : DataEditor { private readonly IEditorConfigurationParser _editorConfigurationParser; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs index 924f6b6940..1e20d8cfec 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs @@ -18,7 +18,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; "contentpicker", ValueType = ValueTypes.Text, Group = Constants.PropertyEditors.Groups.Pickers, - Icon = "icon-page-add")] + Icon = "icon-page-add", + ValueEditorIsReusable = true)] public class MultiNodeTreePickerPropertyEditor : DataEditor { private readonly IEditorConfigurationParser _editorConfigurationParser; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerPropertyEditor.cs index 4ffed0c1da..7387ab7808 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerPropertyEditor.cs @@ -16,7 +16,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; "multiurlpicker", ValueType = ValueTypes.Json, Group = Constants.PropertyEditors.Groups.Pickers, - Icon = "icon-link")] + Icon = "icon-link", + ValueEditorIsReusable = true)] public class MultiUrlPickerPropertyEditor : DataEditor { private readonly IEditorConfigurationParser _editorConfigurationParser; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/MultipleTextStringPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MultipleTextStringPropertyEditor.cs index e80da62e9b..4f25a54162 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/MultipleTextStringPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MultipleTextStringPropertyEditor.cs @@ -25,7 +25,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; "multipletextbox", ValueType = ValueTypes.Text, Group = Constants.PropertyEditors.Groups.Lists, - Icon = "icon-ordered-list")] + Icon = "icon-ordered-list", + ValueEditorIsReusable = true)] public class MultipleTextStringPropertyEditor : DataEditor { private readonly IEditorConfigurationParser _editorConfigurationParser; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs index 880c77134f..230c6e2b59 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs @@ -25,7 +25,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; "nestedcontent", ValueType = ValueTypes.Json, Group = Constants.PropertyEditors.Groups.Lists, - Icon = "icon-thumbnail-list")] + Icon = "icon-thumbnail-list", + ValueEditorIsReusable = false)] public class NestedContentPropertyEditor : DataEditor { public const string ContentTypeAliasPropertyKey = "ncContentTypeAlias"; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/RadioButtonsPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/RadioButtonsPropertyEditor.cs index 4fcfb04126..f121e665fe 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/RadioButtonsPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/RadioButtonsPropertyEditor.cs @@ -17,7 +17,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; "radiobuttons", ValueType = ValueTypes.String, Group = Constants.PropertyEditors.Groups.Lists, - Icon = "icon-target")] + Icon = "icon-target", + ValueEditorIsReusable = true)] public class RadioButtonsPropertyEditor : DataEditor { private readonly IEditorConfigurationParser _editorConfigurationParser; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs index 98f2d028ea..8525de17b6 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs @@ -29,7 +29,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; ValueType = ValueTypes.Text, HideLabel = false, Group = Constants.PropertyEditors.Groups.RichContent, - Icon = "icon-browser-window")] + Icon = "icon-browser-window", + ValueEditorIsReusable = true)] public class RichTextPropertyEditor : DataEditor { private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/SliderPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/SliderPropertyEditor.cs index 48bfb90a39..4ac27824ba 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/SliderPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/SliderPropertyEditor.cs @@ -15,7 +15,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; Constants.PropertyEditors.Aliases.Slider, "Slider", "slider", - Icon = "icon-navigation-horizontal")] + Icon = "icon-navigation-horizontal", + ValueEditorIsReusable = true)] public class SliderPropertyEditor : DataEditor { private readonly IEditorConfigurationParser _editorConfigurationParser; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/TagsPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/TagsPropertyEditor.cs index 8357db5b6b..88648c47fd 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/TagsPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/TagsPropertyEditor.cs @@ -23,7 +23,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; Constants.PropertyEditors.Aliases.Tags, "Tags", "tags", - Icon = "icon-tags")] + Icon = "icon-tags", + ValueEditorIsReusable = true)] public class TagsPropertyEditor : DataEditor { private readonly IEditorConfigurationParser _editorConfigurationParser; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/TextAreaPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/TextAreaPropertyEditor.cs index d72f3cb098..acc33a233b 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/TextAreaPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/TextAreaPropertyEditor.cs @@ -18,7 +18,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; "Textarea", "textarea", ValueType = ValueTypes.Text, - Icon = "icon-application-window-alt")] + Icon = "icon-application-window-alt", + ValueEditorIsReusable = true)] public class TextAreaPropertyEditor : DataEditor { private readonly IEditorConfigurationParser _editorConfigurationParser; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/TextboxPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/TextboxPropertyEditor.cs index 4f81bf410a..bc340b58ba 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/TextboxPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/TextboxPropertyEditor.cs @@ -17,7 +17,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; EditorType.PropertyValue | EditorType.MacroParameter, "Textbox", "textbox", - Group = Constants.PropertyEditors.Groups.Common)] + Group = Constants.PropertyEditors.Groups.Common, + ValueEditorIsReusable = true)] public class TextboxPropertyEditor : DataEditor { private readonly IEditorConfigurationParser _editorConfigurationParser; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/TrueFalsePropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/TrueFalsePropertyEditor.cs index 70ad112470..0a96a3dcee 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/TrueFalsePropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/TrueFalsePropertyEditor.cs @@ -18,7 +18,8 @@ namespace Umbraco.Cms.Core.PropertyEditors; "boolean", ValueType = ValueTypes.Integer, Group = Constants.PropertyEditors.Groups.Common, - Icon = "icon-checkbox")] + Icon = "icon-checkbox", + ValueEditorIsReusable = true)] public class TrueFalsePropertyEditor : DataEditor { private readonly IEditorConfigurationParser _editorConfigurationParser; diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/DataValueEditorReuseTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/DataValueEditorReuseTests.cs new file mode 100644 index 0000000000..46f79ebebc --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/DataValueEditorReuseTests.cs @@ -0,0 +1,132 @@ +using System.Linq; +using Microsoft.Extensions.Logging; +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core.IO; +using Umbraco.Cms.Core.PropertyEditors; +using Umbraco.Cms.Core.Serialization; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Strings; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors; + +[TestFixture] +public class DataValueEditorReuseTests +{ + private Mock _dataValueEditorFactoryMock; + private PropertyEditorCollection _propertyEditorCollection; + + [SetUp] + public void SetUp() + { + _dataValueEditorFactoryMock = new Mock(); + + _dataValueEditorFactoryMock + .Setup(m => m.Create(It.IsAny())) + .Returns(() => new TextOnlyValueEditor( + new DataEditorAttribute("a", "b", "c"), + Mock.Of(), + Mock.Of(), + Mock.Of(), + Mock.Of())); + + _propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty)); + + _dataValueEditorFactoryMock + .Setup(m => + m.Create(It.IsAny())) + .Returns(() => new BlockEditorPropertyEditor.BlockEditorPropertyValueEditor( + new DataEditorAttribute("a", "b", "c"), + _propertyEditorCollection, + Mock.Of(), + Mock.Of(), + Mock.Of(), + Mock.Of>(), + Mock.Of(), + Mock.Of(), + Mock.Of(), + Mock.Of())); + } + + [Test] + public void GetValueEditor_Reusable_Value_Editor_Is_Reused_When_Created_Without_Configuration() + { + var textboxPropertyEditor = new TextboxPropertyEditor( + _dataValueEditorFactoryMock.Object, + Mock.Of(), + Mock.Of()); + + // textbox is set to reuse its data value editor when created *without* configuration + var dataValueEditor1 = textboxPropertyEditor.GetValueEditor(); + Assert.NotNull(dataValueEditor1); + var dataValueEditor2 = textboxPropertyEditor.GetValueEditor(); + Assert.NotNull(dataValueEditor2); + Assert.AreSame(dataValueEditor1, dataValueEditor2); + _dataValueEditorFactoryMock.Verify( + m => m.Create(It.IsAny()), + Times.Once); + } + + [Test] + public void GetValueEditor_Reusable_Value_Editor_Is_Not_Reused_When_Created_With_Configuration() + { + var textboxPropertyEditor = new TextboxPropertyEditor( + _dataValueEditorFactoryMock.Object, + Mock.Of(), + Mock.Of()); + + // no matter what, a property editor should never reuse its data value editor when created *with* configuration + var dataValueEditor1 = textboxPropertyEditor.GetValueEditor("config"); + Assert.NotNull(dataValueEditor1); + Assert.AreEqual("config", ((DataValueEditor)dataValueEditor1).Configuration); + var dataValueEditor2 = textboxPropertyEditor.GetValueEditor("config"); + Assert.NotNull(dataValueEditor2); + Assert.AreEqual("config", ((DataValueEditor)dataValueEditor2).Configuration); + Assert.AreNotSame(dataValueEditor1, dataValueEditor2); + _dataValueEditorFactoryMock.Verify( + m => m.Create(It.IsAny()), + Times.Exactly(2)); + } + + [Test] + public void GetValueEditor_Not_Reusable_Value_Editor_Is_Not_Reused_When_Created_Without_Configuration() + { + var blockListPropertyEditor = new BlockListPropertyEditor( + _dataValueEditorFactoryMock.Object, + new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty)), + Mock.Of(), + Mock.Of()); + + // block list is *not* set to reuse its data value editor + var dataValueEditor1 = blockListPropertyEditor.GetValueEditor(); + Assert.NotNull(dataValueEditor1); + var dataValueEditor2 = blockListPropertyEditor.GetValueEditor(); + Assert.NotNull(dataValueEditor2); + Assert.AreNotSame(dataValueEditor1, dataValueEditor2); + _dataValueEditorFactoryMock.Verify( + m => m.Create(It.IsAny()), + Times.Exactly(2)); + } + + [Test] + public void GetValueEditor_Not_Reusable_Value_Editor_Is_Not_Reused_When_Created_With_Configuration() + { + var blockListPropertyEditor = new BlockListPropertyEditor( + _dataValueEditorFactoryMock.Object, + new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty)), + Mock.Of(), + Mock.Of()); + + // no matter what, a property editor should never reuse its data value editor when created *with* configuration + var dataValueEditor1 = blockListPropertyEditor.GetValueEditor("config"); + Assert.NotNull(dataValueEditor1); + Assert.AreEqual("config", ((DataValueEditor)dataValueEditor1).Configuration); + var dataValueEditor2 = blockListPropertyEditor.GetValueEditor("config"); + Assert.NotNull(dataValueEditor2); + Assert.AreEqual("config", ((DataValueEditor)dataValueEditor2).Configuration); + Assert.AreNotSame(dataValueEditor1, dataValueEditor2); + _dataValueEditorFactoryMock.Verify( + m => m.Create(It.IsAny()), + Times.Exactly(2)); + } +}