Wednesday, September 23, 2020

Shared 404 setting in Sitecore SXA

Sharing a 404 page across SXA sites 

A third post inspired by a project to create a number of (mini-)sites that should share as much as possible and be very easy to create. The first one covered enhancing the language selector,  the second one showed how you can share placeholder settings across sites within a tenant. In this episode we will see how we can make the site setup a bit easier by sharing the 404 error page.

As our main focus is to make creating a new site within the tenant as easy as possible, my idea is to have a shared 404 error page and no worries about any settings when creating a new site.

Delegated area

First thing to do is create an actual page to display in case of a 404 error. This page should be in a delegated area. We have this setup in the master site (the site which is used as a base site for all other sites to be cloned from). The actual item -with content- for the 404 page is in the shared site and can be edited there.

This is a pretty standard setup for content sharing in SXA - nothing fancy so far.

404 setting

In SXA you can/should define the 404 page on each site. In the Settings item of your site you will find a Error Handling section (don't forget to switch to the content tab, as the Settings item is a folder and by default will open the Folder tab with icons of the children).

On the Shared site, we insert our 404 page in the Page Not Found Link.

Normally we should do this on every site. Of course, I could do this on the master site and it would get cloned to all new sites. But maybe there is way to tell our sites to get the value from the Shared site if we don't have a local value...  not out of the box, but with a little bit of custom code we got this working.

The code

public class ErrorPageLinkProvider : Sitecore.XA.Feature.ErrorHandling.Services.ErrorPageLinkProvider, IErrorPageLinkProvider
{
  public ErrorPageLinkProvider(IContext context, IMultisiteContext multisiteContext, BaseLinkManager baseLinkManager, ILinkProviderService linkProviderService)
            : base(context, multisiteContext, baseLinkManager, linkProviderService)
  {
  }

  public override Item Get404ErrorPageItem()
  {
    var item = base.Get404ErrorPageItem();
    if (item != null)
    {
      return item;
    }

    var site = Sitecore.Context.Site;
    if (!site.IsSxaSite())
    {
      return null;
    }

    var home = Sitecore.Context.Database.GetItem(site.StartPath);
    if (home == null)
    {
      return null;
    }

    foreach (var shared in ServiceLocator.ServiceProvider.GetService<ISharedSitesContext>().GetSharedSitesWithoutCurrent(home))
    {
      item = Get404ErrorItem(shared);
      if (item != null)
      {
        return item;
      }
    }

    return null;
  }

  private Item Get404ErrorItem(Item startItem)
  {
    var settingsItem = MultisiteContext.GetSettingsItem(startItem);
    return settingsItem != null ? Context.Database.GetItem(settingsItem[Sitecore.XA.Feature.ErrorHandling.Templates._ErrorHandling.Fields.Error404Page]) : null;
  }
}

We are overriding the ErrorPageLinkProvider and in particular the Get404ErrorPageItem function. If the base call does not find an item as 404 page, we try to fetch it from the shared sites. 

Note that we use the SharedSitesContext to get the shared site info and the MultisiteContext to get the site related information (e.g. here the Settings item). When working with SXA and writing code to do stuff like this those contexts are very useful.

One small tip when working with this 404 solution: make sure to set RequestErrors.UseServerSideRedirect Sitecore setting to true to get a real 404 (instead of a 302 redirect to a 200-status page).

Conclusion

Once again we managed to tweak SXA into doing what we want with very little code. And in the meantime we are almost where we need to be with our multisite project. Almost.. meaning yet a few things to do and a few more blogpost to come - sharing settings, using tokens, overriding logo's...  

No comments:

Post a Comment