Home » Commands » Where Is This Field Defined? Add ‘Goto Template’ Links for Fields in the Sitecore Content Editor

Where Is This Field Defined? Add ‘Goto Template’ Links for Fields in the Sitecore Content Editor

Sitecore Technology MVP 2016
Sitecore MVP 2015
Sitecore MVP 2014

Enter your email address to follow this blog and receive notifications of new posts by email.

About a month ago, I read this answer on Stack Overflow by Sitecore MVP Dan Solovay, and thought to myself “what could I do with a custom EditorFormatter that might be useful?”

Today, I came up with an idea that might be useful, especially when working with many levels of nested base templates: having a ‘Goto Template’ link — or button depending on your naming preference, although I will refer to these as links throughout this post since they are hyperlinks — for each field that, when clicked, will bring you to the Sitecore template where the field is defined.

I first defined a class to manage the display state of our ‘Goto Template’ links in the Content Editor:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Sitecore.Web.UI.HtmlControls;

namespace Sitecore.Sandbox.Utilities.ClientSettings
{
    public class GotoTemplateLinksSettings
    {
        private const string RegistrySettingKey = "/Current_User/Content Editor/Goto Template Links";
        private const string RegistrySettingOnValue = "on";

        private static volatile GotoTemplateLinksSettings current;
        private static object lockObject = new Object();

        public static GotoTemplateLinksSettings Current
        {
            get
            {
                if (current == null)
                {
                    lock (lockObject)
                    {
                        if (current == null)
                            current = new GotoTemplateLinksSettings();
                    }
                }

                return current;
            }
        }

        private GotoTemplateLinksSettings()
        {
        }

        public bool IsOn()
        {
            return Registry.GetString(RegistrySettingKey) == RegistrySettingOnValue;
        }

        public void TurnOn()
        {
            Registry.SetString(RegistrySettingKey, RegistrySettingOnValue);
        }

        public void TurnOff()
        {
            Registry.SetString(RegistrySettingKey, string.Empty);
        }
    }
}

I decided to make the above class be a Singleton — there should only be one central place where the display state of our links is toggled.

I created a subclass of Sitecore.Shell.Applications.ContentEditor.EditorFormatter, and overrode the RenderField(Control, Editor.Field, bool) method to embed additional logic to render a ‘Goto Template’ link for each field in the Content Editor:

using System.Web.UI;

using Sitecore;
using Sitecore.Data.Fields;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;

using Sitecore.Shell.Applications.ContentEditor;
using Sitecore.Shell.Applications.ContentManager;

using Sitecore.Sandbox.Utilities.ClientSettings;

namespace Sitecore.Sandbox.Shell.Applications.ContentEditor
{
    public class GotoTemplateEditorFormatter : EditorFormatter
    {
        public override void RenderField(Control parent, Editor.Field field, bool readOnly)
        {
            Assert.ArgumentNotNull(parent, "parent");
            Assert.ArgumentNotNull(field, "field");
            Field itemField = field.ItemField;
            Item fieldType = GetFieldType(itemField);

            if (fieldType != null)
            {
                if (!itemField.CanWrite)
                {
                    readOnly = true;
                }

                RenderMarkerBegin(parent, field.ControlID);
                RenderMenuButtons(parent, field, fieldType, readOnly);
                RenderLabel(parent, field, fieldType, readOnly);
                AddGotoTemplateLinkIfCanView(parent, field);
                RenderField(parent, field, fieldType, readOnly);
                RenderMarkerEnd(parent);
            }
        }

        public void AddGotoTemplateLinkIfCanView(Control parent, Editor.Field field)
        {
            if (CanViewGotoTemplateLink())
            {
                AddGotoTemplateLink(parent, field);
            }
        }

        private static bool CanViewGotoTemplateLink()
        {
            return IsGotoTemplateLinksOn();
        }

        private static bool IsGotoTemplateLinksOn()
        {
            return GotoTemplateLinksSettings.Current.IsOn();
        }

        public void AddGotoTemplateLink(Control parent, Editor.Field field)
        {
            Assert.ArgumentNotNull(parent, "parent");
            Assert.ArgumentNotNull(field, "field");
            AddLiteralControl(parent, CreateGotoTemplateLink(field));
        }

        private static string CreateGotoTemplateLink(Editor.Field field)
        {
            Assert.ArgumentNotNull(field, "field");
            return string.Format("<a title=\"Navigate to the template where this field is defined.\" style=\"float: right;position:absolute;margin-top:-20px;right:15px;\" href=\"#\" onclick=\"{0}\">{1}</a>", CreateGotoTemplateJavascript(field), CreateGotoTemplateLinkText());
        }

        private static string CreateGotoTemplateJavascript(Editor.Field field)
        {
            Assert.ArgumentNotNull(field, "field");
            return string.Format("javascript:scForm.postRequest('', '', '','item:load(id={0})');return false;", field.TemplateField.Template.ID);
        }

        private static string CreateGotoTemplateLinkText()
        {
            return "<img style=\"border: 0;\" src=\"/~/icon/Applications/16x16/information2.png\" width=\"16\" height=\"16\" />";
        }
    }
}

