Automatically unpublish deleted Sitecore item
Posted 9 Feb 2020 by Marek Musielak
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:
- Change publishing restrictions of the item to make sure if cannot be published, publish it and finally delete the item.
- 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:
- Header: Unpublish and Delete
- Icon: Office/24x24/delete.png
- Click: item:unpublishanddelete(id=$ItemID)
- Tooltip: Unpublish and delete the 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.