uComponents: Added XPathCheckBoxList to the core

This commit is contained in:
leekelleher
2012-04-28 13:51:43 -01:00
parent e66c7188e2
commit bb8d17b741
4 changed files with 527 additions and 0 deletions

View File

@@ -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
{
/// <summary>
/// Renders a CheckBoxList using with option nodes obtained by an XPath expression
/// </summary>
public class XPathCheckBoxListDataEditor : CompositeControl, IDataEditor
{
/// <summary>
/// Field for the data.
/// </summary>
private IData data;
/// <summary>
/// Field for the options.
/// </summary>
private XPathCheckBoxListOptions options;
/// <summary>
/// Field for the checkbox list.
/// </summary>
private CheckBoxList checkBoxList = new CheckBoxList();
/// <summary>
/// Gets a value indicating whether [treat as rich text editor].
/// </summary>
/// <value>
/// <c>true</c> if [treat as rich text editor]; otherwise, <c>false</c>.
/// </value>
public virtual bool TreatAsRichTextEditor
{
get
{
return false;
}
}
/// <summary>
/// Gets a value indicating whether [show label].
/// </summary>
/// <value><c>true</c> if [show label]; otherwise, <c>false</c>.</value>
public virtual bool ShowLabel
{
get
{
return true;
}
}
/// <summary>
/// Gets the editor.
/// </summary>
/// <value>The editor.</value>
public Control Editor
{
get
{
return this;
}
}
/// <summary>
/// Initializes a new instance of XPathCheckBoxListDataEditor
/// </summary>
/// <param name="data"></param>
/// <param name="options"></param>
internal XPathCheckBoxListDataEditor(IData data, XPathCheckBoxListOptions options)
{
this.data = data;
this.options = options;
}
/// <summary>
/// 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.
/// </summary>
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);
}
/// <summary>
/// Raises the <see cref="E:System.Web.UI.Control.Load"/> event.
/// </summary>
/// <param name="e">The <see cref="T:System.EventArgs"/> object that contains the event data.</param>
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<string> selectedValues = new List<string>();
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;
}
}
}
}
/// <summary>
/// Called by Umbraco when saving the node
/// </summary>
public void Save()
{
// Get all checked item values
IEnumerable<string> 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());
}
}
}
}

View File

@@ -0,0 +1,132 @@
using System;
using umbraco.cms.businesslogic.datatype;
using umbraco.interfaces;
namespace umbraco.editorControls.XPathCheckBoxList
{
/// <summary>
/// This datatype will render a CheckBoxList where the options are defined by an XPath expression,
/// </summary>
public class XPathCheckBoxListDataType : umbraco.cms.businesslogic.datatype.BaseDataType, IDataType
{
/// <summary>
/// Field for the preValueEditor.
/// </summary>
private XPathCheckBoxListPreValueEditor preValueEditor;
/// <summary>
/// Field for the dataEditor.
/// </summary>
private IDataEditor dataEditor;
/// <summary>
/// Field for the data.
/// </summary>
private IData data;
/// <summary>
/// Field for the options.
/// </summary>
private XPathCheckBoxListOptions options;
/// <summary>
/// Gets the options.
/// </summary>
/// <value>The options.</value>
public XPathCheckBoxListOptions Options
{
get
{
if (this.options == null)
{
this.options = ((XPathCheckBoxListPreValueEditor)this.PrevalueEditor).Options;
}
return this.options;
}
}
/// <summary>
/// Gets the name of the data type.
/// </summary>
/// <value>The name of the data type.</value>
public override string DataTypeName
{
get
{
return "XPath CheckBoxList";
}
}
/// <summary>
/// Gets the id.
/// </summary>
/// <value>The id.</value>
public override Guid Id
{
get
{
return new Guid(DataTypeGuids.XPathCheckBoxListId);
}
}
/// <summary>
/// Lazy load the associated PreValueEditor instance,
/// this is constructed supplying 'this'
/// </summary>
public override IDataPrevalue PrevalueEditor
{
get
{
if (this.preValueEditor == null)
{
this.preValueEditor = new XPathCheckBoxListPreValueEditor(this);
}
return this.preValueEditor;
}
}
/// <summary>
/// Lazy load the assocated DataEditor,
/// this is constructed supplying the data value stored by the PreValueEditor, and also the configuration settings of the PreValueEditor
/// </summary>
public override IDataEditor DataEditor
{
get
{
if (this.dataEditor == null)
{
this.dataEditor = new XPathCheckBoxListDataEditor(this.Data, this.Options);
}
return this.dataEditor;
}
}
/// <summary>
/// Lazy load an empty DefaultData object, this is used to pass data between the PreValueEditor and the DataEditor
/// </summary>
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;
}
}
}
}

