Wednesday, July 3, 2013

Hiding fields in the Content Editor



The source code, package and documentation for this module are available from the Sitecore Marketplace.
Anyone who has worked much with the Sitecore Content Editor knows that there are a bunch of "standard" fields that Sitecore usually keeps hidden. These fields are either system-managed, or are rarely used and typically only changed by developers or administrators. With proper permissions, you can see these fields (View > Standard Fields), but they are usually turned off to reduce clutter.

Sometimes I want to hide some of the fields that are created as part of my solutions. This may include:
  • Fields that are intended only for administration. To avoid clutter, these fields should be hidden unless the content editor is showing standard (system) fields.
  • Fields that are populated by handlers. This could include fields that are populated by a save handler and should not be edited.
  • Fields that are populated by a publishing processor, to aggregate information or provide hints or shortcuts to the CD, to improve performance or to pass along other data.
  • Fields that are populated or reconciled by a data importer, which should not be edited.

I've created a module (available from the Sitecore Marketplace) to do just that.

This module extends Sitecore to allow developers to designate when a field should be hidden from the content editor. The options are to (1) always show the field, (2) always hide the field, or (3) only show the field when the content editor is showing standard (system) fields.

The module extends the “GetContentEditorFields” pipeline and also extends the Template field template that Sitecore uses for managing template fields in the content editor. A description of the techniques underlying this are included a bit farther down.

Installing the module

There are three key pieces required for this module: the assembly, the config file and Sitecore content items. These are all included in the package Arke.Templates.zip. To install this module, simply upload this package to Sitecore and install it normally.

If security prevents the package installer from deploying the assembly (.dll) file or the Arke.Templates.config file, these can be extracted from the zip file and deployed manually.


Using the module

Once the module is installed, you can manage the visibility of any field by changing the “Show In Content Editor” setting on any field item. The options are…
  • Always: Always show this field.
  • Never: Never show this field.
  • When showing standard fields: Show this field when Standard Fields is turned on in the ribbon (under View).


Configuration options

The Arke.Templates.Config file allows the system administrator to set how the module behaves. “HideEmptySections” governs how to handle content editor sections when all of the fields within that section have been hidden:

<!-- Hide empty sections
Should the template section be hidden entirely if all of the fields
within it are hidden
-->
<setting name="Arke.Templates.HideEmptySections" value="true" />


How the module works

The module works by extending the GetContentEditorFields pipeline.

Like many other Sitecore pipelines, GetContentEditorFields maintains a collection in its PipelineArgs class that is populated by a series of processor items. When Sitecore is rendering the content panel, it uses this pipeline to gather all of the fields and sections that it needs to display. As each processor runs, it adds fields and sections to a collection, which Sitecore uses at the end of the pipeline to form the editing panel.

When developing a pipeline processor in a situation like this, it is tempting to replace Sitecore’s processor with your own, in order to control what data is being added to the args object. However, this often requires extensive duplication of Sitecore’s code, as many of the classes in the Sitecore namespace are not public or not static, and so must be replicated in the custom processor. It also means the custom processor is less “future-proof”. If a future Sitecore release changes the processor you replaced, you will have to re-create your custom processor to reflect Sitecore’s changes.

The architecture of this module leaves Sitecore’s GetContentEditorFields pipeline processors intact, and adds a new custom processor to the end of the pipeline (the namespaces below have been truncated for readability):

<pipelines>
  <getContentEditorFields>
    <processor
      type="Arke…TrimHiddenFields, Arke.SharedSource.Templates"
      patch:after="processor[@type='Sitecore...GetExplicitFields, Sitecore.Client']
    />
  </getContentEditorFields>
</pipelines>

When the default processors have completed, the args class contains all of the fields and sections gathered by the standard processors. The TrimHiddenFields processor then iterates over all of the content sections, examining all the fields to see if their “hide” box is checked, and removing them as appropriate. It them removes the section if it no longer has visible fields.

public void Process(GetContentEditorFieldsArgs args)
{
  Assert.ArgumentNotNull(args, "args");
  if (args.Sections == null || args.Sections.Count == 0)
  {
    return;
  }
  if (Settings.HidingCheckedFields == HideCheckedField.Never)
  {
    return;
  }
  this.args = args;
  for (int sectionIndex = 0; sectionIndex < args.Sections.Count; sectionIndex++)
  {
    Editor.Section section = args.Sections[sectionIndex];
    TrimFields(section);      
    if (Settings.HidingEmptySections && section.Fields.Count == 0)
    {
      args.Sections.RemoveAt(sectionIndex);
      sectionIndex--;
    }
  }
}


private void TrimFields(Editor.Section section)
{
  for (int fieldIndex = 0; fieldIndex < section.Fields.Count; fieldIndex++)
  {
    if (FieldIsHidden(section.Fields[fieldIndex]))
    {
      section.Fields.RemoveAt(fieldIndex);
      fieldIndex--;
    }
  }
}


private bool FieldIsHidden(Editor.Field field)
{
  try
  {
    Item templateFieldItem = GetTemplateFieldItem(field, args.Item.Database);
    if (templateFieldItem == null)
    {
      return false;
    }
    bool isHiddenChecked = false;
    try
    {
      CheckboxField fld =
        (CheckboxField)templateFieldItem.Fields[Settings.HideCheckboxFieldName];
      isHiddenChecked = fld.Checked;
    }
    catch { }
    switch (Settings.HidingCheckedFields)
    {
      case HideCheckedField.Never:
        return false;
        break;
      case HideCheckedField.Always:
        return isHiddenChecked;
        break;
      case HideCheckedField.WhenHidingStandardFields:
        return isHiddenChecked && (!UserOptions.ContentEditor.ShowSystemFields);
        break;
    }
  }
  catch { }
  return false;
}
 
private Item GetTemplateFieldItem(Editor.Field field, Database database)
{
  try
  {
    return database.GetItem(field.TemplateField.ID);
  }
  catch { }
  return null;
}


It may seem wasteful to “undo” some of the work down by previous processors. However, the CPU overhead for this is pretty light, and any item data the custom processor needs (like field definition items) will have already been read (and presumably cached) by previous steps. This slight extra overhead is a small price to pay to keep this additional functionality self-contained, and to avoid replacing any of Sitecore’s default processors. Also bear in mind that this activity only happens in the content management server, where I regard performance as somewhat less critical than the content delivery instance.

More resources...




2 comments:

  1. Hi Andy, Very cool. This feature makes a lot of sense!

    Cheers,
    Pieter

    ReplyDelete
  2. Great idea, Andy! I can see how this could be useful for many customers. This is a great example of what happens when you combine Sitecore's flexibility with some creative thinking to solve problems.

    ReplyDelete