Skip to content

Commit

Permalink
[IMPLEMENT] Inherits From additional data retrieval and displaying in UI
Browse files Browse the repository at this point in the history
  • Loading branch information
NikRimington committed Jul 29, 2024
1 parent bd43b64 commit 7c07f0a
Show file tree
Hide file tree
Showing 25 changed files with 506 additions and 329 deletions.
23 changes: 12 additions & 11 deletions src/HC.PageNotFoundManager/Backoffice/ManagementController.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Threading.Tasks;
using Asp.Versioning;
using HC.PageNotFoundManager.Caching;
using HC.PageNotFoundManager.Config;
Expand All @@ -7,10 +8,7 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Common.Attributes;
using Umbraco.Cms.Api.Common.Filters;
using Umbraco.Cms.Api.Management.Controllers;
using Umbraco.Cms.Api.Management.Filters;
using Umbraco.Cms.Api.Management.Routing;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Web.Common.Authorization;

Expand All @@ -26,30 +24,33 @@ namespace HC.PageNotFoundManager.Backoffice;
[Produces("application/json")]
public class ManagementController : Controller
{
private readonly IPageNotFoundConfig config;
private readonly IPageNotFoundService service;

private readonly DistributedCache distributedCache;

public ManagementController(DistributedCache distributedCache, IPageNotFoundConfig config)
public ManagementController(DistributedCache distributedCache, IPageNotFoundService service)
{
this.distributedCache = distributedCache ?? throw new ArgumentNullException(nameof(distributedCache));
this.config = config ?? throw new ArgumentNullException(nameof(config));
this.service = service ?? throw new ArgumentNullException(nameof(service));
}

[HttpGet("get-not-found")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(Guid?), StatusCodes.Status200OK)]
public Guid? GetNotFoundPage(Guid pageId)
[ProducesResponseType(typeof(PageNotFoundDetails), StatusCodes.Status200OK)]
public PageNotFoundDetails? GetNotFoundPage(Guid pageId)
{
return config.GetNotFoundPage(pageId);
return service.GetNotFoundPage(pageId);
}