View File

@@ -0,0 +1,45 @@
using System.ComponentModel;
using umbraco.cms.businesslogic.datatype;
namespace umbraco.editorControls.XPathCheckBoxList
{
/// <summary>
/// Data Class, used to store the configuration options for the XPathCheckBoxListPreValueEditor
/// </summary>
public class XPathCheckBoxListOptions : AbstractOptions
{
/// <summary>
/// Initializes a new instance of the <see cref="XPathCheckBoxListOptions"/> class.
/// </summary>
public XPathCheckBoxListOptions()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="XPathCheckBoxListOptions"/> class.
/// </summary>
/// <param name="loadDefaults">if set to <c>true</c> [load defaults].</param>
public XPathCheckBoxListOptions(bool loadDefaults)
: base(loadDefaults)
{
}
/// <summary>
/// XPath string used to get Nodes to be used as CheckBox options in a CheckBoxList
/// </summary>
[DefaultValue("")]
public string XPath { get; set; }
/// <summary>
/// Defaults to true, where the property value will be stored as an Xml Fragment, else if false, a Csv will be stored
/// </summary>
[DefaultValue(true)]
public bool UseXml { get; set; }
/// <summary>
/// Defaults to true, where property value stored is NodeIds, else if false, then value stored is the Node Names
/// </summary>
[DefaultValue(true)]
public bool UseIds { get; set; }
}
}

View File

@@ -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
{
/// <summary>
/// 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
/// </summary>
class XPathCheckBoxListPreValueEditor : AbstractJsonPrevalueEditor
{
/// <summary>
/// DropDownList for specifying the database column type.
/// </summary>
private DropDownList dbTypeDropDownList = new DropDownList();
/// <summary>
/// TextBox control to get the XPath expression
/// </summary>
private TextBox xPathTextBox = new TextBox();
/// <summary>
/// RequiredFieldValidator to ensure an XPath expression has been entered
/// </summary>
private RequiredFieldValidator xPathRequiredFieldValidator = new RequiredFieldValidator();
/// <summary>
/// Server side validation of XPath expression, to ensure some nodes are returned
/// </summary>
private CustomValidator xPathCustomValidator = new CustomValidator();
/// <summary>
/// Store an Xml fragment or a Csv
/// </summary>
private RadioButtonList storageTypeRadioButtonList = new RadioButtonList() { RepeatDirection = RepeatDirection.Vertical, RepeatLayout = RepeatLayout.Flow };
/// <summary>
/// Select Node IDs or Node Names as the values to store
/// </summary>
private DropDownList valueTypeDropDownList = new DropDownList();
/// <summary>
/// Data object used to define the configuration status of this PreValueEditor
/// </summary>
private XPathCheckBoxListOptions options = null;
/// <summary>
/// Gets the options data object that represents the current state of this datatypes configuration
/// </summary>
internal XPathCheckBoxListOptions Options
{
get
{
if (this.options == null)
{
// Deserialize any stored settings for this PreValueEditor instance
this.options = this.GetPreValueOptions<XPathCheckBoxListOptions>();
// 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;
}
}
/// <summary>
/// Initialize a new instance of XPathCheckBoxlistPreValueEditor
/// </summary>
/// <param name="dataType">XPathCheckBoxListDataType</param>
public XPathCheckBoxListPreValueEditor(umbraco.cms.businesslogic.datatype.BaseDataType dataType)
: base(dataType)
{
}
/// <summary>
/// Creates all of the controls and assigns all of their properties
/// </summary>
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);
}
/// <summary>
///
/// </summary>
/// <param name="e"></param>
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();
}
/// <summary>
/// Will run the entered XPath expression to ensure it's valid
/// </summary>
/// <param name="source">xPathCustomValidator</param>
/// <param name="args"></param>
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;
}
/// <summary>
/// Saves the pre value data to Umbraco
/// </summary>
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
}
}
/// <summary>
/// Replaces the base class writer and instead uses the shared uComponents extension method, to inject consistant markup
/// </summary>
/// <param name="writer"></param>
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);
}
}
}