To me, the entire point of Sitecore is to power the content authors to be able to create amazing pages with great flexibility without having to rely on a technical team of developers. Sometimes, however, they may need a little extra help on how a component works. Traditional documentation is dificult to read and does not often convey the steps required very well. What has always worked for me are training videos. When combined with a very specific scope of showing just how one component works, it significantly helps boost author confidence and reduces mistakes and repetitive training.

I wanted to incorporate this help directly into the Sitecore authoring experience. To accomplish this, I added a help button directly on the rendering toolbar, which opens a modal with help text and a video.

The Setup

Achieving this was surprisingly simple. To begin with, wee will utililize the existing Help fields in Sitecore to store our text and video link.

Achieving this was surprisingly simple. To begin with, wee will utililize the existing Help fields in Sitecore to store our text and video link.

Begin by navigating to your rendering definition item, which are usually found under: /sitecore/layout/Renderings, and select your rendering.

Then, from the Configure tab, select Help.

In the corresponding modal, we will utilize the Long Decription field to use for our help text and the Help Link field to point to a media library item for our video.

The Code

With the pieces in place, we can now focus on the code needed to render the help modal.

We’ll beging by creating our “Help Modal” command button. In a configuration patch file, add the following:

<commands>
    <command name="webedit:helpdialog" type="MasterToWeb.Foundation.ExperienceEditorExtensions.Commands.HelpCommand, MasterToWeb.Foundation.ExperienceEditorExtensions" />
</commands>

By reviewing Sitecore’s existing functionality, we can hook up the command to a modal and pass in our relevant details. We do this by getting the rendering item ID and extracting the help fields from it.

public class HelpCommand : WebEditCommand
    {
        public override void Execute(CommandContext context)
        {
            string renderingId = context.Parameters["renderingId"];
            NameValueCollection parameters = new NameValueCollection();
            if (!string.IsNullOrEmpty(renderingId))
            {
                parameters["renderingItemId"] = renderingId;
                Context.ClientPage.Start((object)this, "Run", parameters);
            }
        }

        protected virtual void Run(ClientPipelineArgs args)
        {
            Assert.ArgumentNotNull((object)args, nameof(args));
            if (!SheerResponse.CheckModified())
                return;
            Assert.IsNotNull((object)args, nameof(args));
            string parameter = args.Parameters["renderingItemId"];
            Assert.IsNotNullOrEmpty(parameter, "renderingItemId");
            string formValue = WebUtil.GetFormValue("scLanguage");
            Assert.IsNotNullOrEmpty(formValue, "lang");
            Item obj = Client.ContentDatabase.GetItem(parameter, Language.Parse(formValue));
            if (obj == null)
            {
                SheerResponse.Alert("Item not found.");
                return;
            }

            if (args.IsPostBack)
            {
                if (!args.HasResult)
                    return;
                WebEditCommand.Reload();
            }
            else
            {
                SheerResponse.ShowModalDialog(this.GetOptions(args).ToUrlString().ToString(), "740", "600", obj.Name + " Help", true);
                args.WaitForPostBack(true);
            }
        }

        protected virtual HelpDialogOptions GetOptions(ClientPipelineArgs args)
        {
            Assert.ArgumentNotNull((object)args, nameof(args));
            string parameter = args.Parameters["renderingItemId"];
            Language language1 = WebEditUtil.GetClientContentLanguage();
            if ((object)language1 == null)
                language1 = Context.Language;
            Language language2 = language1;
            Item obj = Client.ContentDatabase.GetItem(parameter, language2);
            Assert.IsNotNull((object)obj, "item");
            return new HelpDialogOptions(obj);
        }

    }

We also need our HelpDialogOptions to setup the dialog box.

