diff --git a/global.json b/global.json new file mode 100644 index 0000000000..bf78b4b0a8 --- /dev/null +++ b/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "version": "7.0.400", + "rollForward": "latestFeature", + "allowPrerelease": false + } +} diff --git a/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Umbraco.Cms.Persistence.EFCore.SqlServer.csproj b/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Umbraco.Cms.Persistence.EFCore.SqlServer.csproj index 04e711f8d9..43b5c89848 100644 --- a/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Umbraco.Cms.Persistence.EFCore.SqlServer.csproj +++ b/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Umbraco.Cms.Persistence.EFCore.SqlServer.csproj @@ -6,6 +6,7 @@ + diff --git a/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Umbraco.Cms.Persistence.EFCore.Sqlite.csproj b/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Umbraco.Cms.Persistence.EFCore.Sqlite.csproj index 5cb9949c04..d5f513b5cd 100644 --- a/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Umbraco.Cms.Persistence.EFCore.Sqlite.csproj +++ b/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Umbraco.Cms.Persistence.EFCore.Sqlite.csproj @@ -6,6 +6,7 @@ + diff --git a/src/Umbraco.Cms.Persistence.EFCore/Umbraco.Cms.Persistence.EFCore.csproj b/src/Umbraco.Cms.Persistence.EFCore/Umbraco.Cms.Persistence.EFCore.csproj index 3282f2622d..451ee7a7b7 100644 --- a/src/Umbraco.Cms.Persistence.EFCore/Umbraco.Cms.Persistence.EFCore.csproj +++ b/src/Umbraco.Cms.Persistence.EFCore/Umbraco.Cms.Persistence.EFCore.csproj @@ -9,6 +9,9 @@ + + + diff --git a/src/Umbraco.Cms.Persistence.Sqlite/Umbraco.Cms.Persistence.Sqlite.csproj b/src/Umbraco.Cms.Persistence.Sqlite/Umbraco.Cms.Persistence.Sqlite.csproj index 645e42d666..f82fd15ee0 100644 --- a/src/Umbraco.Cms.Persistence.Sqlite/Umbraco.Cms.Persistence.Sqlite.csproj +++ b/src/Umbraco.Cms.Persistence.Sqlite/Umbraco.Cms.Persistence.Sqlite.csproj @@ -6,6 +6,7 @@ + diff --git a/src/Umbraco.Core/PropertyEditors/DefaultPropertyIndexValueFactory.cs b/src/Umbraco.Core/PropertyEditors/DefaultPropertyIndexValueFactory.cs index 28f41c5a20..78fefda4d1 100644 --- a/src/Umbraco.Core/PropertyEditors/DefaultPropertyIndexValueFactory.cs +++ b/src/Umbraco.Core/PropertyEditors/DefaultPropertyIndexValueFactory.cs @@ -9,15 +9,22 @@ namespace Umbraco.Cms.Core.PropertyEditors; /// public class DefaultPropertyIndexValueFactory : IPropertyIndexValueFactory { - /// - public IEnumerable>> GetIndexValues(IProperty property, string? culture, string? segment, bool published, IEnumerable availableCultures) + public IEnumerable>> GetIndexValues(IProperty property, string? culture, string? segment, bool published, + IEnumerable availableCultures, IDictionary contentTypeDictionary) { yield return new KeyValuePair>( property.Alias, property.GetValue(culture, segment, published).Yield()); } - [Obsolete("Use the overload with the availableCultures parameter instead, scheduled for removal in v14")] + /// + [Obsolete("Use the non-obsolete overload, scheduled for removal in v14")] + public IEnumerable>> GetIndexValues(IProperty property, string? culture, + string? segment, bool published, IEnumerable availableCultures) + => GetIndexValues(property, culture, segment, published, availableCultures, + new Dictionary()); + + [Obsolete("Use the non-obsolete overload, scheduled for removal in v14")] public IEnumerable>> GetIndexValues(IProperty property, string? culture, string? segment, bool published) - => GetIndexValues(property, culture, segment, published, Enumerable.Empty()); + => GetIndexValues(property, culture, segment, published, Enumerable.Empty(), new Dictionary()); } diff --git a/src/Umbraco.Core/PropertyEditors/IPropertyIndexValueFactory.cs b/src/Umbraco.Core/PropertyEditors/IPropertyIndexValueFactory.cs index 732644b288..8f8b64a9eb 100644 --- a/src/Umbraco.Core/PropertyEditors/IPropertyIndexValueFactory.cs +++ b/src/Umbraco.Core/PropertyEditors/IPropertyIndexValueFactory.cs @@ -22,9 +22,14 @@ public interface IPropertyIndexValueFactory /// more than one value for a given field. /// /// + IEnumerable>> GetIndexValues(IProperty property, string? culture, + string? segment, bool published, IEnumerable availableCultures, + IDictionary contentTypeDictionary) => GetIndexValues(property, culture, segment, published); + + [Obsolete("Use non-obsolete overload, scheduled for removal in v14")] IEnumerable>> GetIndexValues(IProperty property, string? culture, string? segment, bool published, IEnumerable availableCultures) => GetIndexValues(property, culture, segment, published); - [Obsolete("Use the overload with the availableCultures parameter instead, scheduled for removal in v14")] + [Obsolete("Use non-obsolete overload, scheduled for removal in v14")] IEnumerable>> GetIndexValues(IProperty property, string? culture, string? segment, bool published); } diff --git a/src/Umbraco.Core/PropertyEditors/JsonPropertyIndexValueFactoryBase.cs b/src/Umbraco.Core/PropertyEditors/JsonPropertyIndexValueFactoryBase.cs index bf549e2d2e..973ee3d40c 100644 --- a/src/Umbraco.Core/PropertyEditors/JsonPropertyIndexValueFactoryBase.cs +++ b/src/Umbraco.Core/PropertyEditors/JsonPropertyIndexValueFactoryBase.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Serialization; +using Umbraco.Cms.Core.Services; using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; @@ -39,13 +40,13 @@ public abstract class JsonPropertyIndexValueFactoryBase : IProperty } - /// public IEnumerable>> GetIndexValues( IProperty property, string? culture, string? segment, bool published, - IEnumerable availableCultures) + IEnumerable availableCultures, + IDictionary contentTypeDictionary) { var result = new List>>(); @@ -63,7 +64,7 @@ public abstract class JsonPropertyIndexValueFactoryBase : IProperty return result; } - result.AddRange(Handle(deserializedPropertyValue, property, culture, segment, published, availableCultures)); + result.AddRange(Handle(deserializedPropertyValue, property, culture, segment, published, availableCultures, contentTypeDictionary)); } catch (InvalidCastException) { @@ -87,9 +88,31 @@ public abstract class JsonPropertyIndexValueFactoryBase : IProperty return summary; } + /// + [Obsolete("Use non-obsolete constructor. This will be removed in Umbraco 14.")] + public IEnumerable>> GetIndexValues( + IProperty property, + string? culture, + string? segment, + bool published, + IEnumerable availableCultures) + => GetIndexValues( + property, + culture, + segment, + published, + Enumerable.Empty(), + StaticServiceProvider.Instance.GetRequiredService().GetAll().ToDictionary(x=>x.Key)); + [Obsolete("Use method overload that has availableCultures, scheduled for removal in v14")] public IEnumerable>> GetIndexValues(IProperty property, string? culture, string? segment, bool published) - => GetIndexValues(property, culture, segment, published, Enumerable.Empty()); + => GetIndexValues( + property, + culture, + segment, + published, + Enumerable.Empty(), + StaticServiceProvider.Instance.GetRequiredService().GetAll().ToDictionary(x=>x.Key)); /// /// Method to return a list of summary of the content. By default this returns an empty list @@ -104,7 +127,7 @@ public abstract class JsonPropertyIndexValueFactoryBase : IProperty /// /// Method that handle the deserialized object. /// - [Obsolete("Use the overload with the availableCultures parameter instead, scheduled for removal in v14")] + [Obsolete("Use the non-obsolete overload instead, scheduled for removal in v14")] protected abstract IEnumerable>> Handle( TSerialized deserializedPropertyValue, IProperty property, @@ -112,6 +135,15 @@ public abstract class JsonPropertyIndexValueFactoryBase : IProperty string? segment, bool published); + [Obsolete("Use the non-obsolete overload instead, scheduled for removal in v14")] + protected virtual IEnumerable>> Handle( + TSerialized deserializedPropertyValue, + IProperty property, + string? culture, + string? segment, + bool published, + IEnumerable availableCultures) => Handle(deserializedPropertyValue, property, culture, segment, published); + /// /// Method that handle the deserialized object. /// @@ -121,6 +153,7 @@ public abstract class JsonPropertyIndexValueFactoryBase : IProperty string? culture, string? segment, bool published, - IEnumerable availableCultures) => - Handle(deserializedPropertyValue, property, culture, segment, published); + IEnumerable availableCultures, + IDictionary contentTypeDictionary) + => Handle(deserializedPropertyValue, property, culture, segment, published, availableCultures); } diff --git a/src/Umbraco.Core/PropertyEditors/NoopPropertyIndexValueFactory.cs b/src/Umbraco.Core/PropertyEditors/NoopPropertyIndexValueFactory.cs index 223f8632ff..004138e370 100644 --- a/src/Umbraco.Core/PropertyEditors/NoopPropertyIndexValueFactory.cs +++ b/src/Umbraco.Core/PropertyEditors/NoopPropertyIndexValueFactory.cs @@ -8,6 +8,12 @@ namespace Umbraco.Cms.Core.PropertyEditors; public class NoopPropertyIndexValueFactory : IPropertyIndexValueFactory { /// + public IEnumerable>> GetIndexValues(IProperty property, string? culture, string? segment, bool published, + IEnumerable availableCultures, IDictionary contentTypeDictionary) + => Array.Empty>>(); + + + [Obsolete("Use the overload with the availableCultures parameter instead, scheduled for removal in v14")] public IEnumerable>> GetIndexValues(IProperty property, string? culture, string? segment, bool published, IEnumerable availableCultures) => Array.Empty>>(); [Obsolete("Use the overload with the availableCultures parameter instead, scheduled for removal in v14")] diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 430a2573af..839a5afe90 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -3575,6 +3575,7 @@ public class ContentService : RepositoryService, IContentService Audit(AuditType.Save, userId, content.Id, $"Saved content template: {content.Name}"); scope.Notifications.Publish(new ContentSavedBlueprintNotification(content, evtMsgs)); + scope.Notifications.Publish(new ContentTreeChangeNotification(content, TreeChangeTypes.RefreshNode, evtMsgs)); scope.Complete(); } @@ -3589,6 +3590,7 @@ public class ContentService : RepositoryService, IContentService scope.WriteLock(Constants.Locks.ContentTree); _documentBlueprintRepository.Delete(content); scope.Notifications.Publish(new ContentDeletedBlueprintNotification(content, evtMsgs)); + scope.Notifications.Publish(new ContentTreeChangeNotification(content, TreeChangeTypes.Remove, evtMsgs)); scope.Complete(); } } @@ -3689,6 +3691,7 @@ public class ContentService : RepositoryService, IContentService } scope.Notifications.Publish(new ContentDeletedBlueprintNotification(blueprints, evtMsgs)); + scope.Notifications.Publish(new ContentTreeChangeNotification(blueprints, TreeChangeTypes.Remove, evtMsgs)); scope.Complete(); } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 4f4dd097b1..bbabfc74e9 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -17,6 +17,25 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/Umbraco.Infrastructure/Examine/BaseValueSetBuilder.cs b/src/Umbraco.Infrastructure/Examine/BaseValueSetBuilder.cs index 3574c3077f..83ecd85da4 100644 --- a/src/Umbraco.Infrastructure/Examine/BaseValueSetBuilder.cs +++ b/src/Umbraco.Infrastructure/Examine/BaseValueSetBuilder.cs @@ -1,6 +1,9 @@ using Examine; +using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.PropertyEditors; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Examine; @@ -24,9 +27,26 @@ public abstract class BaseValueSetBuilder : IValueSetBuilder [Obsolete("Use the overload that specifies availableCultures, scheduled for removal in v14")] protected void AddPropertyValue(IProperty property, string? culture, string? segment, IDictionary>? values) - => AddPropertyValue(property, culture, segment, values, Enumerable.Empty()); + => AddPropertyValue( + property, + culture, + segment, + values, + Enumerable.Empty(), + StaticServiceProvider.Instance.GetRequiredService().GetAll().ToDictionary(x=>x.Key)); - protected void AddPropertyValue(IProperty property, string? culture, string? segment, IDictionary>? values, IEnumerable availableCultures) + [Obsolete("Use the overload that specifies availableCultures, scheduled for removal in v14")] + protected void AddPropertyValue(IProperty property, string? culture, string? segment, + IDictionary>? values, IEnumerable availableCultures) + => AddPropertyValue( + property, + culture, + segment, + values, + Enumerable.Empty(), + StaticServiceProvider.Instance.GetRequiredService().GetAll().ToDictionary(x=>x.Key)); + + protected void AddPropertyValue(IProperty property, string? culture, string? segment, IDictionary>? values, IEnumerable availableCultures, IDictionary contentTypeDictionary) { IDataEditor? editor = _propertyEditors[property.PropertyType.PropertyEditorAlias]; if (editor == null) @@ -35,7 +55,7 @@ public abstract class BaseValueSetBuilder : IValueSetBuilder } IEnumerable>> indexVals = - editor.PropertyIndexValueFactory.GetIndexValues(property, culture, segment, PublishedValuesOnly, availableCultures); + editor.PropertyIndexValueFactory.GetIndexValues(property, culture, segment, PublishedValuesOnly, availableCultures, contentTypeDictionary); foreach (KeyValuePair> keyVal in indexVals) { if (keyVal.Key.IsNullOrWhiteSpace()) diff --git a/src/Umbraco.Infrastructure/Examine/ContentValueSetBuilder.cs b/src/Umbraco.Infrastructure/Examine/ContentValueSetBuilder.cs index 228610879d..2d59e0ebe3 100644 --- a/src/Umbraco.Infrastructure/Examine/ContentValueSetBuilder.cs +++ b/src/Umbraco.Infrastructure/Examine/ContentValueSetBuilder.cs @@ -21,13 +21,34 @@ public class ContentValueSetBuilder : BaseValueSetBuilder, IContentVal private static readonly object[] NoValue = new[] { "n" }; private static readonly object[] YesValue = new[] { "y" }; - private readonly IScopeProvider _scopeProvider; + private readonly ICoreScopeProvider _scopeProvider; private readonly IShortStringHelper _shortStringHelper; private readonly UrlSegmentProviderCollection _urlSegmentProviders; private readonly IUserService _userService; private readonly ILocalizationService _localizationService; + private readonly IContentTypeService _contentTypeService; + public ContentValueSetBuilder( + PropertyEditorCollection propertyEditors, + UrlSegmentProviderCollection urlSegmentProviders, + IUserService userService, + IShortStringHelper shortStringHelper, + ICoreScopeProvider scopeProvider, + bool publishedValuesOnly, + ILocalizationService localizationService, + IContentTypeService contentTypeService) + : base(propertyEditors, publishedValuesOnly) + { + _urlSegmentProviders = urlSegmentProviders; + _userService = userService; + _shortStringHelper = shortStringHelper; + _scopeProvider = scopeProvider; + _localizationService = localizationService; + _contentTypeService = contentTypeService; + } + + [Obsolete("Use non-obsolete ctor, scheduled for removal in v14")] public ContentValueSetBuilder( PropertyEditorCollection propertyEditors, UrlSegmentProviderCollection urlSegmentProviders, @@ -36,16 +57,20 @@ public class ContentValueSetBuilder : BaseValueSetBuilder, IContentVal IScopeProvider scopeProvider, bool publishedValuesOnly, ILocalizationService localizationService) - : base(propertyEditors, publishedValuesOnly) + : this( + propertyEditors, + urlSegmentProviders, + userService, + shortStringHelper, + scopeProvider, + publishedValuesOnly, + localizationService, + StaticServiceProvider.Instance.GetRequiredService()) { - _urlSegmentProviders = urlSegmentProviders; - _userService = userService; - _shortStringHelper = shortStringHelper; - _scopeProvider = scopeProvider; - _localizationService = localizationService; + } - [Obsolete("Use the constructor that takes an ILocalizationService, scheduled for removal in v14")] + [Obsolete("Use non-obsolete ctor, scheduled for removal in v14")] public ContentValueSetBuilder( PropertyEditorCollection propertyEditors, UrlSegmentProviderCollection urlSegmentProviders, @@ -60,7 +85,8 @@ public class ContentValueSetBuilder : BaseValueSetBuilder, IContentVal shortStringHelper, scopeProvider, publishedValuesOnly, - StaticServiceProvider.Instance.GetRequiredService()) + StaticServiceProvider.Instance.GetRequiredService(), + StaticServiceProvider.Instance.GetRequiredService()) { } @@ -72,7 +98,7 @@ public class ContentValueSetBuilder : BaseValueSetBuilder, IContentVal // We can lookup all of the creator/writer names at once which can save some // processing below instead of one by one. - using (IScope scope = _scopeProvider.CreateScope()) + using (ICoreScope scope = _scopeProvider.CreateCoreScope()) { creatorIds = _userService.GetProfilesById(content.Select(x => x.CreatorId).ToArray()) .ToDictionary(x => x.Id, x => x); @@ -86,6 +112,8 @@ public class ContentValueSetBuilder : BaseValueSetBuilder, IContentVal private IEnumerable GetValueSetsEnumerable(IContent[] content, Dictionary creatorIds, Dictionary writerIds) { + IDictionary contentTypeDictionary = _contentTypeService.GetAll().ToDictionary(x => x.Key); + // TODO: There is a lot of boxing going on here and ultimately all values will be boxed by Lucene anyways // but I wonder if there's a way to reduce the boxing that we have to do or if it will matter in the end since // Lucene will do it no matter what? One idea was to create a `FieldValue` struct which would contain `object`, `object[]`, `ValueType` and `ValueType[]` @@ -162,13 +190,13 @@ public class ContentValueSetBuilder : BaseValueSetBuilder, IContentVal { if (!property.PropertyType.VariesByCulture()) { - AddPropertyValue(property, null, null, values, availableCultures); + AddPropertyValue(property, null, null, values, availableCultures, contentTypeDictionary); } else { foreach (var culture in c.AvailableCultures) { - AddPropertyValue(property, culture.ToLowerInvariant(), null, values, availableCultures); + AddPropertyValue(property, culture.ToLowerInvariant(), null, values, availableCultures, contentTypeDictionary); } } } diff --git a/src/Umbraco.Infrastructure/Examine/MediaValueSetBuilder.cs b/src/Umbraco.Infrastructure/Examine/MediaValueSetBuilder.cs index fa7d6509cd..d2da36b347 100644 --- a/src/Umbraco.Infrastructure/Examine/MediaValueSetBuilder.cs +++ b/src/Umbraco.Infrastructure/Examine/MediaValueSetBuilder.cs @@ -1,10 +1,12 @@ using Examine; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Examine; @@ -14,6 +16,7 @@ public class MediaValueSetBuilder : BaseValueSetBuilder private readonly ContentSettings _contentSettings; private readonly MediaUrlGeneratorCollection _mediaUrlGenerators; private readonly IShortStringHelper _shortStringHelper; + private readonly IContentTypeService _contentTypeService; private readonly UrlSegmentProviderCollection _urlSegmentProviders; private readonly IUserService _userService; @@ -23,19 +26,41 @@ public class MediaValueSetBuilder : BaseValueSetBuilder MediaUrlGeneratorCollection mediaUrlGenerators, IUserService userService, IShortStringHelper shortStringHelper, - IOptions contentSettings) + IOptions contentSettings, + IContentTypeService contentTypeService) : base(propertyEditors, false) { _urlSegmentProviders = urlSegmentProviders; _mediaUrlGenerators = mediaUrlGenerators; _userService = userService; _shortStringHelper = shortStringHelper; + _contentTypeService = contentTypeService; _contentSettings = contentSettings.Value; } + [Obsolete("Use non-obsolete ctor, scheduled for removal in v14")] + public MediaValueSetBuilder( + PropertyEditorCollection propertyEditors, + UrlSegmentProviderCollection urlSegmentProviders, + MediaUrlGeneratorCollection mediaUrlGenerators, + IUserService userService, + IShortStringHelper shortStringHelper, + IOptions contentSettings) + : this(propertyEditors, + urlSegmentProviders, + mediaUrlGenerators, + userService, + shortStringHelper, + contentSettings, + StaticServiceProvider.Instance.GetRequiredService()) + { + + } /// public override IEnumerable GetValueSets(params IMedia[] media) { + IDictionary contentTypeDictionary = _contentTypeService.GetAll().ToDictionary(x => x.Key); + foreach (IMedia m in media) { var urlValue = m.GetUrlSegment(_shortStringHelper, _urlSegmentProviders); @@ -65,7 +90,7 @@ public class MediaValueSetBuilder : BaseValueSetBuilder foreach (IProperty property in m.Properties) { - AddPropertyValue(property, null, null, values, m.AvailableCultures); + AddPropertyValue(property, null, null, values, m.AvailableCultures, contentTypeDictionary); } var vs = new ValueSet(m.Id.ToInvariantString(), IndexTypes.Media, m.ContentType.Alias, values); diff --git a/src/Umbraco.Infrastructure/Examine/MemberValueSetBuilder.cs b/src/Umbraco.Infrastructure/Examine/MemberValueSetBuilder.cs index 1b0bf7219f..8fe2a56856 100644 --- a/src/Umbraco.Infrastructure/Examine/MemberValueSetBuilder.cs +++ b/src/Umbraco.Infrastructure/Examine/MemberValueSetBuilder.cs @@ -1,20 +1,34 @@ using Examine; +using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.PropertyEditors; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Examine; public class MemberValueSetBuilder : BaseValueSetBuilder { - public MemberValueSetBuilder(PropertyEditorCollection propertyEditors) + private readonly IContentTypeService _contentTypeService; + + public MemberValueSetBuilder(PropertyEditorCollection propertyEditors, IContentTypeService contentTypeService) : base(propertyEditors, false) + { + _contentTypeService = contentTypeService; + } + + [Obsolete("Use non-obsolete ctor, scheduled for removal in v14")] + public MemberValueSetBuilder(PropertyEditorCollection propertyEditors) + : this(propertyEditors, StaticServiceProvider.Instance.GetRequiredService()) { } /// public override IEnumerable GetValueSets(params IMember[] members) { + IDictionary contentTypeDictionary = _contentTypeService.GetAll().ToDictionary(x => x.Key); + foreach (IMember m in members) { var values = new Dictionary> @@ -37,7 +51,7 @@ public class MemberValueSetBuilder : BaseValueSetBuilder foreach (IProperty property in m.Properties) { - AddPropertyValue(property, null, null, values, m.AvailableCultures); + AddPropertyValue(property, null, null, values, m.AvailableCultures, contentTypeDictionary); } var vs = new ValueSet(m.Id.ToInvariantString(), IndexTypes.Member, m.ContentType.Alias, values); diff --git a/src/Umbraco.Infrastructure/Logging/Serilog/Enrichers/ThreadAbortExceptionEnricher.cs b/src/Umbraco.Infrastructure/Logging/Serilog/Enrichers/ThreadAbortExceptionEnricher.cs index 4083aa7311..650b592779 100644 --- a/src/Umbraco.Infrastructure/Logging/Serilog/Enrichers/ThreadAbortExceptionEnricher.cs +++ b/src/Umbraco.Infrastructure/Logging/Serilog/Enrichers/ThreadAbortExceptionEnricher.cs @@ -67,7 +67,7 @@ public class ThreadAbortExceptionEnricher : ILogEventEnricher private void DumpThreadAborts(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) { - if (!IsTimeoutThreadAbortException(logEvent.Exception)) + if (logEvent.Exception is null || !IsTimeoutThreadAbortException(logEvent.Exception)) { return; } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/BlockValuePropertyIndexValueFactory.cs b/src/Umbraco.Infrastructure/PropertyEditors/BlockValuePropertyIndexValueFactory.cs index adace6126e..95185a3f30 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/BlockValuePropertyIndexValueFactory.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/BlockValuePropertyIndexValueFactory.cs @@ -38,10 +38,13 @@ internal sealed class BlockValuePropertyIndexValueFactory : _contentTypeService = contentTypeService; } - + [Obsolete("Use non-obsolete overload, scheduled for removal in v14")] protected override IContentType? GetContentTypeOfNestedItem(BlockItemData input) => _contentTypeService.Get(input.ContentTypeKey); + protected override IContentType? GetContentTypeOfNestedItem(BlockItemData input, IDictionary contentTypeDictionary) + => contentTypeDictionary.TryGetValue(input.ContentTypeKey, out var result) ? result : null; + protected override IDictionary GetRawProperty(BlockItemData blockItemData) => blockItemData.RawPropertyValues; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerValueEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerValueEditor.cs index 9a7fa4b3bb..1b76cd89a5 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerValueEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerValueEditor.cs @@ -2,19 +2,20 @@ // See LICENSE for more details. using System.Runtime.Serialization; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.ContentEditing; using Umbraco.Cms.Core.Models.Editors; -using Umbraco.Cms.Core.Models.Entities; +using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; -using Umbraco.Extensions; +using Umbraco.Cms.Web.Common.DependencyInjection; namespace Umbraco.Cms.Core.PropertyEditors; @@ -26,11 +27,57 @@ public class MultiUrlPickerValueEditor : DataValueEditor, IDataValueReference NullValueHandling = NullValueHandling.Ignore, }; - private readonly IEntityService _entityService; private readonly ILogger _logger; - private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; private readonly IPublishedUrlProvider _publishedUrlProvider; + private readonly IContentService _contentService; + private readonly IMediaService _mediaService; + public MultiUrlPickerValueEditor( + ILogger logger, + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper, + DataEditorAttribute attribute, + IPublishedUrlProvider publishedUrlProvider, + IJsonSerializer jsonSerializer, + IIOHelper ioHelper, + IContentService contentService, + IMediaService mediaService) + : base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _publishedUrlProvider = publishedUrlProvider; + _contentService = contentService; + _mediaService = mediaService; + } + + [Obsolete("Use non-obsolete constructor. Scheduled for removal in Umbraco 14.")] + public MultiUrlPickerValueEditor( + IEntityService entityService, + IPublishedSnapshotAccessor publishedSnapshotAccessor, + ILogger logger, + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper, + DataEditorAttribute attribute, + IPublishedUrlProvider publishedUrlProvider, + IJsonSerializer jsonSerializer, + IIOHelper ioHelper, + IContentService contentService, + IMediaService mediaService) + :this( + logger, + localizedTextService, + shortStringHelper, + attribute, + publishedUrlProvider, + jsonSerializer, + ioHelper, + contentService, + mediaService) + { + + } + + [Obsolete("Use non-obsolete constructor. Scheduled for removal in Umbraco 14.")] public MultiUrlPickerValueEditor( IEntityService entityService, IPublishedSnapshotAccessor publishedSnapshotAccessor, @@ -41,13 +88,18 @@ public class MultiUrlPickerValueEditor : DataValueEditor, IDataValueReference IPublishedUrlProvider publishedUrlProvider, IJsonSerializer jsonSerializer, IIOHelper ioHelper) - : base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute) + : this( + logger, + localizedTextService, + shortStringHelper, + attribute, + publishedUrlProvider, + jsonSerializer, + ioHelper, + StaticServiceProvider.Instance.GetRequiredService(), + StaticServiceProvider.Instance.GetRequiredService()) { - _entityService = entityService ?? throw new ArgumentNullException(nameof(entityService)); - _publishedSnapshotAccessor = publishedSnapshotAccessor ?? - throw new ArgumentNullException(nameof(publishedSnapshotAccessor)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _publishedUrlProvider = publishedUrlProvider; + } public IEnumerable GetReferences(object? value) @@ -86,26 +138,6 @@ public class MultiUrlPickerValueEditor : DataValueEditor, IDataValueReference { List? links = JsonConvert.DeserializeObject>(value); - List? documentLinks = links?.FindAll(link => - link.Udi != null && link.Udi.EntityType == Constants.UdiEntityType.Document); - List? mediaLinks = links?.FindAll(link => - link.Udi != null && link.Udi.EntityType == Constants.UdiEntityType.Media); - - var entities = new List(); - if (documentLinks?.Count > 0) - { - entities.AddRange( - _entityService.GetAll( - UmbracoObjectTypes.Document, - documentLinks.Select(link => link.Udi!.Guid).ToArray())); - } - - if (mediaLinks?.Count > 0) - { - entities.AddRange( - _entityService.GetAll(UmbracoObjectTypes.Media, mediaLinks.Select(link => link.Udi!.Guid).ToArray())); - } - var result = new List(); if (links is null) { @@ -114,7 +146,7 @@ public class MultiUrlPickerValueEditor : DataValueEditor, IDataValueReference foreach (LinkDto dto in links) { - GuidUdi? udi = null; + GuidUdi? udi = dto.Udi; var icon = "icon-link"; var published = true; var trashed = false; @@ -122,35 +154,30 @@ public class MultiUrlPickerValueEditor : DataValueEditor, IDataValueReference if (dto.Udi != null) { - IUmbracoEntity? entity = entities.Find(e => e.Key == dto.Udi.Guid); - if (entity == null) + if (dto.Udi.EntityType == Constants.UdiEntityType.Document) { - continue; - } + url = _publishedUrlProvider.GetUrl(dto.Udi.Guid, UrlMode.Relative, culture); + IContent? c = _contentService.GetById(dto.Udi.Guid); - IPublishedSnapshot publishedSnapshot = _publishedSnapshotAccessor.GetRequiredPublishedSnapshot(); - if (entity is IDocumentEntitySlim documentEntity) - { - icon = documentEntity.ContentTypeIcon; - published = culture == null - ? documentEntity.Published - : documentEntity.PublishedCultures.Contains(culture); - udi = new GuidUdi(Constants.UdiEntityType.Document, documentEntity.Key); - url = publishedSnapshot.Content?.GetById(entity.Key)?.Url(_publishedUrlProvider) ?? "#"; - trashed = documentEntity.Trashed; + if (c is not null) + { + published = culture == null + ? c.Published + : c.PublishedCultures.Contains(culture); + icon = c.ContentType.Icon; + trashed = c.Trashed; + } } - else if (entity is IContentEntitySlim contentEntity) + else if (dto.Udi.EntityType == Constants.UdiEntityType.Media) { - icon = contentEntity.ContentTypeIcon; - published = !contentEntity.Trashed; - udi = new GuidUdi(Constants.UdiEntityType.Media, contentEntity.Key); - url = publishedSnapshot.Media?.GetById(entity.Key)?.Url(_publishedUrlProvider) ?? "#"; - trashed = contentEntity.Trashed; - } - else - { - // Not supported - continue; + url = _publishedUrlProvider.GetMediaUrl(dto.Udi.Guid, UrlMode.Relative, culture); + IMedia? m = _mediaService.GetById(dto.Udi.Guid); + if (m is not null) + { + published = m.Trashed is false; + icon = m.ContentType.Icon; + trashed = m.Trashed; + } } } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyIndexValueFactory.cs b/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyIndexValueFactory.cs index 121a40bec9..693c21060b 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyIndexValueFactory.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyIndexValueFactory.cs @@ -39,6 +39,11 @@ internal sealed class NestedContentPropertyIndexValueFactory _contentTypeService = contentTypeService; } + protected override IContentType? GetContentTypeOfNestedItem( + NestedContentPropertyEditor.NestedContentValues.NestedContentRowValue input, IDictionary contentTypeDictionary) + => contentTypeDictionary.Values.FirstOrDefault(x=>x.Alias.Equals(input.ContentTypeAlias)); + + [Obsolete("Use non-obsolete overload, scheduled for removal in v14")] protected override IContentType? GetContentTypeOfNestedItem( NestedContentPropertyEditor.NestedContentValues.NestedContentRowValue input) => _contentTypeService.Get(input.ContentTypeAlias); diff --git a/src/Umbraco.Infrastructure/PropertyEditors/NestedPropertyIndexValueFactoryBase.cs b/src/Umbraco.Infrastructure/PropertyEditors/NestedPropertyIndexValueFactoryBase.cs index 94ed0a3e15..a675b38b2c 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/NestedPropertyIndexValueFactoryBase.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/NestedPropertyIndexValueFactoryBase.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Serialization; +using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.Examine; using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; @@ -42,20 +43,39 @@ internal abstract class NestedPropertyIndexValueFactoryBase bool published) => Handle(deserializedPropertyValue, property, culture, segment, published, Enumerable.Empty()); + [Obsolete("Use the overload that specifies availableCultures, scheduled for removal in v14")] protected override IEnumerable>> Handle( TSerialized deserializedPropertyValue, IProperty property, string? culture, string? segment, bool published, - IEnumerable availableCultures) + IEnumerable availableCultures) => + Handle( + deserializedPropertyValue, + property, + culture, + segment, + published, + Enumerable.Empty(), + StaticServiceProvider.Instance.GetRequiredService().GetAll().ToDictionary(x=>x.Key)); + + + protected override IEnumerable>> Handle( + TSerialized deserializedPropertyValue, + IProperty property, + string? culture, + string? segment, + bool published, + IEnumerable availableCultures, + IDictionary contentTypeDictionary) { var result = new List>>(); var index = 0; foreach (TItem nestedContentRowValue in GetDataItems(deserializedPropertyValue)) { - IContentType? contentType = GetContentTypeOfNestedItem(nestedContentRowValue); + IContentType? contentType = GetContentTypeOfNestedItem(nestedContentRowValue, contentTypeDictionary); if (contentType is null) { @@ -125,6 +145,9 @@ internal abstract class NestedPropertyIndexValueFactoryBase /// /// Gets the content type using the nested item. /// + protected abstract IContentType? GetContentTypeOfNestedItem(TItem nestedItem, IDictionary contentTypeDictionary); + + [Obsolete("Use non-obsolete overload. Scheduled for removal in Umbraco 14.")] protected abstract IContentType? GetContentTypeOfNestedItem(TItem nestedItem); /// diff --git a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj index 1c2e897ebc..e20071b19e 100644 --- a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj +++ b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj @@ -27,6 +27,23 @@ + + + + + + + + + + + + + + + + + @@ -37,6 +54,9 @@ + + + diff --git a/src/Umbraco.PublishedCache.NuCache/Persistence/NuCacheContentRepository.cs b/src/Umbraco.PublishedCache.NuCache/Persistence/NuCacheContentRepository.cs index af8c46821f..f54158dac1 100644 --- a/src/Umbraco.PublishedCache.NuCache/Persistence/NuCacheContentRepository.cs +++ b/src/Umbraco.PublishedCache.NuCache/Persistence/NuCacheContentRepository.cs @@ -224,7 +224,7 @@ AND cmsContentNu.nodeId IS NULL IContentCacheDataSerializer serializer = _contentCacheDataSerializerFactory.Create(ContentCacheDataSerializerEntityType.Document); - IEnumerable dtos = GetContentNodeDtos(sql); + IEnumerable dtos = GetContentNodeDtos(sql, Constants.ObjectTypes.Document); foreach (ContentSourceDto row in dtos) { @@ -242,7 +242,7 @@ AND cmsContentNu.nodeId IS NULL IContentCacheDataSerializer serializer = _contentCacheDataSerializerFactory.Create(ContentCacheDataSerializerEntityType.Document); - IEnumerable dtos = GetContentNodeDtos(sql); + IEnumerable dtos = GetContentNodeDtos(sql, Constants.ObjectTypes.Document); foreach (ContentSourceDto row in dtos) { @@ -265,7 +265,7 @@ AND cmsContentNu.nodeId IS NULL IContentCacheDataSerializer serializer = _contentCacheDataSerializerFactory.Create(ContentCacheDataSerializerEntityType.Document); - IEnumerable dtos = GetContentNodeDtos(sql); + IEnumerable dtos = GetContentNodeDtos(sql, Constants.ObjectTypes.Document); foreach (ContentSourceDto row in dtos) { @@ -301,7 +301,7 @@ AND cmsContentNu.nodeId IS NULL IContentCacheDataSerializer serializer = _contentCacheDataSerializerFactory.Create(ContentCacheDataSerializerEntityType.Media); - IEnumerable dtos = GetContentNodeDtos(sql); + IEnumerable dtos = GetContentNodeDtos(sql, Constants.ObjectTypes.Media); foreach (ContentSourceDto row in dtos) { @@ -319,7 +319,7 @@ AND cmsContentNu.nodeId IS NULL IContentCacheDataSerializer serializer = _contentCacheDataSerializerFactory.Create(ContentCacheDataSerializerEntityType.Media); - IEnumerable dtos = GetContentNodeDtos(sql); + IEnumerable dtos = GetContentNodeDtos(sql, Constants.ObjectTypes.Media); foreach (ContentSourceDto row in dtos) { @@ -342,7 +342,7 @@ AND cmsContentNu.nodeId IS NULL IContentCacheDataSerializer serializer = _contentCacheDataSerializerFactory.Create(ContentCacheDataSerializerEntityType.Media); - IEnumerable dtos = GetContentNodeDtos(sql); + IEnumerable dtos = GetContentNodeDtos(sql, Constants.ObjectTypes.Media); foreach (ContentSourceDto row in dtos) { @@ -990,7 +990,7 @@ WHERE cmsContentNu.nodeId IN ( return s; } - private IEnumerable GetContentNodeDtos(Sql sql) + private IEnumerable GetContentNodeDtos(Sql sql, Guid nodeObjectType) { // We need to page here. We don't want to iterate over every single row in one connection cuz this can cause an SQL Timeout. // We also want to read with a db reader and not load everything into memory, QueryPaged lets us do that. @@ -1000,7 +1000,7 @@ WHERE cmsContentNu.nodeId IN ( { // Use a more efficient COUNT query Sql? sqlCountQuery = SqlContentSourcesCount() - .Append(SqlObjectTypeNotTrashed(SqlContext, Constants.ObjectTypes.Document)); + .Append(SqlObjectTypeNotTrashed(SqlContext, nodeObjectType)); Sql? sqlCount = SqlContext.Sql("SELECT COUNT(*) FROM (").Append(sqlCountQuery).Append(") npoco_tbl"); diff --git a/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj b/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj index 5eb8a7a62e..4ceef9469e 100644 --- a/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj +++ b/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj @@ -8,6 +8,7 @@ + diff --git a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj index 0b26d82837..b4f2571df3 100644 --- a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj +++ b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj @@ -12,7 +12,11 @@ + + + + diff --git a/src/Umbraco.Web.UI.Client/src/installer/steps/permissionsreport.html b/src/Umbraco.Web.UI.Client/src/installer/steps/permissionsreport.html index f3067e8fb6..ae3d4ed49a 100644 --- a/src/Umbraco.Web.UI.Client/src/installer/steps/permissionsreport.html +++ b/src/Umbraco.Web.UI.Client/src/installer/steps/permissionsreport.html @@ -3,7 +3,7 @@

In order to run Umbraco, you'll need to update your permission settings. Detailed information about the correct file and folder permissions for Umbraco can be found - here. + here.

The following report list the permissions that are currently failing. Once the permissions are fixed press the 'Go back' button to restart the installation. diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index c7258061d5..1a1f224046 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -17,6 +17,14 @@ + + + + + + + all + diff --git a/tests/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj b/tests/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj index c84c8fad6e..8dd0bd4060 100644 --- a/tests/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj +++ b/tests/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj @@ -13,7 +13,7 @@ - + diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj b/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj index e706538a02..c917f0d120 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj +++ b/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj @@ -13,6 +13,8 @@ + + diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj b/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj index 2a1a8110fa..392144f4a6 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj @@ -7,6 +7,8 @@ + + diff --git a/umbraco.sln b/umbraco.sln index 36d381c729..432c5d1afc 100644 --- a/umbraco.sln +++ b/umbraco.sln @@ -132,6 +132,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution LICENSE.md = LICENSE.md umbraco.sln.DotSettings = umbraco.sln.DotSettings version.json = version.json + global.json = global.json EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{20CE9C97-9314-4A19-BCF1-D12CF49B7205}"