YouTube Video Picker. Part 3 – Sitecore Citizenship

Now available in the Sitecore Marketplace

In part 1 I mentioned the requirements for our custom field to become a law abiding Sitecore citizen. In part 2 I built a complete prototype of the YouTube video picker dialog that we will later convert to the XML control. It’s time to apply for passport.

TL;DR (you are more than welcome to just dive into the code)

Field Type

First things first. New field type in the core database

field type

The field type item is based on /sitecore/templates/System/Templates/Template field type and specifies brainjockscontrols:YouTubeVideoLookup in the Control field. This is the name of the Web Control that will render our field in the Content Editor.

The Select and Clear menu buttons will send youtubevideo:select(id=$Target) and youtubevideo:clear(id=$Target) messages accordingly.

The Page Editor command will trigger the same edit dialog but it speaks a different dialect so the command looks like this:

    chrome:field:editcontrol({command:"brainjockscommands:EditYouTubeVideoField"})

Our field type will need a backing class that extends Sitecore.Data.Fields.CustomField. It implements an implicit type cast operator and a few utility methods to help with the rendering.

We also need to register it with Sitecore:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <fieldtypes>
      <fieldtype name="YouTubeVideo" 
                 type="BrainJocks.YouTube.Custom.FieldTypes.YouTubeVideoField, BrainJocks.YouTube.Custom" />
    </fieldtypes>
  </sitecore>
</configuration>

Content Editor Web Control

As it was mentioned earlier we need a custom Web Control to render our field in the Content Editor. The control extends Sitecore.Web.UI.HtmlControls.Input (our raw value is just a string value) and has to perform two functions:

  • Render itself via the DoRender() method
  • Handle messages sent to it by the Select and Clear buttons

I will get back to the difference in message handling (implementation of Sitecore.Web.UI.Sheer.IMessageHandler) in the Web Control hierarchy and the XML controls hierarchy but for now we just need to override HandleMessage() and make sure our handlers run in proper Sheer UI context. You do it like this:

    var command = message.Name.Split(':');
    Assert.IsTrue(command.Length > 1, "Expected message format is control:message");

    switch (command[1])
    {
        case "clear":
            Sitecore.Context.ClientPage.Start(this, "Reset");
            return;
        case "select":
            Sitecore.Context.ClientPage.Start(this, "Select");
            return;
    }

where Reset and Select are instance methods accepting ClientPipelineArgs as a single argument and using SheerResponse and Sitecore.Context.ClientPage to interact with the user.

The rendering part is pretty straightforward and I will only mention one thing explicitly. If you decide to render different markup when the field is empty and when it has a value make sure to give the enclosing tag the id attribute in both cases:

    if (string.IsNullOrEmpty(Value))
    {
        sb.AppendFormat("<p id='{0}'>", ID)
          .Append(Translate.Text("There is no video selected."))
          .Append("</p>");
    }
    else
    {
        // <div> with the same id and the video thumbnail
    }

The Select button will fire up the XML control dialog that will bring in the new value into the field but it won’t save it yet. Sitecore will re-render that field in the Content Editor by re-running the control and replacing the markup using:

scSitecore.prototype.process = function (request, command, name) {
    // ....
    case "SetOuterHtml":
      var ctl = this.browser.getControl(command.id);
      if (ctl != null) {
        this.browser.setOuterHtml(ctl, this.expandHtml(command.value));
      }
      break;
    // ...
}

so it needs to find the original HTML element by the field ID. Check full implementation of the Content Editor control here.

The control also has to be registered:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <controlSources>
      <source mode="on" namespace="BrainJocks.YouTube.Custom.Controls" 
                        assembly="BrainJocks.YouTube.Custom" prefix="brainjockscontrols"/>

      <source mode="on" namespace="BrainJocks.YouTube.Web.XmlControls" 
                        assembly="BrainJocks.YouTube.Web" folder="/sitecore modules/BrainJocks" />
    </controlSources>    
  </sitecore>
</configuration>

One more thing. I am using fancybox for overlay video preview and I need two external resources in the Context Editor. I included a simplified version of a renderContentEditor processor to go along with the YouTube Field and will probably later write a separate blog post about a more generic solution.

Page Editor Command

The Page Editor command is not like those Content Editor message handlers. Page Editor will enrich our field with the chrome data and will wrap it into special markup to support the Editing mode. We need to teach Page Editor how to launch our edit dialog.

First, we need to register our command:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <commands>      
      <command name="brainjockscommands:EditYouTubeVideoField" 
               type="BrainJocks.YouTube.Custom.Commands.EditYouTubeVideoField, BrainJocks.YouTube.Custom"/>
    </commands>
  </sitecore>
</configuration>

Then we need to implement it. Two notes before we look at the code. Page Editor, just like Content Editor, doesn’t save fields on edit so we will need to work with the value in flight, not the value on the item:

    public override void Execute(CommandContext context)
    {
        string formValue = WebUtil.GetFormValue("scPlainValue");
        // ...
        Context.ClientPage.Start(this, "Run", context.Parameters);
    }

The other thing is we need to explicitly set the raw value (via scPlainValue) and the HTML representation that will be shown to the user (via scHtmlValue):

    SheerResponse.SetAttribute("scHtmlValue", "value", FormatValueForPageEditorDisplay(args.Result));
    SheerResponse.SetAttribute("scPlainValue", "value", args.Result);

    SheerResponse.Eval("scSetHtmlValue('" + controlId + "', false, true)");

Full listing is here.

Page Editor Rendering

Handling menu commands is not the only difference between Content and Page editors. Rendering of the initial field value has to be customized too. This is done via a renderField pipeline processor:

    public class GetYouTubeVideoFieldValue
    {
        public void Process(RenderFieldArgs args)
        {
            Assert.ArgumentNotNull(args, "args");

            // ... 

            YouTubeVideoField video = args.GetField();

            YouTubeVideoField.ThumbnailSize size;
            Enum.TryParse(args.Parameters["Size"], out size); // default is Small

            args.Result.FirstPart = YouTubeVideoField.FormatValueForPageEditorDisplay(video.Value, size);
        }
    }

Almost There

Almost done. We also need to convert the picker dialog HTML prototype into a fully functional XML Control and plug it into the code we explored today. There’s quite a few things I would like to explore in details so we will get to it in Part 4.

Pavel Veller

Add a Comment

Your email address will not be published. Required fields are marked *

Or request call back