Skip to content

Instantly share code, notes, and snippets.

@hfloyd
Created May 3, 2024 15:04
Show Gist options
  • Save hfloyd/f25cc07318003e59a6045f2d4f8febcc to your computer and use it in GitHub Desktop.
Save hfloyd/f25cc07318003e59a6045f2d4f8febcc to your computer and use it in GitHub Desktop.
Umbraco v13+ 404 Error IContentLastChanceFinder - Supporting multiple sites at root
namespace MYSITE.Web;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Cms.Core.Routing;
using Umbraco.Cms.Core.Web;
using Umbraco.Extensions;
using MYSITE.Web.Models; //For the 'ErrorPage' Content Type Model
public class Error404Page : IContentLastChanceFinder
{
private readonly IUmbracoContextAccessor _contextAccessor;
public Error404Page(IUmbracoContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
public Task<bool> TryFindContent(IPublishedRequestBuilder request)
{
// In the rare case that an umbracoContext cannot be build from the request, we will not be able to find the page
if (_contextAccessor.TryGetUmbracoContext(out var umbracoContext) == false)
{
return Task.FromResult(false);
}
// Find the first notFound page at root level through the published content cache by its documentTypeAlias
// You can make this search as complex as you want, you can return different pages based on anything in the original request
IPublishedContent? notFoundPage = null;
//var rootContentNodes = umbracoContext.Content?.GetAtRoot();
var currentCulture = request.Culture;
var realPage = GetClosestContent(umbracoContext, request.Uri); //Include currentCulture param if multi-lingual
var site = realPage.AncestorOrSelf(1); //Website is at Level 1 (usually)
var errorPages = site.Descendants<ErrorPage>().ToList(); // you can also use .Where(c => c.ContentType.Alias == "myErrorPageTypeAlias")
if (errorPages.Count() == 1)
{
notFoundPage = errorPages.First();
}
else if (errorPages.Count() > 1)
{
var match404 = errorPages.First(n => n.Name == "404");
if (match404 != null)
{
notFoundPage = match404;
}
else
{
notFoundPage = errorPages.First();
}
}
if (notFoundPage != null)
{
// set the content on the request and mark our search as successful
request.SetPublishedContent(notFoundPage);
return Task.FromResult(true);
}
//Couldn't find anything
return Task.FromResult(false);
}
private IPublishedContent GetClosestContent(IUmbracoContext UmbracoContext, Uri RequestUri,
string? CurrentCulture = null)
{
//Loop backwards testing each possible ancestral path until a real content node is found
for (int i = RequestUri.Segments.Length - 1; i <= 0; i--)
{
var path = string.Join("/", RequestUri.Segments.Take(i));
var node = UmbracoContext.Content?.GetByRoute(path, false, CurrentCulture);
if (node != null)
{
return node;
}
}
//if we get here, just return the first root node
return UmbracoContext.Content?.GetAtRoot().FirstOrDefault();
}
}
// ContentFinders need to be registered into the DI container through a composer
public class Error404PageComposer : IComposer
{
public void Compose(IUmbracoBuilder builder)
{
builder.SetContentLastChanceFinder<Error404Page>();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment