codeflood logo

Magic unpublish button

Recently I posted the question Alex de Groot asked the Sitecore community last year; "Show Sitecore how to improve..." (I paraphrased to "What's your Sitecore wishlist") to the Sitecore Australia and New Zealand users group. A few ideas flew around, one of which was a small feature request to have a magical "unpublish" button for content. Upon pushing this button the item in question would be unpublished from the live website.

So I got to thinking about how I might implement this small feature. It's actually quite straight forward. I could utilise the content editor as the author is changing attributes on the item. We even already have the attribute we want to change in question: the publishable field. I could even hook the button into one of the existing ribbon chunks on the publish tab.

Incidentally Chris Wojciech is currently doing a series of posts on extending the content editor ribbon with your own commands. Refer to these for more detail on the options you have available when tweaking the ribbon.

Firstly we need to create a new button on the publishing restrictions chunk which will perform the unpublish command for us on the item. Change to the core database and using the content editor, navigate to /sitecore/content/Applications/Content Editor/Ribbons/Chunks/Publish Restrictions. Create a new child item called "Unpublish" based on the /sitecore/templates/System/Ribbon/Large Button template. Fill in the following into the items fields:

Header: Unpublish
Icon: network/16x16/earth_delete.png
Click: item:unpublish
Tooltip: Immediatley unpublish the item

The "Click" field above contains the name of a command to execute on the server when the button is clicked. Normally you define commands in the App_Config/Commands.config file. But let's keep our custom commands separate from the Sitecore commands and define our commands in our own file. Create the App_Config/CustomCommands.config file and place the following inside the file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<command name="item:unpublish" type="MyCommands.UnpublishCommand,MyCommands.dll"/>
</configuration>

We need Sitecore to read our commands file, so open the web.config file and add the following node as a child of the /configuration/sitecore/commands:

<sc.include file="/App_Config/CustomCommands.config"/>

Now we need to implement the command. As you can see from the above configuration, our command is implemented by a .net class. Our class needs to inherit from Sitecore.Shell.Framework.Commands.Command and being that this is an abstract class, we need to implement the "Execute" method.

Inside the execute method we need to get the current item, set it's publishable flag, then perform a publish on that item to remove it from all the publishing targets.

using System.Collections.Generic;
using Sitecore;
using Sitecore.Data;
using Sitecore.Publishing;
using Sitecore.Shell.Framework.Commands;

public override void Execute(CommandContext context)
{
  if (context.Items.Length > 0 && context.Items[0] != null)
  {
    var item = context.Items[0];
    item.Editing.BeginEdit();
    item.Publishing.NeverPublish = true;
    item.Editing.EndEdit();

    // Grab publishing targets
    var targetsRoot = item.Database.GetItem(
      "/sitecore/system/publishing targets");

    if (targetsRoot == null)
    {
      Sitecore.Context.ClientPage.ClientResponse.Alert(
        "Failed to find the publishing targets");
      return;
    }

    var targets = new List<Database>();
    var children = targetsRoot.GetChildren();
    for (int i = 0; i < children.Count; i++)
    {
      var db = Sitecore.Configuration.Factory.GetDatabase(
        children[i]["target database"]);
      
      if(db != null)
        targets.Add(db);
    }

    // Perform publish
    PublishManager.PublishItem(item, targets.ToArray(),
      item.Languages, false, false);
    
    Sitecore.Context.ClientPage.ClientResponse.Alert("Item unpublished");
  }
}

Compile the above code into an assembly and place that assembly in the bin folder. Now everything is ready. Make sure you're back in the master database, then using the content editor, navigate to an item, select the publish tab and click your new unpublish button. You'll receive an alert that the item has been unpublished. If you check your web databases you'll find the item is no longer there.

unpublish before

About to unpublish the item.

unpublish execute

Clicking the new unpublish button.

unpublish after

The item has been unpublished.

And because we used the default Sitecore fields, other features such as the quick action toolbar and the alerts at the top of the fields will work to alert you that your item now has a publishing restriction applied.

A possible extension to this UI tweak would be to have the button as a toggle button. If the item is currently unpublished, then make the item publishable and publish it. Luckily Sitecore commands are not just fire and forget. They also allow feedback so in code I can make my "is item publishable" checks and set the toggle state appropriately. To make this work, I need to override the QueryState method inside my command class.

public override CommandState QueryState(CommandContext context)
{
  if (context.Items.Length > 0 && context.Items[0] != null)
  {
    var item = context.Items[0];
    if (item.Publishing.NeverPublish)
      return CommandState.Down;
  }

  return CommandState.Enabled;
}

Easy! I should probably also in the above code check to see if the current user has write access to the item as well and set the state accordingly. And I'll need to update the Execute method code to toggle the publishable state of the item.