public class HelpDialogOptions
    {
        public Item Item { get; protected set; }

        public ID RenderingId { get; set; }

        public HelpDialogOptions(Item item)
        {
            Assert.ArgumentNotNull((object)item, nameof(item));
            this.Item = item;
            this.RenderingId = item.ID;
        }

        public virtual UrlString ToUrlString()
        {
            Assert.IsNotNull((object)Context.Site, "context site");
            UrlString urlString = new UrlString(Context.Site.XmlControlPage);
            urlString["xmlcontrol"] = this.GetXmlControl();
            this.Item.Uri.AddToUrlString(urlString);
            if (!ID.IsNullOrEmpty(this.RenderingId))
                urlString["renderingId"] = this.RenderingId.ToString();
            return Assert.ResultNotNull<UrlString>(urlString);
        }

        /// <summary>The get xml control.</summary>
        /// <returns>The get xml control.</returns>
        protected virtual string GetXmlControl()
        {
            return "Sitecore.Shell.Applications.Dialogs.HelpDialog";
        }

        public static HelpDialogOptions Parse()
        {
            ItemUri queryString1 = ItemUri.ParseQueryString();
            Assert.IsNotNull((object)queryString1, "itemUri is null");

            Item obj = Database.GetItem(queryString1);
            Assert.IsNotNull((object)obj, "Item \"{0}\" not found", (object)queryString1);
            HelpDialogOptions helpDialogOptions = new HelpDialogOptions(obj);
            return helpDialogOptions;

        }

We tie it all together by creating the XML dialog and binding the code. Begin by creating a folder under:
/sitecore/Shell/Applications/Dialogs/HelpDialog
Here, we can place our XML file and any Javascript or css that may be needed.

Here is our xml file. Name if HelpDialog.xml and place in above folder.

<?xml version="1.0" encoding="utf-8" ?>
<control xmlns:def="Definition" xmlns="http://schemas.sitecore.net/Visual-Studio-Intellisense">
	<Sitecore.Shell.Applications.Dialogs.HelpDialog>
		<FormDialog Icon="WordProcessing/32x32/line_spacing_h.png" Header="Component Help" TextCssClass="scDialogTextWithPadding"
		  Text="Please review the help and videos for this component">
			<CodeBeside Type="MasterToWeb.Foundation.ExperienceEditorExtensions.Dialogs.HelpDialog, MasterToWeb.Foundation.ExperienceEditorExtensions"/>
			<Stylesheet Src="/sitecore/shell/Applications/Dialogs/HelpDialog/HelpDialog.css" DeviceDependant="false" />
			<Script Src="/sitecore/shell/Controls/Lib/scriptaculous/scriptaculous.js?load=effects,dragdrop"></Script>
			<div class="help-dialog-container">
				<Literal class="help-dialog-text" ID="HelpLiteral" />
			</div>
			<div class="help-link-container">
				<Literal ID="HelpLink" />
			</div>
			<div class="help-frame-container">
				<Literal ID="VideoSource" />			
			</div>
		</FormDialog>
	</Sitecore.Shell.Applications.Dialogs.HelpDialog>
</control>

The highlighted line of text above points to our code responsible for getting the and passing the data to our dialog.

 public class HelpDialog : DialogForm
    {
        protected Literal HelpLiteral;
        protected Literal HelpLink;
        protected Literal VideoSource;
        
        private Item renderingItem;

        private const string HelpLinkFieldId = "{56776EDF-261C-4ABC-9FE7-70C618795239}";
        private const string HelpLongDescriptionFieldId = "{577F1689-7DE4-4AD2-A15F-7FDC1759285F}";
        protected override void OnLoad(EventArgs e)
        {
            Assert.ArgumentNotNull((object)e, nameof(e));
            base.OnLoad(e);
            if (Context.ClientPage.IsEvent)
                return;

            HelpDialogOptions options = HelpDialogOptions.Parse();
            this.renderingItem = options.Item;

            if (this.renderingItem != null)
            {
                LinkField linkField = renderingItem.Fields[HelpLinkFieldId];
                if (linkField != null)
                {
                    MediaItem mediaItem = new MediaItem(linkField.TargetItem);
                    MediaUrlBuilderOptions mediaOptions = new MediaUrlBuilderOptions();
                    string url = Sitecore.StringUtil.EnsurePrefix('/', Sitecore.Resources.Media.MediaManager.GetMediaUrl(mediaItem, mediaOptions));

                    this.HelpLink.Text = $"<a href='{url}' target='_blank'>Click here to open the video in a new window</a>";
                    this.VideoSource.Text = $"<video width='100%' controls><source src='{url}' type='video/mp4'> Your browser does not support the video tag.</video>";                    
                }
                this.HelpLiteral.Text = renderingItem[HelpLongDescriptionFieldId];
            }


        }
    }

And add some basic css to HelpDialog.css to make it look nice.

.help-dialog-text {
    font-size: 16px;
}

.help-dialog-container {
    padding: 10px;
}

.help-link-container {
    padding-bottom: 10px;
}

    .help-link-container a {
        font-size: 16px;
    }

.help-frame-container {
    width: 100%;
}

The final piece is to create a WebEdit button in Sitecore and assign it to your rendering. Begin by going to the core database and navgiate to:
/sitecore/content/Applications/WebEdit/Custom Experience Buttons

I recommend following Sitecore Helix patterns and creating the proper folder structure here. In your selected folder, create an new WebEdit Button (template: /sitecore/templates/System/WebEdit/WebEdit Button)

Complete it with the following details.

Now, switch back to your master database and navigate back to your rendering definition item.

In the Experience Editor Buttons section, assign your new Help button to the rendering.

That is all. You will now have the Help button in Experience Editor for this component. To enable this for other components, you simply need to add the Help content and assign the same button to the Experience Editor buttons field.

Enjoy!