marek@skillcore.net

Automatically unpublish deleted Sitecore item

Posted 9 Feb 2020 by Marek Musielak

unpublish sitecore item while deleting

Many many times I heard a question: I deleted an item but I can still see it on the website. Why? If you've been working with Sitecore for a while, you don't even have to check to know the answer: You've deleted an item but you haven't unpublished it first. Is there anything we can do to help content editors with that problem?

By default, if you want to unpublish and delete a Sitecore item, you have 2 options:

  1. Change publishing restrictions of the item to make sure if cannot be published, publish it and finally delete the item.
  2. Delete the item and publish parent item with subitems.

None of the two above is intuitive. And if you already deleted an item and your only option is to publish parent item with subitems, it is possible that you will publish some other items which not necessarily are ready to go public (unless you're using workflows, but let's skip it for today).

Why don't we build a Sitecore ribbon button that will unpublish and delete an item with one click and that will still execute all the actions which Sitecore does when one tries to remove an item (like asking user for confirmation, checking bucket items etc.)?

Start with opening Sitecore Desktop and switching to core database. Find /sitecore/content/Applications/Content Editor/Ribbons/Chunks/Operations item there and add a new Large Button item called Unpublish and Delete. Next change its fields to:

sitecore button - unpublish and delete item

Ok, we have a button item. Now it's time to register the item:unpublishanddelete command:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <commands>
      <command 
        name="item:unpublishanddelete" 
        type="SitecorePlayground.Commands.UnpublishAndDelete, SitecorePlayground"/>
    </commands>
  </sitecore>
</configuration>

Code of the command is pretty simple. It inherits from Sitecore.Shell.Framework.Commands.Delete class (so we don't have to write custom QueryState method) and starts uiDeleteItems pipeline with 1 extra entry in the args.CustomData:

public class UnpublishAndDelete : Sitecore.Shell.Framework.Commands.Delete
{
    public const string CustomDataKey = "UnpublishAndDelete";

    public override void Execute(CommandContext context)
    {
        if (context.Items.Length == 0)
        {
            SheerResponse.Alert("The selected item could not be found.");
            return;
        }

        if (context.Items.Length == 1)
        {
            var item = context.Items[0];

            ClientPipelineArgs args = new ClientPipelineArgs();

            // added to args.CustomData: "UnpublishAndDelete" = true
            // it's used later in UnpublishItem processor
            args.CustomData[CustomDataKey] = true;
            args.Parameters = new NameValueCollection
            {
                {"database", item.Database.Name},
                {"items", item.ID.ToString()},
                {"language", item.Language.ToString()}
            };

            Context.ClientPage.Start("uiDeleteItems", args);
        }
    }
}

Now, when one clicks the button, it will start the default uiDeleteItems pipeline but it will not unpublish the item yet. It's time to add one extra processor to the pipeline which will unpublish the item before deleting it:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <processors>
      <uiDeleteItems>
        <processor 
          patch:before="*[@type='Sitecore.Shell.Framework.Pipelines.DeleteItems,Sitecore.Kernel' and @method='Execute']"
          type="SitecorePlayground.Pipelines.uiDeleteItems.UnpublishItem, SitecorePlayground"
          method="Execute"
          mode="on"
          />
      </uiDeleteItems>
    </processors>
  </sitecore>
</configuration>

And the code of the processor:

public class UnpublishItem
{
    public virtual void Execute(ClientPipelineArgs args)
    {
        Assert.ArgumentNotNull(args, nameof(args));

        // args.CustomData contains "UnpublishAndDelete" key
        // it was added in UnpublishAndDelete command so we will unpublish the item
        if (args.CustomData.TryGetValue(UnpublishAndDelete.CustomDataKey, out _))
        {
            var database = Factory.GetDatabase(args.Parameters["database"]);
            var item = database.GetItem(args.Parameters["items"]);

            if (item == null)
                return;

            using (new SecurityDisabler())
            {
                var targets = PublishManager.GetPublishingTargets(item.Database)
                    .Select(i => Database.GetDatabase(i[FieldIDs.PublishingTargetDatabase]))
                    .ToArray();

                var languages = LanguageManager.GetLanguages(item.Database).ToArray();

                item.Editing.BeginEdit();
                item.Publishing.NeverPublish = true;
                item.Editing.EndEdit();

                var handle = PublishManager.PublishItem(item, targets, languages, false, false);
                PublishManager.WaitFor(handle);
            }
        }
    }
}

UnpublishItem processor is run before Sitecore out of the box DeleteItems.Execute processor. It checks if there is an extra entry in args.CustomData and unpublishes the item from all publishing targets.

Now, when your Content Editors want to quickly unpublish and delete a Sitecore item, they can just do this with a single button click, without changing publishing restrictions or publishing the parent item.

Comments? Find me on or Sitecore Chat