diff --git a/Directory.Packages.props b/Directory.Packages.props index 8bedbf588a..5a8717576b 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -12,28 +12,28 @@ - + - - - + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + @@ -58,9 +58,9 @@ - - - + + + @@ -69,7 +69,7 @@ - + @@ -81,6 +81,8 @@ + + @@ -88,9 +90,9 @@ - + - + diff --git a/global.json b/global.json index 9c2a135743..1ada742598 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.100-rc.1.24452.12", + "version": "9.0.100-rc.2.24474.11", "rollForward": "latestFeature", "allowPrerelease": true } diff --git a/src/Umbraco.Cms.Api.Common/DependencyInjection/UmbracoBuilderAuthExtensions.cs b/src/Umbraco.Cms.Api.Common/DependencyInjection/UmbracoBuilderAuthExtensions.cs index f34fc2dde9..1558329746 100644 --- a/src/Umbraco.Cms.Api.Common/DependencyInjection/UmbracoBuilderAuthExtensions.cs +++ b/src/Umbraco.Cms.Api.Common/DependencyInjection/UmbracoBuilderAuthExtensions.cs @@ -41,7 +41,7 @@ public static class UmbracoBuilderAuthExtensions .SetTokenEndpointUris( Paths.MemberApi.TokenEndpoint.TrimStart(Constants.CharArrays.ForwardSlash), Paths.BackOfficeApi.TokenEndpoint.TrimStart(Constants.CharArrays.ForwardSlash)) - .SetLogoutEndpointUris( + .SetEndSessionEndpointUris( Paths.MemberApi.LogoutEndpoint.TrimStart(Constants.CharArrays.ForwardSlash), Paths.BackOfficeApi.LogoutEndpoint.TrimStart(Constants.CharArrays.ForwardSlash)) .SetRevocationEndpointUris( @@ -62,7 +62,7 @@ public static class UmbracoBuilderAuthExtensions .UseAspNetCore() .EnableAuthorizationEndpointPassthrough() .EnableTokenEndpointPassthrough() - .EnableLogoutEndpointPassthrough(); + .EnableEndSessionEndpointPassthrough(); // Enable reference tokens // - see https://documentation.openiddict.com/configuration/token-storage.html diff --git a/src/Umbraco.Cms.Api.Delivery/Security/MemberApplicationManager.cs b/src/Umbraco.Cms.Api.Delivery/Security/MemberApplicationManager.cs index 67cfb4b7cf..9cfd7b78e1 100644 --- a/src/Umbraco.Cms.Api.Delivery/Security/MemberApplicationManager.cs +++ b/src/Umbraco.Cms.Api.Delivery/Security/MemberApplicationManager.cs @@ -41,7 +41,7 @@ public class MemberApplicationManager : OpenIdDictApplicationManagerBase, IMembe { OpenIddictConstants.Permissions.Endpoints.Authorization, OpenIddictConstants.Permissions.Endpoints.Token, - OpenIddictConstants.Permissions.Endpoints.Logout, + OpenIddictConstants.Permissions.Endpoints.EndSession, OpenIddictConstants.Permissions.Endpoints.Revocation, OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode, OpenIddictConstants.Permissions.GrantTypes.RefreshToken, diff --git a/src/Umbraco.Cms.Api.Management/Security/BackOfficeApplicationManager.cs b/src/Umbraco.Cms.Api.Management/Security/BackOfficeApplicationManager.cs index 36fe1b0acc..cbf0fcd8ad 100644 --- a/src/Umbraco.Cms.Api.Management/Security/BackOfficeApplicationManager.cs +++ b/src/Umbraco.Cms.Api.Management/Security/BackOfficeApplicationManager.cs @@ -132,7 +132,7 @@ public class BackOfficeApplicationManager : OpenIdDictApplicationManagerBase, IB { OpenIddictConstants.Permissions.Endpoints.Authorization, OpenIddictConstants.Permissions.Endpoints.Token, - OpenIddictConstants.Permissions.Endpoints.Logout, + OpenIddictConstants.Permissions.Endpoints.EndSession, OpenIddictConstants.Permissions.Endpoints.Revocation, OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode, OpenIddictConstants.Permissions.GrantTypes.RefreshToken, 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 a1864f636f..264cf02579 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,8 +6,12 @@ + + + + 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 e9d05b5633..bbf03ea87a 100644 --- a/src/Umbraco.Cms.Persistence.EFCore/Umbraco.Cms.Persistence.EFCore.csproj +++ b/src/Umbraco.Cms.Persistence.EFCore/Umbraco.Cms.Persistence.EFCore.csproj @@ -16,6 +16,8 @@ + + diff --git a/src/Umbraco.Cms.Persistence.SqlServer/Umbraco.Cms.Persistence.SqlServer.csproj b/src/Umbraco.Cms.Persistence.SqlServer/Umbraco.Cms.Persistence.SqlServer.csproj index d5edbbee4c..3868136baa 100644 --- a/src/Umbraco.Cms.Persistence.SqlServer/Umbraco.Cms.Persistence.SqlServer.csproj +++ b/src/Umbraco.Cms.Persistence.SqlServer/Umbraco.Cms.Persistence.SqlServer.csproj @@ -18,7 +18,8 @@ - + + diff --git a/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoLogin/Index.cshtml b/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoLogin/Index.cshtml index 716521066c..856796cd25 100644 --- a/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoLogin/Index.cshtml +++ b/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoLogin/Index.cshtml @@ -31,6 +31,7 @@ var allowPasswordReset = SecuritySettings.Value.AllowPasswordReset && EmailSender.CanSendRequiredEmail(); var disableLocalLogin = ExternalLogins.HasDenyLocalLogin(); } +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @@ -44,7 +45,7 @@ Umbraco - + @await Html.BackOfficeImportMapScriptAsync(JsonSerializer, BackOfficePathGenerator, PackageManifestService) - + diff --git a/src/Umbraco.Core/Cache/Refreshers/Implement/ContentTypeCacheRefresher.cs b/src/Umbraco.Core/Cache/Refreshers/Implement/ContentTypeCacheRefresher.cs index dba66ec1b0..f9936933cc 100644 --- a/src/Umbraco.Core/Cache/Refreshers/Implement/ContentTypeCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/Refreshers/Implement/ContentTypeCacheRefresher.cs @@ -14,6 +14,7 @@ public sealed class ContentTypeCacheRefresher : PayloadCacheRefresherBase { }); + _publishedContentTypeFactory.NotifyDataTypeChanges(); + // now we can trigger the event base.Refresh(payloads); } diff --git a/src/Umbraco.Core/Cache/Refreshers/Implement/DataTypeCacheRefresher.cs b/src/Umbraco.Core/Cache/Refreshers/Implement/DataTypeCacheRefresher.cs index f28dd89ea5..de25660fa0 100644 --- a/src/Umbraco.Core/Cache/Refreshers/Implement/DataTypeCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/Refreshers/Implement/DataTypeCacheRefresher.cs @@ -13,6 +13,7 @@ public sealed class DataTypeCacheRefresher : PayloadCacheRefresherBase { }); + var changedIds = payloads.Select(x => x.Id).ToArray(); + _publishedContentTypeFactory.NotifyDataTypeChanges(changedIds); + base.Refresh(payloads); } diff --git a/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs b/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs index 7dde30fb98..7d9f66052f 100644 --- a/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs +++ b/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs @@ -2,7 +2,9 @@ // See LICENSE for more details. using System.Data; +using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Membership; using Umbraco.Cms.Core.Models.PublishedContent; @@ -1909,5 +1911,491 @@ public static class PublishedContentExtensions private static Dictionary GetAliasesAndNames(IContentTypeBase? contentType) => contentType?.PropertyTypes.ToDictionary(x => x.Alias, x => x.Name) ?? new Dictionary(); + + #endregion + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IPublishedContent? Ancestor(this IPublishedContent content, int maxLevel) + { + return content.Ancestor(GetPublishedCache(content), GetNavigationQueryService(content), maxLevel); + } + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IPublishedContent? Ancestor(this IPublishedContent content, string contentTypeAlias) + { + return content.Ancestor(GetPublishedCache(content), GetNavigationQueryService(content), contentTypeAlias); + } + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static T? Ancestor(this IPublishedContent content, int maxLevel) + where T : class, IPublishedContent + { + return Ancestor(content, GetPublishedCache(content), GetNavigationQueryService(content), maxLevel); + } + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable Ancestors(this IPublishedContent content, int maxLevel) + { + return content.Ancestors(GetPublishedCache(content), GetNavigationQueryService(content), maxLevel); + } + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable Ancestors(this IPublishedContent content, string contentTypeAlias) + { + return content.Ancestors(GetPublishedCache(content), GetNavigationQueryService(content), contentTypeAlias); + } + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable Ancestors(this IPublishedContent content) + where T : class, IPublishedContent + { + return Ancestors(content, GetPublishedCache(content), GetNavigationQueryService(content)); + } + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable Ancestors(this IPublishedContent content, int maxLevel) + where T : class, IPublishedContent + { + return Ancestors(content, GetPublishedCache(content), GetNavigationQueryService(content), maxLevel); + } + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IPublishedContent AncestorOrSelf(this IPublishedContent content, int maxLevel) + { + return AncestorOrSelf(content, GetPublishedCache(content), GetNavigationQueryService(content), maxLevel); + } + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IPublishedContent AncestorOrSelf(this IPublishedContent content, string contentTypeAlias) + { + return AncestorOrSelf(content, GetPublishedCache(content), GetNavigationQueryService(content), contentTypeAlias); + } + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static T? AncestorOrSelf(this IPublishedContent content, int maxLevel) + where T : class, IPublishedContent + { + return AncestorOrSelf(content, GetPublishedCache(content), GetNavigationQueryService(content), maxLevel); + } + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable AncestorsOrSelf(this IPublishedContent content, int maxLevel) + { + return content.AncestorsOrSelf(GetPublishedCache(content), GetNavigationQueryService(content), maxLevel); + } + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable AncestorsOrSelf(this IPublishedContent content, string contentTypeAlias) + { + return content.Ancestors(GetPublishedCache(content), GetNavigationQueryService(content), contentTypeAlias); + } + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable AncestorsOrSelf(this IPublishedContent content, int maxLevel) + where T : class, IPublishedContent + { + return AncestorsOrSelf(content, GetPublishedCache(content), GetNavigationQueryService(content), maxLevel); + } + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable AncestorsOrSelf(this IPublishedContent content, bool orSelf, + Func? func) + { + return AncestorsOrSelf(content, GetPublishedCache(content), GetNavigationQueryService(content), orSelf, func); + } + + [Obsolete( + "Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable Breadcrumbs( + this IPublishedContent content, + bool andSelf = true) => + content.Breadcrumbs(GetPublishedCache(content), GetNavigationQueryService(content), andSelf); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable Breadcrumbs( + this IPublishedContent content, + int minLevel, + bool andSelf = true) => + content.Breadcrumbs(GetPublishedCache(content), GetNavigationQueryService(content), minLevel, andSelf); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable Breadcrumbs( + this IPublishedContent content, + bool andSelf = true) + where T : class, IPublishedContent=> + content.Breadcrumbs(GetPublishedCache(content), GetNavigationQueryService(content), andSelf); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable Children( + this IPublishedContent content, + IVariationContextAccessor? variationContextAccessor, + string? culture = null) + => Children(content, variationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), culture); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable Children( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + Func predicate, + string? culture = null) => + content.Children(variationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), culture).Where(predicate); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable ChildrenOfType( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + string? contentTypeAlias, + string? culture = null) => + content.Children(variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), x => x.ContentType.Alias.InvariantEquals(contentTypeAlias), + culture); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable Children( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + string? culture = null) + where T : class, IPublishedContent => + content.Children(variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), culture).OfType(); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static DataTable ChildrenAsTable( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + IContentTypeService contentTypeService, + IMediaTypeService mediaTypeService, + IMemberTypeService memberTypeService, + IPublishedUrlProvider publishedUrlProvider, + string contentTypeAliasFilter = "", + string? culture = null) + => GenerateDataTable(content, variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), contentTypeService, mediaTypeService, memberTypeService, publishedUrlProvider, contentTypeAliasFilter, culture); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable DescendantsOrSelfOfType( + this IEnumerable parentNodes, + IVariationContextAccessor variationContextAccessor, + string docTypeAlias, + string? culture = null) => parentNodes.SelectMany(x => + x.DescendantsOrSelfOfType(variationContextAccessor, GetPublishedCache(parentNodes.First()), + GetNavigationQueryService(parentNodes.First()), docTypeAlias, culture)); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable DescendantsOrSelf( + this IEnumerable parentNodes, + IVariationContextAccessor variationContextAccessor, + string? culture = null) + where T : class, IPublishedContent => + parentNodes.SelectMany(x => x.DescendantsOrSelf(variationContextAccessor, GetPublishedCache(parentNodes.First()), + GetNavigationQueryService(parentNodes.First()), culture)); + + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable Descendants( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + string? culture = null) => + content.DescendantsOrSelf(variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), false, null, culture); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable Descendants( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + int level, + string? culture = null) => + content.DescendantsOrSelf(variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), false, p => p.Level >= level, culture); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable DescendantsOfType( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + string contentTypeAlias, string? culture = null) => + content.DescendantsOrSelf(variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), false, p => p.ContentType.Alias.InvariantEquals(contentTypeAlias), culture); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable Descendants( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + string? culture = null) + where T : class, IPublishedContent => + content.Descendants(variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), culture).OfType(); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable Descendants( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + int level, + string? culture = null) + where T : class, IPublishedContent => + content.Descendants(variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), level, culture).OfType(); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable DescendantsOrSelf( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + string? culture = null) => + content.DescendantsOrSelf(variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), true, null, culture); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable DescendantsOrSelf( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + int level, + string? culture = null) => + content.DescendantsOrSelf(variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), true, p => p.Level >= level, culture); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable DescendantsOrSelfOfType( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + string contentTypeAlias, + string? culture = null) => + content.DescendantsOrSelf(variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), true, p => p.ContentType.Alias.InvariantEquals(contentTypeAlias), culture); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable DescendantsOrSelf( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + string? culture = null) + where T : class, IPublishedContent => + content.DescendantsOrSelf(variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), culture).OfType(); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable DescendantsOrSelf( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + int level, + string? culture = null) + where T : class, IPublishedContent => + content.DescendantsOrSelf(variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), level, culture).OfType(); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IPublishedContent? Descendant( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + string? culture = null) => + content.Children(variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), culture)?.FirstOrDefault(); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IPublishedContent? Descendant( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + int level, + string? culture = null) => content + .EnumerateDescendants(variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), false, culture).FirstOrDefault(x => x.Level == level); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IPublishedContent? DescendantOfType( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + string contentTypeAlias, + string? culture = null) => content + .EnumerateDescendants(variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), false, culture) + .FirstOrDefault(x => x.ContentType.Alias.InvariantEquals(contentTypeAlias)); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static T? Descendant( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + string? culture = null) + where T : class, IPublishedContent => + content.EnumerateDescendants(variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), false, culture).FirstOrDefault(x => x is T) as T; + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static T? Descendant( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + int level, + string? culture = null) + where T : class, IPublishedContent => + content.Descendant(variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), level, culture) as T; + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IPublishedContent? DescendantOrSelf( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + int level, + string? culture = null) => content + .EnumerateDescendants(variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), true, culture).FirstOrDefault(x => x.Level == level); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IPublishedContent? DescendantOrSelfOfType( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + string contentTypeAlias, + string? culture = null) => content + .EnumerateDescendants(variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), true, culture) + .FirstOrDefault(x => x.ContentType.Alias.InvariantEquals(contentTypeAlias)); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static T? DescendantOrSelf( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + string? culture = null) + where T : class, IPublishedContent => + content.EnumerateDescendants(variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), true, culture).FirstOrDefault(x => x is T) as T; + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static T? DescendantOrSelf( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + int level, + string? culture = null) + where T : class, IPublishedContent => + content.DescendantOrSelf(variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), level, culture) as T; + + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IPublishedContent? FirstChild( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + string? culture = null) => + content.Children(variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), culture)?.FirstOrDefault(); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IPublishedContent? FirstChildOfType( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + string contentTypeAlias, + string? culture = null) => + content.ChildrenOfType(variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), contentTypeAlias, culture)?.FirstOrDefault(); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IPublishedContent? FirstChild( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + Func predicate, + string? culture = null) + => content.Children(variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), predicate, culture)?.FirstOrDefault(); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IPublishedContent? FirstChild( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + Guid uniqueId, + string? culture = null) => content + .Children(variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), x => x.Key == uniqueId, culture)?.FirstOrDefault(); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static T? FirstChild( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + string? culture = null) + where T : class, IPublishedContent => + content.Children(variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), culture)?.FirstOrDefault(); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static T? FirstChild( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + Func predicate, + string? culture = null) + where T : class, IPublishedContent => + content.Children(variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), culture)?.FirstOrDefault(predicate); + + [Obsolete( + "Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable Siblings( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + string? culture = null) => + Siblings(content, GetPublishedCache(content), GetNavigationQueryService(content), variationContextAccessor, culture); + + [Obsolete( + "Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable SiblingsOfType( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + string contentTypeAlias, + string? culture = null) => + SiblingsOfType(content, variationContextAccessor, + GetPublishedCache(content), GetNavigationQueryService(content), contentTypeAlias, culture); + + [Obsolete( + "Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable Siblings( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + string? culture = null) + where T : class, IPublishedContent => + Siblings(content, variationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), culture); + + [Obsolete( + "Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable? SiblingsAndSelf( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + string? culture = null) => SiblingsAndSelf(content, GetPublishedCache(content), GetNavigationQueryService(content), variationContextAccessor, culture); + + [Obsolete( + "Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable SiblingsAndSelfOfType( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + string contentTypeAlias, + string? culture = null) => SiblingsAndSelfOfType(content, variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), contentTypeAlias, culture); + + [Obsolete("Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + public static IEnumerable SiblingsAndSelf( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + string? culture = null) + where T : class, IPublishedContent => SiblingsAndSelf(content, variationContextAccessor, GetPublishedCache(content), + GetNavigationQueryService(content), culture); + + + private static INavigationQueryService GetNavigationQueryService(IPublishedContent content) + { + switch (content.ContentType.ItemType) + { + case PublishedItemType.Content: + return StaticServiceProvider.Instance.GetRequiredService(); + case PublishedItemType.Media: + return StaticServiceProvider.Instance.GetRequiredService(); + default: + throw new NotSupportedException("Unsupported content type."); + } + + } + + private static IPublishedCache GetPublishedCache(IPublishedContent content) + { + switch (content.ContentType.ItemType) + { + case PublishedItemType.Content: + return StaticServiceProvider.Instance.GetRequiredService(); + case PublishedItemType.Media: + return StaticServiceProvider.Instance.GetRequiredService(); + default: + throw new NotSupportedException("Unsupported content type."); + } + } } diff --git a/src/Umbraco.Core/PublishedCache/IPublishedContentCache.cs b/src/Umbraco.Core/PublishedCache/IPublishedContentCache.cs index 8353225f10..8230b0f306 100644 --- a/src/Umbraco.Core/PublishedCache/IPublishedContentCache.cs +++ b/src/Umbraco.Core/PublishedCache/IPublishedContentCache.cs @@ -11,7 +11,7 @@ public interface IPublishedContentCache : IPublishedCache /// A value indicating whether to consider unpublished content. /// The content, or null. /// Considers published or unpublished content depending on defaults. - Task GetByIdAsync(int id, bool preview = false); + Task GetByIdAsync(int id, bool? preview = null); /// /// Gets a content identified by its unique identifier. @@ -20,7 +20,7 @@ public interface IPublishedContentCache : IPublishedCache /// A value indicating whether to consider unpublished content. /// The content, or null. /// Considers published or unpublished content depending on defaults. - Task GetByIdAsync(Guid key, bool preview = false); + Task GetByIdAsync(Guid key, bool? preview = null); // FIXME: All these routing methods needs to be removed, as they are no longer part of the content cache /// diff --git a/src/Umbraco.Core/Services/EntityTypeContainerService.cs b/src/Umbraco.Core/Services/EntityTypeContainerService.cs index 9263d07530..962d853ce8 100644 --- a/src/Umbraco.Core/Services/EntityTypeContainerService.cs +++ b/src/Umbraco.Core/Services/EntityTypeContainerService.cs @@ -50,6 +50,22 @@ internal abstract class EntityTypeContainerService + public async Task> GetAsync(string name, int level) + { + using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true); + ReadLock(scope); + return await Task.FromResult(_entityContainerRepository.Get(name, level)); + } + /// + public async Task> GetAllAsync() + { + using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true); + ReadLock(scope); + return await Task.FromResult(_entityContainerRepository.GetMany()); + } + /// public async Task GetParentAsync(EntityContainer container) => await Task.FromResult(GetParent(container)); diff --git a/src/Umbraco.Core/Services/IEntityTypeContainerService.cs b/src/Umbraco.Core/Services/IEntityTypeContainerService.cs index e58e322304..831eb4b2e4 100644 --- a/src/Umbraco.Core/Services/IEntityTypeContainerService.cs +++ b/src/Umbraco.Core/Services/IEntityTypeContainerService.cs @@ -14,6 +14,20 @@ public interface IEntityTypeContainerService /// Task GetAsync(Guid id); + /// + /// Gets containers by name and level + /// + /// The name of the containers to get. + /// The level in the tree of the containers to get. + /// + Task> GetAsync(string name, int level); + + /// + /// Gets all containers + /// + /// + Task> GetAllAsync(); + /// /// Gets the parent container of a container /// diff --git a/src/Umbraco.Core/Services/IPreviewService.cs b/src/Umbraco.Core/Services/IPreviewService.cs index c9e276d83e..c65dbe16bb 100644 --- a/src/Umbraco.Core/Services/IPreviewService.cs +++ b/src/Umbraco.Core/Services/IPreviewService.cs @@ -15,5 +15,7 @@ public interface IPreviewService /// Task EndPreviewAsync(); + bool IsInPreview(); + Task> TryGetPreviewClaimsIdentityAsync(); } diff --git a/src/Umbraco.Core/Services/PreviewService.cs b/src/Umbraco.Core/Services/PreviewService.cs index bc11ab53bf..c487d4d407 100644 --- a/src/Umbraco.Core/Services/PreviewService.cs +++ b/src/Umbraco.Core/Services/PreviewService.cs @@ -1,5 +1,6 @@ using System.Security.Claims; using Microsoft.Extensions.DependencyInjection; +using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Models.Membership; using Umbraco.Cms.Core.Preview; using Umbraco.Cms.Core.Security; @@ -13,15 +14,18 @@ public class PreviewService : IPreviewService private readonly ICookieManager _cookieManager; private readonly IPreviewTokenGenerator _previewTokenGenerator; private readonly IServiceScopeFactory _serviceScopeFactory; + private readonly IRequestCache _requestCache; public PreviewService( ICookieManager cookieManager, IPreviewTokenGenerator previewTokenGenerator, - IServiceScopeFactory serviceScopeFactory) + IServiceScopeFactory serviceScopeFactory, + IRequestCache requestCache) { _cookieManager = cookieManager; _previewTokenGenerator = previewTokenGenerator; _serviceScopeFactory = serviceScopeFactory; + _requestCache = requestCache; } public async Task TryEnterPreviewAsync(IUser user) @@ -42,6 +46,11 @@ public class PreviewService : IPreviewService return Task.CompletedTask; } + public bool IsInPreview() => + _requestCache.Get( + "IsInPreview", + () => TryGetPreviewClaimsIdentityAsync().GetAwaiter().GetResult().Success) as bool? ?? false; + public async Task> TryGetPreviewClaimsIdentityAsync() { var cookieValue = _cookieManager.GetCookieValue(Constants.Web.PreviewCookieName); diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_15_0_0/AddDocumentUrl.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_15_0_0/AddDocumentUrl.cs index d1b70f034e..0fc6c3ba11 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_15_0_0/AddDocumentUrl.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_15_0_0/AddDocumentUrl.cs @@ -1,4 +1,5 @@ -using Umbraco.Cms.Infrastructure.Persistence.Dtos; +using Umbraco.Cms.Core; +using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_15_0_0; @@ -11,5 +12,10 @@ public class AddDocumentUrl : MigrationBase } protected override void Migrate() - => Create.Table().Do(); + { + if (TableExists(Constants.DatabaseSchema.Tables.DocumentUrl) is false) + { + Create.Table().Do(); + } + } } diff --git a/src/Umbraco.PublishedCache.HybridCache/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.PublishedCache.HybridCache/DependencyInjection/UmbracoBuilderExtensions.cs index c8e93b6351..191927ba37 100644 --- a/src/Umbraco.PublishedCache.HybridCache/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.PublishedCache.HybridCache/DependencyInjection/UmbracoBuilderExtensions.cs @@ -29,7 +29,9 @@ public static class UmbracoBuilderExtensions /// public static IUmbracoBuilder AddUmbracoHybridCache(this IUmbracoBuilder builder) { +#pragma warning disable EXTEXP0018 builder.Services.AddHybridCache(); +#pragma warning restore EXTEXP0018 builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); diff --git a/src/Umbraco.PublishedCache.HybridCache/DocumentCache.cs b/src/Umbraco.PublishedCache.HybridCache/DocumentCache.cs index d978919a65..9be78a9ea2 100644 --- a/src/Umbraco.PublishedCache.HybridCache/DocumentCache.cs +++ b/src/Umbraco.PublishedCache.HybridCache/DocumentCache.cs @@ -21,19 +21,19 @@ public sealed class DocumentCache : IPublishedContentCache _publishedContentTypeCache = publishedContentTypeCache; } - public async Task GetByIdAsync(int id, bool preview = false) => await _documentCacheService.GetByIdAsync(id, preview); + public async Task GetByIdAsync(int id, bool? preview = null) => await _documentCacheService.GetByIdAsync(id, preview); - public async Task GetByIdAsync(Guid key, bool preview = false) => await _documentCacheService.GetByKeyAsync(key, preview); + public async Task GetByIdAsync(Guid key, bool? preview = null) => await _documentCacheService.GetByKeyAsync(key, preview); public IPublishedContent? GetById(bool preview, int contentId) => GetByIdAsync(contentId, preview).GetAwaiter().GetResult(); public IPublishedContent? GetById(bool preview, Guid contentId) => GetByIdAsync(contentId, preview).GetAwaiter().GetResult(); - public IPublishedContent? GetById(int contentId) => GetByIdAsync(contentId, false).GetAwaiter().GetResult(); + public IPublishedContent? GetById(int contentId) => GetByIdAsync(contentId).GetAwaiter().GetResult(); - public IPublishedContent? GetById(Guid contentId) => GetByIdAsync(contentId, false).GetAwaiter().GetResult(); + public IPublishedContent? GetById(Guid contentId) => GetByIdAsync(contentId).GetAwaiter().GetResult(); public IPublishedContentType? GetContentType(int id) => _publishedContentTypeCache.Get(PublishedItemType.Content, id); diff --git a/src/Umbraco.PublishedCache.HybridCache/Services/DocumentCacheService.cs b/src/Umbraco.PublishedCache.HybridCache/Services/DocumentCacheService.cs index 4b8628574f..b73f48dcd7 100644 --- a/src/Umbraco.PublishedCache.HybridCache/Services/DocumentCacheService.cs +++ b/src/Umbraco.PublishedCache.HybridCache/Services/DocumentCacheService.cs @@ -22,6 +22,7 @@ internal sealed class DocumentCacheService : IDocumentCacheService private readonly ICacheNodeFactory _cacheNodeFactory; private readonly IEnumerable _seedKeyProviders; private readonly IPublishedModelFactory _publishedModelFactory; + private readonly IPreviewService _previewService; private readonly CacheSettings _cacheSettings; private HashSet? _seedKeys; @@ -54,7 +55,8 @@ internal sealed class DocumentCacheService : IDocumentCacheService ICacheNodeFactory cacheNodeFactory, IEnumerable seedKeyProviders, IOptions cacheSettings, - IPublishedModelFactory publishedModelFactory) + IPublishedModelFactory publishedModelFactory, + IPreviewService previewService) { _databaseCacheRepository = databaseCacheRepository; _idKeyMap = idKeyMap; @@ -64,22 +66,30 @@ internal sealed class DocumentCacheService : IDocumentCacheService _cacheNodeFactory = cacheNodeFactory; _seedKeyProviders = seedKeyProviders; _publishedModelFactory = publishedModelFactory; + _previewService = previewService; _cacheSettings = cacheSettings.Value; } - public async Task GetByKeyAsync(Guid key, bool preview = false) + public async Task GetByKeyAsync(Guid key, bool? preview = null) { using ICoreScope scope = _scopeProvider.CreateCoreScope(); + bool calculatedPreview = preview ?? GetPreview(); + ContentCacheNode? contentCacheNode = await _hybridCache.GetOrCreateAsync( - GetCacheKey(key, preview), // Unique key to the cache entry - async cancel => await _databaseCacheRepository.GetContentSourceAsync(key, preview)); + GetCacheKey(key, calculatedPreview), // Unique key to the cache entry + async cancel => await _databaseCacheRepository.GetContentSourceAsync(key, calculatedPreview)); scope.Complete(); - return contentCacheNode is null ? null : _publishedContentFactory.ToIPublishedContent(contentCacheNode, preview).CreateModel(_publishedModelFactory); + return contentCacheNode is null ? null : _publishedContentFactory.ToIPublishedContent(contentCacheNode, calculatedPreview).CreateModel(_publishedModelFactory); } - public async Task GetByIdAsync(int id, bool preview = false) + private bool GetPreview() + { + return _previewService.IsInPreview(); + } + + public async Task GetByIdAsync(int id, bool? preview = null) { Attempt keyAttempt = _idKeyMap.GetKeyForId(id, UmbracoObjectTypes.Document); if (keyAttempt.Success is false) @@ -87,12 +97,14 @@ internal sealed class DocumentCacheService : IDocumentCacheService return null; } + bool calculatedPreview = preview ?? GetPreview(); + using ICoreScope scope = _scopeProvider.CreateCoreScope(); ContentCacheNode? contentCacheNode = await _hybridCache.GetOrCreateAsync( - GetCacheKey(keyAttempt.Result, preview), // Unique key to the cache entry - async cancel => await _databaseCacheRepository.GetContentSourceAsync(id, preview)); + GetCacheKey(keyAttempt.Result, calculatedPreview), // Unique key to the cache entry + async cancel => await _databaseCacheRepository.GetContentSourceAsync(id, calculatedPreview)); scope.Complete(); - return contentCacheNode is null ? null : _publishedContentFactory.ToIPublishedContent(contentCacheNode, preview).CreateModel(_publishedModelFactory);; + return contentCacheNode is null ? null : _publishedContentFactory.ToIPublishedContent(contentCacheNode, calculatedPreview).CreateModel(_publishedModelFactory);; } public IEnumerable GetByContentType(IPublishedContentType contentType) diff --git a/src/Umbraco.PublishedCache.HybridCache/Services/IDocumentCacheService.cs b/src/Umbraco.PublishedCache.HybridCache/Services/IDocumentCacheService.cs index 873551c06b..18a25496e2 100644 --- a/src/Umbraco.PublishedCache.HybridCache/Services/IDocumentCacheService.cs +++ b/src/Umbraco.PublishedCache.HybridCache/Services/IDocumentCacheService.cs @@ -5,9 +5,9 @@ namespace Umbraco.Cms.Infrastructure.HybridCache.Services; public interface IDocumentCacheService { - Task GetByKeyAsync(Guid key, bool preview = false); + Task GetByKeyAsync(Guid key, bool? preview = null); - Task GetByIdAsync(int id, bool preview = false); + Task GetByIdAsync(int id, bool? preview = null); Task SeedAsync(CancellationToken cancellationToken); diff --git a/src/Umbraco.PublishedCache.HybridCache/Services/MediaCacheService.cs b/src/Umbraco.PublishedCache.HybridCache/Services/MediaCacheService.cs index 78e456dbf0..1bd2fe3e2b 100644 --- a/src/Umbraco.PublishedCache.HybridCache/Services/MediaCacheService.cs +++ b/src/Umbraco.PublishedCache.HybridCache/Services/MediaCacheService.cs @@ -8,6 +8,7 @@ using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.HybridCache.Factories; using Umbraco.Cms.Infrastructure.HybridCache.Persistence; using Umbraco.Cms.Infrastructure.HybridCache.Serialization; +using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.HybridCache.Services; @@ -20,6 +21,7 @@ internal class MediaCacheService : IMediaCacheService private readonly IPublishedContentFactory _publishedContentFactory; private readonly ICacheNodeFactory _cacheNodeFactory; private readonly IEnumerable _seedKeyProviders; + private readonly IPublishedModelFactory _publishedModelFactory; private readonly CacheSettings _cacheSettings; private HashSet? _seedKeys; @@ -51,6 +53,7 @@ internal class MediaCacheService : IMediaCacheService IPublishedContentFactory publishedContentFactory, ICacheNodeFactory cacheNodeFactory, IEnumerable seedKeyProviders, + IPublishedModelFactory publishedModelFactory, IOptions cacheSettings) { _databaseCacheRepository = databaseCacheRepository; @@ -60,6 +63,7 @@ internal class MediaCacheService : IMediaCacheService _publishedContentFactory = publishedContentFactory; _cacheNodeFactory = cacheNodeFactory; _seedKeyProviders = seedKeyProviders; + _publishedModelFactory = publishedModelFactory; _cacheSettings = cacheSettings.Value; } @@ -78,7 +82,7 @@ internal class MediaCacheService : IMediaCacheService async cancel => await _databaseCacheRepository.GetMediaSourceAsync(idAttempt.Result)); scope.Complete(); - return contentCacheNode is null ? null : _publishedContentFactory.ToIPublishedMedia(contentCacheNode); + return contentCacheNode is null ? null : _publishedContentFactory.ToIPublishedMedia(contentCacheNode).CreateModel(_publishedModelFactory); } public async Task GetByIdAsync(int id) @@ -94,7 +98,7 @@ internal class MediaCacheService : IMediaCacheService $"{keyAttempt.Result}", // Unique key to the cache entry async cancel => await _databaseCacheRepository.GetMediaSourceAsync(id)); scope.Complete(); - return contentCacheNode is null ? null : _publishedContentFactory.ToIPublishedMedia(contentCacheNode); + return contentCacheNode is null ? null : _publishedContentFactory.ToIPublishedMedia(contentCacheNode).CreateModel(_publishedModelFactory); } public async Task HasContentByIdAsync(int id) @@ -144,7 +148,7 @@ internal class MediaCacheService : IMediaCacheService foreach (Guid key in SeedKeys) { - if(cancellationToken.IsCancellationRequested) + if (cancellationToken.IsCancellationRequested) { break; } @@ -187,7 +191,8 @@ internal class MediaCacheService : IMediaCacheService private HybridCacheEntryOptions GetSeedEntryOptions() => new() { - Expiration = _cacheSettings.SeedCacheDuration, LocalCacheExpiration = _cacheSettings.SeedCacheDuration, + Expiration = _cacheSettings.SeedCacheDuration, + LocalCacheExpiration = _cacheSettings.SeedCacheDuration, }; private string GetCacheKey(Guid key, bool preview) => preview ? $"{key}+draft" : $"{key}"; diff --git a/src/Umbraco.Web.Common/Extensions/FriendlyPublishedContentExtensions.cs b/src/Umbraco.Web.Common/Extensions/FriendlyPublishedContentExtensions.cs index 858c7d82cd..60c7a27a57 100644 --- a/src/Umbraco.Web.Common/Extensions/FriendlyPublishedContentExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/FriendlyPublishedContentExtensions.cs @@ -22,9 +22,15 @@ public static class FriendlyPublishedContentExtensions private static IPublishedContentCache PublishedContentCache { get; } = StaticServiceProvider.Instance.GetRequiredService(); + private static IPublishedMediaCache PublishedMediaCache { get; } = + StaticServiceProvider.Instance.GetRequiredService(); + private static IDocumentNavigationQueryService DocumentNavigationQueryService { get; } = StaticServiceProvider.Instance.GetRequiredService(); + private static IMediaNavigationQueryService MediaNavigationQueryService { get; } = + StaticServiceProvider.Instance.GetRequiredService(); + private static IPublishedModelFactory PublishedModelFactory { get; } = StaticServiceProvider.Instance.GetRequiredService(); @@ -61,6 +67,33 @@ public static class FriendlyPublishedContentExtensions private static IMemberTypeService MemberTypeService { get; } = StaticServiceProvider.Instance.GetRequiredService(); + private static INavigationQueryService GetNavigationQueryService(IPublishedContent content) + { + switch (content.ContentType.ItemType) + { + case PublishedItemType.Content: + return DocumentNavigationQueryService; + case PublishedItemType.Media: + return MediaNavigationQueryService; + default: + throw new NotSupportedException("Unsupported content type."); + } + + } + + private static IPublishedCache GetPublishedCache(IPublishedContent content) + { + switch (content.ContentType.ItemType) + { + case PublishedItemType.Content: + return PublishedContentCache; + case PublishedItemType.Media: + return PublishedMediaCache; + default: + throw new NotSupportedException("Unsupported content type."); + } + } + /// /// Creates a strongly typed published content model for an internal published content. /// @@ -207,7 +240,7 @@ public static class FriendlyPublishedContentExtensions /// set to 1. /// public static IPublishedContent Root(this IPublishedContent content) - => content.Root(PublishedContentCache, DocumentNavigationQueryService); + => content.Root(GetPublishedCache(content), GetNavigationQueryService(content)); /// /// Gets the root content (ancestor or self at level 1) for the specified if it's of the @@ -226,7 +259,7 @@ public static class FriendlyPublishedContentExtensions /// public static T? Root(this IPublishedContent content) where T : class, IPublishedContent - => content.Root(PublishedContentCache, DocumentNavigationQueryService); + => content.Root(GetPublishedCache(content), GetNavigationQueryService(content)); /// /// Gets the parent of the content item. @@ -236,7 +269,7 @@ public static class FriendlyPublishedContentExtensions /// The parent of content of the specified content type or null. public static T? Parent(this IPublishedContent content) where T : class, IPublishedContent - => content.Parent(PublishedContentCache, DocumentNavigationQueryService); + => content.Parent(GetPublishedCache(content), GetNavigationQueryService(content)); /// /// Gets the ancestors of the content. @@ -245,7 +278,7 @@ public static class FriendlyPublishedContentExtensions /// The ancestors of the content, in down-top order. /// Does not consider the content itself. public static IEnumerable Ancestors(this IPublishedContent content) - => content.Ancestors(PublishedContentCache, DocumentNavigationQueryService); + => content.Ancestors(GetPublishedCache(content), GetNavigationQueryService(content)); /// /// Gets the content and its ancestors. @@ -253,7 +286,7 @@ public static class FriendlyPublishedContentExtensions /// The content. /// The content and its ancestors, in down-top order. public static IEnumerable AncestorsOrSelf(this IPublishedContent content) - => content.AncestorsOrSelf(PublishedContentCache, DocumentNavigationQueryService); + => content.AncestorsOrSelf(GetPublishedCache(content), GetNavigationQueryService(content)); /// /// Gets the content and its ancestors, of a specified content type. @@ -264,7 +297,7 @@ public static class FriendlyPublishedContentExtensions /// May or may not begin with the content itself, depending on its content type. public static IEnumerable AncestorsOrSelf(this IPublishedContent content) where T : class, IPublishedContent - => content.AncestorsOrSelf(PublishedContentCache, DocumentNavigationQueryService); + => content.AncestorsOrSelf(GetPublishedCache(content), GetNavigationQueryService(content)); /// /// Gets the ancestor of the content, i.e. its parent. @@ -272,7 +305,7 @@ public static class FriendlyPublishedContentExtensions /// The content. /// The ancestor of the content. public static IPublishedContent? Ancestor(this IPublishedContent content) - => content.Ancestor(PublishedContentCache, DocumentNavigationQueryService); + => content.Ancestor(GetPublishedCache(content), GetNavigationQueryService(content)); /// /// Gets the nearest ancestor of the content, of a specified content type. @@ -283,7 +316,7 @@ public static class FriendlyPublishedContentExtensions /// Does not consider the content itself. May return null. public static T? Ancestor(this IPublishedContent content) where T : class, IPublishedContent - => content.Ancestor(PublishedContentCache, DocumentNavigationQueryService); + => content.Ancestor(GetPublishedCache(content), GetNavigationQueryService(content)); /// /// Gets the content or its nearest ancestor, of a specified content type. @@ -294,7 +327,7 @@ public static class FriendlyPublishedContentExtensions /// May or may not return the content itself depending on its content type. May return null. public static T? AncestorOrSelf(this IPublishedContent content) where T : class, IPublishedContent - => content.AncestorOrSelf(PublishedContentCache, DocumentNavigationQueryService); + => content.AncestorOrSelf(GetPublishedCache(content), GetNavigationQueryService(content)); /// /// Returns all DescendantsOrSelf of all content referenced @@ -311,7 +344,7 @@ public static class FriendlyPublishedContentExtensions /// public static IEnumerable DescendantsOrSelfOfType( this IEnumerable parentNodes, string docTypeAlias, string? culture = null) - => parentNodes.DescendantsOrSelfOfType(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, docTypeAlias, culture); + => parentNodes.DescendantsOrSelfOfType(VariationContextAccessor, GetPublishedCache(parentNodes.First()), GetNavigationQueryService(parentNodes.First()), docTypeAlias, culture); /// /// Returns all DescendantsOrSelf of all content referenced @@ -329,77 +362,77 @@ public static class FriendlyPublishedContentExtensions this IEnumerable parentNodes, string? culture = null) where T : class, IPublishedContent - => parentNodes.DescendantsOrSelf(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, culture); + => parentNodes.DescendantsOrSelf(VariationContextAccessor, GetPublishedCache(parentNodes.First()), GetNavigationQueryService(parentNodes.First()), culture); public static IEnumerable Descendants(this IPublishedContent content, string? culture = null) - => content.Descendants(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, culture); + => content.Descendants(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), culture); public static IEnumerable Descendants(this IPublishedContent content, int level, string? culture = null) - => content.Descendants(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, level, culture); + => content.Descendants(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), level, culture); public static IEnumerable DescendantsOfType(this IPublishedContent content, string contentTypeAlias, string? culture = null) - => content.DescendantsOfType(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, contentTypeAlias, culture); + => content.DescendantsOfType(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), contentTypeAlias, culture); public static IEnumerable Descendants(this IPublishedContent content, string? culture = null) where T : class, IPublishedContent - => content.Descendants(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, culture); + => content.Descendants(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), culture); public static IEnumerable Descendants(this IPublishedContent content, int level, string? culture = null) where T : class, IPublishedContent - => content.Descendants(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, level, culture); + => content.Descendants(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), level, culture); public static IEnumerable DescendantsOrSelf( this IPublishedContent content, string? culture = null) - => content.DescendantsOrSelf(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, culture); + => content.DescendantsOrSelf(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), culture); public static IEnumerable DescendantsOrSelf(this IPublishedContent content, int level, string? culture = null) - => content.DescendantsOrSelf(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, level, culture); + => content.DescendantsOrSelf(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), level, culture); public static IEnumerable DescendantsOrSelfOfType(this IPublishedContent content, string contentTypeAlias, string? culture = null) - => content.DescendantsOrSelfOfType(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, contentTypeAlias, culture); + => content.DescendantsOrSelfOfType(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), contentTypeAlias, culture); public static IEnumerable DescendantsOrSelf(this IPublishedContent content, string? culture = null) where T : class, IPublishedContent - => content.DescendantsOrSelf(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, culture); + => content.DescendantsOrSelf(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), culture); public static IEnumerable DescendantsOrSelf(this IPublishedContent content, int level, string? culture = null) where T : class, IPublishedContent - => content.DescendantsOrSelf(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, level, culture); + => content.DescendantsOrSelf(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), level, culture); public static IPublishedContent? Descendant(this IPublishedContent content, string? culture = null) - => content.Descendant(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, culture); + => content.Descendant(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), culture); public static IPublishedContent? Descendant(this IPublishedContent content, int level, string? culture = null) - => content.Descendant(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, level, culture); + => content.Descendant(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), level, culture); public static IPublishedContent? DescendantOfType(this IPublishedContent content, string contentTypeAlias, string? culture = null) - => content.DescendantOfType(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, contentTypeAlias, culture); + => content.DescendantOfType(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), contentTypeAlias, culture); public static T? Descendant(this IPublishedContent content, string? culture = null) where T : class, IPublishedContent - => content.Descendant(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, culture); + => content.Descendant(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), culture); public static T? Descendant(this IPublishedContent content, int level, string? culture = null) where T : class, IPublishedContent - => content.Descendant(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, level, culture); + => content.Descendant(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), level, culture); public static IPublishedContent DescendantOrSelf(this IPublishedContent content, string? culture = null) => content.DescendantOrSelf(VariationContextAccessor, culture); public static IPublishedContent? DescendantOrSelf(this IPublishedContent content, int level, string? culture = null) - => content.DescendantOrSelf(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, level, culture); + => content.DescendantOrSelf(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), level, culture); public static IPublishedContent? DescendantOrSelfOfType(this IPublishedContent content, string contentTypeAlias, string? culture = null) - => content.DescendantOrSelfOfType(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, contentTypeAlias, culture); + => content.DescendantOrSelfOfType(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), contentTypeAlias, culture); public static T? DescendantOrSelf(this IPublishedContent content, string? culture = null) where T : class, IPublishedContent - => content.DescendantOrSelf(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, culture); + => content.DescendantOrSelf(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), culture); public static T? DescendantOrSelf(this IPublishedContent content, int level, string? culture = null) where T : class, IPublishedContent - => content.DescendantOrSelf(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, level, culture); + => content.DescendantOrSelf(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), level, culture); /// /// Gets the children of the content item. @@ -427,7 +460,7 @@ public static class FriendlyPublishedContentExtensions /// /// public static IEnumerable Children(this IPublishedContent content, string? culture = null) - => content.Children(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, culture); + => content.Children(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), culture); /// /// Gets the children of the content, filtered by a predicate. @@ -446,7 +479,7 @@ public static class FriendlyPublishedContentExtensions this IPublishedContent content, Func predicate, string? culture = null) - => content.Children(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, predicate, culture); + => content.Children(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), predicate, culture); /// /// Gets the children of the content, of any of the specified types. @@ -459,7 +492,7 @@ public static class FriendlyPublishedContentExtensions /// The content type alias. /// The children of the content, of any of the specified types. public static IEnumerable? ChildrenOfType(this IPublishedContent content, string contentTypeAlias, string? culture = null) - => content.ChildrenOfType(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, contentTypeAlias, culture); + => content.ChildrenOfType(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), contentTypeAlias, culture); /// /// Gets the children of the content, of a given content type. @@ -476,30 +509,30 @@ public static class FriendlyPublishedContentExtensions /// public static IEnumerable? Children(this IPublishedContent content, string? culture = null) where T : class, IPublishedContent - => content.Children(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, culture); + => content.Children(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), culture); public static IPublishedContent? FirstChild(this IPublishedContent content, string? culture = null) - => content.FirstChild(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, culture); + => content.FirstChild(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), culture); /// /// Gets the first child of the content, of a given content type. /// public static IPublishedContent? FirstChildOfType(this IPublishedContent content, string contentTypeAlias, string? culture = null) - => content.FirstChildOfType(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, contentTypeAlias, culture); + => content.FirstChildOfType(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), contentTypeAlias, culture); public static IPublishedContent? FirstChild(this IPublishedContent content, Func predicate, string? culture = null) - => content.FirstChild(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, predicate, culture); + => content.FirstChild(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), predicate, culture); public static IPublishedContent? FirstChild(this IPublishedContent content, Guid uniqueId, string? culture = null) - => content.FirstChild(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, uniqueId, culture); + => content.FirstChild(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), uniqueId, culture); public static T? FirstChild(this IPublishedContent content, string? culture = null) where T : class, IPublishedContent - => content.FirstChild(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, culture); + => content.FirstChild(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), culture); public static T? FirstChild(this IPublishedContent content, Func predicate, string? culture = null) where T : class, IPublishedContent - => content.FirstChild(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, predicate, culture); + => content.FirstChild(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), predicate, culture); /// /// Gets the siblings of the content. @@ -514,7 +547,7 @@ public static class FriendlyPublishedContentExtensions /// Note that in V7 this method also return the content node self. /// public static IEnumerable? Siblings(this IPublishedContent content, string? culture = null) - => content.Siblings(PublishedContentCache, DocumentNavigationQueryService, VariationContextAccessor, culture); + => content.Siblings(GetPublishedCache(content), GetNavigationQueryService(content), VariationContextAccessor, culture); /// /// Gets the siblings of the content, of a given content type. @@ -530,7 +563,7 @@ public static class FriendlyPublishedContentExtensions /// Note that in V7 this method also return the content node self. /// public static IEnumerable? SiblingsOfType(this IPublishedContent content, string contentTypeAlias, string? culture = null) - => content.SiblingsOfType(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, contentTypeAlias, culture); + => content.SiblingsOfType(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), contentTypeAlias, culture); /// /// Gets the siblings of the content, of a given content type. @@ -547,7 +580,7 @@ public static class FriendlyPublishedContentExtensions /// public static IEnumerable? Siblings(this IPublishedContent content, string? culture = null) where T : class, IPublishedContent - => content.Siblings(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, culture); + => content.Siblings(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), culture); /// /// Gets the siblings of the content including the node itself to indicate the position. @@ -561,7 +594,7 @@ public static class FriendlyPublishedContentExtensions public static IEnumerable? SiblingsAndSelf( this IPublishedContent content, string? culture = null) - => content.SiblingsAndSelf(PublishedContentCache, DocumentNavigationQueryService, VariationContextAccessor, culture); + => content.SiblingsAndSelf(GetPublishedCache(content), GetNavigationQueryService(content), VariationContextAccessor, culture); /// /// Gets the siblings of the content including the node itself to indicate the position, of a given content type. @@ -577,7 +610,7 @@ public static class FriendlyPublishedContentExtensions this IPublishedContent content, string contentTypeAlias, string? culture = null) - => content.SiblingsAndSelfOfType(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, contentTypeAlias, culture); + => content.SiblingsAndSelfOfType(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), contentTypeAlias, culture); /// /// Gets the siblings of the content including the node itself to indicate the position, of a given content type. @@ -591,7 +624,7 @@ public static class FriendlyPublishedContentExtensions /// The siblings of the content including the node itself, of the given content type. public static IEnumerable? SiblingsAndSelf(this IPublishedContent content, string? culture = null) where T : class, IPublishedContent - => content.SiblingsAndSelf(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService, culture); + => content.SiblingsAndSelf(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), culture); /// /// Gets the url of the content item. @@ -626,8 +659,8 @@ public static class FriendlyPublishedContentExtensions => content.ChildrenAsTable( VariationContextAccessor, - PublishedContentCache, - DocumentNavigationQueryService, + GetPublishedCache(content), + GetNavigationQueryService(content), ContentTypeService, MediaTypeService, MemberTypeService, @@ -700,4 +733,5 @@ public static class FriendlyPublishedContentExtensions string term, string? indexName = null) => content.SearchChildren(ExamineManager, UmbracoContextAccessor, term, indexName); + } diff --git a/src/Umbraco.Web.UI.Client b/src/Umbraco.Web.UI.Client index 00e5576244..4729d3baa7 160000 --- a/src/Umbraco.Web.UI.Client +++ b/src/Umbraco.Web.UI.Client @@ -1 +1 @@ -Subproject commit 00e5576244a11e0fc1ea9aa2b79d3deba04e1609 +Subproject commit 4729d3baa7611ed63380abcfc184c1bb5a48b3bb diff --git a/src/Umbraco.Web.UI.Login/public/login.jpg b/src/Umbraco.Web.UI.Login/public/login.jpg index 4be79488eb..ed893bf3c0 100644 Binary files a/src/Umbraco.Web.UI.Login/public/login.jpg and b/src/Umbraco.Web.UI.Login/public/login.jpg differ diff --git a/tests/Directory.Packages.props b/tests/Directory.Packages.props index a3b4ceff47..3b4a2714b8 100644 --- a/tests/Directory.Packages.props +++ b/tests/Directory.Packages.props @@ -5,12 +5,12 @@ - - + + - - + + @@ -22,4 +22,4 @@ - + \ No newline at end of file diff --git a/tests/Umbraco.Tests.Common/Builders/Extensions/BuilderExtensions.cs b/tests/Umbraco.Tests.Common/Builders/Extensions/BuilderExtensions.cs index accee29bae..a44c5c7ba9 100644 --- a/tests/Umbraco.Tests.Common/Builders/Extensions/BuilderExtensions.cs +++ b/tests/Umbraco.Tests.Common/Builders/Extensions/BuilderExtensions.cs @@ -232,7 +232,8 @@ public static class BuilderExtensions return builder; } - public static T WithPropertyValues(this T builder, object propertyValues, string? culture = null, string? segment = null) + public static T WithPropertyValues(this T builder, object propertyValues, string? culture = null, + string? segment = null) where T : IWithPropertyValues { builder.PropertyValues = propertyValues; diff --git a/tests/Umbraco.Tests.Common/Builders/MediaTypeEditingBuilder.cs b/tests/Umbraco.Tests.Common/Builders/MediaTypeEditingBuilder.cs index eae219ecd1..c1291d64db 100644 --- a/tests/Umbraco.Tests.Common/Builders/MediaTypeEditingBuilder.cs +++ b/tests/Umbraco.Tests.Common/Builders/MediaTypeEditingBuilder.cs @@ -41,7 +41,8 @@ public class MediaTypeEditingBuilder : ContentTypeEditingBaseBuilder(contentType, updateModel); return model; } - private T MapBaseProperties(IContentType contentType, T model) where T : ContentTypeModelBase + private static T MapBaseProperties(IContentType contentType, T model) where T : ContentTypeModelBase { model.Alias = contentType.Alias; model.Name = contentType.Name; diff --git a/tests/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTestWithContentEditing.cs b/tests/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTestWithContentEditing.cs index e1f047dd90..fba87a9893 100644 --- a/tests/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTestWithContentEditing.cs +++ b/tests/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTestWithContentEditing.cs @@ -67,8 +67,7 @@ public abstract class UmbracoIntegrationTestWithContentEditing : UmbracoIntegrat Assert.IsTrue(contentTypeAttempt.Success); var contentTypeResult = contentTypeAttempt.Result; - ContentTypeUpdateHelper contentTypeUpdateHelper = new ContentTypeUpdateHelper(); - ContentTypeUpdateModel = contentTypeUpdateHelper.CreateContentTypeUpdateModel(contentTypeResult); ContentTypeUpdateModel.AllowedContentTypes = new[] + ContentTypeUpdateModel = ContentTypeUpdateHelper.CreateContentTypeUpdateModel(contentTypeResult); ContentTypeUpdateModel.AllowedContentTypes = new[] { new ContentTypeSort(contentTypeResult.Key, 0, ContentTypeCreateModel.Alias), }; diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Cache/PublishedContentTypeCacheTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Cache/PublishedContentTypeCacheTests.cs index d74439634a..71c450b54c 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Cache/PublishedContentTypeCacheTests.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Cache/PublishedContentTypeCacheTests.cs @@ -37,8 +37,7 @@ public class PublishedContentTypeCacheTests : UmbracoIntegrationTestWithContentE Assert.IsNotNull(contentType); Assert.AreEqual(1, ContentType.PropertyTypes.Count()); // Update the content type - ContentTypeUpdateHelper contentTypeUpdateHelper = new ContentTypeUpdateHelper(); - var updateModel = contentTypeUpdateHelper.CreateContentTypeUpdateModel(ContentType); + var updateModel = ContentTypeUpdateHelper.CreateContentTypeUpdateModel(ContentType); updateModel.Properties = new List(); await ContentTypeEditingService.UpdateAsync(ContentType, updateModel, Constants.Security.SuperUserKey); diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/PublishedContent/PublishContentTypeFactoryTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/PublishedContent/PublishContentTypeFactoryTest.cs new file mode 100644 index 0000000000..c8805492a9 --- /dev/null +++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/PublishedContent/PublishContentTypeFactoryTest.cs @@ -0,0 +1,98 @@ +using NUnit.Framework; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Services.ContentTypeEditing; +using Umbraco.Cms.Core.Sync; +using Umbraco.Cms.Tests.Common.Builders; +using Umbraco.Cms.Tests.Common.Builders.Extensions; +using Umbraco.Cms.Tests.Common.TestHelpers; +using Umbraco.Cms.Tests.Common.Testing; +using Umbraco.Cms.Tests.Integration.Testing; +using Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services; + +namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.PublishedContent; + +[TestFixture] +[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] +public class PublishContentTypeFactoryTest : UmbracoIntegrationTest +{ + protected override void CustomTestSetup(IUmbracoBuilder builder) + { + builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + builder.Services.AddUnique(); + base.CustomTestSetup(builder); + } + + private ITemplateService TemplateService => GetRequiredService(); + + private IContentTypeEditingService ContentTypeEditingService => GetRequiredService(); + + private IDataTypeService DataTypeService => GetRequiredService(); + + private IPublishedContentTypeFactory PublishedContentTypeFactory => GetRequiredService(); + + [Test] + public async Task Can_Update_Content_Type() + { + // Create a content type + var template = TemplateBuilder.CreateTextPageTemplate("defaultTemplate"); + await TemplateService.CreateAsync(template, Constants.Security.SuperUserKey); + var contentTypeCreateModel = ContentTypeEditingBuilder.CreateSimpleContentType("umbTextpage", "Textpage", defaultTemplateKey: template.Key); + var contentTypeAttempt = await ContentTypeEditingService.CreateAsync(contentTypeCreateModel, Constants.Security.SuperUserKey); + Assert.IsTrue(contentTypeAttempt.Success); + Assert.IsNotNull(contentTypeAttempt.Result); + + // Fetch the content type to cache data types + var contentType = contentTypeAttempt.Result; + PublishedContentTypeFactory.CreateContentType(contentType); + + var dataType = new DataTypeBuilder() + .WithId(0) + .Build(); + var dataTypeCreateResult = await DataTypeService.CreateAsync(dataType, Constants.Security.SuperUserKey); + Assert.IsTrue(dataTypeCreateResult.Success); + + contentType.AddPropertyGroup("group", "Group"); + var propertyTypeAlias = "test"; + var propertyType = new PropertyTypeBuilder() + .WithAlias(propertyTypeAlias) + .WithDataTypeId(dataTypeCreateResult.Result.Id) + .Build(); + propertyType.DataTypeKey = dataType.Key; + + contentType.AddPropertyType(propertyType, "group", "Group"); + + // Update the content type + var contentTypeUpdate = ContentTypeUpdateHelper.CreateContentTypeUpdateModel(contentType); + var updateResult = await ContentTypeEditingService.UpdateAsync(contentType, contentTypeUpdate, Constants.Security.SuperUserKey); + Assert.IsTrue(updateResult.Success); + + + var publishedContentType = PublishedContentTypeFactory.CreateContentType(updateResult.Result); + Assert.That(publishedContentType.PropertyTypes.Where(x => x.Alias == propertyTypeAlias), Is.Not.Empty); + } + + [Test] + public async Task Can_Get_Updated_Datatype() + { + var dataType = new DataTypeBuilder() + .WithId(0) + .Build(); + dataType.EditorUiAlias = "NotUpdated"; + var dataTypeCreateResult = await DataTypeService.CreateAsync(dataType, Constants.Security.SuperUserKey); + Assert.IsTrue(dataTypeCreateResult.Success); + var createdDataType = dataTypeCreateResult.Result; + PublishedDataType createdPublishedDataType = PublishedContentTypeFactory.GetDataType(createdDataType.Id); + Assert.That(createdPublishedDataType.EditorUiAlias, Is.EqualTo("NotUpdated")); + + createdDataType.EditorUiAlias = "Updated"; + var dataTypeUpdateResult = await DataTypeService.UpdateAsync(createdDataType, Constants.Security.SuperUserKey); + Assert.IsTrue(dataTypeUpdateResult.Success); + var updatedPublishedDataType = PublishedContentTypeFactory.GetDataType(createdDataType.Id); + Assert.That(updatedPublishedDataType.EditorUiAlias, Is.EqualTo("Updated")); + } +} diff --git a/tests/Umbraco.Tests.Integration/Umbraco.PublishedCache.HybridCache/DocumentHybridCacheMockTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.PublishedCache.HybridCache/DocumentHybridCacheMockTests.cs index b0e3bc6ec7..2e810114d0 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.PublishedCache.HybridCache/DocumentHybridCacheMockTests.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.PublishedCache.HybridCache/DocumentHybridCacheMockTests.cs @@ -104,7 +104,8 @@ public class DocumentHybridCacheMockTests : UmbracoIntegrationTestWithContent GetRequiredService(), GetSeedProviders(), Options.Create(new CacheSettings()), - GetRequiredService()); + GetRequiredService(), + GetRequiredService()); _mockedCache = new DocumentCache(_mockDocumentCacheService, GetRequiredService()); } diff --git a/tests/Umbraco.Tests.Integration/Umbraco.PublishedCache.HybridCache/DocumentHybridCacheTemplateTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.PublishedCache.HybridCache/DocumentHybridCacheTemplateTests.cs index eb352ff6c8..a0873bd983 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.PublishedCache.HybridCache/DocumentHybridCacheTemplateTests.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.PublishedCache.HybridCache/DocumentHybridCacheTemplateTests.cs @@ -1,4 +1,4 @@ -using NUnit.Framework; +using NUnit.Framework; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models.ContentEditing; using Umbraco.Cms.Core.PublishedCache; @@ -36,7 +36,6 @@ public class DocumentHybridCacheTemplateTests : UmbracoIntegrationTestWithConten // Assert Assert.AreEqual(updateContentResult.Status, ContentEditingOperationStatus.Success); var textPageAfter = await PublishedContentHybridCache.GetByIdAsync(TextpageId, true); - // Should this not be null? Assert.AreEqual(textPageAfter.TemplateId, null); } }