Calling all developers! We invite you to provide your input on Feature Experimentation by completing this brief survey.

 

Bartosz Sekula
Feb 2, 2024
  984
(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
Find and delete non used media and blocks

On my new quest to play around with Blazor and MudBlazor I'm going back memory lane and porting some previously plugins. So this time up is my plug...

Per Nergård (MVP) | Jan 21, 2025

Optimizely Content Graph on mobile application

CG everywhere! I pull schema from our default index https://cg.optimizely.com/app/graphiql?auth=eBrGunULiC5TziTCtiOLEmov2LijBf30obh0KmhcBlyTktGZ in...

Cuong Nguyen Dinh | Jan 20, 2025

Image Analyzer with AI Assistant for Optimizely

The Smart Image Analyzer is a new feature in the Epicweb AI Assistant for Optimizely CMS that automates the management of image metadata, such as...

Luc Gosso (MVP) | Jan 16, 2025 | Syndicated blog

How to: create Decimal metafield with custom precision

If you are using catalog system, the way of creating metafields are easy – in fact, you can forget about “metafields”, all you should be using is t...

Quan Mai | Jan 16, 2025 | Syndicated blog

Level Up with Optimizely's Newly Relaunched Certifications!

We're thrilled to announce the relaunch of our Optimizely Certifications—designed to help partners, customers, and developers redefine what it mean...

Satata Satez | Jan 14, 2025

Introducing AI Assistance for DBLocalizationProvider

The LocalizationProvider for Optimizely has long been a powerful tool for enhancing the localization capabilities of Optimizely CMS. Designed to ma...

Luc Gosso (MVP) | Jan 14, 2025 | Syndicated blog