[su_note note_color=”#fafafa”]Now available in the Sitecore Marketplace[/su_note]
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
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
andClear
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
andSelect
are instance methods acceptingClientPipelineArgs
as a single argument and usingSheerResponse
andSitecore.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 (viascHtmlValue
):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.