public override void Execute(CommandContext context)
{
  if (context.Items.Length > 0 && context.Items[0] != null)
  {
    var item = context.Items[0];
    item.Editing.BeginEdit();
item.Publishing.NeverPublish = !item.Publishing.NeverPublish;
    item.Editing.EndEdit();

    // Grab publishing targets
    var targetsRoot = item.Database.GetItem(
      "/sitecore/system/publishing targets");

    if (targetsRoot == null)
    {
      Context.ClientPage.ClientResponse.Alert(
        "Failed to find the publishing targets");

      return;
    }

    var targets = new List<Database>();
    var children = targetsRoot.GetChildren();
    for (int i = 0; i < children.Count; i++)
    {
      var db = Sitecore.Configuration.Factory.GetDatabase(
        children[i]["target database"]);

      if(db != null)
        targets.Add(db);
    }

    // Perform publish
    PublishManager.PublishItem(item, targets.ToArray(),
      item.Languages, false, false);

string response;
    if (item.Publishing.NeverPublish)
      response = "Item Unpublished";
    else                
      response = "Item published";

    Context.ClientPage.ClientResponse.Alert(response);
  }
}

unpublish toggle

Unpublish button in toggle mode.

This toggle approach to publishing opens a new publishing style for Sitecore. As content authors are happy with their content, just hit the published (unpublish) toggle button. When you want to take the content offline, hit it again. For some content authors this may feel a bit more comfortable, particularly if they have used other CMSs in the past which employed a similar publishing model to this. Yes this publishing style is already available in Sitecore, but it involves clicking through a few dialogs to make it work. This tweak allows you to bypass those dialogs. And to make this new publishing model really work you'd have to tweak the publishing code to also publish any referenced and dependent items such as the items template, internal linked items and media items.

So there you go Steve, your requested tweak.

Comments

I absolutely love the state feedback on the publish/unpublish button.
But I'm thinking it would be easier to understand if the button said "Publish", and if it is glowing, than this item is already on the website. Otherwise I'm not sure what glowing "unpublish" button means

There's lots of questions in that implementation, so your design might just be the sweet spot in means of complexity. Great stuff.

Alistair Deneys

Thanks Alexey, Yeah, I agree. If you wanted to use this simplified publishing style it should be a publish button which is "down" when the item is published. This post was just to show how it could be done. If you did want to use this it would require a little tweaking, in particular with publishing the references and dependencies.

Steven

Awesome Al!. Just want to comment on few stuff. I believe making the unpublish button that toggle will defeat the purpose of having the publish button. I believe it is more clear cut to have the functions separated.
Secondly, setting the "item.Publishing.NeverPublish" back to its original state will remove the "confusing alert" especially for someone who is new to Sitecore (i.e. they probably dont know how to get restriction back to normal).
Perhaps something like,
var originalNeverPublishValue;
originalNeverPublishValue = item.Publishing.NeverPublish;

.
.
.

// Perform publish
// Which will remove the item
PublishManager.PublishItem(item, targets.ToArray(),
item.Languages, false, false);

//Set the neverpublish field to orginal. Perhaps to remove alert. //Depending on the original field value
item.Editing.BeginEdit();
item.Publishing.NeverPublish = originalNeverPublishValue
item.Editing.EndEdit();

Sitecore.Context.ClientPage.ClientResponse.Alert("Item unpublished");

Also, to better communicate state you can also override GetHeader and GetIcon methods in the command, making it possible to change button text (make publishable / make unpublishable, or publish / unpublish ?), and change the icon slightly to reflect that.
Changing between different versions of the same base icon can work better than lighting up the button in some cases, having more subtle effect.

Neel

Nice informative article. Instead of setting 'NeverPublish', I am looking to put item in state where item is publishable only to one database. When I tried to set publishing target using sitecore API. like itm.Fields["Publishing targets"], the Publishing target field is always null. Any thoughts from you guys?

Alistair Deneys

Hi Neel, Are you accessing the correct field? Remember Sitecore field names are prefixed with 2 underscores, plus the name of the field may not be what's displayed in the content editor as that's the "title" of the field. The field name you're after is "__publishing groups", from the publishing base template.

Neel

Thanks for the suggestion Alistair.Yes prefixing with "__" against system field would work, I completely forgot that. but I ended up using field id from sitecore's provided static class "FieldIDs". here is how: itm.Fields[Sitecore.FieldIDs.PublishingTargets]
Thanks

Bhuvi

What if i want to unpublish an item with particular date/time?What i need to do?

Alistair Deneys

Hi Bhuvi, Do you mean you want to unpublish an item at a specific date and time or find an item that was created at a specific date and time and unpublish it?

Leave a comment

All fields are required.