Monthly Archives: February 2014

Make your own Droplink control for the Sitecore Page Editor

Like I mentioned in previous post, How to make a customized control for the SitecorePageEditor,  there are some basic controls missing… One of them is the Droplink control.

So let’s make one 🙂

I have a page with a “typical” column layout, I want my widget/spot to be flexible so it can fit/span over one or many columns. In this case it’s a registration form.

Registration form spans over 2 columns
RegistrationWidgetColumn

Registration form spans over 1 column
RegistrationWidgetColumn1

In the Page Editor the column layout for the widget/spot is controlled with a dropdown.
RegistrationWidgetColumnPageEditor

In the Content Editor the item(registration form) has a droplink containing the column values.
ColumnSelected

The droplink contains a number of “column value” items, each “column value” has one “friendly value” and one “real value”.
The “real value” is the actual column value and the “friendly value” is what presents to the editor. In this case “1 column” is spanning over 4 columns
ColumnValue

The code for rendering the column span looks like this:

<div <%# !string.IsNullOrWhiteSpace(this.GetDataSourceOrContextItem().GetString(Sandbox.Identity.Constants.Fields.FooterTextSection.ColumnSelectorNumberOfColumns)) ? string.Format(@"class='col-md-{0}'", this.GetDataSourceOrContextItem().GetString(Sandbox.Identity.Constants.Fields.FooterTextSection.ColumnSelectorNumberOfColumns)) : string.Empty %>>

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/.
(GetDropLinkSelectedItem and GetString are also extension methods)

Now it’s time to make the Droplink control for the Page Editor. The column layout for the widget/spot is controlled with a dropdown. That’s what we are going to do. But first I’ll do a dirty version.

<div  runat="server" visible="<%# Sitecore.Context.PageMode.IsPageEditorEditing %>">
    <span style="font-size: x-small">Number of columns it will span over</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.Identity.Constants.Fields.FooterTextSection.ColumnSelectorNumberOfColumns.ToShortID() %>',
        this.options[this.selectedIndex].value);">
        <option value="" <%# string.IsNullOrWhiteSpace(this.GetDataSourceOrContextItem().GetString(Sandbox.Identity.Constants.Fields.FooterTextSection.ColumnSelectorNumberOfColumns)) ? "selected" : string.Empty   %>>No column</option>
        <option value="4" <%# this.GetDataSourceOrContextItem().GetString(Sandbox.Identity.Constants.Fields.FooterTextSection.ColumnSelectorNumberOfColumns).Equals("4") ? "selected" : string.Empty   %>>1 column</option>
        <option value="8" <%# this.GetDataSourceOrContextItem().GetString(Sandbox.Identity.Constants.Fields.FooterTextSection.ColumnSelectorNumberOfColumns).Equals("8") ? "selected" : string.Empty   %>>2 columns</option>
        <option value="12" <%# this.GetDataSourceOrContextItem().GetString(Sandbox.Identity.Constants.Fields.FooterTextSection.ColumnSelectorNumberOfColumns).Equals("12") ? "selected" : string.Empty   %>>3 columns</option>
    </select>
</div>

Previous post, How to make a customized control for the Sitecore Page Editor, explains what Sitecore.WebEdit.setFieldValue does.

Let’s redo the dirty version to a nice control. It will look like this when it’s finished.

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

<div runat="server" visible="<%# Sitecore.Context.PageMode.IsPageEditorEditing %>">
    <span style="font-size: x-small">Number of columns it will span over</span>
    <mycontrol:PageEditorDropLinkControl runat="server"
            DataSource="<%# this.GetDataSourceOrContextItem().ID %>"
            Field="<%# Sandbox.Newsletter.Constants.Fields.RegistrationForm.ColumnSelectorDropdown %>"
            EmptyText="No column"
            DropLinkItemTextField="<%# Sandbox.Design.Constants.Fields.DesignColumn.ColumnValueFriendly %>"
            DropLinkItemTemplate="<%# Sandbox.Design.Constants.Templates.DesignColumn %>"
            DropLinkItems="<%# Sandbox.Design.Model.Repositories.PageColumnRepository.PageColumnsSettingsItem().GetChildren()  %>" />

</div>

Finally! Here is the code for the “PageEditorDropLinkControl”

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


    public string EmptyText { get; set; }

    public ID DropLinkItemTextField { get; set; }

    public IEnumerable<Item> DropLinkItems { get; set; }

    public ID DropLinkItemTemplate { get; set; }

    protected override void DoRender(HtmlTextWriter output)
    {

        if (string.IsNullOrWhiteSpace(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} >{1}</option>", GetItem().GetDropLinkSelectedItem(new ID(Field)) == null ? "selected" : string.Empty, EmptyText);
        stringBuilderSelect.AppendLine();
            
        foreach (Item dropLinkItem in DropLinkItems.Where(dropLinkItem => dropLinkItem.IsDerived(DropLinkItemTemplate)))
        {
            stringBuilderSelect.AppendFormat(@"<option value='{0}' {1} >{2}</option>", dropLinkItem.ID, GetItem().GetDropLinkSelectedItem(new ID(Field)).ID.Equals(dropLinkItem.ID) ? "selected" : string.Empty, dropLinkItem.GetString(DropLinkItemTextField));
            stringBuilderSelect.AppendLine();
        }           

            
           
        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 🙂

Advertisements

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 🙂