If you have ever used Sitecore buckets you have probably wondered how it hides items in buckets from the Content Tree. In this blog post I will show you how you can make a similar feature in your solution.

The aim of this solution will be to hide items that have been marked as never publish from the content tree and to indicate to the user that these items have been hidden as shown in the image below:

I will be using Sitecore 8 Update 4 for this demo.

The DataView

First thing to know is that the rendering of items in the Content Tree is controlled by a DataView and that for each Content Tree you can only have a single DataView, therefore we need to inherit the Sitecore.Buckets.Forms.BasketDataView to avoid breaking the existing bucket features

public class NeverPublishDataView : BucketDataView
{

}

This means that if you want to filter using many different conditions and keep the logic separate then you need to make a chain of inherited DataViews.

Next we need to create some method of telling our DataView if it should display the Never Publish items or not. We will use the Sitecore registry to maintain this for us:

public class NeverPublishDataView : BucketDataView
{
        public static bool ShowNeverPublishItems
        {
            get { return Registry.GetBool("/Current_User/UserOptions.View.ShowNeverPublishItems", false); }
            set { Registry.SetBool("/Current_User/UserOptions.View.ShowNeverPublishItems", value); }
        }
}

Notice how we can set these registry values per a user, this pretty cool.

Next we need a method that will create the temporary item that gets injected into the Content Tree to inform the user that items have been hidden, this is the item that says "There are hidden never publish items" in the above image:

public class NeverPublishDataView : BucketDataView
{
        public static bool ShowNeverPublishItems
        {
            get { return Registry.GetBool("/Current_User/UserOptions.View.ShowNeverPublishItems", false); }
            set { Registry.SetBool("/Current_User/UserOptions.View.ShowNeverPublishItems", value); }
        }

        private Item GetTempItem(Item parent)
        {
            ID newID = ID.NewID;
            ItemDefinition definition = new ItemDefinition(newID, "There are hidden never publish items.",
                TemplateIDs.Folder, ID.Null);
            FieldList list2 = new FieldList();
            list2.Add(FieldIDs.DisplayName, Translate.Text("There are hidden never publish items."));
            list2.Add(FieldIDs.Icon, Images.GetThemedImageSource("Network/32x32/earth_delete.png"));
            list2.Add(FieldIDs.UIStaticItem, "1");
            FieldList fields = list2;
            return new Item(newID, new ItemData(definition, Language.Invariant, Version.Latest, fields), parent.Database)
            {
                RuntimeSettings = {Temporary = true}
            };
        }
}

Finally we need to bring these two things together, to do this we override the GetChildItems method:

public class NeverPublishDataView : BucketDataView
{
        public static bool ShowNeverPublishItems
        {
            get { return Registry.GetBool("/Current_User/UserOptions.View.ShowNeverPublishItems", false); }
            set { Registry.SetBool("/Current_User/UserOptions.View.ShowNeverPublishItems", value); }
        }

        private Item GetTempItem(Item parent)
        {
            ID newID = ID.NewID;
            ItemDefinition definition = new ItemDefinition(newID, "There are hidden never publish items.",
                TemplateIDs.Folder, ID.Null);
            FieldList list2 = new FieldList();
            list2.Add(FieldIDs.DisplayName, Translate.Text("There are hidden never publish items."));
            list2.Add(FieldIDs.Icon, Images.GetThemedImageSource("Network/32x32/earth_delete.png"));
            list2.Add(FieldIDs.UIStaticItem, "1");
            FieldList fields = list2;
            return new Item(newID, new ItemData(definition, Language.Invariant, Version.Latest, fields), parent.Database)
            {
                RuntimeSettings = {Temporary = true}
            };
        }

        protected override void GetChildItems(ItemCollection items, Item item)
        {
            base.GetChildItems(items, item);

            var flag = false;

            for (int i = items.Count - 1; i >= 0; i--)
            {
                // add your condition logic here
                if (items[i].Publishing.NeverPublish && !ShowNeverPublishItems)
                {
                    flag = true;
                    items.Remove(i);
                }
            }
           
            //if we have removed any items then add the temporary item
            if (flag)
            {
                var tempItem = GetTempItem(item);
                items.Insert(0, tempItem);
            }
        }
}

This method is very straight forward, we just loop through the list of child items and any that have the "Never Publish" flag we remove from the list. If we remove an item we then inject the temporary item. This logic could be altered to fit almost any requirements. In previous projects I have used it to hide redirect items and data source items for components that were in the main Content Tree.

The Data View is now complete, now to allow the user to toggle the button.

The Button

First start by creating a Small Button item at /sitecore/content/Applications/Content Editor/Ribbons/Chunks/Publish/Show Never Publish in the Core database.

Set the Click field to contenteditor:neverPublish and fill in the Header, Icon and  Tooltip fields.

You should now see a Show Never Publish button in your Publish tab:

Now to put some code behind it:

    [Serializable]
    public class ToggleNeverPublishView : Command
    {
        public override void Execute(CommandContext context)
        {
            bool flag = !NeverPublishDataView.ShowNeverPublishItems;
            NeverPublishDataView.ShowNeverPublishItems = flag;

            Item obj = context.Items[0];
            if (obj != null && flag)
            {
                UrlString urlString = new UrlString(WebUtil.GetRawUrl());
                obj.Uri.AddToUrlString(urlString);
                urlString["pa" + WebUtil.GetQueryString("pa", "0")] = obj.Uri.ToString();
                urlString["ras"] = "PublishStrip";
                SheerResponse.SetLocation(urlString.ToString());
            }
            else
                SheerResponse.SetLocation(string.Empty);
        }

        public override CommandState QueryState(CommandContext context)
        {
            Assert.IsNotNull((object) context, "context is null");
            return !NeverPublishDataView.ShowNeverPublishItems ? CommandState.Enabled : CommandState.Down;
        }
    }

Notice that this code simply toggles the ShowNeverPublishItems value. The second part also causes the Content Editor to refresh after button as been clicked.

Config

Finally we just need to update the Sitecore config to use our new DataView and Command:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:set="http://www.sitecore.net/xmlconfig/set/">
  <sitecore>
    <commands>
      <command name="contenteditor:neverPublished" type="Sc8.ToggleNeverPublishView, Sc8"/>
    </commands>
    <dataviews>
      <dataview name="Master" >
        <patch:attribute name="assembly">Sc8</patch:attribute>
        <patch:attribute name="type">Sc8.NeverPublishDataView</patch:attribute>
      </dataview>
    </dataviews>
  </sitecore>
</configuration>

Testing

If we build and deploy everything to out Sitecore solution we should now be able to toggle Never Publish items in our content tree.

With the button pressed we can see everything (Item 2 is set to Never Publish):

If we toggle Show Never Published off then Item 2 is hidden and we get a prompt that there are hidden items:

This gives us a really easy and powerful way of controlling the items that the content editor sees. This logic could be much more complex to meet very complex business rules.

One thing to remember is that this code is called when rendering any item in the content tree, if you make these rules too complex then you may cause a performance issue.



Glass needs your support!

Glass.Mappper.Sc is supported by the generous donations of the Glass.Mapper.Sc community! Their donations help fund the time, effort and hosting for the project.

These supporters are AMAZING and a huge thank you to all of you. You can find our list of supporters on the Rockstars page.

If you use Glass.Mapper.Sc and find it useful please consider supporting the project. Not only will you help the project but you could also get a discount on the Glass.Mapper.Sc training and join the Rockstars page.

Become a Rockstar