Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Microsoft.Extensions.DependencyInjection;

Check notice on line 1 in src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Code Health Review (main)

✅ Getting better: Overall Code Complexity

The mean cyclomatic complexity decreases from 4.50 to 4.33, threshold = 4. This file has many conditional statements (e.g. if, for, while) across its implementation, leading to lower code health. Avoid adding more conditionals.
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Notifications;
Expand Down Expand Up @@ -28,6 +30,7 @@
private readonly IDomainService _domainService;
private readonly IDomainCacheService _domainCacheService;
private readonly IDocumentUrlService _documentUrlService;
private readonly IDocumentUrlAliasService _documentUrlAliasService;
private readonly IDocumentNavigationQueryService _documentNavigationQueryService;
private readonly IDocumentNavigationManagementService _documentNavigationManagementService;
private readonly IContentService _contentService;
Expand All @@ -39,6 +42,7 @@
/// <summary>
/// Initializes a new instance of the <see cref="ContentCacheRefresher"/> class.
/// </summary>
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 19.")]
public ContentCacheRefresher(
AppCaches appCaches,
IJsonSerializer serializer,
Expand All @@ -54,12 +58,51 @@
IPublishStatusManagementService publishStatusManagementService,
IDocumentCacheService documentCacheService,
ICacheManager cacheManager)
: this(
appCaches,
serializer,
idKeyMap,
domainService,
eventAggregator,
factory,
documentUrlService,
StaticServiceProvider.Instance.GetRequiredService<IDocumentUrlAliasService>(),
domainCacheService,
documentNavigationQueryService,
documentNavigationManagementService,
contentService,
publishStatusManagementService,
documentCacheService,
cacheManager)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="ContentCacheRefresher"/> class.
/// </summary>
public ContentCacheRefresher(
AppCaches appCaches,
IJsonSerializer serializer,
IIdKeyMap idKeyMap,
IDomainService domainService,
IEventAggregator eventAggregator,
ICacheRefresherNotificationFactory factory,
IDocumentUrlService documentUrlService,
IDocumentUrlAliasService documentUrlAliasService,
IDomainCacheService domainCacheService,
IDocumentNavigationQueryService documentNavigationQueryService,
IDocumentNavigationManagementService documentNavigationManagementService,
IContentService contentService,
IPublishStatusManagementService publishStatusManagementService,
IDocumentCacheService documentCacheService,
ICacheManager cacheManager)
: base(appCaches, serializer, eventAggregator, factory)
{
_idKeyMap = idKeyMap;
_domainService = domainService;
_domainCacheService = domainCacheService;
_documentUrlService = documentUrlService;
_documentUrlAliasService = documentUrlAliasService;
_documentNavigationQueryService = documentNavigationQueryService;
_documentNavigationManagementService = documentNavigationManagementService;
_contentService = contentService;
Expand Down Expand Up @@ -270,28 +313,33 @@
if (_documentNavigationQueryService.TryGetDescendantsKeysOrSelfKeys(key, out IEnumerable<Guid>? descendantsOrSelfKeys))
{
_documentUrlService.DeleteUrlsFromCacheAsync(descendantsOrSelfKeys).GetAwaiter().GetResult();
_documentUrlAliasService.DeleteAliasesFromCacheAsync(descendantsOrSelfKeys).GetAwaiter().GetResult();
}
else if (_documentNavigationQueryService.TryGetDescendantsKeysOrSelfKeysInBin(key, out IEnumerable<Guid>? descendantsOrSelfKeysInBin))
{
_documentUrlService.DeleteUrlsFromCacheAsync(descendantsOrSelfKeysInBin).GetAwaiter().GetResult();
_documentUrlAliasService.DeleteAliasesFromCacheAsync(descendantsOrSelfKeysInBin).GetAwaiter().GetResult();
}
}

if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshAll))
{
_documentUrlService.InitAsync(false, CancellationToken.None).GetAwaiter().GetResult(); // TODO: make async
_documentUrlAliasService.InitAsync(false, CancellationToken.None).GetAwaiter().GetResult();
}

if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshNode))
{
Guid key = payload.Key ?? _idKeyMap.GetKeyForId(payload.Id, UmbracoObjectTypes.Document).Result;
_documentUrlService.CreateOrUpdateUrlSegmentsAsync(key).GetAwaiter().GetResult();
_documentUrlAliasService.CreateOrUpdateAliasesAsync(key).GetAwaiter().GetResult();
}