[HttpPost("set-not-found")]
[MapToApiVersion("1.0")]
public void SetNotFoundPage(PageNotFoundRequest request)
[ProducesResponseType(typeof(PageNotFoundDetails), StatusCodes.Status200OK)]
public async Task<PageNotFoundDetails> SetNotFoundPage(PageNotFoundRequest request)
{
config.SetNotFoundPage(request.ParentId, request.NotFoundPageId ?? Guid.Empty, true);
var res = await service.SetNotFoundPage(request.ParentId, request.NotFoundPageId ?? Guid.Empty, true);

distributedCache.RefreshPageNotFoundConfig(request);

return res;
}
}
4 changes: 2 additions & 2 deletions src/HC.PageNotFoundManager/Caching/CacheRefresher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ public class PageNotFoundCacheRefresher : PayloadCacheRefresherBase<CacheRefresh
{
public const string Id = "fb984dde-7b8b-4e58-bb5c-5f0cb043af4e";

private readonly IPageNotFoundConfig config;
private readonly IPageNotFoundService config;

public PageNotFoundCacheRefresher(
AppCaches appCaches,
IPageNotFoundConfig config,
IPageNotFoundService config,
IJsonSerializer jsonSerializer,
IEventAggregator eventAggregator,
ICacheRefresherNotificationFactory cacheRefresherNotificationFactory)
Expand Down
16 changes: 0 additions & 16 deletions src/HC.PageNotFoundManager/Config/IPageNotFoundConfig.cs

This file was deleted.

18 changes: 18 additions & 0 deletions src/HC.PageNotFoundManager/Config/IPageNotFoundService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using HC.PageNotFoundManager.Models;
using System;
using System.Threading.Tasks;

namespace HC.PageNotFoundManager.Config;

public interface IPageNotFoundService
{
PageNotFoundDetails? GetNotFoundPage(int nodeId);

PageNotFoundDetails? GetNotFoundPage(Guid nodeKey);

void RefreshCache();

Task<PageNotFoundDetails> SetNotFoundPage(int parentId, int pageNotFoundId, bool refreshCache);

Task<PageNotFoundDetails> SetNotFoundPage(Guid parentKey, Guid pageNotFoundKey, bool refreshCache);
}
112 changes: 0 additions & 112 deletions src/HC.PageNotFoundManager/Config/PageNotFoundConfig.cs

This file was deleted.

161 changes: 161 additions & 0 deletions src/HC.PageNotFoundManager/Config/PageNotFoundConfigService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
using HC.PageNotFoundManager.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Cms.Core.Web;
using Umbraco.Cms.Infrastructure.Scoping;
using Umbraco.Extensions;

namespace HC.PageNotFoundManager.Config;

public class PageNotFoundConfigService : IPageNotFoundService
{
private const string CacheKey = "PageNotFoundConfig";

private readonly IAppPolicyCache appPolicyCache;

private readonly IScopeProvider scopeProvider;

private readonly IUmbracoContextFactory umbracoContextFactory;

public PageNotFoundConfigService(
IScopeProvider scopeProvider,
IUmbracoContextFactory umbracoContextFactory,
IAppPolicyCache appPolicyCache)
{
this.scopeProvider = scopeProvider ?? throw new ArgumentNullException(nameof(scopeProvider));
this.umbracoContextFactory =
umbracoContextFactory ?? throw new ArgumentNullException(nameof(umbracoContextFactory));
this.appPolicyCache = appPolicyCache ?? throw new ArgumentNullException(nameof(appPolicyCache));
}

private List<PageNotFoundDetails> ConfiguredPages
{
get
{
var us = (List<PageNotFoundDetails>?)appPolicyCache.Get(CacheKey, LoadFromDb);
return us ?? [];
}
}

public PageNotFoundDetails? GetNotFoundPage(int nodeId)
{
using var scope = scopeProvider.CreateScope(autoComplete: true);
using var umbracoContext = umbracoContextFactory.EnsureUmbracoContext();
var node = umbracoContext.UmbracoContext.Content?.GetById(nodeId);
return node != null ? GetNotFoundPage(node, true) : null;
}

public PageNotFoundDetails? GetNotFoundPage(Guid nodeKey)
{
using var scope = scopeProvider.CreateScope(autoComplete: true);
using var umbracoContext = umbracoContextFactory.EnsureUmbracoContext();
var node = umbracoContext.UmbracoContext.Content?.GetById(nodeKey);

return node != null ? GetNotFoundPage(node, true) : null;
}

private PageNotFoundDetails? GetNotFoundPage(IPublishedContent node, bool fetchInherited)
{
var x = ConfiguredPages.FirstOrDefault(p => p.PageId == node.Key);
x ??= new PageNotFoundDetails
{
PageId = node.Key,
};
if(fetchInherited)
x.Inherited404 = GetAncestor404(node.Parent);
return x;
}

public void RefreshCache()
{
appPolicyCache.ClearByKey(CacheKey);
appPolicyCache.Insert(CacheKey, LoadFromDb);
}

public async Task<PageNotFoundDetails> SetNotFoundPage(int parentId, int pageNotFoundId, bool refreshCache)
{
using var umbracoContext = umbracoContextFactory.EnsureUmbracoContext();
var parentPage = umbracoContext.UmbracoContext.Content?.GetById(parentId);
var pageNotFoundPage = umbracoContext.UmbracoContext.Content?.GetById(pageNotFoundId);
return await SetNotFoundPage(parentPage?.Key ?? Guid.Empty, pageNotFoundPage != null ? pageNotFoundPage.Key : Guid.Empty, refreshCache);
}

public async Task<PageNotFoundDetails> SetNotFoundPage(Guid parentKey, Guid pageNotFoundKey, bool refreshCache)
{
using (var scope = scopeProvider.CreateScope())
{
var db = scope.Database;
var page = db.Query<Models.DatabaseModels.PageNotFound>().Where(p => p.ParentId == parentKey).FirstOrDefault();
if (page == null && !Guid.Empty.Equals(pageNotFoundKey))
{
// create the page
await db.InsertAsync(new Models.DatabaseModels.PageNotFound { ParentId = parentKey, NotFoundPageId = pageNotFoundKey });
}
else if (page != null)
{
if (Guid.Empty.Equals(pageNotFoundKey))
{
await db.DeleteAsync(page);
}
else
{
// update the existing page
page.NotFoundPageId = pageNotFoundKey;
db.Update(Models.DatabaseModels.PageNotFound.TableName, "ParentId", page);
}
}

scope.Complete();


}

if (refreshCache)
{
RefreshCache();
}

return new PageNotFoundDetails
{
PageId = parentKey,
Explicit404 = pageNotFoundKey == Guid.Empty ? null : pageNotFoundKey,
Inherited404 = pageNotFoundKey != Guid.Empty ? null : GetAncestor404(parentKey)
};
}

private PageNotFoundDetails? GetAncestor404(Guid nodeKey)
{
using var umbracoContext = umbracoContextFactory.EnsureUmbracoContext();
var node = umbracoContext.UmbracoContext.Content?.GetById(nodeKey);
return node == null ? null : GetAncestor404(node);
}

private PageNotFoundDetails? GetAncestor404(IPublishedContent? node)
{
if (node == null)
return null;
//TODO: Need some logic, we don't want the whole tree just the first inherited 404
var ancestor404 = GetNotFoundPage(node, false);

if((ancestor404 == null || !ancestor404.Has404()) && node.Parent != null)
return GetAncestor404(node.Parent);
return ancestor404;
}

private List<PageNotFoundDetails> LoadFromDb()
{
using var scope = scopeProvider.CreateScope(autoComplete: true);
var sql = scope.SqlContext.Sql().Select("*").From<Models.DatabaseModels.PageNotFound>();
var pages = scope.Database.Fetch<Models.DatabaseModels.PageNotFound>(sql);
scope.Complete();
return pages.Select(p => new PageNotFoundDetails
{
PageId = p.ParentId,
Explicit404 = p.NotFoundPageId == Guid.Empty ? null : p.NotFoundPageId
}).ToList();
}
}
Loading

0 comments on commit 7c07f0a

Please sign in to comment.