using System;
using System.Collections;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using umbraco.cms.businesslogic.datatype;
using umbraco.interfaces;
using umbraco.uicontrols.TreePicker;
namespace umbraco.editorControls.MultiNodeTreePicker
{
///
/// The pre-value editor for the multi node tree picker.
///
public class MNTP_PrevalueEditor : Control, IDataPrevalue
{
private readonly umbraco.cms.businesslogic.datatype.BaseDataType m_DataType;
private static readonly object Locker = new object();
private SortedList m_PreValues = null;
#region Public properties
///
/// The chosen tree type to render
///
public string SelectedTreeType
{
get
{
return GetPreValue(PropertyIndex.TreeType, x => x.Value, "content");
}
}
///
/// An xpath filter to disable nodes to be selectable
///
public string XPathFilter
{
get
{
return GetPreValue(PropertyIndex.XPathFilter, x => x.Value, string.Empty);
}
}
///
/// The number of nodes this picker will support picking
///
public int MaxNodeCount
{
get
{
return GetPreValue(PropertyIndex.MaxNodeCount, x =>
{
var max = -1;
return int.TryParse(x.Value, out max) ? max : -1;
}, -1);
}
}
///
/// The minimum number of nodes this picker will support picking
///
public int MinNodeCount
{
get
{
return GetPreValue(PropertyIndex.MinNodeCount, x =>
{
var min = 0;
return int.TryParse(x.Value, out min) ? min : 0;
}, 0);
}
}
///
/// A boolean value indicating whether or not to show the informational tool tips
///
public bool ShowToolTip
{
get
{
return GetPreValue(PropertyIndex.ShowToolTip, x =>
{
var show = true;
return bool.TryParse(x.Value, out show) ? show : true;
}, true);
}
}
///
/// Value to check if the data should be stored as CSV or XML
///
public bool StoreAsCommaDelimited
{
get
{
return GetPreValue(PropertyIndex.StoreAsCommaDelimited, x =>
{
var asComma = 0;
return int.TryParse(x.Value, out asComma) ? asComma == 1 : false;
}, false);
}
}
///
/// The XPath expression used when the node type selection is Xpath
///
public string StartNodeXPathExpression
{
get
{
return GetPreValue(PropertyIndex.StartNodeXPathExpression, x => x.Value, string.Empty);
}
}
///
/// The type of xpath expression used for the xpathexpressiontext if using an xpath node selection
///
public XPathExpressionType StartNodeXPathExpressionType
{
get
{
return GetPreValue(PropertyIndex.StartNodeXPathExpressionType,
x => (XPathExpressionType)Enum.ToObject(typeof(XPathExpressionType), int.Parse(x.Value)),
XPathExpressionType.Global);
}
}
///
/// The type of selection type to use for the start node
///
public NodeSelectionType StartNodeSelectionType
{
get
{
return GetPreValue(PropertyIndex.StartNodeSelectionType,
x => (NodeSelectionType)Enum.ToObject(typeof(NodeSelectionType), int.Parse(x.Value)),
NodeSelectionType.Picker);
}
}
///
/// The type of xpath filter applied
///
public XPathFilterType XPathFilterMatchType
{
get
{
return GetPreValue(PropertyIndex.XPathFilterType,
x => (XPathFilterType)Enum.ToObject(typeof(XPathFilterType), int.Parse(x.Value)),
XPathFilterType.Disable);
}
}
///
/// The start node id used when the node selection is a picker
///
public int StartNodeId
{
get
{
return GetPreValue(PropertyIndex.StartNodeId, x =>
{
var max = 0;
return int.TryParse(x.Value, out max) ? max : uQuery.RootNodeId;
}, uQuery.RootNodeId);
}
}
///
/// A boolean value indicating whether or not to show the thumbnails for media
///
public bool ShowThumbnailsForMedia
{
get
{
return GetPreValue(PropertyIndex.ShowThumbnails, x =>
{
var show = true;
bool.TryParse(x.Value, out show);
return show;
}, true);
}
}
///
/// Returns the control height in pixels
///
public int ControlHeight
{
get
{
return GetPreValue(PropertyIndex.ControlHeight, x =>
{
var max = 0;
return int.TryParse(x.Value, out max) ? max : 200;
}, 200);
}
}
#endregion
#region Protected properties
///
/// The control height text box
///
protected TextBox ControlHeightTextBox;
///
///
///
protected RadioButtonList StartNodeSelectionTypeRadioButtons;
///
/// The start node id content picker
///
protected SimpleContentPicker StartContentNodeIdPicker;
///
/// The start node id media picker
///
protected SimpleMediaPicker StartMediaNodeIdPicker;
///
/// XPath expression type radio button list
///
protected RadioButtonList StartNodeXPathExpressionTypeRadioButtons;
///
///
///
protected TextBox StartNodeXPathExpressionTextBox;
///
///
///
protected CheckBox ShowThumbnailsForMediaCheckBox;
///
///
///
protected DropDownList TreeTypeDropDown;
///
///
///
protected TextBox XPathFilterTextBox;
///
/// Text box for maximum amount of items
///
protected TextBox MaxItemsTextBox;
///
/// Text box for minimum amount of items
///
protected TextBox MinItemsTextBox;
///
/// Minimum items validator
///
protected RegularExpressionValidator NumbersMinItemsValidator;
///
/// Validator for validating relative xpath expressions
///
protected CustomValidator RelativeXpathValidator;
///
///
///
protected RegularExpressionValidator NumbersMaxItemsValidator;
///
///
///
protected RegularExpressionValidator ControlHeightValidatator;
///
///
///
protected CheckBox ShowItemInfoTooltipCheckBox;
///
///
///
protected RadioButtonList StoreAsCommaDelimitedRadioButtons;
///
///
///
protected RadioButtonList XPathFilterTypeRadioButtons;
#endregion
///
/// Initializes a new instance of the class.
///
/// Type of the data.
public MNTP_PrevalueEditor(umbraco.cms.businesslogic.datatype.BaseDataType dataType)
{
this.m_DataType = dataType;
}
///
/// Override on init to ensure child controls
///
/// An object that contains the event data.
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
this.EnsureChildControls();
this.RegisterEmbeddedClientResource("umbraco.editorControls.PrevalueEditor.css", ClientDependencyType.Css);
}
///
/// Ensures the css to render this control is included.
/// Binds the saved value to the drop down.
///
///
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
//add the css required
//// this.AddCssMNTPClientDependencies();
//let view state handle the rest
if (!Page.IsPostBack)
{
TreeTypeDropDown.SelectedValue = SelectedTreeType;
XPathFilterTextBox.Text = XPathFilter;
MaxItemsTextBox.Text = MaxNodeCount.ToString();
ShowItemInfoTooltipCheckBox.Checked = ShowToolTip;
StoreAsCommaDelimitedRadioButtons.SelectedIndex = StoreAsCommaDelimited ? 1 : 0;
XPathFilterTypeRadioButtons.SelectedIndex = XPathFilterMatchType == XPathFilterType.Disable ? 0 : 1;
ShowThumbnailsForMediaCheckBox.Checked = ShowThumbnailsForMedia;
StartNodeXPathExpressionTextBox.Text = StartNodeXPathExpression;
StartNodeXPathExpressionTypeRadioButtons.SelectedIndex = StartNodeXPathExpressionType == XPathExpressionType.Global ? 0 : 1;
StartNodeSelectionTypeRadioButtons.SelectedIndex = StartNodeSelectionType == NodeSelectionType.Picker ? 0 : 1;
switch (SelectedTreeType.ToLower())
{
case "content":
StartContentNodeIdPicker.Value = StartNodeId.ToString();
break;
case "media":
default:
StartMediaNodeIdPicker.Value = StartNodeId.ToString();
break;
}
ControlHeightTextBox.Text = ControlHeight.ToString();
MinItemsTextBox.Text = MinNodeCount.ToString();
}
}
///
/// Creates child controls for this control
///
protected override void CreateChildControls()
{
base.CreateChildControls();
TreeTypeDropDown = new DropDownList { ID = "TreeTypeList" };
TreeTypeDropDown.Items.Add(new ListItem("Content", "content"));
TreeTypeDropDown.Items.Add(new ListItem("Media", "media"));
TreeTypeDropDown.AutoPostBack = true;
AddPreValueRow(MNTPResources.Lbl_SelectTreeType, "", TreeTypeDropDown);
StartNodeSelectionTypeRadioButtons = new RadioButtonList { ID = "NodeSelectionTypeRadioButtons" };
StartNodeSelectionTypeRadioButtons.Items.Add(MNTPResources.Item_NodeSelectionType_Picker);
StartNodeSelectionTypeRadioButtons.Items.Add(new ListItem(MNTPResources.Item_NodeSelectionType_XPath, MNTPResources.Item_NodeSelectionType_XPath.Replace(" ", "")));
StartNodeSelectionTypeRadioButtons.RepeatDirection = RepeatDirection.Horizontal;
StartNodeSelectionTypeRadioButtons.AutoPostBack = true;
AddPreValueRow(MNTPResources.Lbl_NodeSelectionType, MNTPResources.Desc_NodeSelectionType, StartNodeSelectionTypeRadioButtons);
StartContentNodeIdPicker = new SimpleContentPicker { ID = "StartNodeIdTextBox" };
AddPreValueRow(MNTPResources.Lbl_StartNodeId, MNTPResources.Desc_StartNodeId, StartContentNodeIdPicker);
StartMediaNodeIdPicker = new SimpleMediaPicker { ID = "StartMediaNodeIdPicker" };
AddPreValueRow(MNTPResources.Lbl_StartNodeId, MNTPResources.Desc_StartNodeId, StartMediaNodeIdPicker);
ShowThumbnailsForMediaCheckBox = new CheckBox { ID = "ShowThumbnailsForMedia" };
AddPreValueRow(MNTPResources.Lbl_ShowThumbnails, MNTPResources.Desc_ShowThumbnails, ShowThumbnailsForMediaCheckBox);
StartNodeXPathExpressionTypeRadioButtons = new RadioButtonList { ID = "XPathExpressionTypeRadioButtons" };
StartNodeXPathExpressionTypeRadioButtons.Items.Add(MNTPResources.Item_XPathExpressionType_Global);
StartNodeXPathExpressionTypeRadioButtons.Items.Add(new ListItem(MNTPResources.Item_XPathExpressionType_CurrentNode, MNTPResources.Item_XPathExpressionType_CurrentNode.Replace(" ", "")));
StartNodeXPathExpressionTypeRadioButtons.RepeatDirection = RepeatDirection.Horizontal;
AddPreValueRow(MNTPResources.Lbl_XPathExpressionType, MNTPResources.Desc_XPathExpressionType, StartNodeXPathExpressionTypeRadioButtons);
StartNodeXPathExpressionTextBox = new TextBox { ID = "XPathExpressionTextBox", Width = Unit.Pixel(400) };
RelativeXpathValidator = new CustomValidator()
{
ID = "RelativeXpathValidator",
ControlToValidate = "XPathExpressionTextBox",
ErrorMessage = MNTPResources.Val_RelativeXpath,
CssClass = "validator"
};
RelativeXpathValidator.ServerValidate += new ServerValidateEventHandler(RelativeXpathValidator_ServerValidate);
AddPreValueRow(MNTPResources.Lbl_XPathExpression, MNTPResources.Desc_XPathExpression, StartNodeXPathExpressionTextBox, RelativeXpathValidator);
XPathFilterTypeRadioButtons = new RadioButtonList { ID = "XPathMatchTypeRadioButtons" };
XPathFilterTypeRadioButtons.Items.Add(MNTPResources.Item_XPathMatchType_Disable);
XPathFilterTypeRadioButtons.Items.Add(MNTPResources.Item_XPathMatchType_Enable);
XPathFilterTypeRadioButtons.RepeatDirection = RepeatDirection.Horizontal;
AddPreValueRow(MNTPResources.Lbl_XPathFilterType, MNTPResources.Desc_XPathFilterType, XPathFilterTypeRadioButtons);
XPathFilterTextBox = new TextBox { ID = "XPathFilter", Width = Unit.Pixel(400) };
AddPreValueRow(MNTPResources.Lbl_XPathFilter, MNTPResources.Desc_XPathFilter, XPathFilterTextBox);
MaxItemsTextBox = new TextBox { ID = "MaxItemsCount", Width = Unit.Pixel(50) };
NumbersMaxItemsValidator = new RegularExpressionValidator
{
ID = "NumbersMaxItemsValidator",
ControlToValidate = "MaxItemsCount",
CssClass = "validator",
ErrorMessage = MNTPResources.Val_MaxItemsMsg,
ValidationExpression = @"^-{0,1}\d*\.{0,1}\d+$"
};
AddPreValueRow(MNTPResources.Lbl_MaxItemsAllowed, MNTPResources.Desc_MaxItemsAllowed, MaxItemsTextBox, NumbersMaxItemsValidator);
MinItemsTextBox = new TextBox { ID = "MinItemsCount", Width = Unit.Pixel(50) };
NumbersMinItemsValidator = new RegularExpressionValidator
{
ID = "NumbersMinItemsValidator",
ControlToValidate = "MinItemsCount",
CssClass = "validator",
ErrorMessage = MNTPResources.Val_MinItemsMsg,
ValidationExpression = @"^\d{1,3}$"
};
AddPreValueRow(MNTPResources.Lbl_MinItemsAllowed, MNTPResources.Desc_MinItemsAllowed, MinItemsTextBox, NumbersMinItemsValidator);
ShowItemInfoTooltipCheckBox = new CheckBox { ID = "ShowItemInfoTooltipCheckBox" };
AddPreValueRow(MNTPResources.Lbl_ShowItemInfoTooltipCheckBox, MNTPResources.Desc_ShowTooltips, ShowItemInfoTooltipCheckBox);
StoreAsCommaDelimitedRadioButtons = new RadioButtonList { ID = "StoreAsCommaDelimitedRadioButtons" };
StoreAsCommaDelimitedRadioButtons.Items.Add("XML");
StoreAsCommaDelimitedRadioButtons.Items.Add("CSV");
StoreAsCommaDelimitedRadioButtons.RepeatDirection = RepeatDirection.Horizontal;
AddPreValueRow(MNTPResources.Lbl_StoreAsComma, MNTPResources.Desc_StoreAsComma, StoreAsCommaDelimitedRadioButtons);
ControlHeightTextBox = new TextBox() { ID = "ControlHeightTextBox", Width = Unit.Pixel(50) };
ControlHeightValidatator = new RegularExpressionValidator
{
ID = "ControlHeightValidator",
ControlToValidate = "ControlHeightTextBox",
CssClass = "validator",
ErrorMessage = MNTPResources.Val_ControlHeightMsg,
ValidationExpression = @"^\d{1,3}$"
};
AddPreValueRow(MNTPResources.Lbl_ControlHeight, "", ControlHeightTextBox, ControlHeightValidatator);
}
void RelativeXpathValidator_ServerValidate(object source, ServerValidateEventArgs args)
{
args.IsValid = true;
if (TreeTypeDropDown.SelectedIndex == 0 && StartNodeXPathExpressionTypeRadioButtons.SelectedIndex == 1 && StartNodeXPathExpressionTextBox.Text.StartsWith("/"))
{
args.IsValid = false;
}
}
///
/// Helper method to add a server side pre value row
///
///
///
///
///
/// Using server side syntax because of the post backs and because i don't want to manage the view state manually
///
private void AddPreValueRow(string lbl, string description, params Control[] ctl)
{
var div = new HtmlGenericControl("div");
div.Attributes.Add("class", "row clearfix");
var label = new HtmlGenericControl("div");
label.Attributes.Add("class", "label");
var span = new HtmlGenericControl("span");
span.InnerText = lbl;
label.Controls.Add(span);
var field = new HtmlGenericControl("div");
field.Attributes.Add("class", "field");
foreach (var c in ctl)
{
field.Controls.Add(c);
}
div.Controls.Add(label);
div.Controls.Add(field);
if (!string.IsNullOrEmpty(description))
{
var desc = new HtmlGenericControl("div");
var descSpan = new HtmlGenericControl("span");
descSpan.InnerHtml = description;
desc.Attributes.Add("class", "description");
desc.Controls.Add(descSpan);
div.Controls.Add(desc);
}
this.Controls.Add(div);
}
///
/// Hides/Shows controls based on the selection of other controls
///
///
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
//we can only show the node selection type based on content
if (TreeTypeDropDown.SelectedIndex == 0)
{
this.StartNodeSelectionTypeRadioButtons.Parent.Parent.Visible = true;
}
else
{
this.StartNodeSelectionTypeRadioButtons.Parent.Parent.Visible = false;
}
//if media is selected, or the node type selection is a picker type
if (TreeTypeDropDown.SelectedIndex == 1 || StartNodeSelectionTypeRadioButtons.SelectedIndex == 0)
{
StartNodeXPathExpressionTypeRadioButtons.Parent.Parent.Visible = false;
StartNodeXPathExpressionTextBox.Parent.Parent.Visible = false;
switch (TreeTypeDropDown.SelectedIndex)
{
case 0:
//content selected
StartContentNodeIdPicker.Parent.Parent.Visible = true;
StartMediaNodeIdPicker.Parent.Parent.Visible = false;
ShowThumbnailsForMediaCheckBox.Parent.Parent.Visible = false;
break;
case 1:
default:
//media selected:
StartContentNodeIdPicker.Parent.Parent.Visible = false;
StartMediaNodeIdPicker.Parent.Parent.Visible = true;
ShowThumbnailsForMediaCheckBox.Parent.Parent.Visible = true;
break;
}
}
else
{
//since it's an xpath expression, not node picker, hide all node pickers
StartContentNodeIdPicker.Parent.Parent.Visible = false;
StartMediaNodeIdPicker.Parent.Parent.Visible = false;
ShowThumbnailsForMediaCheckBox.Parent.Parent.Visible = false;
StartNodeXPathExpressionTypeRadioButtons.Parent.Parent.Visible = true;
StartNodeXPathExpressionTextBox.Parent.Parent.Visible = true;
}
}
///
/// render our own custom markup
///
/// The object that receives the server control content.
protected override void Render(HtmlTextWriter writer)
{
writer.AddAttribute(HtmlTextWriterAttribute.Class, "PrevalueEditor");
writer.RenderBeginTag(HtmlTextWriterTag.Div); //start 'PrevalueEditor'
base.Render(writer);
writer.RenderEndTag(); //end 'PrevalueEditor'
}
///
/// Lazy loads the prevalues for this data type
///
///
private SortedList GetPreValues()
{
if (m_PreValues == null)
{
m_PreValues = PreValues.GetPreValues(m_DataType.DataTypeDefinitionId);
}
return m_PreValues;
}
#region IDataPrevalue Members
///
/// returns this as it's own editor
///
public Control Editor
{
get { return this; }
}
///
/// Saves data to Umbraco
///
public void Save()
{
if (!Page.IsValid) { return; }
//it will always be text since people may save a huge amount of selected nodes and serializing to xml could be large.
m_DataType.DBType = umbraco.cms.businesslogic.datatype.DBTypes.Ntext;
//need to lock this operation since multiple inserts are happening and if 2 threads reach here at the same time, there
//could be issues.
lock (Locker)
{
var vals = GetPreValues();
//store the tree type
SavePreValue(PropertyIndex.TreeType, TreeTypeDropDown.SelectedValue, vals);
//store the xpath
SavePreValue(PropertyIndex.XPathFilter, XPathFilterTextBox.Text, vals);
//store the max node count
SavePreValue(PropertyIndex.MaxNodeCount, string.IsNullOrEmpty(MaxItemsTextBox.Text) ? "-1" : MaxItemsTextBox.Text, vals);
//store the 'show tooltips'
SavePreValue(PropertyIndex.ShowToolTip, ShowItemInfoTooltipCheckBox.Checked.ToString(), vals);
//store the 'as comma'
SavePreValue(PropertyIndex.StoreAsCommaDelimited, StoreAsCommaDelimitedRadioButtons.SelectedIndex.ToString(), vals);
//the xpath filter type
SavePreValue(PropertyIndex.XPathFilterType, XPathFilterTypeRadioButtons.SelectedIndex.ToString(), vals);
//based on the media type selected, we need to get the correct start node id
//from the correct control.
var startNodeId = -1;
switch (TreeTypeDropDown.SelectedIndex)
{
case 0:
int.TryParse(StartContentNodeIdPicker.Value, out startNodeId);
break;
case 1:
default:
int.TryParse(StartMediaNodeIdPicker.Value, out startNodeId);
break;
}
//store the start node id
SavePreValue(PropertyIndex.StartNodeId, startNodeId.ToString(), vals);
//store the 'show thumbnails'
SavePreValue(PropertyIndex.ShowThumbnails, ShowThumbnailsForMediaCheckBox.Checked.ToString(), vals);
//store the 'node selection type'
SavePreValue(PropertyIndex.StartNodeSelectionType, StartNodeSelectionTypeRadioButtons.SelectedIndex.ToString(), vals);
//store the 'xpath expression type'
SavePreValue(PropertyIndex.StartNodeXPathExpressionType, StartNodeXPathExpressionTypeRadioButtons.SelectedIndex.ToString(), vals);
//save the 'xpath expression'
SavePreValue(PropertyIndex.StartNodeXPathExpression, StartNodeXPathExpressionTextBox.Text, vals);
//save the control height
SavePreValue(PropertyIndex.ControlHeight, ControlHeightTextBox.Text, vals);
//save the min amount
SavePreValue(PropertyIndex.MinNodeCount, MinItemsTextBox.Text, vals);
}
//once everything is saved, clear the cookie vals
MNTP_DataType.ClearCookiePersistence();
}
///
/// Used to determine the index number of where the property is saved in the pre values repository
///
private enum PropertyIndex
{
TreeType,
XPathFilter,
MaxNodeCount,
ShowToolTip,
StoreAsCommaDelimited,
XPathFilterType,
StartNodeId,
ShowThumbnails,
StartNodeSelectionType,
StartNodeXPathExpressionType,
StartNodeXPathExpression,
ControlHeight,
MinNodeCount
}
///
/// Helper method to save/create pre value values in the db
///
///
///
///
private void SavePreValue(PropertyIndex propIndex, string value, SortedList currentVals)
{
var index = (int)propIndex;
if (currentVals.Count >= ((int)propIndex + 1))
{
//update
((PreValue)currentVals[index]).Value = value;
((PreValue)currentVals[index]).Save();
}
else
{
//insert
PreValue.MakeNew(m_DataType.DataTypeDefinitionId, value);
}
}
///
/// Generic method to return a strongly typed object from the pre value bucket
///
///
///
///
///
///
private T GetPreValue(PropertyIndex index, Func output, T defaultVal)
{
var vals = GetPreValues();
return vals.Count >= (int)index + 1 ? output((PreValue)vals[(int)index]) : defaultVal;
}
#endregion
}
}