‘Goto Template’ links are only rendered to the Sitecore Client when the display state for showing them is turned on.

Plus, each ‘Goto Template’ link is locked and loaded to invoke the item load command to navigate to the template item where the field is defined.

As highlighted by Dan in his Stack Overflow answer above, I created a new Sitecore.Shell.Applications.ContentEditor.Pipelines.RenderContentEditor pipeline processor, and hooked in an instance of the GotoTemplateEditorFormatter class defined above:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Sitecore.Diagnostics;

using Sitecore.Shell.Applications.ContentEditor;
using Sitecore.Shell.Applications.ContentEditor.Pipelines.RenderContentEditor;

namespace Sitecore.Sandbox.Shell.Applications.ContentEditor.Pipelines.RenderContentEditor
{
    public class RenderStandardContentEditor
    {
        public void Process(RenderContentEditorArgs args)
        {
            Assert.ArgumentNotNull(args, "args");
            args.EditorFormatter = CreateNewGotoTemplateEditorFormatter(args);
            args.EditorFormatter.RenderSections(args.Parent, args.Sections, args.ReadOnly);
        }

        private static EditorFormatter CreateNewGotoTemplateEditorFormatter(RenderContentEditorArgs args)
        {
            Assert.ArgumentNotNull(args, "args");
            Assert.ArgumentNotNull(args.EditorFormatter, "args.EditorFormatter");
            return new GotoTemplateEditorFormatter 
            { 
                Arguments = args.EditorFormatter.Arguments, 
                IsFieldEditor = args.EditorFormatter.IsFieldEditor 
            };
        }
    }
}

Now we need a way to toggle the display state of our ‘Goto Template’ links. I decided to create a command to turn this state on and off:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Shell;
using Sitecore.Shell.Framework.Commands;
using Sitecore.Web.UI.HtmlControls;

using Sitecore.Sandbox.Utilities.ClientSettings;

namespace Sitecore.Sandbox.Commands
{
    public class ToggleGotoTemplateLinks : Command
    {
        public override void Execute(CommandContext commandContext)
        {
            ToggleGotoTemplateLinksOn();
            Refresh(commandContext);
        }

        private static void ToggleGotoTemplateLinksOn()
        {
            GotoTemplateLinksSettings gotoTemplateLinksSettings = GotoTemplateLinksSettings.Current;

            if (!gotoTemplateLinksSettings.IsOn())
            {
                gotoTemplateLinksSettings.TurnOn();
            }
            else
            {
                gotoTemplateLinksSettings.TurnOff();
            }
        }

        private static void Refresh(CommandContext commandContext)
        {
            Refresh(GetItem(commandContext));
        }

        private static void Refresh(Item item)
        {
            Assert.ArgumentNotNull(item, "item");
            Context.ClientPage.ClientResponse.Timer(string.Format("item:load(id={0})", item.ID), 1);
        }

        private static Item GetItem(CommandContext commandContext)
        {
            Assert.ArgumentNotNull(commandContext, "commandContext");
            return commandContext.Items.FirstOrDefault();
        }

        public override CommandState QueryState(CommandContext context)
        {
            if (!GotoTemplateLinksSettings.Current.IsOn())
            {
                return CommandState.Enabled;
            }

            return CommandState.Down;
        }
    }
}

I registered the pipeline processor defined above coupled with the toggle command in a patch include configuration file:

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <commands>
      <command name="contenteditor:togglegototemplatelinks" type="Sitecore.Sandbox.Commands.ToggleGotoTemplateLinks, Sitecore.Sandbox"/>
    </commands>
    <pipelines>
      <renderContentEditor>
        <processor 
            patch:instead="*[@type='Sitecore.Shell.Applications.ContentEditor.Pipelines.RenderContentEditor.RenderStandardContentEditor, Sitecore.Client']" 
            type="Sitecore.Sandbox.Shell.Applications.ContentEditor.Pipelines.RenderContentEditor.RenderStandardContentEditor, Sitecore.Sandbox"/>
      </renderContentEditor>
      
    </pipelines>
  </sitecore>
</configuration>

I then added a toggle checkbox to the View ribbon, and wired up the ToggleGotoTemplateLinks command to it:

goto-template-links-view-checkbox

When the ‘Goto Template Links’ checkbox is checked, ‘Goto Template’ links are displayed for each field in the Content Editor:

goto-template-links-turned-on

When unchecked, the ‘Goto Template’ links are not rendered:

goto-template-links-turned-off

Let’s try it out.

Let’s click one of these ‘Goto Template’ links and see what it does, or where it takes us:

Home-Data-Title-Goto-Template-Link

It brought us to the template where the Title field is defined:

Goto-Template-Title-Template

Let’s try another. How about the ‘Created By’ field?

home-created-by-goto-template-link

Its link brought us to its template:

Goto-Template-CreatedBy-Template

Without a doubt, the functionality above would be useful to developers and advanced users.

I’ve been trying to figure out other potential uses for other subclasses of EditorFormatter. Can you think of other ways we could leverage a custom EditorFormatter, especially one for non-technical Sitecore users? If you have any ideas, please drop a comment. 🙂

Advertisement

Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: