From bb8d17b7413803b21552945fa27a3ab7dce5390a Mon Sep 17 00:00:00 2001 From: leekelleher Date: Sat, 28 Apr 2012 13:51:43 -0100 Subject: [PATCH] uComponents: Added XPathCheckBoxList to the core --- .../XPathCheckBoxListDataEditor.cs | 159 +++++++++++++++ .../XPathCheckBoxListDataType.cs | 132 ++++++++++++ .../XPathCheckBoxListOptions.cs | 45 +++++ .../XPathCheckBoxListPreValueEditor.cs | 191 ++++++++++++++++++ 4 files changed, 527 insertions(+) create mode 100644 components/editorControls/XPathCheckBoxList/XPathCheckBoxListDataEditor.cs create mode 100644 components/editorControls/XPathCheckBoxList/XPathCheckBoxListDataType.cs create mode 100644 components/editorControls/XPathCheckBoxList/XPathCheckBoxListOptions.cs create mode 100644 components/editorControls/XPathCheckBoxList/XPathCheckBoxListPreValueEditor.cs 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); + } + } +}