Skip to content

Instantly share code, notes, and snippets.

@jamiepollock
Last active February 7, 2020 07:37
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jamiepollock/9361444b1755848ee676aad9e6346fd9 to your computer and use it in GitHub Desktop.
Save jamiepollock/9361444b1755848ee676aad9e6346fd9 to your computer and use it in GitHub Desktop.
An Umbraco 8 component/composer for protecting pages that are configured as 404 pages in umbracoSettings.config.
namespace MyApplication.Components
{
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Events;
using Umbraco.Core.Models.Entities;
using Umbraco.Core.Services.Implement;
/// <summary>
/// A component to protect content which is configured as an error404 page in the umbracoSettings.config from being unpublished or deleted.
/// </summary>
public class ProtectConfiguredError404PagesComponent : IComponent
{
/// <summary>
/// The content error pages.
/// </summary>
private readonly IEnumerable<IContentErrorPage> contentErrorPages;
/// <summary>
/// Initializes a new instance of the <see cref="ProtectConfiguredError404PagesComponent"/> class.
/// </summary>
/// <param name="umbracoSettings">The umbraco settings.</param>
public ProtectConfiguredError404PagesComponent(IUmbracoSettingsSection umbracoSettings)
{
this.contentErrorPages = umbracoSettings.Content.Error404Collection.ToArray();
}
/// <inheritdoc />
public void Initialize()
{
if (this.contentErrorPages.Any())
{
ContentService.Deleting += this.ContentServiceOnDeleting;
ContentService.Unpublishing += this.ContentServiceOnUnpublishing;
}
}
/// <inheritdoc />
public void Terminate()
{
if (this.contentErrorPages.Any())
{
ContentService.Deleting += this.ContentServiceOnDeleting;
ContentService.Unpublishing -= this.ContentServiceOnUnpublishing;
}
}
/// <summary>
/// Creates an <see cref="EventMessage"/> for a given verb.
/// </summary>
/// <param name="verb">The verb causing the event message.</param>
/// <returns>A <see cref="EventMessage"/> for the given verb.</returns>
private EventMessage CreateMessage(string verb)
{
// category & message could be localized instead of hardcoded.
return new EventMessage("404 Page Protection", $"Unable to {verb} page. This page is used by the Umbraco 404 handler.", EventMessageType.Error);
}
/// <summary>
/// An event that triggers when <see cref="ContentService"/> Deleting event fires.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The event args.</param>
private void ContentServiceOnDeleting(Umbraco.Core.Services.IContentService sender, DeleteEventArgs<Umbraco.Core.Models.IContent> e)
{
if (this.ShouldCancel(e.DeletedEntities))
{
e.Cancel = true;
e.Messages.Add(this.CreateMessage("delete"));
}
}
/// <summary>
/// An event that triggers when <see cref="ContentService"/> Unpublishing event fires.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The event args.</param>
private void ContentServiceOnUnpublishing(Umbraco.Core.Services.IContentService sender, PublishEventArgs<Umbraco.Core.Models.IContent> e)
{
if (this.ShouldCancel(e.PublishedEntities))
{
e.Cancel = true;
e.Messages.Add(this.CreateMessage("unpublish"));
}
}
/// <summary>
/// Checks if the incoming collection of entities has a node which matches a error 404 page.
/// </summary>
/// <param name="entities">The entities.</param>
/// <returns>A <see cref="bool"/> of whether it should be canceled or not.</returns>
private bool ShouldCancel(IEnumerable<IEntity> entities)
{
foreach (var entity in entities)
{
if (this.contentErrorPages.Any(x => (x.HasContentId && x.ContentId == entity.Id) || (x.HasContentKey && x.ContentKey == entity.Key)))
{
return true;
}
}
return false;
}
}
}
namespace MyApplication.Composers
{
using Umbraco.Core;
using Umbraco.Core.Composing;
using MyApplication.Components;
/// <summary>
/// A <see cref="IComposer"/> class to register the <see cref="ProtectConfiguredError404PagesComponent"/> class.
/// </summary>
[RuntimeLevel(MinLevel = RuntimeLevel.Run)]
public class ProtectConfiguredError404PagesComponentComposer : ComponentComposer<ProtectConfiguredError404PagesComponent>
{
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment