How to make a customized control for the Sitecore Page Editor

For not so long ago I saw the Content Editor as the only true tool that the editors should use for administering websites.

But then I had a revelation – the DMS – and the Page Editor showed me the way.

The Page Editor feels really nice and it’s so easy to work with. It has some nice controls like the sc:text, sc:Image and sc:link. But where are the others? I mean the sitecore dropdown control or something simple as the checkbox control…

First I thought it must be easy to implement. I could always use the sc:EditFrame but then I realized it’s just another way of using the Content Editor, it’s not good enough. If you are using the Page Editor then you should go all the way.

So I started to analyze how Sitecore did in the Page Editor and I found the magic ingredient, the javascript method: Sitecore.WebEdit.setFieldValue

This little baby creates a hidden field which the Page Editor reads when a “Save” or “Save/Close” button is clicked.

The parameters for the method are:

  • Sitecore.ItemUri
  • FieldId
  • The actual value

The Sitecore.ItemUri needs following parameters:

  • Item Id
  • LanguageVersion
  • Revision.

So now I could finally start doing some cool stuff.

In this case I have a page that presents a simple registration form. I wanted to give the editors the choice to decide if the text fields needs to be validated.

RegistrationWidget

The item itself contains some labels and checkboxes (for the validation).

RegistrationWidgetContentEditor

I was thinking of presenting the validation choices as checkboxes in the Page Editor , but unfortunately the Page Editor seems to interfere with checkbox control (html). I couldn’t get it to work so instead I used a select box containing “true/1” and “false/0” values.

RegistrationWidgetPageEditor

The code for the validation “checkboxes” in the Page Editor:

<span runat="server" visible="<%# Sitecore.Context.PageMode.IsPageEditorEditing %>">
    <span style="font-size: x-small">Should field be validated?</span>
    <select onchange="var itemUri = new Sitecore.ItemUri( '<%# this.GetDataSourceOrContextItem().ID.ToShortID() %>', 
        '<%# this.GetDataSourceOrContextItem().Language.ToString() %>', 
        '<%# this.GetDataSourceOrContextItem().Version.ToString() %>', 
        '<%# new ID(this.GetDataSourceOrContextItem()[FieldIDs.Revision]).ToShortID() %>'); 
        Sitecore.WebEdit.setFieldValue( itemUri, 
        '<%# Sandbox.Newsletter.Constants.FieldIDs.RegistrationForm.RegistrationFormFirstNameShouldBeValidated.ToShortID() %>', 
        this.options[this.selectedIndex].value); ">
        <option value="1" <%# this.GetDataSourceOrContextItem().GetCheckBoxValue(Sandbox.Newsletter.Constants.FieldIDs.RegistrationForm.RegistrationFormFirstNameShouldBeValidated) ? "selected" : string.Empty %>>Yes</option>
        <option value="0" <%# !this.GetDataSourceOrContextItem().GetCheckBoxValue(Sandbox.Newsletter.Constants.FieldIDs.RegistrationForm.RegistrationFormFirstNameShouldBeValidated) ? "selected" : string.Empty %>>No</option>
    </select>
</span>

If you are wondering what the GetDataSourceOrContextItem is? It’s something I used from Brian Pedersen’s blog, http://briancaos.wordpress.com/2013/09/13/avoid-the-use-of-sitecore-context-item/.

(Method: GetCheckboxValue is also an extension method)

Everything worked and I was a happy man, but then I realized that I have to repeat this for every input field in the registration form. Not pretty at all…

So I looked how Sitecore did with their “sc:controls” and finally I came up with a control which will look like this when used in the Page Editor:

<%@ Register TagPrefix="mycontrol" Namespace="Sandbox.Framework.Controls.PageEditor" Assembly="Sandbox.Framework.Controls" %>

