diff --git a/src/Umbraco.Core/PropertyEditors/BlockListConfiguration.cs b/src/Umbraco.Core/PropertyEditors/BlockListConfiguration.cs index 1dec9946d306..1184f2524f47 100644 --- a/src/Umbraco.Core/PropertyEditors/BlockListConfiguration.cs +++ b/src/Umbraco.Core/PropertyEditors/BlockListConfiguration.cs @@ -16,6 +16,13 @@ public class BlockListConfiguration [ConfigurationField("validationLimit", "Amount", "numberrange", Description = "Set a required range of blocks")] public NumberRange ValidationLimit { get; set; } = new(); + [ConfigurationField("useSingleBlockMode", "Single block mode", "boolean", + Description = @"When in Single block mode, the output will be BlockListItem<>, instead of BlockListModel. + +**NOTE:** +Single block mode requires a maximum of one available block, and an amount set to minimum 1 and maximum 1 blocks.")] + public bool UseSingleBlockMode { get; set; } + [ConfigurationField("useLiveEditing", "Live editing mode", "boolean", Description = "Live editing in editor overlays for live updated custom views or labels using custom expression.")] public bool UseLiveEditing { get; set; } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs index af1c51c37e30..b56f43e0efbb 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs @@ -1,9 +1,14 @@ // Copyright (c) Umbraco. // See LICENSE for more details. +using System.Reflection; +using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Core.Logging; +using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Blocks; using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; using static Umbraco.Cms.Core.PropertyEditors.BlockListConfiguration; @@ -12,18 +17,70 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters; [DefaultPropertyValueConverter(typeof(JsonValueConverter))] public class BlockListPropertyValueConverter : BlockPropertyValueConverterBase { + private readonly IContentTypeService _contentTypeService; + private readonly BlockEditorConverter _blockConverter; + private readonly BlockListEditorDataConverter _blockListEditorDataConverter; private readonly IProfilingLogger _proflog; - public BlockListPropertyValueConverter(IProfilingLogger proflog, BlockEditorConverter blockConverter) + [Obsolete("Use the constructor with the IContentTypeService")] + public BlockListPropertyValueConverter(IProfilingLogger proflog, BlockEditorConverter blockConverter) : this(proflog, blockConverter, StaticServiceProvider.Instance.GetRequiredService()) { } + + public BlockListPropertyValueConverter(IProfilingLogger proflog, BlockEditorConverter blockConverter, IContentTypeService contentTypeService) : base(blockConverter) { _proflog = proflog; + _blockConverter = blockConverter; + _blockListEditorDataConverter = new BlockListEditorDataConverter(); + _contentTypeService = contentTypeService; } /// public override bool IsConverter(IPublishedPropertyType propertyType) => propertyType.EditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.BlockList); + /// + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) + { + var isSingleBlockMode = IsSingleBlockMode(propertyType.DataType); + if (isSingleBlockMode) + { + BlockListConfiguration.BlockConfiguration? block = + ConfigurationEditor.ConfigurationAs(propertyType.DataType.Configuration)?.Blocks.FirstOrDefault(); + + ModelType? contentElementType = block?.ContentElementTypeKey is Guid contentElementTypeKey && _contentTypeService.Get(contentElementTypeKey) is IContentType contentType ? ModelType.For(contentType.Alias) : null; + ModelType? settingsElementType = block?.SettingsElementTypeKey is Guid settingsElementTypeKey && _contentTypeService.Get(settingsElementTypeKey) is IContentType settingsType ? ModelType.For(settingsType.Alias) : null; + + if (contentElementType is not null) + { + if (settingsElementType is not null) + { + return typeof(BlockListItem<,>).MakeGenericType(contentElementType, settingsElementType); + } + + return typeof(BlockListItem<>).MakeGenericType(contentElementType); + } + + return typeof(BlockListItem); + } + + return typeof(BlockListModel); + } + + private bool IsSingleBlockMode(PublishedDataType dataType) + { + BlockListConfiguration? config = + ConfigurationEditor.ConfigurationAs(dataType.Configuration); + return (config?.UseSingleBlockMode ?? false) && config?.Blocks.Length == 1 && config?.ValidationLimit?.Min == 1 && config?.ValidationLimit?.Max == 1; + } + + /// + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) + => PropertyCacheLevel.Element; + + /// + public override object? ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object? source, bool preview) + => source?.ToString(); + /// public override object? ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object? inter, bool preview) { @@ -44,7 +101,7 @@ public override bool IsConverter(IPublishedPropertyType propertyType) BlockListModel blockModel = UnwrapBlockModel(referenceCacheLevel, inter, preview, configuration.Blocks, CreateEmptyModel, CreateModel); - return blockModel; + return IsSingleBlockMode(propertyType.DataType) ? blockModel.FirstOrDefault() : blockModel; } } diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.controller.js index ec30c7e4503c..b4531276134d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/blockpicker/blockpicker.controller.js @@ -38,12 +38,18 @@ angular.module("umbraco") "disabled": vm.model.clipboardItems.length === 0 }]; - if (vm.model.openClipboard === true) { + if (vm.model.singleBlockMode === true && vm.model.openClipboard === true) { + vm.navigation.splice(0,1); + vm.activeTab = vm.navigation[0]; + } + else if (vm.model.openClipboard === true) { vm.activeTab = vm.navigation[1]; } else { vm.activeTab = vm.navigation[0]; } + + vm.activeTab.active = true; } ); @@ -55,10 +61,16 @@ angular.module("umbraco") }; vm.clickClearClipboard = function () { - vm.onNavigationChanged(vm.navigation[0]); - vm.navigation[1].disabled = true;// disabled ws determined when creating the navigation, so we need to update it here. vm.model.clipboardItems = [];// This dialog is not connected via the clipboardService events, so we need to update manually. vm.model.clickClearClipboard(); + if (vm.model.singleBlockMode !== true && vm.model.openClipboard !== true) + { + vm.onNavigationChanged(vm.navigation[0]); + vm.navigation[1].disabled = true;// disabled ws determined when creating the navigation, so we need to update it here. + } + else { + vm.close(); + } }; vm.model = $scope.model; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umb-block-list-property-editor.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umb-block-list-property-editor.html index 30151accc9c7..a13826b86b27 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umb-block-list-property-editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umb-block-list-property-editor.html @@ -13,6 +13,7 @@ ng-click="vm.requestShowCreate($index, $event)" ng-controller="Umbraco.PropertyEditors.BlockListPropertyEditor.CreateButtonController as inlineCreateButtonCtrl" ng-mousemove="inlineCreateButtonCtrl.onMouseMove($event)" + ng-if="!vm.singleBlockMode" ng-show="!vm.readonly">
@@ -28,7 +29,7 @@
-
+
-