if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch))
{
Guid key = payload.Key ?? _idKeyMap.GetKeyForId(payload.Id, UmbracoObjectTypes.Document).Result;
_documentUrlService.CreateOrUpdateUrlSegmentsWithDescendantsAsync(key).GetAwaiter().GetResult();
_documentUrlAliasService.CreateOrUpdateAliasesWithDescendantsAsync(key).GetAwaiter().GetResult();
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,8 @@
// Routing
Services.AddUnique<IDocumentUrlService, DocumentUrlService>();
Services.AddNotificationAsyncHandler<UmbracoApplicationStartingNotification, DocumentUrlServiceInitializerNotificationHandler>();
Services.AddUnique<IDocumentUrlAliasService, DocumentUrlAliasService>();
Services.AddNotificationAsyncHandler<UmbracoApplicationStartingNotification, DocumentUrlAliasServiceInitializerNotificationHandler>();

Check warning on line 443 in src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Code Health Review (main)

❌ Getting worse: Large Method

AddCoreServices increases from 213 to 215 lines of code, threshold = 70. Large functions with many lines of code are generally harder to understand and lower the code health. Avoid adding more lines to this function.
}
}
}
22 changes: 22 additions & 0 deletions src/Umbraco.Core/Extensions/DocumentUrlAliasServiceExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Umbraco.Cms.Core.Services;

namespace Umbraco.Cms.Core.Extensions;

/// <summary>
/// Provides extension methods for <see cref="IDocumentUrlAliasService"/>.
/// </summary>
internal static class DocumentUrlAliasServiceExtensions
{
/// <summary>
/// Normalizes a URL alias by trimming whitespace, removing leading/trailing slashes, and converting to lowercase.
/// </summary>
/// <param name="service">The <see cref="IDocumentUrlAliasService"/>.</param>
/// <param name="alias">The alias to normalize.</param>
/// <returns>The normalized alias.</returns>
public static string NormalizeAlias(this IDocumentUrlAliasService service, string alias) =>
alias
.Trim()
.TrimStart('/')
.TrimEnd('/')
.ToLowerInvariant();
}
22 changes: 22 additions & 0 deletions src/Umbraco.Core/Models/PublishedDocumentUrlAlias.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace Umbraco.Cms.Core.Models;

/// <summary>
/// Represents a URL alias for a published document.
/// </summary>
public class PublishedDocumentUrlAlias
{
/// <summary>
/// Gets or sets the document key.
/// </summary>
public required Guid DocumentKey { get; set; }

/// <summary>
/// Gets or sets the language Id.
/// </summary>
public required int LanguageId { get; set; }

/// <summary>
/// Gets or sets the normalized URL alias (lowercase, no leading slash).
/// </summary>
public required string Alias { get; set; }
}
1 change: 1 addition & 0 deletions src/Umbraco.Core/Persistence/Constants-DatabaseSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public static class Tables
public const string DocumentCultureVariation = TableNamePrefix + "DocumentCultureVariation";
public const string DocumentVersion = TableNamePrefix + "DocumentVersion";
public const string DocumentUrl = TableNamePrefix + "DocumentUrl";
public const string DocumentUrlAlias = TableNamePrefix + "DocumentUrlAlias";
public const string MediaVersion = TableNamePrefix + "MediaVersion";
public const string ContentSchedule = TableNamePrefix + "ContentSchedule";

Expand Down
5 changes: 5 additions & 0 deletions src/Umbraco.Core/Persistence/Constants-Locks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,5 +100,10 @@ public static class Locks
/// All distributed jobs.
/// </summary>
public const int DistributedJobs = -347;

/// <summary>
/// All document URL aliases.
/// </summary>
public const int DocumentUrlAliases = -348;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using Umbraco.Cms.Core.Models;

namespace Umbraco.Cms.Core.Persistence.Repositories;

/// <summary>
/// Repository for document URL aliases.
/// </summary>
public interface IDocumentUrlAliasRepository
{
/// <summary>
/// Saves the specified aliases to the database.
/// Handles insert/update/delete via diff - existing aliases not in the new set are deleted.
/// </summary>
/// <param name="aliases">The aliases to save.</param>
void Save(IEnumerable<PublishedDocumentUrlAlias> aliases);

/// <summary>
/// Gets all persisted aliases from the database.
/// </summary>
/// <returns>All persisted aliases.</returns>
IEnumerable<PublishedDocumentUrlAlias> GetAll();

/// <summary>
/// Deletes all aliases for the specified document keys.
/// </summary>
/// <param name="documentKeys">The document keys to delete aliases for.</param>
void DeleteByDocumentKey(IEnumerable<Guid> documentKeys);

/// <summary>
/// Gets all document aliases.
/// </summary>
/// <returns>Raw alias data from documents with umbracoUrlAlias property.</returns>
IEnumerable<DocumentUrlAliasRaw> GetAllDocumentUrlAliases();
}

/// <summary>
/// Raw alias data from a direct SQL query.
/// </summary>
public class DocumentUrlAliasRaw
{
/// <summary>
/// Gets or sets the document key.
/// </summary>
public Guid DocumentKey { get; set; }

/// <summary>
/// Gets or sets the language ID (null for invariant).
/// </summary>
public int? LanguageId { get; set; }

/// <summary>
/// Gets or sets the raw alias value (may be comma-separated).
/// </summary>
public string AliasValue { get; set; } = string.Empty;
}
Loading
Loading