Bartosz Sekula
Feb 2, 2024
visibility 2339
star star star star star
(4 votes)

How to prevent publishing a page with unpublished blocks or assets

EPiServer has a very powerful validation engine which is easily pluggable with custom validation rules.

I've been asked by a colleague to write a validation rule which would prevent a page from being published if it referenced an unpublished dependency (block/asset/...).

I thought it might be useful for some of you.

We need to hook to IContentEvents.PublishingEvent. We can do that from an initializable module:

[InitializableModule]
[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class PublishEventInitializationModule : IInitializableModule
{
    private DependenciesResolver dependenciesResolver;

    public void Initialize(InitializationEngine context)
    {
        //Add initialization logic, this method is called once after CMS has been initialized
        var contentEvents = ServiceLocator.Current.GetInstance<IContentEvents>();
        contentEvents.PublishingContent += contentEvents_PublishingContent;

        dependenciesResolver = ServiceLocator.Current.GetInstance<DependenciesResolver>();
    }

    void contentEvents_PublishingContent(object sender, EPiServer.ContentEventArgs e)
    {
        var dependencies = dependenciesResolver.GetUnpublishedDependencies(e.ContentLink).ToList();
        if (!dependencies.Any()) return;

        var text = dependencies.Count == 1 ? "dependency" : "dependencies";
        e.CancelAction = true;
        e.CancelReason =
            $"You can't publish because you have {dependencies.Count} unpublished {text}. {string.Join(',', dependencies.Select(x => $"{x.Name} [{x.ContentLink}] "))}";
    }

    public void Preload(string[] parameters)
    {
    }

    public void Uninitialize(InitializationEngine context)
    {
        //Add uninitialization logic
        var contentEvents = ServiceLocator.Current.GetInstance<IContentEvents>();
        contentEvents.PublishingContent -= contentEvents_PublishingContent;
    }
}

As you can see we calculate unpublished dependencies and cancel the operation if necessary.

DependencyResolver traverses the dependencies, both direct and nested, so validation will still fail if for example a page depends on an ImageBlock which has an unpublished ImageData.

Full gist is here: https://gist.github.com/barteksekula/78ff1df20df2f2449497d94c218cfdad

You can just copy paste the file to your project and it will work like this:

Feb 02, 2024

Comments

error Please login to comment.
Latest blogs
You’re not headless. You’re hybrid — and that’s the CMS 12 default.

For CMS 12 / Commerce teams with a React (or similar) storefront who keep hearing “just go headless” or “just move to Graph.” This is Part 1 of Fin...

Hanskumar Tripathi | Aug 13, 2026

Stott Security for Optimizely SaaS CMS

Introduction Stott Security  has been helping developers and editors manage Content Security Policies and security headers on  Optimizely PaaS CMS...

Mark Stott | Aug 13, 2026

Optimizely XML Resource Builder: A Visual Studio Extension for Language Resource Files

Anyone who has worked on a larger Optimizely solution will know the feeling. You add or update a page type, block type, or shared base class,

Adnan Zameer | Aug 13, 2026 |

Optimizely SaaS CMS Management API: The Write Side of Headless

Everything I’ve written in this series so far has been about reading content — Optimizely Graph, webhooks, ISR, caching layers. But at some point y...

Kiran Patil | Aug 12, 2026 |