<span runat="server" visible="<%# Sitecore.Context.PageMode.IsPageEditorEditing %>">
  <span style="font-size: x-small">Should field be validated?</span>
  <mycontrol:PageEditorCheckboxControl TrueText="Yes" FalseText="No" DataSource="<%# this.GetDataSourceOrContextItem().ID %>" Field="<%# Sandbox.Newsletter.Constants.FieldIDs.RegistrationForm.RegistrationFormFirstNameShouldBeValidated %>" runat="server" />
</span>

And here is the complete code for the customized control:

public class PageEditorCheckboxControl : FieldControl
{
    private readonly SafeDictionary<string> _parameters = new SafeDictionary<string>();

    public string TrueText { get; set; }

    public string FalseText { get; set; }

    protected override void DoRender(HtmlTextWriter output)
    {

        if (string.IsNullOrEmpty(Field))
            throw new InvalidOperationException("Field property is required. All field web controls require the field name to be set.");

        FieldRenderer fieldRenderer = new FieldRenderer
        {
            Parameters = GetParameters(),
            DisableWebEditing = DisableWebEditing
        };


        StringBuilder stringBuilderOnChange = new StringBuilder();

        StringBuilder stringBuilderSelect = new StringBuilder();

        stringBuilderOnChange.AppendFormat(@"var itemUri = new Sitecore.ItemUri('{0}','{1}','{2}','{3}');", GetItem().ID.ToShortID(),
            GetItem().Language, GetItem().Version, new ID(GetItem()[FieldIDs.Revision]).ToShortID());

        stringBuilderOnChange.AppendFormat(@"Sitecore.WebEdit.setFieldValue(itemUri,'{0}',this.options[this.selectedIndex].value);", Field);

        stringBuilderSelect.AppendFormat(@"<select onchange=""{0}"" >", stringBuilderOnChange.ToString());
        stringBuilderSelect.AppendLine();
        stringBuilderSelect.AppendFormat(@"<option value='0' {0} >{1}</option>", GetItem()[Field] == "0" ? "selected" : string.Empty, FalseText);
        stringBuilderSelect.AppendFormat(@"<option value='1' {0} >{1}</option>", GetItem()[Field] == "1" ? "selected" : string.Empty, TrueText);
        stringBuilderSelect.AppendLine();
        stringBuilderSelect.Append("</select>");

        output.Write(stringBuilderSelect.ToString());


    }

    protected override Item GetItem()
    {
        if (this.Item != null)
            return this.Item;

        return !string.IsNullOrWhiteSpace(this.DataSource) ? Sitecore.Context.Item.Database.GetItem(this.DataSource) : base.GetItem();
    }

    private string GetParameters()
    {
        PopulateParameters(_parameters);
        return WebUtil.BuildQueryString(_parameters, false);
    }

}

That’s all for now folks 🙂


6 thoughts on “How to make a customized control for the Sitecore Page Editor

  1. Hi Göran,

    I have been trying to implement this example but i keep running into problems when I am trying to save. When I save it throws a “System.ArgumentOutOfRangeException” with the title:

    “Index and length must refer to a location within the string. Parameter name: length”

    I’m not sure where I’m going wrong since what I’m trying to do is exactly the same as you.

    Hope to hear from you soon,
    Vincent Kong

    Like

  2. `stringBuilderOnChange.AppendFormat(@”Sitecore.WebEdit.setFieldValue(itemUri,'{0}’,this.options[this.selectedIndex].value);”, Field);`

    `Field` needs to be the FieldID not the field name – I replaced with `GetItem().Fields[Field].ID.ToShortID()` and all is now working correctly.

    Liked by 1 person

    1. Hi

      Thanks for reading the post and testing my code 🙂
      Well pointed out. The constant(Sandbox.Newsletter.Constants.Fields.RegistrationForm.RegistrationFormFirstNameShouldBeValidated) I’m sending to the control, mycontrol:PageEditorCheckboxControl, is a field Id. Unfortunately it’s not clear enough. I will change the constant to be more understandable.

      Like

Leave a comment

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