diff --git a/components/editorControls/XPathCheckBoxList/XPathCheckBoxListDataEditor.cs b/components/editorControls/XPathCheckBoxList/XPathCheckBoxListDataEditor.cs
new file mode 100644
index 0000000000..88b050027c
--- /dev/null
+++ b/components/editorControls/XPathCheckBoxList/XPathCheckBoxListDataEditor.cs
@@ -0,0 +1,159 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web.UI;
+using System.Web.UI.WebControls;
+using System.Xml.Linq;
+// using uComponents.Core;
+using umbraco.interfaces;
+
+namespace umbraco.editorControls.XPathCheckBoxList
+{
+ ///
+ /// Renders a CheckBoxList using with option nodes obtained by an XPath expression
+ ///
+ public class XPathCheckBoxListDataEditor : CompositeControl, IDataEditor
+ {
+ ///
+ /// Field for the data.
+ ///
+ private IData data;
+
+ ///
+ /// Field for the options.
+ ///
+ private XPathCheckBoxListOptions options;
+
+ ///
+ /// Field for the checkbox list.
+ ///
+ private CheckBoxList checkBoxList = new CheckBoxList();
+
+ ///
+ /// Gets a value indicating whether [treat as rich text editor].
+ ///
+ ///
+ /// true if [treat as rich text editor]; otherwise, false.
+ ///
+ public virtual bool TreatAsRichTextEditor
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether [show label].
+ ///
+ /// true if [show label]; otherwise, false.
+ public virtual bool ShowLabel
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ ///
+ /// Gets the editor.
+ ///
+ /// The editor.
+ public Control Editor
+ {
+ get
+ {
+ return this;
+ }
+ }
+
+ ///
+ /// Initializes a new instance of XPathCheckBoxListDataEditor
+ ///
+ ///
+ ///
+ internal XPathCheckBoxListDataEditor(IData data, XPathCheckBoxListOptions options)
+ {
+ this.data = data;
+ this.options = options;
+ }
+
+ ///
+ /// Called by the ASP.NET page framework to notify server controls that use composition-based implementation to create any child controls they contain in preparation for posting back or rendering.
+ ///
+ protected override void CreateChildControls()
+ {
+ this.checkBoxList.DataSource = uQuery.GetNodesByXPath(this.options.XPath).ToNameIds();
+ this.checkBoxList.DataTextField = "Value";
+ this.checkBoxList.DataValueField = this.options.UseIds ? "Key" : "Value";
+ this.checkBoxList.DataBind();
+
+ this.Controls.Add(this.checkBoxList);
+ }
+
+ ///
+ /// Raises the event.
+ ///
+ /// The object that contains the event data.
+ protected override void OnLoad(EventArgs e)
+ {
+ base.OnLoad(e);
+ this.EnsureChildControls();
+
+ if (!this.Page.IsPostBack && this.data.Value != null)
+ {
+ string value = this.data.Value.ToString();
+ List selectedValues = new List();
+
+ if (xmlHelper.CouldItBeXml(value))
+ {
+ // build selected values from XML fragment
+ foreach (XElement nodeXElement in XElement.Parse(value).Elements())
+ {
+ selectedValues.Add(nodeXElement.Value);
+ }
+ }
+ else
+ {
+ // Assume a CSV source
+ selectedValues = value.Split(',').ToList();
+ }
+
+ // Find checkboxes where values match the stored values and set to selected
+ ListItem checkBoxListItem = null;
+ foreach (string selectedValue in selectedValues)
+ {
+ checkBoxListItem = this.checkBoxList.Items.FindByValue(selectedValue);
+ if (checkBoxListItem != null)
+ {
+ checkBoxListItem.Selected = true;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Called by Umbraco when saving the node
+ ///
+ public void Save()
+ {
+ // Get all checked item values
+ IEnumerable selectedOptions = from ListItem item in this.checkBoxList.Items
+ where item.Selected
+ select item.Value;
+
+ if (this.options.UseXml)
+ {
+ string elementName = this.options.UseIds ? "nodeId" : "nodeName";
+
+ this.data.Value = new XElement("XPathCheckBoxList",
+ selectedOptions.Select(x => new XElement(elementName, x.ToString()))).ToString();
+ }
+ else
+ {
+ // Save the CSV
+ this.data.Value = string.Join(",", selectedOptions.ToArray());
+ }
+ }
+ }
+}
diff --git a/components/editorControls/XPathCheckBoxList/XPathCheckBoxListDataType.cs b/components/editorControls/XPathCheckBoxList/XPathCheckBoxListDataType.cs
new file mode 100644
index 0000000000..6862a3d242
--- /dev/null
+++ b/components/editorControls/XPathCheckBoxList/XPathCheckBoxListDataType.cs
@@ -0,0 +1,132 @@
+using System;
+using umbraco.cms.businesslogic.datatype;
+using umbraco.interfaces;
+
+namespace umbraco.editorControls.XPathCheckBoxList
+{
+ ///
+ /// This datatype will render a CheckBoxList where the options are defined by an XPath expression,
+ ///
+ public class XPathCheckBoxListDataType : umbraco.cms.businesslogic.datatype.BaseDataType, IDataType
+ {
+ ///
+ /// Field for the preValueEditor.
+ ///
+ private XPathCheckBoxListPreValueEditor preValueEditor;
+
+ ///
+ /// Field for the dataEditor.
+ ///
+ private IDataEditor dataEditor;
+
+ ///
+ /// Field for the data.
+ ///
+ private IData data;
+
+ ///
+ /// Field for the options.
+ ///
+ private XPathCheckBoxListOptions options;
+
+ ///
+ /// Gets the options.
+ ///
+ /// The options.
+ public XPathCheckBoxListOptions Options
+ {
+ get
+ {
+ if (this.options == null)
+ {
+ this.options = ((XPathCheckBoxListPreValueEditor)this.PrevalueEditor).Options;
+ }
+
+ return this.options;
+ }
+ }
+
+ ///
+ /// Gets the name of the data type.
+ ///
+ /// The name of the data type.
+ public override string DataTypeName
+ {
+ get
+ {
+ return "XPath CheckBoxList";
+ }
+ }
+
+ ///
+ /// Gets the id.
+ ///
+ /// The id.
+ public override Guid Id
+ {
+ get
+ {
+ return new Guid(DataTypeGuids.XPathCheckBoxListId);
+ }
+ }
+
+ ///
+ /// Lazy load the associated PreValueEditor instance,
+ /// this is constructed supplying 'this'
+ ///
+ public override IDataPrevalue PrevalueEditor
+ {
+ get
+ {
+ if (this.preValueEditor == null)
+ {
+ this.preValueEditor = new XPathCheckBoxListPreValueEditor(this);
+ }
+
+ return this.preValueEditor;
+ }
+ }
+
+ ///
+ /// Lazy load the assocated DataEditor,
+ /// this is constructed supplying the data value stored by the PreValueEditor, and also the configuration settings of the PreValueEditor
+ ///
+ public override IDataEditor DataEditor
+ {
+ get
+ {
+ if (this.dataEditor == null)
+ {
+ this.dataEditor = new XPathCheckBoxListDataEditor(this.Data, this.Options);
+ }
+
+ return this.dataEditor;
+ }
+ }
+
+ ///
+ /// Lazy load an empty DefaultData object, this is used to pass data between the PreValueEditor and the DataEditor
+ ///
+ public override IData Data
+ {
+ get
+ {
+ if (this.data == null)
+ {
+ if (((XPathCheckBoxListPreValueEditor)this.PrevalueEditor).Options.UseXml)
+ {
+ // Storing an Xml fragment
+ this.data = new XmlData(this);
+ }
+ else
+ {
+ // Storing a Csv
+ this.data = new umbraco.cms.businesslogic.datatype.DefaultData(this);
+ }
+ }
+
+ return this.data;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/components/editorControls/XPathCheckBoxList/XPathCheckBoxListOptions.cs b/components/editorControls/XPathCheckBoxList/XPathCheckBoxListOptions.cs
new file mode 100644
index 0000000000..7c8905d912
--- /dev/null
+++ b/components/editorControls/XPathCheckBoxList/XPathCheckBoxListOptions.cs
@@ -0,0 +1,45 @@
+using System.ComponentModel;
+using umbraco.cms.businesslogic.datatype;
+
+namespace umbraco.editorControls.XPathCheckBoxList
+{
+ ///
+ /// Data Class, used to store the configuration options for the XPathCheckBoxListPreValueEditor
+ ///
+ public class XPathCheckBoxListOptions : AbstractOptions
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public XPathCheckBoxListOptions()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// if set to true [load defaults].
+ public XPathCheckBoxListOptions(bool loadDefaults)
+ : base(loadDefaults)
+ {
+ }
+
+ ///
+ /// XPath string used to get Nodes to be used as CheckBox options in a CheckBoxList
+ ///
+ [DefaultValue("")]
+ public string XPath { get; set; }
+
+ ///
+ /// Defaults to true, where the property value will be stored as an Xml Fragment, else if false, a Csv will be stored
+ ///
+ [DefaultValue(true)]
+ public bool UseXml { get; set; }
+
+ ///
+ /// Defaults to true, where property value stored is NodeIds, else if false, then value stored is the Node Names
+ ///
+ [DefaultValue(true)]
+ public bool UseIds { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/components/editorControls/XPathCheckBoxList/XPathCheckBoxListPreValueEditor.cs b/components/editorControls/XPathCheckBoxList/XPathCheckBoxListPreValueEditor.cs
new file mode 100644
index 0000000000..aacb9d4284
--- /dev/null
+++ b/components/editorControls/XPathCheckBoxList/XPathCheckBoxListPreValueEditor.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Web.UI;
+using System.Web.UI.WebControls;
+using System.Xml.XPath;
+// using uComponents.DataTypes.Shared.Extensions;
+// using uComponents.DataTypes.Shared.PrevalueEditors;
+using umbraco.cms.businesslogic.datatype;
+
+namespace umbraco.editorControls.XPathCheckBoxList
+{
+ ///
+ /// This PreValueEditor will require an XPath expression to define the nodes to pick as CheckBox options,
+ /// TODO: [HR] min / max selections ?
+ /// Uses the shared JsonPreValueEditor as nice way of lightweight serializing a config data class object into a single DB field
+ ///
+ class XPathCheckBoxListPreValueEditor : AbstractJsonPrevalueEditor
+ {
+ ///
+ /// DropDownList for specifying the database column type.
+ ///
+ private DropDownList dbTypeDropDownList = new DropDownList();
+
+ ///
+ /// TextBox control to get the XPath expression
+ ///
+ private TextBox xPathTextBox = new TextBox();
+
+ ///
+ /// RequiredFieldValidator to ensure an XPath expression has been entered
+ ///
+ private RequiredFieldValidator xPathRequiredFieldValidator = new RequiredFieldValidator();
+
+ ///
+ /// Server side validation of XPath expression, to ensure some nodes are returned
+ ///
+ private CustomValidator xPathCustomValidator = new CustomValidator();
+
+ ///
+ /// Store an Xml fragment or a Csv
+ ///
+ private RadioButtonList storageTypeRadioButtonList = new RadioButtonList() { RepeatDirection = RepeatDirection.Vertical, RepeatLayout = RepeatLayout.Flow };
+
+ ///
+ /// Select Node IDs or Node Names as the values to store
+ ///
+ private DropDownList valueTypeDropDownList = new DropDownList();
+
+ ///
+ /// Data object used to define the configuration status of this PreValueEditor
+ ///
+ private XPathCheckBoxListOptions options = null;
+
+ ///
+ /// Gets the options data object that represents the current state of this datatypes configuration
+ ///
+ internal XPathCheckBoxListOptions Options
+ {
+ get
+ {
+ if (this.options == null)
+ {
+ // Deserialize any stored settings for this PreValueEditor instance
+ this.options = this.GetPreValueOptions();
+
+ // If still null, ie, object couldn't be de-serialized from PreValue[0] string value
+ if (this.options == null)
+ {
+ // Create a new Options data object with the default values
+ this.options = new XPathCheckBoxListOptions();
+ }
+ }
+ return this.options;
+ }
+ }
+
+ ///
+ /// Initialize a new instance of XPathCheckBoxlistPreValueEditor
+ ///
+ /// XPathCheckBoxListDataType
+ public XPathCheckBoxListPreValueEditor(umbraco.cms.businesslogic.datatype.BaseDataType dataType)
+ : base(dataType)
+ {
+ }
+
+ ///
+ /// Creates all of the controls and assigns all of their properties
+ ///
+ protected override void CreateChildControls()
+ {
+ this.dbTypeDropDownList.ID = "dbTypeDropDownList";
+ this.dbTypeDropDownList.Items.Add(new ListItem(DBTypes.Nvarchar.ToString()));
+ this.dbTypeDropDownList.Items.Add(new ListItem(DBTypes.Ntext.ToString()));
+
+ this.xPathTextBox.ID = "xPathTextBox";
+ this.xPathTextBox.CssClass = "umbEditorTextField";
+
+ this.xPathRequiredFieldValidator.ControlToValidate = this.xPathTextBox.ID;
+ this.xPathRequiredFieldValidator.Display = ValidatorDisplay.Dynamic;
+ this.xPathRequiredFieldValidator.ErrorMessage = " XPath expression required";
+
+ this.xPathCustomValidator.ControlToValidate = this.xPathTextBox.ID;
+ this.xPathCustomValidator.Display = ValidatorDisplay.Dynamic;
+ this.xPathCustomValidator.ServerValidate += new ServerValidateEventHandler(XPathCustomValidator_ServerValidate);
+
+ this.storageTypeRadioButtonList.ID = "storageTypeRadioButtonList";
+ this.storageTypeRadioButtonList.Items.Add(new ListItem("Xml", bool.TrueString));
+ this.storageTypeRadioButtonList.Items.Add(new ListItem("Csv", bool.FalseString));
+
+ this.valueTypeDropDownList.ID = "valueTypeDropDownList";
+ this.valueTypeDropDownList.Items.Add(new ListItem("Node Ids", bool.TrueString));
+ this.valueTypeDropDownList.Items.Add(new ListItem("Node Names", bool.FalseString));
+
+ this.Controls.AddPrevalueControls(
+ this.dbTypeDropDownList,
+ this.xPathTextBox,
+ this.xPathRequiredFieldValidator,
+ this.xPathCustomValidator,
+ this.storageTypeRadioButtonList,
+ this.valueTypeDropDownList);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ protected override void OnLoad(EventArgs e)
+ {
+ base.OnLoad(e);
+
+ // Read in stored configuration values
+ this.dbTypeDropDownList.SelectedValue = base.m_DataType.DBType.ToString();
+ this.xPathTextBox.Text = this.Options.XPath;
+ this.storageTypeRadioButtonList.SelectedValue = this.Options.UseXml.ToString();
+ this.valueTypeDropDownList.SelectedValue = this.Options.UseIds.ToString();
+ }
+
+ ///
+ /// Will run the entered XPath expression to ensure it's valid
+ ///
+ /// xPathCustomValidator
+ ///
+ private void XPathCustomValidator_ServerValidate(object source, ServerValidateEventArgs args)
+ {
+ string xPath = args.Value;
+ bool isValid = false;
+
+ try
+ {
+ if (uQuery.GetNodesByXPath(xPath).Count >= 0)
+ {
+ isValid = true;
+ }
+ }
+ catch (XPathException)
+ {
+ this.xPathCustomValidator.ErrorMessage = " Syntax error in XPath expression";
+ }
+
+ args.IsValid = isValid;
+ }
+
+ ///
+ /// Saves the pre value data to Umbraco
+ ///
+ public override void Save()
+ {
+ if (this.Page.IsValid)
+ {
+ base.m_DataType.DBType = (umbraco.cms.businesslogic.datatype.DBTypes)Enum.Parse(typeof(umbraco.cms.businesslogic.datatype.DBTypes), this.dbTypeDropDownList.SelectedValue, true);
+
+ this.Options.XPath = this.xPathTextBox.Text;
+ this.Options.UseXml = bool.Parse(this.storageTypeRadioButtonList.SelectedValue);
+ this.Options.UseIds = bool.Parse(this.valueTypeDropDownList.SelectedValue);
+
+ this.SaveAsJson(this.Options); // Serialize to Umbraco database field
+ }
+ }
+
+ ///
+ /// Replaces the base class writer and instead uses the shared uComponents extension method, to inject consistant markup
+ ///
+ ///
+ protected override void RenderContents(HtmlTextWriter writer)
+ {
+ writer.AddPrevalueRow("Database Type", this.dbTypeDropDownList);
+ writer.AddPrevalueRow("XPath Expression", this.xPathTextBox, this.xPathRequiredFieldValidator, this.xPathCustomValidator);
+ writer.AddPrevalueRow("Storage Type", this.storageTypeRadioButtonList);
+ writer.AddPrevalueRow("Values", this.valueTypeDropDownList);
+ }
+ }
+}