Non existing property editor (#19997)
* Initial implementation of non existing property editor * Adjust `MissingPropertyEditor` to not require registering in PropertyEditorCollection * Add `MissingPropertyEditor.name` back * Remove unused dependencies from DataTypeService * Removed reference to non existing property * Add parameterless constructor back to MissingPropertyEditor * Add validation error on document open to property with missing editor * Update labels * Removed public editor alias const * Update src/Umbraco.Web.UI.Client/src/packages/property-editors/missing/manifests.ts * Add test that checks whether the new MissingPropertyEditor is returned when an editor is not found * Also check if the editor UI alias is correct in the test * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Share property editor instances between properties * Only store missing property editors in memory in `ContentMapDefinition.MapValueViewModels()` * Add value converter for the missing property editor to always return a string (same as the Label did previously) * Small improvements to code block --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -1,5 +1,10 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
using Umbraco.Cms.Core.Strings;
|
||||
|
||||
namespace Umbraco.Cms.Core.PropertyEditors;
|
||||
|
||||
@@ -10,19 +15,73 @@ namespace Umbraco.Cms.Core.PropertyEditors;
|
||||
[HideFromTypeFinder]
|
||||
public class MissingPropertyEditor : IDataEditor
|
||||
{
|
||||
public string Alias => "Umbraco.Missing";
|
||||
private const string EditorAlias = "Umbraco.Missing";
|
||||
private readonly IDataValueEditorFactory _dataValueEditorFactory;
|
||||
private IDataValueEditor? _valueEditor;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MissingPropertyEditor"/> class.
|
||||
/// </summary>
|
||||
public MissingPropertyEditor(
|
||||
string missingEditorAlias,
|
||||
IDataValueEditorFactory dataValueEditorFactory)
|
||||
{
|
||||
_dataValueEditorFactory = dataValueEditorFactory;
|
||||
Alias = missingEditorAlias;
|
||||
}
|
||||
|
||||
[Obsolete("Use the non-obsolete constructor instead. Scheduled for removal in Umbraco 18.")]
|
||||
public MissingPropertyEditor()
|
||||
: this(
|
||||
EditorAlias,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IDataValueEditorFactory>())
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Alias { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the editor.
|
||||
/// </summary>
|
||||
public string Name => "Missing property editor";
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsDeprecated => false;
|
||||
|
||||
public IDictionary<string, object> DefaultConfiguration => throw new NotImplementedException();
|
||||
/// <inheritdoc />
|
||||
public bool SupportsReadOnly => true;
|
||||
|
||||
public IPropertyIndexValueFactory PropertyIndexValueFactory => throw new NotImplementedException();
|
||||
/// <inheritdoc />
|
||||
public IDictionary<string, object> DefaultConfiguration => new Dictionary<string, object>();
|
||||
|
||||
/// <inheritdoc />
|
||||
public IPropertyIndexValueFactory PropertyIndexValueFactory => new DefaultPropertyIndexValueFactory();
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDataValueEditor GetValueEditor() => _valueEditor
|
||||
??= _dataValueEditorFactory.Create<MissingPropertyValueEditor>(
|
||||
new DataEditorAttribute(EditorAlias));
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDataValueEditor GetValueEditor(object? configurationObject) => GetValueEditor();
|
||||
|
||||
/// <inheritdoc />
|
||||
public IConfigurationEditor GetConfigurationEditor() => new ConfigurationEditor();
|
||||
|
||||
public IDataValueEditor GetValueEditor() => throw new NotImplementedException();
|
||||
// provides the property value editor
|
||||
internal sealed class MissingPropertyValueEditor : DataValueEditor
|
||||
{
|
||||
public MissingPropertyValueEditor(
|
||||
IShortStringHelper shortStringHelper,
|
||||
IJsonSerializer jsonSerializer,
|
||||
IIOHelper ioHelper,
|
||||
DataEditorAttribute attribute)
|
||||
: base(shortStringHelper, jsonSerializer, ioHelper, attribute)
|
||||
{
|
||||
}
|
||||
|
||||
public IDataValueEditor GetValueEditor(object? configurationObject) => throw new NotImplementedException();
|
||||
/// <inheritdoc />
|
||||
public override bool IsReadOnly => true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
|
||||
namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters;
|
||||
|
||||
/// <summary>
|
||||
/// A value converter for the missing property editor, which always returns a string.
|
||||
/// </summary>
|
||||
[DefaultPropertyValueConverter]
|
||||
public class MissingPropertyEditorValueConverter : PropertyValueConverterBase
|
||||
{
|
||||
public override bool IsConverter(IPublishedPropertyType propertyType)
|
||||
=> "Umb.PropertyEditorUi.Missing".Equals(propertyType.EditorUiAlias);
|
||||
|
||||
public override Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof(string);
|
||||
|
||||
public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType)
|
||||
=> PropertyCacheLevel.Element;
|
||||
|
||||
public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object? source, bool preview)
|
||||
=> source?.ToString() ?? string.Empty;
|
||||
}
|
||||
@@ -21,14 +21,12 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// </summary>
|
||||
public class DataTypeService : RepositoryService, IDataTypeService
|
||||
{
|
||||
private readonly IDataValueEditorFactory _dataValueEditorFactory;
|
||||
private readonly IDataTypeRepository _dataTypeRepository;
|
||||
private readonly IDataTypeContainerRepository _dataTypeContainerRepository;
|
||||
private readonly IContentTypeRepository _contentTypeRepository;
|
||||
private readonly IMediaTypeRepository _mediaTypeRepository;
|
||||
private readonly IMemberTypeRepository _memberTypeRepository;
|
||||
private readonly IAuditRepository _auditRepository;
|
||||
private readonly IIOHelper _ioHelper;
|
||||
private readonly IDataTypeContainerService _dataTypeContainerService;
|
||||
private readonly IUserIdKeyResolver _userIdKeyResolver;
|
||||
private readonly Lazy<IIdKeyMap> _idKeyMap;
|
||||
@@ -59,6 +57,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 18.")]
|
||||
public DataTypeService(
|
||||
ICoreScopeProvider provider,
|
||||
ILoggerFactory loggerFactory,
|
||||
@@ -71,15 +70,36 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
IMemberTypeRepository memberTypeRepository,
|
||||
IIOHelper ioHelper,
|
||||
Lazy<IIdKeyMap> idKeyMap)
|
||||
: this(
|
||||
provider,
|
||||
loggerFactory,
|
||||
eventMessagesFactory,
|
||||
dataTypeRepository,
|
||||
auditRepository,
|
||||
contentTypeRepository,
|
||||
mediaTypeRepository,
|
||||
memberTypeRepository,
|
||||
idKeyMap)
|
||||
{
|
||||
}
|
||||
|
||||
public DataTypeService(
|
||||
ICoreScopeProvider provider,
|
||||
ILoggerFactory loggerFactory,
|
||||
IEventMessagesFactory eventMessagesFactory,
|
||||
IDataTypeRepository dataTypeRepository,
|
||||
IAuditRepository auditRepository,
|
||||
IContentTypeRepository contentTypeRepository,
|
||||
IMediaTypeRepository mediaTypeRepository,
|
||||
IMemberTypeRepository memberTypeRepository,
|
||||
Lazy<IIdKeyMap> idKeyMap)
|
||||
: base(provider, loggerFactory, eventMessagesFactory)
|
||||
{
|
||||
_dataValueEditorFactory = dataValueEditorFactory;
|
||||
_dataTypeRepository = dataTypeRepository;
|
||||
_auditRepository = auditRepository;
|
||||
_contentTypeRepository = contentTypeRepository;
|
||||
_mediaTypeRepository = mediaTypeRepository;
|
||||
_memberTypeRepository = memberTypeRepository;
|
||||
_ioHelper = ioHelper;
|
||||
_idKeyMap = idKeyMap;
|
||||
|
||||
// resolve dependencies for obsolete methods through the static service provider, so they don't pollute the constructor signature
|
||||
@@ -258,7 +278,6 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
{
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
IDataType? dataType = _dataTypeRepository.Get(Query<IDataType>().Where(x => x.Name == name))?.FirstOrDefault();
|
||||
ConvertMissingEditorOfDataTypeToLabel(dataType);
|
||||
|
||||
return Task.FromResult(dataType);
|
||||
}
|
||||
@@ -275,7 +294,6 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
}
|
||||
|
||||
IDataType[] dataTypes = _dataTypeRepository.Get(query).ToArray();
|
||||
ConvertMissingEditorsOfDataTypesToLabels(dataTypes);
|
||||
|
||||
return Task.FromResult<IEnumerable<IDataType>>(dataTypes);
|
||||
}
|
||||
@@ -319,7 +337,6 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
{
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
IDataType? dataType = _dataTypeRepository.Get(id);
|
||||
ConvertMissingEditorOfDataTypeToLabel(dataType);
|
||||
|
||||
return dataType;
|
||||
}
|
||||
@@ -329,7 +346,6 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
{
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
IDataType? dataType = GetDataTypeFromRepository(id);
|
||||
ConvertMissingEditorOfDataTypeToLabel(dataType);
|
||||
|
||||
return Task.FromResult(dataType);
|
||||
}
|
||||
@@ -349,7 +365,6 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
IQuery<IDataType> query = Query<IDataType>().Where(x => x.EditorAlias == propertyEditorAlias);
|
||||
IEnumerable<IDataType> dataTypes = _dataTypeRepository.Get(query).ToArray();
|
||||
ConvertMissingEditorsOfDataTypesToLabels(dataTypes);
|
||||
|
||||
return Task.FromResult(dataTypes);
|
||||
}
|
||||
@@ -360,7 +375,6 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
IQuery<IDataType> query = Query<IDataType>().Where(x => propertyEditorAlias.Contains(x.EditorAlias));
|
||||
IEnumerable<IDataType> dataTypes = _dataTypeRepository.Get(query).ToArray();
|
||||
ConvertMissingEditorsOfDataTypesToLabels(dataTypes);
|
||||
return Task.FromResult(dataTypes);
|
||||
}
|
||||
|
||||
@@ -370,7 +384,6 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
IQuery<IDataType> query = Query<IDataType>().Where(x => x.EditorUiAlias == editorUiAlias);
|
||||
IEnumerable<IDataType> dataTypes = _dataTypeRepository.Get(query).ToArray();
|
||||
ConvertMissingEditorsOfDataTypesToLabels(dataTypes);
|
||||
|
||||
return Task.FromResult(dataTypes);
|
||||
}
|
||||
@@ -384,32 +397,10 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
{
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
IEnumerable<IDataType> dataTypes = _dataTypeRepository.GetMany(ids).ToArray();
|
||||
ConvertMissingEditorsOfDataTypesToLabels(dataTypes);
|
||||
|
||||
return dataTypes;
|
||||
}
|
||||
|
||||
private void ConvertMissingEditorOfDataTypeToLabel(IDataType? dataType)
|
||||
{
|
||||
if (dataType == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ConvertMissingEditorsOfDataTypesToLabels([dataType]);
|
||||
}
|
||||
|
||||
private void ConvertMissingEditorsOfDataTypesToLabels(IEnumerable<IDataType> dataTypes)
|
||||
{
|
||||
// Any data types that don't have an associated editor are created of a specific type.
|
||||
// We convert them to labels to make clear to the user why the data type cannot be used.
|
||||
IEnumerable<IDataType> dataTypesWithMissingEditors = dataTypes.Where(x => x.Editor is MissingPropertyEditor);
|
||||
foreach (IDataType dataType in dataTypesWithMissingEditors)
|
||||
{
|
||||
dataType.Editor = new LabelPropertyEditor(_dataValueEditorFactory, _ioHelper);
|
||||
}
|
||||
}
|
||||
|
||||
public Attempt<OperationResult<MoveOperationStatusType>?> Move(IDataType toMove, int parentId)
|
||||
{
|
||||
Guid? containerKey = null;
|
||||
|
||||
Reference in New Issue
Block a user