A bunch of minor performance optimizations (#16335)
* Do not execute query if no macros found * Request cache the permission lookup * Unbreak change by adding obsolete ctor * Clean up * Wrap indexing for delivery API in a scope * Do not ask options every time for the timeout, instead listen for updates * Lookup content types once instead of one by one * Use TryGetValue instead * Do a distinct on user ids before building index, to avoid issue with more than 2100 parameters * Don't map ContentDto (it's unused) * Introduce request bound block editor element cache --------- Co-authored-by: kjac <kja@umbraco.dk>
This commit is contained in:
@@ -16,8 +16,8 @@ namespace Umbraco.Cms.Persistence.EFCore.Locking;
|
||||
internal class SqlServerEFCoreDistributedLockingMechanism<T> : IDistributedLockingMechanism
|
||||
where T : DbContext
|
||||
{
|
||||
private readonly IOptionsMonitor<ConnectionStrings> _connectionStrings;
|
||||
private readonly IOptionsMonitor<GlobalSettings> _globalSettings;
|
||||
private ConnectionStrings _connectionStrings;
|
||||
private GlobalSettings _globalSettings;
|
||||
private readonly ILogger<SqlServerEFCoreDistributedLockingMechanism<T>> _logger;
|
||||
private readonly Lazy<IEFCoreScopeAccessor<T>> _scopeAccessor; // Hooray it's a circular dependency.
|
||||
|
||||
@@ -32,27 +32,29 @@ internal class SqlServerEFCoreDistributedLockingMechanism<T> : IDistributedLocki
|
||||
{
|
||||
_logger = logger;
|
||||
_scopeAccessor = scopeAccessor;
|
||||
_globalSettings = globalSettings;
|
||||
_connectionStrings = connectionStrings;
|
||||
_globalSettings = globalSettings.CurrentValue;
|
||||
_connectionStrings = connectionStrings.CurrentValue;
|
||||
globalSettings.OnChange(x=>_globalSettings = x);
|
||||
connectionStrings.OnChange(x=>_connectionStrings = x);
|
||||
}
|
||||
|
||||
public bool HasActiveRelatedScope => _scopeAccessor.Value.AmbientScope is not null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Enabled => _connectionStrings.CurrentValue.IsConnectionStringConfigured() &&
|
||||
string.Equals(_connectionStrings.CurrentValue.ProviderName, "Microsoft.Data.SqlClient", StringComparison.InvariantCultureIgnoreCase) && _scopeAccessor.Value.AmbientScope is not null;
|
||||
public bool Enabled => _connectionStrings.IsConnectionStringConfigured() &&
|
||||
string.Equals(_connectionStrings.ProviderName, "Microsoft.Data.SqlClient", StringComparison.InvariantCultureIgnoreCase) && _scopeAccessor.Value.AmbientScope is not null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDistributedLock ReadLock(int lockId, TimeSpan? obtainLockTimeout = null)
|
||||
{
|
||||
obtainLockTimeout ??= _globalSettings.CurrentValue.DistributedLockingReadLockDefaultTimeout;
|
||||
obtainLockTimeout ??= _globalSettings.DistributedLockingReadLockDefaultTimeout;
|
||||
return new SqlServerDistributedLock(this, lockId, DistributedLockType.ReadLock, obtainLockTimeout.Value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDistributedLock WriteLock(int lockId, TimeSpan? obtainLockTimeout = null)
|
||||
{
|
||||
obtainLockTimeout ??= _globalSettings.CurrentValue.DistributedLockingWriteLockDefaultTimeout;
|
||||
obtainLockTimeout ??= _globalSettings.DistributedLockingWriteLockDefaultTimeout;
|
||||
return new SqlServerDistributedLock(this, lockId, DistributedLockType.WriteLock, obtainLockTimeout.Value);
|
||||
}
|
||||
|
||||
@@ -168,9 +170,7 @@ internal class SqlServerEFCoreDistributedLockingMechanism<T> : IDistributedLocki
|
||||
"A transaction with minimum ReadCommitted isolation level is required.");
|
||||
}
|
||||
|
||||
await dbContext.Database.ExecuteSqlRawAsync($"SET LOCK_TIMEOUT {(int)_timeout.TotalMilliseconds};");
|
||||
|
||||
var rowsAffected = await dbContext.Database.ExecuteSqlAsync(@$"UPDATE umbracoLock WITH (REPEATABLEREAD) SET value = (CASE WHEN (value=1) THEN -1 ELSE 1 END) WHERE id={LockId}");
|
||||
var rowsAffected = await dbContext.Database.ExecuteSqlAsync(@$"SET LOCK_TIMEOUT {(int)_timeout.TotalMilliseconds};UPDATE umbracoLock WITH (REPEATABLEREAD) SET value = (CASE WHEN (value=1) THEN -1 ELSE 1 END) WHERE id={LockId}");
|
||||
|
||||
if (rowsAffected == 0)
|
||||
{
|
||||
|
||||
@@ -16,8 +16,8 @@ namespace Umbraco.Cms.Persistence.EFCore.Locking;
|
||||
internal class SqliteEFCoreDistributedLockingMechanism<T> : IDistributedLockingMechanism
|
||||
where T : DbContext
|
||||
{
|
||||
private readonly IOptionsMonitor<ConnectionStrings> _connectionStrings;
|
||||
private readonly IOptionsMonitor<GlobalSettings> _globalSettings;
|
||||
private ConnectionStrings _connectionStrings;
|
||||
private GlobalSettings _globalSettings;
|
||||
private readonly ILogger<SqliteEFCoreDistributedLockingMechanism<T>> _logger;
|
||||
private readonly Lazy<IEFCoreScopeAccessor<T>> _efCoreScopeAccessor;
|
||||
|
||||
@@ -29,27 +29,29 @@ internal class SqliteEFCoreDistributedLockingMechanism<T> : IDistributedLockingM
|
||||
{
|
||||
_logger = logger;
|
||||
_efCoreScopeAccessor = efCoreScopeAccessor;
|
||||
_connectionStrings = connectionStrings;
|
||||
_globalSettings = globalSettings;
|
||||
_globalSettings = globalSettings.CurrentValue;
|
||||
_connectionStrings = connectionStrings.CurrentValue;
|
||||
globalSettings.OnChange(x=>_globalSettings = x);
|
||||
connectionStrings.OnChange(x=>_connectionStrings = x);
|
||||
}
|
||||
|
||||
public bool HasActiveRelatedScope => _efCoreScopeAccessor.Value.AmbientScope is not null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Enabled => _connectionStrings.CurrentValue.IsConnectionStringConfigured() &&
|
||||
string.Equals(_connectionStrings.CurrentValue.ProviderName, "Microsoft.Data.Sqlite", StringComparison.InvariantCultureIgnoreCase) && _efCoreScopeAccessor.Value.AmbientScope is not null;
|
||||
public bool Enabled => _connectionStrings.IsConnectionStringConfigured() &&
|
||||
string.Equals(_connectionStrings.ProviderName, "Microsoft.Data.Sqlite", StringComparison.InvariantCultureIgnoreCase) && _efCoreScopeAccessor.Value.AmbientScope is not null;
|
||||
|
||||
// With journal_mode=wal we can always read a snapshot.
|
||||
public IDistributedLock ReadLock(int lockId, TimeSpan? obtainLockTimeout = null)
|
||||
{
|
||||
obtainLockTimeout ??= _globalSettings.CurrentValue.DistributedLockingReadLockDefaultTimeout;
|
||||
obtainLockTimeout ??= _globalSettings.DistributedLockingReadLockDefaultTimeout;
|
||||
return new SqliteDistributedLock(this, lockId, DistributedLockType.ReadLock, obtainLockTimeout.Value);
|
||||
}
|
||||
|
||||
// With journal_mode=wal only a single write transaction can exist at a time.
|
||||
public IDistributedLock WriteLock(int lockId, TimeSpan? obtainLockTimeout = null)
|
||||
{
|
||||
obtainLockTimeout ??= _globalSettings.CurrentValue.DistributedLockingWriteLockDefaultTimeout;
|
||||
obtainLockTimeout ??= _globalSettings.DistributedLockingWriteLockDefaultTimeout;
|
||||
return new SqliteDistributedLock(this, lockId, DistributedLockType.WriteLock, obtainLockTimeout.Value);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@ namespace Umbraco.Cms.Persistence.SqlServer.Services;
|
||||
/// </summary>
|
||||
public class SqlServerDistributedLockingMechanism : IDistributedLockingMechanism
|
||||
{
|
||||
private readonly IOptionsMonitor<ConnectionStrings> _connectionStrings;
|
||||
private readonly IOptionsMonitor<GlobalSettings> _globalSettings;
|
||||
private ConnectionStrings _connectionStrings;
|
||||
private GlobalSettings _globalSettings;
|
||||
private readonly ILogger<SqlServerDistributedLockingMechanism> _logger;
|
||||
private readonly Lazy<IScopeAccessor> _scopeAccessor; // Hooray it's a circular dependency.
|
||||
|
||||
@@ -33,25 +33,28 @@ public class SqlServerDistributedLockingMechanism : IDistributedLockingMechanism
|
||||
{
|
||||
_logger = logger;
|
||||
_scopeAccessor = scopeAccessor;
|
||||
_globalSettings = globalSettings;
|
||||
_connectionStrings = connectionStrings;
|
||||
_globalSettings = globalSettings.CurrentValue;
|
||||
_connectionStrings = connectionStrings.CurrentValue;
|
||||
globalSettings.OnChange(x => _globalSettings = x);
|
||||
connectionStrings.OnChange(x => _connectionStrings = x);
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Enabled => _connectionStrings.CurrentValue.IsConnectionStringConfigured() &&
|
||||
string.Equals(_connectionStrings.CurrentValue.ProviderName,Constants.ProviderName, StringComparison.InvariantCultureIgnoreCase);
|
||||
public bool Enabled => _connectionStrings.IsConnectionStringConfigured() &&
|
||||
string.Equals(_connectionStrings.ProviderName,Constants.ProviderName, StringComparison.InvariantCultureIgnoreCase);
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDistributedLock ReadLock(int lockId, TimeSpan? obtainLockTimeout = null)
|
||||
{
|
||||
obtainLockTimeout ??= _globalSettings.CurrentValue.DistributedLockingReadLockDefaultTimeout;
|
||||
obtainLockTimeout ??= _globalSettings.DistributedLockingReadLockDefaultTimeout;
|
||||
return new SqlServerDistributedLock(this, lockId, DistributedLockType.ReadLock, obtainLockTimeout.Value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDistributedLock WriteLock(int lockId, TimeSpan? obtainLockTimeout = null)
|
||||
{
|
||||
obtainLockTimeout ??= _globalSettings.CurrentValue.DistributedLockingWriteLockDefaultTimeout;
|
||||
obtainLockTimeout ??= _globalSettings.DistributedLockingWriteLockDefaultTimeout;
|
||||
return new SqlServerDistributedLock(this, lockId, DistributedLockType.WriteLock, obtainLockTimeout.Value);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ namespace Umbraco.Cms.Persistence.Sqlite.Services;
|
||||
|
||||
public class SqliteDistributedLockingMechanism : IDistributedLockingMechanism
|
||||
{
|
||||
private readonly IOptionsMonitor<ConnectionStrings> _connectionStrings;
|
||||
private readonly IOptionsMonitor<GlobalSettings> _globalSettings;
|
||||
private ConnectionStrings _connectionStrings;
|
||||
private GlobalSettings _globalSettings;
|
||||
private readonly ILogger<SqliteDistributedLockingMechanism> _logger;
|
||||
private readonly Lazy<IScopeAccessor> _scopeAccessor;
|
||||
|
||||
@@ -29,25 +29,27 @@ public class SqliteDistributedLockingMechanism : IDistributedLockingMechanism
|
||||
{
|
||||
_logger = logger;
|
||||
_scopeAccessor = scopeAccessor;
|
||||
_connectionStrings = connectionStrings;
|
||||
_globalSettings = globalSettings;
|
||||
_connectionStrings = connectionStrings.CurrentValue;
|
||||
_globalSettings = globalSettings.CurrentValue;
|
||||
globalSettings.OnChange(x=>_globalSettings = x);
|
||||
connectionStrings.OnChange(x=>_connectionStrings = x);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Enabled => _connectionStrings.CurrentValue.IsConnectionStringConfigured() &&
|
||||
string.Equals(_connectionStrings.CurrentValue.ProviderName, Constants.ProviderName, StringComparison.InvariantCultureIgnoreCase);
|
||||
public bool Enabled => _connectionStrings.IsConnectionStringConfigured() &&
|
||||
string.Equals(_connectionStrings.ProviderName, Constants.ProviderName, StringComparison.InvariantCultureIgnoreCase);
|
||||
|
||||
// With journal_mode=wal we can always read a snapshot.
|
||||
public IDistributedLock ReadLock(int lockId, TimeSpan? obtainLockTimeout = null)
|
||||
{
|
||||
obtainLockTimeout ??= _globalSettings.CurrentValue.DistributedLockingReadLockDefaultTimeout;
|
||||
obtainLockTimeout ??= _globalSettings.DistributedLockingReadLockDefaultTimeout;
|
||||
return new SqliteDistributedLock(this, lockId, DistributedLockType.ReadLock, obtainLockTimeout.Value);
|
||||
}
|
||||
|
||||
// With journal_mode=wal only a single write transaction can exist at a time.
|
||||
public IDistributedLock WriteLock(int lockId, TimeSpan? obtainLockTimeout = null)
|
||||
{
|
||||
obtainLockTimeout ??= _globalSettings.CurrentValue.DistributedLockingWriteLockDefaultTimeout;
|
||||
obtainLockTimeout ??= _globalSettings.DistributedLockingWriteLockDefaultTimeout;
|
||||
return new SqliteDistributedLock(this, lockId, DistributedLockType.WriteLock, obtainLockTimeout.Value);
|
||||
}
|
||||
|
||||
|
||||
@@ -194,6 +194,7 @@ public class ContentItemDisplay<TVariant> :
|
||||
/// This is not used for outgoing model information.
|
||||
/// </remarks>
|
||||
[IgnoreDataMember]
|
||||
[Obsolete("No longer used. Will be removed in V15.")]
|
||||
public ContentPropertyCollectionDto? ContentDto { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
using System.Data.Common;
|
||||
using System.Globalization;
|
||||
using System.Linq.Expressions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Notifications;
|
||||
@@ -25,8 +28,11 @@ internal class UserService : RepositoryService, IUserService
|
||||
private readonly ILogger<UserService> _logger;
|
||||
private readonly IRuntimeState _runtimeState;
|
||||
private readonly IUserGroupRepository _userGroupRepository;
|
||||
private readonly IRequestCache _requestCache;
|
||||
private readonly IUserRepository _userRepository;
|
||||
|
||||
|
||||
[Obsolete("Use non-obsolete constructor. This will be removed in Umbraco 15.")]
|
||||
public UserService(
|
||||
ICoreScopeProvider provider,
|
||||
ILoggerFactory loggerFactory,
|
||||
@@ -35,11 +41,26 @@ internal class UserService : RepositoryService, IUserService
|
||||
IUserRepository userRepository,
|
||||
IUserGroupRepository userGroupRepository,
|
||||
IOptions<GlobalSettings> globalSettings)
|
||||
: this(provider, loggerFactory, eventMessagesFactory, runtimeState, userRepository, userGroupRepository, globalSettings, StaticServiceProvider.Instance.GetRequiredService<IRequestCache>())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public UserService(
|
||||
ICoreScopeProvider provider,
|
||||
ILoggerFactory loggerFactory,
|
||||
IEventMessagesFactory eventMessagesFactory,
|
||||
IRuntimeState runtimeState,
|
||||
IUserRepository userRepository,
|
||||
IUserGroupRepository userGroupRepository,
|
||||
IOptions<GlobalSettings> globalSettings,
|
||||
IRequestCache requestCache)
|
||||
: base(provider, loggerFactory, eventMessagesFactory)
|
||||
{
|
||||
_runtimeState = runtimeState;
|
||||
_userRepository = userRepository;
|
||||
_userGroupRepository = userGroupRepository;
|
||||
_requestCache = requestCache;
|
||||
_globalSettings = globalSettings.Value;
|
||||
_logger = loggerFactory.CreateLogger<UserService>();
|
||||
}
|
||||
@@ -1125,17 +1146,23 @@ internal class UserService : RepositoryService, IUserService
|
||||
/// <param name="path">Path to check permissions for</param>
|
||||
public EntityPermissionSet GetPermissionsForPath(IUser? user, string? path)
|
||||
{
|
||||
var nodeIds = path?.GetIdsFromPathReversed();
|
||||
|
||||
if (nodeIds is null || nodeIds.Length == 0 || user is null)
|
||||
var result = (EntityPermissionSet?)_requestCache.Get($"{nameof(GetPermissionsForPath)}|{path}|{user?.Id}", () =>
|
||||
{
|
||||
return EntityPermissionSet.Empty();
|
||||
}
|
||||
var nodeIds = path?.GetIdsFromPathReversed();
|
||||
|
||||
// collect all permissions structures for all nodes for all groups belonging to the user
|
||||
EntityPermission[] groupPermissions = GetPermissionsForPath(user.Groups.ToArray(), nodeIds, true).ToArray();
|
||||
if (nodeIds is null || nodeIds.Length == 0 || user is null)
|
||||
{
|
||||
return EntityPermissionSet.Empty();
|
||||
}
|
||||
|
||||
// collect all permissions structures for all nodes for all groups belonging to the user
|
||||
EntityPermission[] groupPermissions = GetPermissionsForPath(user.Groups.ToArray(), nodeIds, true).ToArray();
|
||||
|
||||
return CalculatePermissionsForPathForUser(groupPermissions, nodeIds);
|
||||
});
|
||||
|
||||
return result ?? EntityPermissionSet.Empty();
|
||||
|
||||
return CalculatePermissionsForPathForUser(groupPermissions, nodeIds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache.PropertyEditors;
|
||||
|
||||
internal sealed class BlockEditorElementTypeCache : IBlockEditorElementTypeCache
|
||||
{
|
||||
private readonly IContentTypeService _contentTypeService;
|
||||
private readonly AppCaches _appCaches;
|
||||
|
||||
public BlockEditorElementTypeCache(IContentTypeService contentTypeService, AppCaches appCaches)
|
||||
{
|
||||
_contentTypeService = contentTypeService;
|
||||
_appCaches = appCaches;
|
||||
}
|
||||
|
||||
public IEnumerable<IContentType> GetAll(IEnumerable<Guid> keys)
|
||||
{
|
||||
// TODO: make this less dumb; don't fetch all elements, only fetch the items that aren't yet in the cache and amend the cache as more elements are loaded
|
||||
|
||||
const string cacheKey = $"{nameof(BlockEditorElementTypeCache)}_ElementTypes";
|
||||
IEnumerable<IContentType>? cachedElements = _appCaches.RequestCache.GetCacheItem<IEnumerable<IContentType>>(cacheKey);
|
||||
if (cachedElements is null)
|
||||
{
|
||||
cachedElements = _contentTypeService.GetAllElementTypes();
|
||||
_appCaches.RequestCache.Set(cacheKey, cachedElements);
|
||||
}
|
||||
|
||||
return cachedElements.Where(elementType => keys.Contains(elementType.Key));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache.PropertyEditors;
|
||||
|
||||
public interface IBlockEditorElementTypeCache
|
||||
{
|
||||
IEnumerable<IContentType> GetAll(IEnumerable<Guid> keys);
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using Microsoft.Extensions.Options;
|
||||
using Serilog;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Cache.PropertyEditors;
|
||||
using Umbraco.Cms.Core.Configuration;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.DeliveryApi;
|
||||
@@ -235,6 +236,8 @@ public static partial class UmbracoBuilderExtensions
|
||||
builder.AddDeliveryApiCoreServices();
|
||||
builder.Services.AddTransient<IWebhookFiringService, WebhookFiringService>();
|
||||
|
||||
builder.Services.AddSingleton<IBlockEditorElementTypeCache, BlockEditorElementTypeCache>();
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
||||
@@ -128,9 +128,9 @@ public class ContentValueSetBuilder : BaseValueSetBuilder<IContent>, IContentVal
|
||||
// processing below instead of one by one.
|
||||
using (ICoreScope scope = _scopeProvider.CreateCoreScope())
|
||||
{
|
||||
creatorIds = _userService.GetProfilesById(content.Select(x => x.CreatorId).ToArray())
|
||||
creatorIds = _userService.GetProfilesById(content.Select(x => x.CreatorId).Distinct().ToArray())
|
||||
.ToDictionary(x => x.Id, x => x);
|
||||
writerIds = _userService.GetProfilesById(content.Select(x => x.WriterId).ToArray())
|
||||
writerIds = _userService.GetProfilesById(content.Select(x => x.WriterId).Distinct().ToArray())
|
||||
.ToDictionary(x => x.Id, x => x);
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.DeliveryApi;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
@@ -21,6 +22,7 @@ internal sealed class DeliveryApiContentIndexValueSetBuilder : IDeliveryApiConte
|
||||
private readonly IDeliveryApiContentIndexFieldDefinitionBuilder _deliveryApiContentIndexFieldDefinitionBuilder;
|
||||
private readonly IMemberService _memberService;
|
||||
private readonly IDeliveryApiCompositeIdHandler _deliveryApiCompositeIdHandler;
|
||||
private readonly ICoreScopeProvider _coreScopeProvider;
|
||||
private DeliveryApiSettings _deliveryApiSettings;
|
||||
|
||||
[Obsolete("Please use ctor that takes an IDeliveryApiCompositeIdHandler. Scheduled for removal in v15")]
|
||||
@@ -40,8 +42,33 @@ internal sealed class DeliveryApiContentIndexValueSetBuilder : IDeliveryApiConte
|
||||
deliveryApiContentIndexFieldDefinitionBuilder,
|
||||
deliveryApiSettings,
|
||||
memberService,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IDeliveryApiCompositeIdHandler>())
|
||||
StaticServiceProvider.Instance.GetRequiredService<IDeliveryApiCompositeIdHandler>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<ICoreScopeProvider>()
|
||||
)
|
||||
{
|
||||
}
|
||||
[Obsolete("Please use ctor that takes an IDeliveryApiCompositeIdHandler. Scheduled for removal in v15")]
|
||||
public DeliveryApiContentIndexValueSetBuilder(
|
||||
ContentIndexHandlerCollection contentIndexHandlerCollection,
|
||||
IContentService contentService,
|
||||
IPublicAccessService publicAccessService,
|
||||
ILogger<DeliveryApiContentIndexValueSetBuilder> logger,
|
||||
IDeliveryApiContentIndexFieldDefinitionBuilder deliveryApiContentIndexFieldDefinitionBuilder,
|
||||
IOptionsMonitor<DeliveryApiSettings> deliveryApiSettings,
|
||||
IMemberService memberService,
|
||||
IDeliveryApiCompositeIdHandler deliveryApiCompositeIdHandle)
|
||||
:this(
|
||||
contentIndexHandlerCollection,
|
||||
contentService,
|
||||
publicAccessService,
|
||||
logger,
|
||||
deliveryApiContentIndexFieldDefinitionBuilder,
|
||||
deliveryApiSettings,
|
||||
memberService,
|
||||
deliveryApiCompositeIdHandle,
|
||||
StaticServiceProvider.Instance.GetRequiredService<ICoreScopeProvider>())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public DeliveryApiContentIndexValueSetBuilder(
|
||||
@@ -52,7 +79,8 @@ internal sealed class DeliveryApiContentIndexValueSetBuilder : IDeliveryApiConte
|
||||
IDeliveryApiContentIndexFieldDefinitionBuilder deliveryApiContentIndexFieldDefinitionBuilder,
|
||||
IOptionsMonitor<DeliveryApiSettings> deliveryApiSettings,
|
||||
IMemberService memberService,
|
||||
IDeliveryApiCompositeIdHandler deliveryApiCompositeIdHandler)
|
||||
IDeliveryApiCompositeIdHandler deliveryApiCompositeIdHandler,
|
||||
ICoreScopeProvider coreScopeProvider)
|
||||
{
|
||||
_contentIndexHandlerCollection = contentIndexHandlerCollection;
|
||||
_publicAccessService = publicAccessService;
|
||||
@@ -60,6 +88,7 @@ internal sealed class DeliveryApiContentIndexValueSetBuilder : IDeliveryApiConte
|
||||
_deliveryApiContentIndexFieldDefinitionBuilder = deliveryApiContentIndexFieldDefinitionBuilder;
|
||||
_memberService = memberService;
|
||||
_deliveryApiCompositeIdHandler = deliveryApiCompositeIdHandler;
|
||||
_coreScopeProvider = coreScopeProvider;
|
||||
_contentService = contentService;
|
||||
_deliveryApiSettings = deliveryApiSettings.CurrentValue;
|
||||
deliveryApiSettings.OnChange(settings => _deliveryApiSettings = settings);
|
||||
@@ -68,6 +97,7 @@ internal sealed class DeliveryApiContentIndexValueSetBuilder : IDeliveryApiConte
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<ValueSet> GetValueSets(params IContent[] contents)
|
||||
{
|
||||
using ICoreScope scope = _coreScopeProvider.CreateCoreScope();
|
||||
FieldDefinitionCollection fieldDefinitions = _deliveryApiContentIndexFieldDefinitionBuilder.Build();
|
||||
foreach (IContent content in contents.Where(CanIndex))
|
||||
{
|
||||
@@ -101,6 +131,8 @@ internal sealed class DeliveryApiContentIndexValueSetBuilder : IDeliveryApiConte
|
||||
|
||||
yield return new ValueSet(_deliveryApiCompositeIdHandler.IndexId(content.Id, indexCulture), IndexTypes.Content, content.ContentType.Alias, indexValues);
|
||||
}
|
||||
|
||||
scope.Complete();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Umbraco.Cms.Core.Cache.PropertyEditors;
|
||||
using Umbraco.Cms.Core.Models.Blocks;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
@@ -13,8 +14,8 @@ internal class BlockEditorValidator : BlockEditorValidatorBase
|
||||
public BlockEditorValidator(
|
||||
IPropertyValidationService propertyValidationService,
|
||||
BlockEditorValues blockEditorValues,
|
||||
IContentTypeService contentTypeService)
|
||||
: base(propertyValidationService, contentTypeService)
|
||||
IBlockEditorElementTypeCache elementTypeCache)
|
||||
: base(propertyValidationService, elementTypeCache)
|
||||
=> _blockEditorValues = blockEditorValues;
|
||||
|
||||
protected override IEnumerable<ElementTypeValidationModel> GetElementTypeValidation(object? value)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Cache.PropertyEditors;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Blocks;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
@@ -6,11 +7,11 @@ namespace Umbraco.Cms.Core.PropertyEditors;
|
||||
|
||||
internal abstract class BlockEditorValidatorBase : ComplexEditorValidator
|
||||
{
|
||||
private readonly IContentTypeService _contentTypeService;
|
||||
private readonly IBlockEditorElementTypeCache _elementTypeCache;
|
||||
|
||||
protected BlockEditorValidatorBase(IPropertyValidationService propertyValidationService, IContentTypeService contentTypeService)
|
||||
protected BlockEditorValidatorBase(IPropertyValidationService propertyValidationService, IBlockEditorElementTypeCache elementTypeCache)
|
||||
: base(propertyValidationService)
|
||||
=> _contentTypeService = contentTypeService;
|
||||
=> _elementTypeCache = elementTypeCache;
|
||||
|
||||
protected IEnumerable<ElementTypeValidationModel> GetBlockEditorDataValidation(BlockEditorData blockEditorData)
|
||||
{
|
||||
@@ -18,7 +19,7 @@ internal abstract class BlockEditorValidatorBase : ComplexEditorValidator
|
||||
// need to validate that data for each property especially for things like 'required' data to work.
|
||||
// Lookup all element types for all content/settings and then we can populate any empty properties.
|
||||
var allElements = blockEditorData.BlockValue.ContentData.Concat(blockEditorData.BlockValue.SettingsData).ToList();
|
||||
var allElementTypes = _contentTypeService.GetAll(allElements.Select(x => x.ContentTypeKey).ToArray()).ToDictionary(x => x.Key);
|
||||
var allElementTypes = _elementTypeCache.GetAll(allElements.Select(x => x.ContentTypeKey).ToArray()).ToDictionary(x => x.Key);
|
||||
|
||||
foreach (BlockItemData row in allElements)
|
||||
{
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Core.Cache.PropertyEditors;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Blocks;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.PropertyEditors;
|
||||
@@ -15,13 +15,13 @@ namespace Umbraco.Cms.Core.PropertyEditors;
|
||||
internal class BlockEditorValues
|
||||
{
|
||||
private readonly BlockEditorDataConverter _dataConverter;
|
||||
private readonly IContentTypeService _contentTypeService;
|
||||
private readonly IBlockEditorElementTypeCache _elementTypeCache;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public BlockEditorValues(BlockEditorDataConverter dataConverter, IContentTypeService contentTypeService, ILogger logger)
|
||||
public BlockEditorValues(BlockEditorDataConverter dataConverter, IBlockEditorElementTypeCache elementTypeCache, ILogger logger)
|
||||
{
|
||||
_dataConverter = dataConverter;
|
||||
_contentTypeService = contentTypeService;
|
||||
_elementTypeCache = elementTypeCache;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@@ -55,10 +55,14 @@ internal class BlockEditorValues
|
||||
var contentTypePropertyTypes = new Dictionary<string, Dictionary<string, IPropertyType>>();
|
||||
|
||||
// filter out any content that isn't referenced in the layout references
|
||||
IEnumerable<Guid> contentTypeKeys = blockEditorData.BlockValue.ContentData.Select(x => x.ContentTypeKey)
|
||||
.Union(blockEditorData.BlockValue.SettingsData.Select(x => x.ContentTypeKey)).Distinct();
|
||||
IDictionary<Guid, IContentType> contentTypesDictionary = _elementTypeCache.GetAll(contentTypeKeys).ToDictionary(x=>x.Key);
|
||||
|
||||
foreach (BlockItemData block in blockEditorData.BlockValue.ContentData.Where(x =>
|
||||
blockEditorData.References.Any(r => x.Udi is not null && r.ContentUdi == x.Udi)))
|
||||
{
|
||||
ResolveBlockItemData(block, contentTypePropertyTypes);
|
||||
ResolveBlockItemData(block, contentTypePropertyTypes, contentTypesDictionary);
|
||||
}
|
||||
|
||||
// filter out any settings that isn't referenced in the layout references
|
||||
@@ -66,7 +70,7 @@ internal class BlockEditorValues
|
||||
blockEditorData.References.Any(r =>
|
||||
r.SettingsUdi is not null && x.Udi is not null && r.SettingsUdi == x.Udi)))
|
||||
{
|
||||
ResolveBlockItemData(block, contentTypePropertyTypes);
|
||||
ResolveBlockItemData(block, contentTypePropertyTypes, contentTypesDictionary);
|
||||
}
|
||||
|
||||
// remove blocks that couldn't be resolved
|
||||
@@ -76,12 +80,10 @@ internal class BlockEditorValues
|
||||
return blockEditorData;
|
||||
}
|
||||
|
||||
private IContentType? GetElementType(BlockItemData item) => _contentTypeService.Get(item.ContentTypeKey);
|
||||
|
||||
private bool ResolveBlockItemData(BlockItemData block, Dictionary<string, Dictionary<string, IPropertyType>> contentTypePropertyTypes)
|
||||
private bool ResolveBlockItemData(BlockItemData block, Dictionary<string, Dictionary<string, IPropertyType>> contentTypePropertyTypes, IDictionary<Guid, IContentType> contentTypesDictionary)
|
||||
{
|
||||
IContentType? contentType = GetElementType(block);
|
||||
if (contentType == null)
|
||||
if (contentTypesDictionary.TryGetValue(block.ContentTypeKey, out IContentType? contentType) is false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Cache.PropertyEditors;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Blocks;
|
||||
@@ -58,12 +59,12 @@ public abstract class BlockGridPropertyEditorBase : DataEditor
|
||||
IShortStringHelper shortStringHelper,
|
||||
IJsonSerializer jsonSerializer,
|
||||
IIOHelper ioHelper,
|
||||
IContentTypeService contentTypeService,
|
||||
IBlockEditorElementTypeCache elementTypeCache,
|
||||
IPropertyValidationService propertyValidationService)
|
||||
: base(attribute, propertyEditors, dataValueReferenceFactories, dataTypeConfigurationCache, textService, logger, shortStringHelper, jsonSerializer, ioHelper)
|
||||
{
|
||||
BlockEditorValues = new BlockEditorValues(new BlockGridEditorDataConverter(jsonSerializer), contentTypeService, logger);
|
||||
Validators.Add(new BlockEditorValidator(propertyValidationService, BlockEditorValues, contentTypeService));
|
||||
BlockEditorValues = new BlockEditorValues(new BlockGridEditorDataConverter(jsonSerializer), elementTypeCache, logger);
|
||||
Validators.Add(new BlockEditorValidator(propertyValidationService, BlockEditorValues, elementTypeCache));
|
||||
Validators.Add(new MinMaxValidator(BlockEditorValues, textService));
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Cache.PropertyEditors;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Blocks;
|
||||
@@ -55,7 +56,7 @@ public abstract class BlockListPropertyEditorBase : DataEditor
|
||||
PropertyEditorCollection propertyEditors,
|
||||
DataValueReferenceFactoryCollection dataValueReferenceFactories,
|
||||
IDataTypeConfigurationCache dataTypeConfigurationCache,
|
||||
IContentTypeService contentTypeService,
|
||||
IBlockEditorElementTypeCache elementTypeCache,
|
||||
ILocalizedTextService textService,
|
||||
ILogger<BlockEditorPropertyValueEditor> logger,
|
||||
IShortStringHelper shortStringHelper,
|
||||
@@ -64,8 +65,8 @@ public abstract class BlockListPropertyEditorBase : DataEditor
|
||||
IPropertyValidationService propertyValidationService) :
|
||||
base(attribute, propertyEditors, dataValueReferenceFactories, dataTypeConfigurationCache, textService, logger, shortStringHelper, jsonSerializer, ioHelper)
|
||||
{
|
||||
BlockEditorValues = new BlockEditorValues(blockEditorDataConverter, contentTypeService, logger);
|
||||
Validators.Add(new BlockEditorValidator(propertyValidationService, BlockEditorValues, contentTypeService));
|
||||
BlockEditorValues = new BlockEditorValues(blockEditorDataConverter, elementTypeCache, logger);
|
||||
Validators.Add(new BlockEditorValidator(propertyValidationService, BlockEditorValues, elementTypeCache));
|
||||
Validators.Add(new MinMaxValidator(BlockEditorValues, textService));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Core.Cache.PropertyEditors;
|
||||
using Umbraco.Cms.Core.Models.Blocks;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
@@ -14,10 +15,10 @@ internal class RichTextEditorBlockValidator : BlockEditorValidatorBase
|
||||
public RichTextEditorBlockValidator(
|
||||
IPropertyValidationService propertyValidationService,
|
||||
BlockEditorValues blockEditorValues,
|
||||
IContentTypeService contentTypeService,
|
||||
IBlockEditorElementTypeCache elementTypeCache,
|
||||
IJsonSerializer jsonSerializer,
|
||||
ILogger logger)
|
||||
: base(propertyValidationService, contentTypeService)
|
||||
: base(propertyValidationService, elementTypeCache)
|
||||
{
|
||||
_blockEditorValues = blockEditorValues;
|
||||
_jsonSerializer = jsonSerializer;
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Cache.PropertyEditors;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Core.Media;
|
||||
@@ -171,7 +172,7 @@ public class RichTextPropertyEditor : DataEditor
|
||||
private readonly IHtmlMacroParameterParser _macroParameterParser;
|
||||
private readonly RichTextEditorPastedImages _pastedImages;
|
||||
private readonly IJsonSerializer _jsonSerializer;
|
||||
private readonly IContentTypeService _contentTypeService;
|
||||
private readonly IBlockEditorElementTypeCache _elementTypeCache;
|
||||
private readonly ILogger<RichTextPropertyValueEditor> _logger;
|
||||
|
||||
public RichTextPropertyValueEditor(
|
||||
@@ -189,7 +190,7 @@ public class RichTextPropertyEditor : DataEditor
|
||||
IIOHelper ioHelper,
|
||||
IHtmlSanitizer htmlSanitizer,
|
||||
IHtmlMacroParameterParser macroParameterParser,
|
||||
IContentTypeService contentTypeService,
|
||||
IBlockEditorElementTypeCache elementTypeCache,
|
||||
IPropertyValidationService propertyValidationService,
|
||||
DataValueReferenceFactoryCollection dataValueReferenceFactoryCollection)
|
||||
: base(attribute, propertyEditors, dataTypeReadCache, localizedTextService, logger, shortStringHelper, jsonSerializer, ioHelper, dataValueReferenceFactoryCollection)
|
||||
@@ -200,11 +201,11 @@ public class RichTextPropertyEditor : DataEditor
|
||||
_pastedImages = pastedImages;
|
||||
_htmlSanitizer = htmlSanitizer;
|
||||
_macroParameterParser = macroParameterParser;
|
||||
_contentTypeService = contentTypeService;
|
||||
_elementTypeCache = elementTypeCache;
|
||||
_jsonSerializer = jsonSerializer;
|
||||
_logger = logger;
|
||||
|
||||
Validators.Add(new RichTextEditorBlockValidator(propertyValidationService, CreateBlockEditorValues(), contentTypeService, jsonSerializer, logger));
|
||||
Validators.Add(new RichTextEditorBlockValidator(propertyValidationService, CreateBlockEditorValues(), elementTypeCache, jsonSerializer, logger));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -392,6 +393,6 @@ public class RichTextPropertyEditor : DataEditor
|
||||
}
|
||||
|
||||
private BlockEditorValues CreateBlockEditorValues()
|
||||
=> new(new RichTextEditorBlockDataConverter(), _contentTypeService, _logger);
|
||||
=> new(new RichTextEditorBlockDataConverter(), _elementTypeCache, _logger);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,10 +55,9 @@ public sealed class HtmlMacroParameterParser : IHtmlMacroParameterParser
|
||||
macroAlias,
|
||||
new Dictionary<string, string>(macroAttributes, StringComparer.OrdinalIgnoreCase))));
|
||||
|
||||
foreach (UmbracoEntityReference umbracoEntityReference in GetUmbracoEntityReferencesFromMacros(foundMacros))
|
||||
{
|
||||
yield return umbracoEntityReference;
|
||||
}
|
||||
return foundMacros.Count > 0
|
||||
? GetUmbracoEntityReferencesFromMacros(foundMacros)
|
||||
: Enumerable.Empty<UmbracoEntityReference>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -82,10 +81,9 @@ public sealed class HtmlMacroParameterParser : IHtmlMacroParameterParser
|
||||
}
|
||||
}
|
||||
|
||||
foreach (UmbracoEntityReference umbracoEntityReference in GetUmbracoEntityReferencesFromMacros(foundMacros))
|
||||
{
|
||||
yield return umbracoEntityReference;
|
||||
}
|
||||
return foundMacros.Count > 0
|
||||
? GetUmbracoEntityReferencesFromMacros(foundMacros)
|
||||
: Enumerable.Empty<UmbracoEntityReference>();
|
||||
}
|
||||
|
||||
private IEnumerable<UmbracoEntityReference> GetUmbracoEntityReferencesFromMacros(
|
||||
@@ -96,6 +94,7 @@ public sealed class HtmlMacroParameterParser : IHtmlMacroParameterParser
|
||||
yield break;
|
||||
}
|
||||
|
||||
|
||||
IEnumerable<string?> uniqueMacroAliases = macros.Select(f => f.Item1).Distinct();
|
||||
|
||||
// TODO: Tracking Macro references
|
||||
@@ -103,7 +102,9 @@ public sealed class HtmlMacroParameterParser : IHtmlMacroParameterParser
|
||||
var foundMacroUmbracoEntityReferences = new List<UmbracoEntityReference>();
|
||||
|
||||
// Get all the macro configs in one hit for these unique macro aliases - this is now cached with a custom cache policy
|
||||
IEnumerable<IMacro> macroConfigs = macroWithAliasService.GetAll(uniqueMacroAliases.WhereNotNull().ToArray());
|
||||
IEnumerable<IMacro> macroConfigs = uniqueMacroAliases.Any()
|
||||
? macroWithAliasService.GetAll(uniqueMacroAliases.WhereNotNull().ToArray())
|
||||
: Enumerable.Empty<IMacro>();
|
||||
|
||||
foreach (Tuple<string?, Dictionary<string, string>> macro in macros)
|
||||
{
|
||||
|
||||
@@ -128,7 +128,7 @@ internal class ContentMapDefinition : IMapDefinition
|
||||
target.AdditionalPreviewUrls = source.AdditionalPreviewUrls;
|
||||
}
|
||||
|
||||
// Umbraco.Code.MapAll
|
||||
// Umbraco.Code.MapAll -ContentDto
|
||||
private void Map(ContentItemDisplay source, ContentItemDisplayWithSchedule target, MapperContext context)
|
||||
{
|
||||
foreach (KeyValuePair<string, object> additionalData in source.AdditionalData)
|
||||
@@ -140,7 +140,6 @@ internal class ContentMapDefinition : IMapDefinition
|
||||
target.AllowedTemplates = source.AllowedTemplates;
|
||||
target.AllowPreview = source.AllowPreview;
|
||||
target.ContentApps = source.ContentApps;
|
||||
target.ContentDto = source.ContentDto;
|
||||
target.ContentTypeAlias = source.ContentTypeAlias;
|
||||
target.ContentTypeId = source.ContentTypeId;
|
||||
target.ContentTypeKey = source.ContentTypeKey;
|
||||
@@ -207,7 +206,7 @@ internal class ContentMapDefinition : IMapDefinition
|
||||
}
|
||||
}
|
||||
|
||||
// Umbraco.Code.MapAll
|
||||
// Umbraco.Code.MapAll -ContentDto
|
||||
private static void Map(ContentItemDisplayWithSchedule source, ContentItemDisplay target, MapperContext context)
|
||||
{
|
||||
foreach (KeyValuePair<string, object> additionalData in source.AdditionalData)
|
||||
@@ -219,7 +218,6 @@ internal class ContentMapDefinition : IMapDefinition
|
||||
target.AllowedTemplates = source.AllowedTemplates;
|
||||
target.AllowPreview = source.AllowPreview;
|
||||
target.ContentApps = source.ContentApps;
|
||||
target.ContentDto = source.ContentDto;
|
||||
target.ContentTypeAlias = source.ContentTypeAlias;
|
||||
target.ContentTypeId = source.ContentTypeId;
|
||||
target.ContentTypeKey = source.ContentTypeKey;
|
||||
@@ -253,7 +251,7 @@ internal class ContentMapDefinition : IMapDefinition
|
||||
private static void Map(IContent source, ContentPropertyCollectionDto target, MapperContext context) =>
|
||||
target.Properties = context.MapEnumerable<IProperty, ContentPropertyDto>(source.Properties).WhereNotNull();
|
||||
|
||||
// Umbraco.Code.MapAll -AllowPreview -Errors -PersistedContent
|
||||
// Umbraco.Code.MapAll -AllowPreview -Errors -PersistedContent -ContentDto
|
||||
private void Map<TVariant>(IContent source, ContentItemDisplay<TVariant> target, MapperContext context)
|
||||
where TVariant : ContentVariantDisplay
|
||||
{
|
||||
@@ -300,11 +298,6 @@ internal class ContentMapDefinition : IMapDefinition
|
||||
target.Updater = _commonMapper.GetCreator(source, context);
|
||||
target.Urls = GetUrls(source);
|
||||
target.Variants = _contentVariantMapper.Map<TVariant>(source, context);
|
||||
|
||||
target.ContentDto = new ContentPropertyCollectionDto
|
||||
{
|
||||
Properties = context.MapEnumerable<IProperty, ContentPropertyDto>(source.Properties).WhereNotNull()
|
||||
};
|
||||
}
|
||||
|
||||
// Umbraco.Code.MapAll -Segment -Language -DisplayName -AdditionalPreviewUrls
|
||||
|
||||
@@ -2,6 +2,7 @@ using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Cache.PropertyEditors;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Core.Models.Blocks;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
@@ -44,7 +45,7 @@ public class DataValueEditorReuseTests
|
||||
_propertyEditorCollection,
|
||||
_dataValueReferenceFactories,
|
||||
Mock.Of<IDataTypeConfigurationCache>(),
|
||||
Mock.Of<IContentTypeService>(),
|
||||
Mock.Of<IBlockEditorElementTypeCache>(),
|
||||
Mock.Of<ILocalizedTextService>(),
|
||||
Mock.Of<ILogger<BlockListPropertyEditorBase.BlockListEditorPropertyValueEditor>>(),
|
||||
Mock.Of<IShortStringHelper>(),
|
||||
|
||||
Reference in New Issue
Block a user