{"results":{"result":{"added-files":{"code-health":9.748954928688482,"old-code-health":0.0,"files":[{"file":"src/Umbraco.Cms.Api.Delivery/Caching/DefaultDeliveryApiOutputCacheRequestFilter.cs","loc":21,"code-health":10.0},{"file":"src/Umbraco.Cms.Api.Delivery/Caching/DeliveryApiDocumentOutputCacheEvictionHandler.cs","loc":100,"code-health":10.0},{"file":"src/Umbraco.Cms.Api.Delivery/Caching/DeliveryApiMediaOutputCacheEvictionHandler.cs","loc":55,"code-health":10.0},{"file":"src/Umbraco.Cms.Api.Delivery/Caching/DeliveryApiMemberOutputCacheEvictionHandler.cs","loc":36,"code-health":10.0},{"file":"src/Umbraco.Cms.Api.Delivery/Caching/DeliveryApiOutputCacheContentPolicy.cs","loc":28,"code-health":10.0},{"file":"src/Umbraco.Cms.Api.Delivery/Caching/DeliveryApiOutputCacheManager.cs","loc":22,"code-health":10.0},{"file":"src/Umbraco.Cms.Api.Delivery/Caching/DeliveryApiOutputCacheMediaPolicy.cs","loc":13,"code-health":10.0},{"file":"src/Umbraco.Cms.Api.Delivery/Caching/DeliveryApiOutputCachePolicyBase.cs","loc":93,"code-health":9.536386775820924},{"file":"tests/Umbraco.Tests.UnitTests/Umbraco.Cms.Api.Delivery/Caching/DefaultDeliveryApiOutputCacheRequestFilterTests.cs","loc":50,"code-health":10.0},{"file":"tests/Umbraco.Tests.UnitTests/Umbraco.Cms.Api.Delivery/Caching/DeliveryApiDocumentOutputCacheEvictionHandlerTests.cs","loc":237,"code-health":9.387218218812514},{"file":"tests/Umbraco.Tests.UnitTests/Umbraco.Cms.Api.Delivery/Caching/DeliveryApiMediaOutputCacheEvictionHandlerTests.cs","loc":96,"code-health":10.0},{"file":"tests/Umbraco.Tests.UnitTests/Umbraco.Cms.Api.Delivery/Caching/DeliveryApiMemberOutputCacheEvictionHandlerTests.cs","loc":90,"code-health":10.0},{"file":"tests/Umbraco.Tests.UnitTests/Umbraco.Cms.Api.Delivery/Caching/DeliveryApiOutputCacheContentPolicyTests.cs","loc":200,"code-health":9.387218218812514},{"file":"tests/Umbraco.Tests.UnitTests/Umbraco.Cms.Api.Delivery/Caching/DeliveryApiOutputCacheManagerTests.cs","loc":70,"code-health":10.0},{"file":"tests/Umbraco.Tests.UnitTests/Umbraco.Cms.Api.Delivery/Caching/DeliveryApiOutputCacheMediaPolicyTests.cs","loc":114,"code-health":10.0},{"file":"src/Umbraco.Web.Common/Caching/RelationOutputCacheEvictionHandlerBase.cs","loc":55,"code-health":9.536386775820924},{"file":"src/Umbraco.Cms.Api.Delivery/Caching/DeliveryApiContentTypeOutputCacheTagProvider.cs","loc":11,"code-health":10.0},{"file":"src/Umbraco.Web.Website/Caching/WebsiteContentTypeOutputCacheTagProvider.cs","loc":11,"code-health":10.0},{"file":"tests/Umbraco.Tests.UnitTests/Umbraco.Cms.Api.Delivery/Caching/DeliveryApiContentTypeOutputCacheTagProviderTests.cs","loc":19,"code-health":10.0},{"file":"tests/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Caching/WebsiteContentTypeOutputCacheTagProviderTests.cs","loc":19,"code-health":10.0}]},"external-review-url":"https://github.com/umbraco/Umbraco-CMS/pull/22456","old-code-health":9.713606049842188,"modified-files":{"code-health":9.718046037474268,"old-code-health":9.713606049842188,"files":[{"file":"src/Umbraco.Cms.Api.Delivery/Controllers/Content/ByIdContentApiController.cs","loc":46,"old-loc":45,"code-health":10.0,"old-code-health":10.0},{"file":"src/Umbraco.Cms.Api.Delivery/Controllers/Content/ByIdsContentApiController.cs","loc":41,"old-loc":40,"code-health":10.0,"old-code-health":10.0},{"file":"src/Umbraco.Cms.Api.Delivery/Controllers/Content/ByRouteContentApiController.cs","loc":84,"old-loc":83,"code-health":9.387218218812514,"old-code-health":9.387218218812514},{"file":"src/Umbraco.Cms.Api.Delivery/Controllers/Content/ContentApiControllerBase.cs","loc":52,"old-loc":48,"code-health":10.0,"old-code-health":10.0},{"file":"src/Umbraco.Cms.Api.Delivery/Controllers/Content/QueryContentApiController.cs","loc":59,"old-loc":58,"code-health":9.6882083290695,"old-code-health":9.6882083290695},{"file":"src/Umbraco.Cms.Api.Delivery/Controllers/Media/ByIdMediaApiController.cs","loc":34,"old-loc":33,"code-health":10.0,"old-code-health":10.0},{"file":"src/Umbraco.Cms.Api.Delivery/Controllers/Media/ByIdsMediaApiController.cs","loc":34,"old-loc":33,"code-health":10.0,"old-code-health":10.0},{"file":"src/Umbraco.Cms.Api.Delivery/Controllers/Media/ByPathMediaApiController.cs","loc":37,"old-loc":36,"code-health":10.0,"old-code-health":10.0},{"file":"src/Umbraco.Cms.Api.Delivery/Controllers/Media/MediaApiControllerBase.cs","loc":53,"old-loc":50,"code-health":10.0,"old-code-health":10.0},{"file":"src/Umbraco.Cms.Api.Delivery/Controllers/Media/QueryMediaApiController.cs","loc":53,"old-loc":52,"code-health":10.0,"old-code-health":10.0},{"file":"src/Umbraco.Cms.Api.Delivery/DependencyInjection/UmbracoBuilderExtensions.cs","loc":126,"old-loc":120,"code-health":10.0,"old-code-health":10.0},{"file":"src/Umbraco.Web.Website/Caching/RelationOutputCacheEvictionHandlerBase.cs","loc":27,"old-loc":54,"code-health":10.0,"old-code-health":9.842730062691357},{"file":"src/Umbraco.Web.Common/ApplicationBuilder/UmbracoApplicationBuilder.cs","loc":120,"old-loc":115,"code-health":10.0,"old-code-health":10.0},{"file":"src/Umbraco.Web.Website/DependencyInjection/UmbracoBuilderExtensions.cs","loc":99,"old-loc":101,"code-health":10.0,"old-code-health":10.0},{"file":"src/Umbraco.Web.Website/Caching/WebsiteDocumentOutputCacheEvictionHandler.cs","loc":102,"old-loc":102,"code-health":10.0,"old-code-health":10.0},{"file":"src/Umbraco.Web.Website/Caching/WebsiteMediaOutputCacheEvictionHandler.cs","loc":44,"old-loc":44,"code-health":10.0,"old-code-health":10.0},{"file":"src/Umbraco.Web.Website/Caching/WebsiteMemberOutputCacheEvictionHandler.cs","loc":34,"old-loc":34,"code-health":10.0,"old-code-health":10.0},{"file":"tests/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Caching/WebsiteDocumentOutputCacheEvictionHandlerTests.cs","loc":223,"old-loc":223,"code-health":9.387218218812514,"old-code-health":9.387218218812514},{"file":"tests/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Caching/WebsiteOutputCachePolicyTests.cs","loc":243,"old-loc":243,"code-health":9.096655465156704,"old-code-health":9.096655465156704}]},"removed-files":{"code-health":0.0,"old-code-health":0.0,"files":[]},"external-review-id":"22456","analysis-time":"2026-04-14T05:05:25Z","negative-impact-count":6,"suppressions":{"number-of-types":0,"number-of-files-touched":0,"findings":[]},"affected-hotspots":0,"commits":["e3287f537802f603b029aede89a0b9641299e2b9","6183a1ff058f236e0b960870cf3e46c6ff493e4b","c83384bf77f3c72bc61b4d8d4ee5aa5b666d1607","3c0e5bd83eecdc5b5941c7c00014d159073312e4","33216eda56f4b8636bfb6bce29161686f9f4465c","3a53be20c257c4e39baee7dd77ad1b5a554234b7"],"is-negative-review":true,"negative-findings":{"number-of-types":4,"number-of-files-touched":4,"findings":[{"method":"IOutputCachePolicy.ServeResponseAsync","why-it-occurs":"A Complex Method has a high cyclomatic complexity. The recommended threshold for the C# language is a cyclomatic complexity lower than 9.","name":"Complex Method","file":"src/Umbraco.Cms.Api.Delivery/Caching/DeliveryApiOutputCachePolicyBase.cs","refactoring-examples":[{"architectural-component-id":null,"author-name":"Bjarke Berg","training-data":{"loc-added":"24","loc-deleted":"26","delta-cc-mean":"0.0","delta-cc-total":"0","delta-penalties":"1.0","delta-n-functions":"0","current-file-score":"10.0"},"author-email":"mail@bergmania.dk","commit-full-message":"* AB40660 - untangle the preview cookie from the auth cookie\r\n\r\n* Clean up\r\n\r\n* Allow anonymous to end preview sessions\r\n\r\n* Some refinements\r\n\r\n* update OpenApi.json\r\n\r\n* Fix enter preview test\r\n\r\n* correct tests to match new expectations of the preview cookie\r\n\r\n* sync preview tests with correct expectations of access level\r\n\r\n---------\r\n\r\nCo-authored-by: Sven Geusens <sge@umbraco.dk>\r\nCo-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>","commit-date":"2024-05-17T14:06:26Z","current-rev":"11e5257b56","filename":"Umbraco-CMS/src/Umbraco.Web.Common/Middleware/PreviewAuthenticationMiddleware.cs","previous-rev":"80794f3efd","commit-title":"V14: Untangle the preview functionality from the auth cookie (#16308)","language":"C#","id":"7ecfb0a869f8c3df64c3c2a4602ef34642b49720","model-score":0.52,"author-id":null,"project-id":33308,"delta-file-score":1.044829,"diff":"diff --git a/src/Umbraco.Web.Common/Middleware/PreviewAuthenticationMiddleware.cs b/src/Umbraco.Web.Common/Middleware/PreviewAuthenticationMiddleware.cs\nindex 5bd16867ca..e61b8682af 100644\n--- a/src/Umbraco.Web.Common/Middleware/PreviewAuthenticationMiddleware.cs\n+++ b/src/Umbraco.Web.Common/Middleware/PreviewAuthenticationMiddleware.cs\n@@ -7,2 +7,8 @@\n using Microsoft.Extensions.Options;\n+using Umbraco.Cms.Core;\n+using Umbraco.Cms.Core.Preview;\n+using Umbraco.Cms.Core.Security;\n+using Umbraco.Cms.Core.Services;\n+using Umbraco.Cms.Web.Common.AspNetCore;\n+using Umbraco.Cms.Web.Common.Security;\n using Umbraco.Extensions;\n@@ -18,4 +24,15 @@ public class PreviewAuthenticationMiddleware : IMiddleware\n     private readonly ILogger<PreviewAuthenticationMiddleware> _logger;\n+    private readonly IPreviewTokenGenerator _previewTokenGenerator;\n+    private readonly IPreviewService _previewService;\n \n-    public PreviewAuthenticationMiddleware(ILogger<PreviewAuthenticationMiddleware> logger) => _logger = logger;\n+\n+    public PreviewAuthenticationMiddleware(\n+        ILogger<PreviewAuthenticationMiddleware> logger,\n+        IPreviewTokenGenerator previewTokenGenerator,\n+        IPreviewService previewService)\n+    {\n+        _logger = logger;\n+        _previewTokenGenerator = previewTokenGenerator;\n+        _previewService = previewService;\n+    }\n \n@@ -40,33 +57,14 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next)\n             {\n-                CookieAuthenticationOptions? cookieOptions = context.RequestServices\n-                    .GetRequiredService<IOptionsSnapshot<CookieAuthenticationOptions>>()\n-                    .Get(Core.Constants.Security.BackOfficeAuthenticationType);\n+                Attempt<ClaimsIdentity> backOfficeIdentityAttempt = await _previewService.TryGetPreviewClaimsIdentityAsync();\n \n-                if (cookieOptions == null)\n+                if (backOfficeIdentityAttempt.Success)\n                 {\n-                    throw new InvalidOperationException(\"No cookie options found with name \" +\n-                                                        Core.Constants.Security.BackOfficeAuthenticationType);\n+                    // Ok, we've got a real ticket, now we can add this ticket's identity to the current\n+                    // Principal, this means we'll have 2 identities assigned to the principal which we can\n+                    // use to authorize the preview and allow for a back office User.\n+                    context.User.AddIdentity(backOfficeIdentityAttempt.Result!);\n                 }\n-\n-                // If we've gotten this far it means a preview cookie has been set and a front-end umbraco document request is executing.\n-                // In this case, authentication will not have occurred for an Umbraco back office User, however we need to perform the authentication\n-                // for the user here so that the preview capability can be authorized otherwise only the non-preview page will be rendered.\n-                if (cookieOptions.Cookie.Name != null)\n+                else\n                 {\n-                    var chunkingCookieManager = new ChunkingCookieManager();\n-                    var cookie = chunkingCookieManager.GetRequestCookie(context, cookieOptions.Cookie.Name);\n-\n-                    if (!string.IsNullOrEmpty(cookie))\n-                    {\n-                        AuthenticationTicket? unprotected = cookieOptions.TicketDataFormat.Unprotect(cookie);\n-                        ClaimsIdentity? backOfficeIdentity = unprotected?.Principal.GetUmbracoIdentity();\n-\n-                        if (backOfficeIdentity != null)\n-                        {\n-                            // Ok, we've got a real ticket, now we can add this ticket's identity to the current\n-                            // Principal, this means we'll have 2 identities assigned to the principal which we can\n-                            // use to authorize the preview and allow for a back office User.\n-                            context.User.AddIdentity(backOfficeIdentity);\n-                        }\n-                    }\n+                    _logger.LogDebug(\"Could not transform previewCookie value into a claimsIdentity\");\n                 }\n","improvement-type":"Complex Method"},{"architectural-component-id":null,"author-name":"Sven Geusens","training-data":{"loc-added":"74","loc-deleted":"9","delta-cc-mean":"0.0","delta-cc-total":"0","delta-penalties":"1.0","delta-n-functions":"0","current-file-score":"9.6882083290695"},"author-email":"sge@umbraco.dk","commit-full-message":"* Support new localLink format in core link parsing\r\n\r\n* Updated devliery api to work with the new locallinks format\r\n\r\nAdded tests for old and new format handling.\r\n\r\n* Fix error regarding type attribute not always being present (for example old format or non local links)","commit-date":"2024-07-02T12:22:19Z","current-rev":"46acd51759","filename":"Umbraco-CMS/src/Umbraco.Infrastructure/DeliveryApi/ApiRichTextParserBase.cs","previous-rev":"13b77d35df","commit-title":"[V14] Make the backend work with the new localLinks format (#16661)","language":"C#","id":"7d7b589a86f716e705df415598cc4c8f2a02af1b","model-score":0.42,"author-id":null,"project-id":33308,"delta-file-score":0.3009901,"diff":"diff --git a/src/Umbraco.Infrastructure/DeliveryApi/ApiRichTextParserBase.cs b/src/Umbraco.Infrastructure/DeliveryApi/ApiRichTextParserBase.cs\nindex 7723fc835c..407bc1a022 100644\n--- a/src/Umbraco.Infrastructure/DeliveryApi/ApiRichTextParserBase.cs\n+++ b/src/Umbraco.Infrastructure/DeliveryApi/ApiRichTextParserBase.cs\n@@ -6,2 +6,3 @@\n using Umbraco.Cms.Core.PublishedCache;\n+using Umbraco.Cms.Core.Templates;\n \n@@ -20,3 +21,17 @@ protected ApiRichTextParserBase(IApiContentRouteBuilder apiContentRouteBuilder,\n \n-    protected void ReplaceLocalLinks(IPublishedSnapshot publishedSnapshot, string href, Action<IApiContentRoute> handleContentRoute, Action<string> handleMediaUrl, Action handleInvalidLink)\n+    protected void ReplaceLocalLinks(IPublishedSnapshot publishedSnapshot, string href, string type, Action<IApiContentRoute> handleContentRoute, Action<string> handleMediaUrl, Action handleInvalidLink)\n+    {\n+        ReplaceStatus replaceAttempt = ReplaceLocalLink(publishedSnapshot, href, type, handleContentRoute, handleMediaUrl);\n+        if (replaceAttempt == ReplaceStatus.Success)\n+        {\n+            return;\n+        }\n+\n+        if (replaceAttempt == ReplaceStatus.InvalidEntityType || ReplaceLegacyLocalLink(publishedSnapshot, href, handleContentRoute, handleMediaUrl) == ReplaceStatus.InvalidEntityType)\n+        {\n+            handleInvalidLink();\n+        }\n+    }\n+\n+    private ReplaceStatus ReplaceLocalLink(IPublishedSnapshot publishedSnapshot, string href, string type, Action<IApiContentRoute> handleContentRoute, Action<string> handleMediaUrl)\n     {\n@@ -25,11 +40,12 @@ protected void ReplaceLocalLinks(IPublishedSnapshot publishedSnapshot, string hr\n         {\n-            return;\n+            return ReplaceStatus.NoMatch;\n         }\n \n-        if (UdiParser.TryParse(match.Groups[\"udi\"].Value, out Udi? udi) is false)\n+        if (Guid.TryParse(match.Groups[\"guid\"].Value, out Guid guid) is false)\n         {\n-            return;\n+            return ReplaceStatus.NoMatch;\n         }\n \n-        bool handled = false;\n+        var udi = new GuidUdi(type, guid);\n+\n         switch (udi.EntityType)\n@@ -43,4 +59,4 @@ protected void ReplaceLocalLinks(IPublishedSnapshot publishedSnapshot, string hr\n                 {\n-                    handled = true;\n                     handleContentRoute(route);\n+                    return ReplaceStatus.Success;\n                 }\n@@ -52,4 +68,4 @@ protected void ReplaceLocalLinks(IPublishedSnapshot publishedSnapshot, string hr\n                 {\n-                    handled = true;\n                     handleMediaUrl(_apiMediaUrlProvider.GetUrl(media));\n+                    return ReplaceStatus.Success;\n                 }\n@@ -59,6 +75,45 @@ protected void ReplaceLocalLinks(IPublishedSnapshot publishedSnapshot, string hr\n \n-        if(handled is false)\n+        return ReplaceStatus.InvalidEntityType;\n+    }\n+\n+    private ReplaceStatus ReplaceLegacyLocalLink(IPublishedSnapshot publishedSnapshot, string href, Action<IApiContentRoute> handleContentRoute, Action<string> handleMediaUrl)\n+    {\n+        Match match = LegacyLocalLinkRegex().Match(href);\n+        if (match.Success is false)\n         {\n-            handleInvalidLink();\n+            return ReplaceStatus.NoMatch;\n+        }\n+\n+        if (UdiParser.TryParse(match.Groups[\"udi\"].Value, out Udi? udi) is false)\n+        {\n+            return ReplaceStatus.NoMatch;\n+        }\n+\n+\n+        switch (udi.EntityType)\n+        {\n+            case Constants.UdiEntityType.Document:\n+                IPublishedContent? content = publishedSnapshot.Content?.GetById(udi);\n+                IApiContentRoute? route = content != null\n+                    ? _apiContentRouteBuilder.Build(content)\n+                    : null;\n+                if (route != null)\n+                {\n+                    handleContentRoute(route);\n+                    return ReplaceStatus.Success;\n+                }\n+\n+                break;\n+            case Constants.UdiEntityType.Media:\n+                IPublishedContent? media = publishedSnapshot.Media?.GetById(udi);\n+                if (media != null)\n+                {\n+                    handleMediaUrl(_apiMediaUrlProvider.GetUrl(media));\n+                    return ReplaceStatus.Success;\n+                }\n+\n+                break;\n         }\n+\n+        return ReplaceStatus.InvalidEntityType;\n     }\n@@ -82,3 +137,13 @@ protected void ReplaceLocalImages(IPublishedSnapshot publishedSnapshot, string u\n     [GeneratedRegex(\"{localLink:(?<udi>umb:.+)}\")]\n+    private static partial Regex LegacyLocalLinkRegex();\n+\n+    [GeneratedRegex(\"{localLink:(?<guid>.+)}\")]\n     private static partial Regex LocalLinkRegex();\n+\n+    private enum ReplaceStatus\n+    {\n+        NoMatch,\n+        Success,\n+        InvalidEntityType\n+    }\n }\n","improvement-type":"Complex Method"},{"architectural-component-id":null,"author-name":"Vitor Rodrigues","training-data":{"loc-added":"16","loc-deleted":"14","delta-cc-mean":"0.0","delta-cc-total":"0","delta-penalties":"1.0","delta-n-functions":"0","current-file-score":"8.545379580978913"},"author-email":"vsilvar@users.noreply.github.com","commit-full-message":"* Fixes #15136: Search includes fields from other cultures\r\n\r\nRegex was updated to support block list fields\r\nUnpublished nodes on the supplied culture are not filtered out\r\n\r\n* Making the code non-breaking\r\n\r\n* Fixed failing publish content query integration tests\r\n\r\nThe tests were not setting the content as publish in the specifed culture\r\ncausing the content items to be ignored\r\n\r\n---------\r\n\r\nCo-authored-by: Laura Neto <12862535+lauraneto@users.noreply.github.com>","commit-date":"2024-02-07T12:43:37Z","current-rev":"839b2ff6a2","filename":"Umbraco-CMS/src/Umbraco.Infrastructure/PublishedContentQuery.cs","previous-rev":"2221c4f1c7","commit-title":"Fixes #15136: Search includes fields from other cultures (#15148)","language":"C#","id":"ac0709776b22e60e777651712009344d5d3c3529","model-score":0.3,"author-id":null,"project-id":33308,"delta-file-score":0.2613985,"diff":"diff --git a/src/Umbraco.Infrastructure/PublishedContentQuery.cs b/src/Umbraco.Infrastructure/PublishedContentQuery.cs\nindex d075e8b9d2..758115b9ca 100644\n--- a/src/Umbraco.Infrastructure/PublishedContentQuery.cs\n+++ b/src/Umbraco.Infrastructure/PublishedContentQuery.cs\n@@ -295,17 +295,13 @@ public IEnumerable<PublishedSearchResult> Search(string term, int skip, int take\n                     .ToArray(); // Get all index fields suffixed with the culture name supplied\n-            ordering = query.ManagedQuery(term, fields);\n-        }\n-\n-            // Filter selected fields because results are loaded from the published snapshot based on these\n-            IOrdering? queryExecutor = ordering.SelectFields(_returnedQueryFields);\n-\n-\n-        ISearchResults? results = skip == 0 && take == 0\n-            ? queryExecutor.Execute()\n-            : queryExecutor.Execute(QueryOptions.SkipTake(skip, take));\n \n-        totalRecords = results.TotalItemCount;\n+            // Filter out unpublished content for the specified culture if the content varies by culture\n+            // The published__{culture} field is not populated when the content is not published in that culture\n+            ordering = query\n+                .ManagedQuery(term, fields)\n+                .Not().Group(q => q\n+                    .Field(UmbracoExamineFieldNames.VariesByCultureFieldName, \"y\")\n+                    .Not().Field($\"{UmbracoExamineFieldNames.PublishedFieldName}_{culture.ToLowerInvariant()}\", \"y\"));\n+        }\n \n-        return new CultureContextualSearchResults(results.ToPublishedSearchResults(_publishedSnapshot.Content),\n-            _variationContextAccessor, culture);\n+        return Search(ordering, skip, take, out totalRecords, culture);\n     }\n@@ -318,2 +314,6 @@ public IEnumerable<PublishedSearchResult> Search(IQueryExecutor query)\n     public IEnumerable<PublishedSearchResult> Search(IQueryExecutor query, int skip, int take, out long totalRecords)\n+        => Search(query, skip, take, out totalRecords, null);\n+\n+    /// <inheritdoc />\n+    public IEnumerable<PublishedSearchResult> Search(IQueryExecutor query, int skip, int take, out long totalRecords, string? culture)\n     {\n@@ -333,4 +333,4 @@ public IEnumerable<PublishedSearchResult> Search(IQueryExecutor query, int skip,\n         {\n-                // Filter selected fields because results are loaded from the published snapshot based on these\n-                query = ordering.SelectFields(_returnedQueryFields);\n+            // Filter selected fields because results are loaded from the published snapshot based on these\n+            query = ordering.SelectFields(_returnedQueryFields);\n         }\n@@ -343,3 +343,5 @@ public IEnumerable<PublishedSearchResult> Search(IQueryExecutor query, int skip,\n \n-        return results.ToPublishedSearchResults(_publishedSnapshot);\n+        return culture.IsNullOrWhiteSpace()\n+            ? results.ToPublishedSearchResults(_publishedSnapshot)\n+            : new CultureContextualSearchResults(results.ToPublishedSearchResults(_publishedSnapshot.Content), _variationContextAccessor, culture);\n     }\n","improvement-type":"Complex Method"},{"architectural-component-id":null,"author-name":"Nikolaj E. Lauridsen","training-data":{"loc-added":"6","loc-deleted":"36","delta-cc-mean":"0.0","delta-cc-total":"0","delta-penalties":"1.0","delta-n-functions":"0","current-file-score":"9.387218218812514"},"author-email":"nikolajlauridsen@protonmail.ch","commit-full-message":"* Remove nucache reference from Web.Common\r\n\r\n* Get tests building-ish\r\n\r\n* Move ReservedFieldNamesService to the right project\r\n\r\n* Remove IPublishedSnapshotStatus\r\n\r\n* Added functionality to the INavigationQueryService to get root keys\r\n\r\n* Fixed issue with navigation\r\n\r\n* Remove IPublishedSnapshot from UmbracoContext\r\n\r\n* Begin removing usage of IPublishedSnapshot from PublishedContentExtensions\r\n\r\n* Fix PublishedContentExtensions.cs\r\n\r\n* Don't use snapshots in delivery media api\r\n\r\n* Use IPublishedMediaCache in QueryMediaApiController\r\n\r\n* Remove more usages of IPublishedSnapshotAccessor\r\n\r\n* Comment out tests\r\n\r\n* Remove more usages of PublishedSnapshotAccessor\r\n\r\n* Remove PublishedSnapshot from property\r\n\r\n* Fixed test build\r\n\r\n* Fix errors\r\n\r\n* Fix some tests\r\n\r\n* Delete NuCache 🎉\r\n\r\n* Implement DatabaseCacheRebuilder\r\n\r\n* Remove usage of IPublishedSnapshotService\r\n\r\n* Remove IPublishedSnapshotService\r\n\r\n* Remove TestPublishedSnapshotAccessor and make tests build\r\n\r\n* Don't test Snapshot cachelevel\r\n\r\nIt's no longer supported\r\n\r\n* Fix BlockEditorConverter\r\n\r\nElement != Element document type\r\n\r\n* Remember to set cachemanager\r\n\r\n* Fix RichTextParserTests\r\n\r\n* Implement TryGetLevel on INavigationQueryService\r\n\r\n* Fake level and obsolete it in PublishedContent\r\n\r\n* Remove ChildrenForAllCultures\r\n\r\n* Hack Path property on PublishedContent\r\n\r\n* Remove usages of IPublishedSnapshot in tests\r\n\r\n* More ConvertersTests\r\n\r\n* Add hybrid cache to integration tests\r\n\r\nWe can actually do this now because we no longer save files on disk\r\n\r\n* Rename IPublishedSnapshotRebuilder to ICacheRebuilder\r\n\r\n* Comment out tests\r\n\r\n* V15: Replacing the usages of Parent (navigation data) from IPublishedContent (#17125)\r\n\r\n* Fix .Parent references in PublishedContentExtensions\r\n\r\n* Add missing methods to FriendlyPublishedContentExtensions (ones that you were able to call on the content directly as they now require extra params)\r\n\r\n* Fix references from the extension methods\r\n\r\n* Fix dependencies in tests\r\n\r\n* Replace IPublishedSnapshotAccessor with the content cache in tests\r\n\r\n* Resolving more .Parent references\r\n\r\n* Fix unit tests\r\n\r\n* Obsolete and use extension methods\r\n\r\n* Remove private method and use extension instead\r\n\r\n* Moving code around\r\n\r\n* Fix tests\r\n\r\n* Fix more references\r\n\r\n* Cleanup\r\n\r\n* Fix more usages\r\n\r\n* Resolve merge conflict\r\n\r\n* Fix tests\r\n\r\n* Cleanup\r\n\r\n* Fix more tests\r\n\r\n* Fixed unit tests\r\n\r\n* Cleanup\r\n\r\n* Replace last usages\r\n\r\n---------\r\n\r\nCo-authored-by: Bjarke Berg <mail@bergmania.dk>\r\n\r\n* Remove usage of IPublishedSnapshotAccessor from IRequestItemProvider\r\n\r\n* Post merge fixup\r\n\r\n* Remo IPublishedSnapshot\r\n\r\n* Add HasAny to IDocumentUrlService\r\n\r\n* Fix TextBuilder\r\n\r\n* Fix modelsbuilder tests\r\n\r\n* Use explicit types\r\n\r\n* Implement GetByContentType\r\n\r\n* Support element types in PublishedContentTypeCache\r\n\r\n* Run enlistments before publishing notifications\r\n\r\n* Fix elements cache refreshing\r\n\r\n* Implement GetByUdi\r\n\r\n* Implement GetAtRoot\r\n\r\n* Implement GetByRoute\r\n\r\n* Reimplement GetRouteById\r\n\r\n* Fix blocks unit tests\r\n\r\n* Initialize domain cache on boot\r\n\r\n* Only return routes with domains on non default lanauges\r\n\r\n* V15: Replacing the usages of `Children` (navigation data) from `IPublishedContent` (#17159)\r\n\r\n* Update params in PublishedContentExtensions to the general interfaces for the published cache and navigation service, so that we can use the extension methods on both documents and media\r\n\r\n* Introduce GetParent() which uses the right services\r\n\r\n* Fix obsolete message on .Parent\r\n\r\n* Obsolete .Children\r\n\r\n* Fix usages of Children for ApiMediaQueryService\r\n\r\n* Fix usage in internal\r\n\r\n* Fix usages in views\r\n\r\n* Fix indentation\r\n\r\n* Fix issue with delete language\r\n\r\n* Update nuget pacakges\r\n\r\n* Clear elements cache when content is deleted\r\n\r\ninstead of trying to update it\r\n\r\n* Reset publishedModelFactory\r\n\r\n* Fixed publishing\r\n\r\n---------\r\n\r\nCo-authored-by: Bjarke Berg <mail@bergmania.dk>\r\nCo-authored-by: Elitsa Marinovska <21998037+elit0451@users.noreply.github.com>\r\nCo-authored-by: kjac <kja@umbraco.dk>","commit-date":"2024-10-01T13:03:02Z","current-rev":"1258962429","filename":"Umbraco-CMS/src/Umbraco.Core/PublishedCache/PublishedElementPropertyBase.cs","previous-rev":"7ca96423f8","commit-title":"V15: Remove Nucache (#17166)","language":"C#","id":"c5107d73a299c92b12ef250416ebfdc0fe984c06","model-score":0.2,"author-id":null,"project-id":33308,"delta-file-score":0.29056275,"diff":"diff --git a/src/Umbraco.Core/PublishedCache/PublishedElementPropertyBase.cs b/src/Umbraco.Core/PublishedCache/PublishedElementPropertyBase.cs\nindex 53e8156538..0452cf0b03 100644\n--- a/src/Umbraco.Core/PublishedCache/PublishedElementPropertyBase.cs\n+++ b/src/Umbraco.Core/PublishedCache/PublishedElementPropertyBase.cs\n@@ -17,3 +17,2 @@ internal class PublishedElementPropertyBase : PublishedPropertyBase\n     private readonly object _locko = new();\n-    private readonly IPublishedSnapshotAccessor? _publishedSnapshotAccessor;\n     private readonly object? _sourceValue;\n@@ -21,2 +20,3 @@ internal class PublishedElementPropertyBase : PublishedPropertyBase\n     protected readonly bool IsPreviewing;\n+    private readonly ICacheManager? _cacheManager;\n     private CacheValues? _cacheValues;\n@@ -32,4 +32,4 @@ public PublishedElementPropertyBase(\n         PropertyCacheLevel referenceCacheLevel,\n-        object? sourceValue = null,\n-        IPublishedSnapshotAccessor? publishedSnapshotAccessor = null)\n+        ICacheManager? cacheManager,\n+        object? sourceValue = null)\n         : base(propertyType, referenceCacheLevel)\n@@ -37,5 +37,5 @@ public PublishedElementPropertyBase(\n         _sourceValue = sourceValue;\n-        _publishedSnapshotAccessor = publishedSnapshotAccessor;\n         Element = element;\n         IsPreviewing = previewing;\n+        _cacheManager = cacheManager;\n         IsMember = propertyType.ContentType?.ItemType == PublishedItemType.Member;\n@@ -120,23 +120,2 @@ private void GetCacheLevels(PropertyCacheLevel propertyTypeCacheLevel, out Prope\n \n-    private IAppCache? GetSnapshotCache()\n-    {\n-        // cache within the snapshot cache, unless previewing, then use the snapshot or\n-        // elements cache (if we don't want to pollute the elements cache with short-lived\n-        // data) depending on settings\n-        // for members, always cache in the snapshot cache - never pollute elements cache\n-        if (_publishedSnapshotAccessor is null)\n-        {\n-            return null;\n-        }\n-\n-        if (!_publishedSnapshotAccessor.TryGetPublishedSnapshot(out IPublishedSnapshot? publishedSnapshot))\n-        {\n-            return null;\n-        }\n-\n-        return (IsPreviewing == false || FullCacheWhenPreviewing) && IsMember == false\n-            ? publishedSnapshot!.ElementsCache\n-            : publishedSnapshot!.SnapshotCache;\n-    }\n-\n     private CacheValues GetCacheValues(PropertyCacheLevel cacheLevel)\n@@ -147,2 +126,3 @@ private CacheValues GetCacheValues(PropertyCacheLevel cacheLevel)\n             case PropertyCacheLevel.None:\n+            case PropertyCacheLevel.Snapshot:\n                 // never cache anything\n@@ -155,13 +135,3 @@ private CacheValues GetCacheValues(PropertyCacheLevel cacheLevel)\n             case PropertyCacheLevel.Elements:\n-                // cache within the elements  cache, depending...\n-                IAppCache? snapshotCache = GetSnapshotCache();\n-                cacheValues = (CacheValues?)snapshotCache?.Get(ValuesCacheKey, () => new CacheValues()) ??\n-                              new CacheValues();\n-                break;\n-            case PropertyCacheLevel.Snapshot:\n-                IPublishedSnapshot? publishedSnapshot = _publishedSnapshotAccessor?.GetRequiredPublishedSnapshot();\n-\n-                // cache within the snapshot cache\n-                IAppCache? facadeCache = publishedSnapshot?.SnapshotCache;\n-                cacheValues = (CacheValues?)facadeCache?.Get(ValuesCacheKey, () => new CacheValues()) ??\n+                cacheValues = (CacheValues?)_cacheManager?.ElementsCache.Get(ValuesCacheKey, () => new CacheValues()) ??\n                               new CacheValues();\n","improvement-type":"Complex Method"},{"architectural-component-id":null,"author-name":"Nikolaj E. Lauridsen","training-data":{"loc-added":"11","loc-deleted":"48","delta-cc-mean":"0.0","delta-cc-total":"0","delta-penalties":"1.0","delta-n-functions":"0","current-file-score":"9.387218218812514"},"author-email":"nikolajlauridsen@protonmail.ch","commit-full-message":"","commit-date":"2024-12-03T12:40:05Z","current-rev":"380f3f7e87","filename":"Umbraco-CMS/src/Umbraco.PublishedCache.HybridCache/NotificationHandlers/CacheRefreshingNotificationHandler.cs","previous-rev":"437fcba4e5","commit-title":"Clear elements cache instead of refreshing it (#17708)","language":"C#","id":"520cf3bc55fbfa6fb3c07f3560d781b1e2034c12","model-score":0.19,"author-id":null,"project-id":33308,"delta-file-score":0.7076424,"diff":"diff --git a/src/Umbraco.PublishedCache.HybridCache/NotificationHandlers/CacheRefreshingNotificationHandler.cs b/src/Umbraco.PublishedCache.HybridCache/NotificationHandlers/CacheRefreshingNotificationHandler.cs\nindex f61968365c..6ff5a5fe1e 100644\n--- a/src/Umbraco.PublishedCache.HybridCache/NotificationHandlers/CacheRefreshingNotificationHandler.cs\n+++ b/src/Umbraco.PublishedCache.HybridCache/NotificationHandlers/CacheRefreshingNotificationHandler.cs\n@@ -47,3 +47,3 @@ public async Task HandleAsync(ContentRefreshNotification notification, Cancellat\n     {\n-        await RefreshElementsCacheAsync(notification.Entity);\n+        ClearElementsCache();\n \n@@ -56,3 +56,3 @@ public async Task HandleAsync(ContentDeletedNotification notification, Cancellat\n         {\n-            RemoveFromElementsCache(deletedEntity);\n+            ClearElementsCache();\n             await _documentCacheService.DeleteItemAsync(deletedEntity);\n@@ -63,3 +63,3 @@ public async Task HandleAsync(MediaRefreshNotification notification, Cancellatio\n     {\n-        await RefreshElementsCacheAsync(notification.Entity);\n+        ClearElementsCache();\n         await _mediaCacheService.RefreshMediaAsync(notification.Entity);\n@@ -71,3 +71,3 @@ public async Task HandleAsync(MediaDeletedNotification notification, Cancellatio\n         {\n-            RemoveFromElementsCache(deletedEntity);\n+            ClearElementsCache();\n             await _mediaCacheService.DeleteItemAsync(deletedEntity);\n@@ -76,49 +76,12 @@ public async Task HandleAsync(MediaDeletedNotification notification, Cancellatio\n \n-    private async Task RefreshElementsCacheAsync(IUmbracoEntity content)\n+    private void ClearElementsCache()\n     {\n-        IEnumerable<IRelation> parentRelations = _relationService.GetByParent(content)!;\n-        IEnumerable<IRelation> childRelations = _relationService.GetByChild(content);\n-\n-        var ids = parentRelations.Select(x => x.ChildId).Concat(childRelations.Select(x => x.ParentId)).ToHashSet();\n-        // We need to add ourselves to the list of ids to clear\n-        ids.Add(content.Id);\n-        foreach (var id in ids)\n-        {\n-            if (await _documentCacheService.HasContentByIdAsync(id) is false)\n-            {\n-                continue;\n-            }\n-\n-            IPublishedContent? publishedContent = await _documentCacheService.GetByIdAsync(id);\n-            if (publishedContent is null)\n-            {\n-                continue;\n-            }\n-\n-            foreach (IPublishedProperty publishedProperty in publishedContent.Properties)\n-            {\n-                var property = (PublishedProperty) publishedProperty;\n-                if (property.ReferenceCacheLevel is PropertyCacheLevel.Elements\n-                    || property.PropertyType.DeliveryApiCacheLevel is PropertyCacheLevel.Elements\n-                    || property.PropertyType.DeliveryApiCacheLevelForExpansion is PropertyCacheLevel.Elements)\n-                {\n-                    _elementsCache.ClearByKey(property.ValuesCacheKey);\n-                }\n-            }\n-        }\n+        // Ideally we'd like to not have to clear the entire cache here. However, this was the existing behavior in NuCache.\n+        // The reason for this is that we have no way to know which elements are affected by the changes. or what their keys are.\n+        // This is because currently published elements lives exclusively in a JSON blob in the umbracoPropertyData table.\n+        // This means that the only way to resolve these keys are to actually parse this data with a specific value converter, and for all cultures, which is not feasible.\n+        // If published elements become their own entities with relations, instead of just property data, we can revisit this,\n+        _elementsCache.Clear();\n     }\n \n-    private void RemoveFromElementsCache(IUmbracoEntity content)\n-    {\n-        // ClearByKey clears by \"startsWith\" so we'll clear by the cachekey prefix + contentKey\n-        // This will clear any and all properties for this content item, this is important because\n-        // we cannot resolve the PublishedContent for this entity since it and its content type is deleted.\n-        _elementsCache.ClearByKey(GetContentWideCacheKey(content.Key, true));\n-        _elementsCache.ClearByKey(GetContentWideCacheKey(content.Key, false));\n-    }\n-\n-    private string GetContentWideCacheKey(Guid contentKey, bool isPreviewing) => isPreviewing\n-        ? CacheKeys.PreviewPropertyCacheKeyPrefix + contentKey\n-        : CacheKeys.PropertyCacheKeyPrefix + contentKey;\n-\n     public Task HandleAsync(ContentTypeRefreshedNotification notification, CancellationToken cancellationToken)\n","improvement-type":"Complex Method"}],"change-level":"warning","is-hotspot?":false,"line":101,"what-changed":"IOutputCachePolicy.ServeResponseAsync has a cyclomatic complexity of 9, threshold = 9","how-to-fix":"There are many reasons for Complex Method. Sometimes, another design approach is beneficial such as a) modeling state using an explicit state machine rather than conditionals, or b) using table lookup rather than long chains of logic. In other scenarios, the function can be split using [EXTRACT FUNCTION](https://refactoring.com/catalog/extractFunction.html). Just make sure you extract natural and cohesive functions. Complex Methods can also be addressed by identifying complex conditional expressions and then using the [DECOMPOSE CONDITIONAL](https://refactoring.com/catalog/decomposeConditional.html) refactoring.","change-type":"introduced"},{"method":"IOutputCachePolicy.ServeResponseAsync","why-it-occurs":"A Bumpy Road is a function that contains multiple chunks of nested conditional logic inside the same function. The deeper the nesting and the more bumps, the lower the code health.\n\nA bumpy code road represents a lack of encapsulation which becomes an obstacle to comprehension. In imperative languages there’s also an increased risk for feature entanglement, which leads to complex state management. CodeScene considers the following rules for the code health impact: 1) The deeper the nested conditional logic of each bump, the higher the tax on our working memory. 2) The more bumps inside a function, the more expensive it is to refactor as each bump represents a missing abstraction. 3) The larger each bump – that is, the more lines of code it spans – the harder it is to build up a mental model of the function. The nesting depth for what is considered a bump is  levels of conditionals.","name":"Bumpy Road Ahead","file":"src/Umbraco.Cms.Api.Delivery/Caching/DeliveryApiOutputCachePolicyBase.cs","refactoring-examples":null,"change-level":"warning","is-hotspot?":false,"line":101,"what-changed":"IOutputCachePolicy.ServeResponseAsync has 2 blocks with nested conditional logic. Any nesting of 2 or deeper is considered. Threshold is 2 blocks per function","how-to-fix":"Bumpy Road implementations indicate a lack of encapsulation. Check out the detailed description of the [Bumpy Road code health issue](https://codescene.com/blog/bumpy-road-code-complexity-in-context/).\n\nA Bumpy Road often suggests that the function/method does too many things. The first refactoring step is to identify the different possible responsibilities of the function. Consider extracting those responsibilities into smaller, cohesive, and well-named functions. The [EXTRACT FUNCTION](https://refactoring.com/catalog/extractFunction.html) refactoring is the primary response.","change-type":"introduced"},{"why-it-occurs":"Duplicated code often leads to code that's harder to change since the same logical change has to be done in multiple functions. More duplication gives lower code health.","name":"Code Duplication","file":"tests/Umbraco.Tests.UnitTests/Umbraco.Cms.Api.Delivery/Caching/DeliveryApiDocumentOutputCacheEvictionHandlerTests.cs","refactoring-examples":null,"change-level":"warning","is-hotspot?":false,"line":45,"what-changed":"The module contains 6 functions with similar structure: HandleAsync_MultiplePayloads_EvictsAll,HandleAsync_RefreshBranch_EvictsContentKeyAndAncestorTag,HandleAsync_RefreshNode_EvictsContentKey,HandleAsync_Remove_EvictsContentKey and 2 more functions","how-to-fix":"A certain degree of duplicated code might be acceptable. The problems start when it is the same behavior that is duplicated across the functions in the module, ie. a violation of the Don't Repeat Yourself (DRY) principle. DRY violations lead to code that is changed together in predictable patterns, which is both expensive and risky. DRY violations can be identified using CodeScene's X-Ray analysis to detect clusters of change coupled functions with high code similarity. [Read More](https://codescene.com/blog/software-revolution-part3/)\n\nOnce you have identified the similarities across functions, look to extract and encapsulate the concept that varies into its own function(s). These shared abstractions can then be re-used, which minimizes the amount of duplication and simplifies change.","change-type":"introduced"},{"why-it-occurs":"Duplicated code often leads to code that's harder to change since the same logical change has to be done in multiple functions. More duplication gives lower code health.","name":"Code Duplication","file":"tests/Umbraco.Tests.UnitTests/Umbraco.Cms.Api.Delivery/Caching/DeliveryApiOutputCacheContentPolicyTests.cs","refactoring-examples":null,"change-level":"warning","is-hotspot?":false,"line":43,"what-changed":"The module contains 2 functions with similar structure: CacheRequestAsync_WhenRequestFilterReturnsNotCacheable_DoesNotCache,ServeResponseAsync_WhenContentFilterReturnsNotCacheable_DisablesCaching","how-to-fix":"A certain degree of duplicated code might be acceptable. The problems start when it is the same behavior that is duplicated across the functions in the module, ie. a violation of the Don't Repeat Yourself (DRY) principle. DRY violations lead to code that is changed together in predictable patterns, which is both expensive and risky. DRY violations can be identified using CodeScene's X-Ray analysis to detect clusters of change coupled functions with high code similarity. [Read More](https://codescene.com/blog/software-revolution-part3/)\n\nOnce you have identified the similarities across functions, look to extract and encapsulate the concept that varies into its own function(s). These shared abstractions can then be re-used, which minimizes the amount of duplication and simplifies change.","change-type":"introduced"},{"method":"EvictRelatedContentAsync","why-it-occurs":"A Bumpy Road is a function that contains multiple chunks of nested conditional logic inside the same function. The deeper the nesting and the more bumps, the lower the code health.\n\nA bumpy code road represents a lack of encapsulation which becomes an obstacle to comprehension. In imperative languages there’s also an increased risk for feature entanglement, which leads to complex state management. CodeScene considers the following rules for the code health impact: 1) The deeper the nested conditional logic of each bump, the higher the tax on our working memory. 2) The more bumps inside a function, the more expensive it is to refactor as each bump represents a missing abstraction. 3) The larger each bump – that is, the more lines of code it spans – the harder it is to build up a mental model of the function. The nesting depth for what is considered a bump is  levels of conditionals.","name":"Bumpy Road Ahead","file":"src/Umbraco.Web.Common/Caching/RelationOutputCacheEvictionHandlerBase.cs","refactoring-examples":null,"change-level":"warning","is-hotspot?":false,"line":48,"what-changed":"EvictRelatedContentAsync has 2 blocks with nested conditional logic. Any nesting of 2 or deeper is considered. Threshold is 2 blocks per function","how-to-fix":"Bumpy Road implementations indicate a lack of encapsulation. Check out the detailed description of the [Bumpy Road code health issue](https://codescene.com/blog/bumpy-road-code-complexity-in-context/).\n\nA Bumpy Road often suggests that the function/method does too many things. The first refactoring step is to identify the different possible responsibilities of the function. Consider extracting those responsibilities into smaller, cohesive, and well-named functions. The [EXTRACT FUNCTION](https://refactoring.com/catalog/extractFunction.html) refactoring is the primary response.","change-type":"introduced"},{"method":"EvictRelatedContentAsync","why-it-occurs":"Functions with many arguments indicate either a) low cohesion where the function has too many responsibilities, or b) a missing abstraction that encapsulates those arguments.\n\nThe threshold for the C# language is 4 function arguments.","name":"Excess Number of Function Arguments","file":"src/Umbraco.Web.Common/Caching/RelationOutputCacheEvictionHandlerBase.cs","refactoring-examples":[{"architectural-component-id":null,"author-name":"Kenn Jacobsen","training-data":{"loc-added":"2","loc-deleted":"2","delta-cc-mean":"0.0","delta-cc-total":"0","delta-penalties":"1.0","delta-n-functions":"0","current-file-score":"10.0"},"author-email":"kja@umbraco.dk","commit-full-message":"Co-authored-by: Elitsa <elm@umbraco.dk>","commit-date":"2023-06-26T05:33:16Z","current-rev":"2c9d0b2cb1","filename":"Umbraco-CMS/src/Umbraco.Core/DeliveryApi/ApiContentBuilder.cs","previous-rev":"e92ea34098","commit-title":"Add create and update dates to Delivery API response (#14427)","language":"C#","id":"c9f57f2ba9707ffb5bf810e853e28a00b83b45fc","model-score":0.96,"author-id":null,"project-id":33308,"delta-file-score":0.31179166,"diff":"diff --git a/src/Umbraco.Core/DeliveryApi/ApiContentBuilder.cs b/src/Umbraco.Core/DeliveryApi/ApiContentBuilder.cs\nindex 11ea4faf77..135afe068a 100644\n--- a/src/Umbraco.Core/DeliveryApi/ApiContentBuilder.cs\n+++ b/src/Umbraco.Core/DeliveryApi/ApiContentBuilder.cs\n@@ -12,4 +12,4 @@ public ApiContentBuilder(IApiContentNameProvider apiContentNameProvider, IApiCon\n \n-    protected override IApiContent Create(IPublishedContent content, Guid id, string name, string contentType, IApiContentRoute route, IDictionary<string, object?> properties)\n-        => new ApiContent(id, name, contentType, route, properties);\n+    protected override IApiContent Create(IPublishedContent content, string name, IApiContentRoute route, IDictionary<string, object?> properties)\n+        => new ApiContent(content.Key, name, content.ContentType.Alias, content.CreateDate, content.UpdateDate, route, properties);\n }\n","improvement-type":"Excess Number of Function Arguments"},{"architectural-component-id":null,"author-name":"Elitsa Marinovska","training-data":{"loc-added":"1","loc-deleted":"23","delta-cc-mean":"0.0","delta-cc-total":"0","delta-penalties":"1.0","delta-n-functions":"0","current-file-score":"10.0"},"author-email":"21998037+elit0451@users.noreply.github.com","commit-full-message":"* Obsoletions related to Delivery API\r\n\r\n* Fix TypeLoader and TypeFinder tests\r\n\r\n* Remove obsolete and default implementations of IFileSource and IFileTypeCollection\r\n\r\n* More Delivery API related obsoletions\r\n\r\n* VariationContextAccessor related\r\n\r\n* ValueFactories obsoletion and fix references\r\n\r\n* ValueSetBuilders obsoletions\r\n\r\n* ValueConverters obsoletions\r\n\r\n* Other obsolete ctors and methods\r\n\r\n* Forgotten VariationContextAccessor obsoletion\r\n\r\n* More obsoletions\r\n\r\n* XPath related obsoletions\r\n\r\n* Revert XmlHelper changes\r\n\r\n* Delete RenamedRootNavigator and its tests\r\n\r\n* Fix test\r\n\r\n* XmlHelper obsoletion\r\n\r\n* Return null instead of GetXPathValue\r\n\r\n* Obsolete entire class instead\r\n\r\n* Remove XPath obsoletions from IPublishedCache\r\n\r\n* Remove XPath-related if-block that is no longer needed\r\n\r\n* Change obsolete msg for classes needed for NuCache\r\n\r\n* Moving classes to NuCache and making them internal\r\n\r\n* Remove more XPath-related obsoletions\r\n\r\n* Remove NavigableNavigator and its tests\r\n\r\n* Cleanup\r\n\r\n* Remove Xpath references from tests\r\n\r\n* Revert interface deletion in MediaCache\r\n\r\n* Using XOR operation\r\n\r\nCo-authored-by: Nuklon <Nuklon@users.noreply.github.com>\r\n\r\n---------\r\n\r\nCo-authored-by: Nuklon <Nuklon@users.noreply.github.com>","commit-date":"2024-04-09T07:06:48Z","current-rev":"9c18cd22e0","filename":"Umbraco-CMS/src/Umbraco.Core/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs","previous-rev":"187d45860a","commit-title":"V14: Deleted code marked as obsolete for V14 (#15998)","language":"C#","id":"5a0a3e3ca094c91b1e473f50f09c522acaad7c3f","model-score":0.51,"author-id":null,"project-id":33308,"delta-file-score":0.31179166,"diff":"diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs\nindex e3dcd6ae78..23241e8cd9 100644\n--- a/src/Umbraco.Core/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs\n+++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs\n@@ -1,3 +1,2 @@\n-﻿using System.Xml;\n-using Umbraco.Cms.Core.Models.PublishedContent;\n+﻿using Umbraco.Cms.Core.Models.PublishedContent;\n \n@@ -58,23 +57,2 @@ public override object ConvertSourceToIntermediate(IPublishedElement owner, IPub\n     }\n-\n-    [Obsolete(\"The current implementation of XPath is suboptimal and will be removed entirely in a future version. Scheduled for removal in v14\")]\n-    public override object? ConvertIntermediateToXPath(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object? inter, bool preview)\n-    {\n-        var d = new XmlDocument();\n-        XmlElement e = d.CreateElement(\"values\");\n-        d.AppendChild(e);\n-\n-        var values = (IEnumerable<string>?)inter;\n-        if (values is not null)\n-        {\n-            foreach (var value in values)\n-            {\n-                XmlElement ee = d.CreateElement(\"value\");\n-                ee.InnerText = value;\n-                e.AppendChild(ee);\n-            }\n-        }\n-\n-        return d.CreateNavigator();\n-    }\n }\n","improvement-type":"Excess Number of Function Arguments"},{"architectural-component-id":null,"author-name":"Ronald Barendse","training-data":{"loc-added":"37","loc-deleted":"41","delta-cc-mean":"0.0","delta-cc-total":"0","delta-penalties":"1.0","delta-n-functions":"0","current-file-score":"10.0"},"author-email":"ronald@barend.se","commit-full-message":"* Fix IndexOutOfRangeException when converting single value to range in SliderValueConverter\r\n\r\n* Fix NullReferenceException while deserializing empty value in TagsValueConverter\r\n\r\n* Use invariant decimal parsing\r\n\r\n* Handle converting from slider to single value\r\n\r\n* Fix parsing range as single value\r\n\r\n* Make Handle methods autonomous\r\n\r\n---------\r\n\r\nCo-authored-by: nikolajlauridsen <nikolajlauridsen@protonmail.ch>","commit-date":"2023-08-23T08:09:43Z","current-rev":"f97e9a9f34","filename":"Umbraco-CMS/src/Umbraco.Core/PropertyEditors/ValueConverters/TagsValueConverter.cs","previous-rev":"19ee8e9254","commit-title":"Fix exceptions in Slider and Tags property value converters (#13782)","language":"C#","id":"69c4f0775fc3beb01b4297882b107deebc0194ae","model-score":0.36,"author-id":null,"project-id":33308,"delta-file-score":0.31179166,"diff":"diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/TagsValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/TagsValueConverter.cs\nindex 3afc5a6596..2dd1c1d56e 100644\n--- a/src/Umbraco.Core/PropertyEditors/ValueConverters/TagsValueConverter.cs\n+++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/TagsValueConverter.cs\n@@ -1,2 +1 @@\n-﻿using System.Collections.Concurrent;\n using Umbraco.Cms.Core.Models;\n@@ -9,2 +8,6 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters;\n \n+/// <summary>\n+/// The tags property value converter.\n+/// </summary>\n+/// <seealso cref=\"Umbraco.Cms.Core.PropertyEditors.PropertyValueConverterBase\" />\n [DefaultPropertyValueConverter]\n@@ -12,14 +15,30 @@ public class TagsValueConverter : PropertyValueConverterBase\n {\n-    private static readonly ConcurrentDictionary<int, bool> Storages = new();\n-    private readonly IDataTypeService _dataTypeService;\n     private readonly IJsonSerializer _jsonSerializer;\n \n+    /// <summary>\n+    /// Initializes a new instance of the <see cref=\"TagsValueConverter\" /> class.\n+    /// </summary>\n+    /// <param name=\"jsonSerializer\">The JSON serializer.</param>\n+    /// <exception cref=\"System.ArgumentNullException\">jsonSerializer</exception>\n+    public TagsValueConverter(IJsonSerializer jsonSerializer)\n+        => _jsonSerializer = jsonSerializer ?? throw new ArgumentNullException(nameof(jsonSerializer));\n+\n+    /// <summary>\n+    /// Initializes a new instance of the <see cref=\"TagsValueConverter\" /> class.\n+    /// </summary>\n+    /// <param name=\"dataTypeService\">The data type service.</param>\n+    /// <param name=\"jsonSerializer\">The JSON serializer.</param>\n+    [Obsolete(\"The IDataTypeService is not used anymore. This constructor will be removed in a future version.\")]\n     public TagsValueConverter(IDataTypeService dataTypeService, IJsonSerializer jsonSerializer)\n-    {\n-        _dataTypeService = dataTypeService ?? throw new ArgumentNullException(nameof(dataTypeService));\n-        _jsonSerializer = jsonSerializer ?? throw new ArgumentNullException(nameof(jsonSerializer));\n-    }\n+        : this(jsonSerializer)\n+    { }\n \n-    public static void ClearCaches() => Storages.Clear();\n+    /// <summary>\n+    /// Clears the data type configuration caches.\n+    /// </summary>\n+    [Obsolete(\"Caching of data type configuration is not done anymore. This method will be removed in a future version.\")]\n+    public static void ClearCaches()\n+    { }\n \n+    /// <inheritdoc />\n     public override bool IsConverter(IPublishedPropertyType propertyType)\n@@ -27,2 +46,3 @@ public override bool IsConverter(IPublishedPropertyType propertyType)\n \n+    /// <inheritdoc />\n     public override Type GetPropertyValueType(IPublishedPropertyType propertyType)\n@@ -30,2 +50,3 @@ public override Type GetPropertyValueType(IPublishedPropertyType propertyType)\n \n+    /// <inheritdoc />\n     public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType)\n@@ -33,5 +54,7 @@ public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType\n \n+    /// <inheritdoc />\n     public override object? ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object? source, bool preview)\n     {\n-        if (source == null)\n+        string? sourceString = source?.ToString();\n+        if (string.IsNullOrEmpty(sourceString))\n         {\n@@ -40,36 +63,9 @@ public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType\n \n-        // if Json storage type deserialize and return as string array\n-        if (JsonStorageType(propertyType.DataType.Id))\n-        {\n-            var array = source.ToString() is not null\n-                ? _jsonSerializer.Deserialize<string[]>(source.ToString()!)\n-                : null;\n-            return array ?? Array.Empty<string>();\n-        }\n-\n-        // Otherwise assume CSV storage type and return as string array\n-        return source.ToString()?.Split(Constants.CharArrays.Comma, StringSplitOptions.RemoveEmptyEntries);\n+        return IsJson(propertyType)\n+            ? _jsonSerializer.Deserialize<string[]>(sourceString) ?? Array.Empty<string>()\n+            : sourceString.Split(Constants.CharArrays.Comma, StringSplitOptions.RemoveEmptyEntries);\n     }\n \n-    public override object? ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel cacheLevel, object? source, bool preview) => (string[]?)source;\n-\n-    /// <summary>\n-    ///     Discovers if the tags data type is storing its data in a Json format\n-    /// </summary>\n-    /// <param name=\"dataTypeId\">\n-    ///     The data type id.\n-    /// </param>\n-    /// <returns>\n-    ///     The <see cref=\"bool\" />.\n-    /// </returns>\n-    private bool JsonStorageType(int dataTypeId) =>\n-\n-        // GetDataType(id) is cached at repository level; still, there is some\n-        // deep-cloning involved (expensive) - better cache here + trigger\n-        // refresh in DataTypeCacheRefresher\n-        Storages.GetOrAdd(dataTypeId, id =>\n-        {\n-            TagConfiguration? configuration = _dataTypeService.GetDataType(id)?.ConfigurationAs<TagConfiguration>();\n-            return configuration?.StorageType == TagsStorageType.Json;\n-        });\n+    private static bool IsJson(IPublishedPropertyType propertyType)\n+        => propertyType.DataType.ConfigurationAs<TagConfiguration>()?.StorageType == TagsStorageType.Json;\n }\n","improvement-type":"Excess Number of Function Arguments"}],"change-level":"warning","is-hotspot?":false,"line":48,"what-changed":"EvictRelatedContentAsync has 5 arguments, max arguments = 4","how-to-fix":"Start by investigating the responsibilities of the function. Make sure it doesn't do too many things, in which case it should be split into smaller and more cohesive functions. Consider the refactoring [INTRODUCE PARAMETER OBJECT](https://refactoring.com/catalog/introduceParameterObject.html) to encapsulate arguments that refer to the same logical concept.","change-type":"introduced"}]},"positive-impact-count":1,"repo":"Umbraco-CMS","code-health":9.732573541587579,"version":"3.0","authors":["Andy Butland"],"directives":{"added":[],"removed":[]},"positive-findings":{"number-of-types":1,"number-of-files-touched":1,"findings":[{"method":"EvictRelatedPagesAsync","why-it-occurs":"A Bumpy Road is a function that contains multiple chunks of nested conditional logic inside the same function. The deeper the nesting and the more bumps, the lower the code health.\n\nA bumpy code road represents a lack of encapsulation which becomes an obstacle to comprehension. In imperative languages there’s also an increased risk for feature entanglement, which leads to complex state management. CodeScene considers the following rules for the code health impact: 1) The deeper the nested conditional logic of each bump, the higher the tax on our working memory. 2) The more bumps inside a function, the more expensive it is to refactor as each bump represents a missing abstraction. 3) The larger each bump – that is, the more lines of code it spans – the harder it is to build up a mental model of the function. The nesting depth for what is considered a bump is  levels of conditionals.","name":"Bumpy Road Ahead","file":"src/Umbraco.Web.Website/Caching/RelationOutputCacheEvictionHandlerBase.cs","change-level":"improvement","is-hotspot?":false,"line":43,"what-changed":"EvictRelatedPagesAsync is no longer above the threshold for logical blocks with deeply nested code","how-to-fix":"Bumpy Road implementations indicate a lack of encapsulation. Check out the detailed description of the [Bumpy Road code health issue](https://codescene.com/blog/bumpy-road-code-complexity-in-context/).\n\nA Bumpy Road often suggests that the function/method does too many things. The first refactoring step is to identify the different possible responsibilities of the function. Consider extracting those responsibilities into smaller, cohesive, and well-named functions. The [EXTRACT FUNCTION](https://refactoring.com/catalog/extractFunction.html) refactoring is the primary response.","change-type":"fixed"}]},"notices":{"number-of-types":0,"number-of-files-touched":0,"findings":[]},"external-review-provider":"GitHub"},"analysistime":"2026-04-14T05:05:25.000Z","project-name":"Umbraco-CMS","repository":"https://github.com/umbraco/Umbraco-CMS.git"}}