Bartosz Sekula
Feb 2, 2024
  2120
(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

Please login to comment.
Latest blogs
A First Look at Optimizely Remote MCP Server for Experimentation

Optimizely just released a Remote MCP Server for Experimentation and I've been trying it out to see what it can do. If you don't know, MCP (Model...

Jacob Pretorius | May 1, 2026

Promoted and Certified

What a busy week

Andy Blyth | May 1, 2026 |

Announcing new library: SettingsManager

When you run .net app, there have been a few ways to store settings. Those can be set via appSettings.json, or via Azure Portal AppService...

Quan Mai | Apr 30, 2026

From Prompting to Production: Optimizely Opal University Cohort and the Future of Agentic MarTech

Most organizations today are still playing with AI. They experiment with prompts, test ideas in isolated chats, and occasionally automate a task or...

Augusto Davalos | Apr 28, 2026