2012-04-28 13:57:04 -01:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Web;
|
|
|
|
|
|
using System.Web.UI;
|
|
|
|
|
|
using System.Web.UI.HtmlControls;
|
|
|
|
|
|
using System.Web.UI.WebControls;
|
|
|
|
|
|
using System.Xml.Linq;
|
|
|
|
|
|
using ClientDependency.Core;
|
|
|
|
|
|
using umbraco.cms.presentation.Trees;
|
|
|
|
|
|
using umbraco.controls.Images;
|
|
|
|
|
|
using umbraco.controls.Tree;
|
|
|
|
|
|
using umbraco.IO;
|
|
|
|
|
|
|
|
|
|
|
|
namespace umbraco.editorControls.MultiNodeTreePicker
|
|
|
|
|
|
{
|
2012-04-29 20:16:31 -01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The user interface to display to the content editor
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[ClientDependency(ClientDependencyType.Javascript, "ui/jqueryui.js", "UmbracoClient")]
|
|
|
|
|
|
[ClientDependency(ClientDependencyType.Javascript, "ui/jquery.tooltip.min.js", "UmbracoClient")]
|
2012-04-28 13:57:04 -01:00
|
|
|
|
[ClientDependency(ClientDependencyType.Javascript, "controls/Images/ImageViewer.js", "UmbracoRoot")]
|
|
|
|
|
|
public class MNTP_DataEditor : Control, INamingContainer
|
|
|
|
|
|
{
|
|
|
|
|
|
#region Static Constructor
|
2012-04-29 20:16:31 -01:00
|
|
|
|
|
2012-04-28 13:57:04 -01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// This adds our filtered tree definition to the TreeDefinitionCollection at runtime
|
|
|
|
|
|
/// instead of having to declare it in the database
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
static MNTP_DataEditor()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (TreeDefinitionCollection.Instance
|
|
|
|
|
|
.Where(x => x.TreeType == typeof(FilteredContentTree))
|
|
|
|
|
|
.Count() == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
lock (m_Locker)
|
|
|
|
|
|
{
|
|
|
|
|
|
//double check lock....
|
|
|
|
|
|
|
|
|
|
|
|
if (TreeDefinitionCollection.Instance
|
|
|
|
|
|
.Where(x => x.TreeType == typeof(FilteredContentTree))
|
|
|
|
|
|
.Count() == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
//need to add our tree definitions to the collection.
|
|
|
|
|
|
|
|
|
|
|
|
//find the content tree to duplicate
|
|
|
|
|
|
var contentTree = TreeDefinitionCollection.Instance.Where(x => x.Tree.Alias.ToUpper() == "CONTENT").Single();
|
|
|
|
|
|
var filteredContentTree = new TreeDefinition(typeof(FilteredContentTree),
|
|
|
|
|
|
new umbraco.BusinessLogic.ApplicationTree(true, false, 0,
|
|
|
|
|
|
contentTree.Tree.ApplicationAlias,
|
|
|
|
|
|
"FilteredContentTree",
|
|
|
|
|
|
contentTree.Tree.Title,
|
|
|
|
|
|
contentTree.Tree.IconClosed,
|
|
|
|
|
|
contentTree.Tree.IconOpened,
|
|
|
|
|
|
"uComponents.Core",
|
|
|
|
|
|
"DataTypes.MultiNodeTreePicker.FilteredContentTree",
|
|
|
|
|
|
contentTree.Tree.Action),
|
|
|
|
|
|
contentTree.App);
|
|
|
|
|
|
|
|
|
|
|
|
//find the media tree to duplicate
|
|
|
|
|
|
var mediaTree = TreeDefinitionCollection.Instance.Where(x => x.Tree.Alias.ToUpper() == "MEDIA").Single();
|
|
|
|
|
|
var filteredMediaTree = new TreeDefinition(typeof(FilteredMediaTree),
|
|
|
|
|
|
new umbraco.BusinessLogic.ApplicationTree(true, false, 0,
|
|
|
|
|
|
mediaTree.Tree.ApplicationAlias,
|
|
|
|
|
|
"FilteredMediaTree",
|
|
|
|
|
|
contentTree.Tree.Title,
|
|
|
|
|
|
contentTree.Tree.IconClosed,
|
|
|
|
|
|
contentTree.Tree.IconOpened,
|
|
|
|
|
|
"uComponents.Core",
|
|
|
|
|
|
"DataTypes.MultiNodeTreePicker.FilteredMediaTree",
|
|
|
|
|
|
contentTree.Tree.Action),
|
|
|
|
|
|
contentTree.App);
|
|
|
|
|
|
|
|
|
|
|
|
//add it to the collection at runtime
|
|
|
|
|
|
TreeDefinitionCollection.Instance.Add(filteredContentTree);
|
|
|
|
|
|
TreeDefinitionCollection.Instance.Add(filteredMediaTree);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2012-04-29 20:16:31 -01:00
|
|
|
|
}
|
2012-04-28 13:57:04 -01:00
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2012-04-29 20:16:31 -01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Initializes a new instance of the <see cref="MNTP_DataEditor"/> class.
|
|
|
|
|
|
/// </summary>
|
2012-04-28 13:57:04 -01:00
|
|
|
|
public MNTP_DataEditor()
|
|
|
|
|
|
{
|
|
|
|
|
|
this.MediaTypesWithThumbnails = new string[] { "image" };
|
|
|
|
|
|
ShowThumbnailsForMedia = true;
|
|
|
|
|
|
TreeToRender = "content";
|
|
|
|
|
|
MaxNodeCount = -1;
|
|
|
|
|
|
MinNodeCount = 0;
|
2012-04-29 20:16:31 -01:00
|
|
|
|
StartNodeId = uQuery.RootNodeId;
|
2012-04-28 13:57:04 -01:00
|
|
|
|
ShowToolTips = true;
|
|
|
|
|
|
ControlHeight = 200;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#region Static members
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Used for locking code blocks
|
|
|
|
|
|
/// </summary>
|
2012-04-29 20:16:31 -01:00
|
|
|
|
private static readonly object m_Locker = new object();
|
2012-04-28 13:57:04 -01:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region Protected members
|
|
|
|
|
|
|
2012-04-29 20:16:31 -01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
///
|
|
|
|
|
|
/// </summary>
|
2012-04-28 13:57:04 -01:00
|
|
|
|
protected CustomValidator MinItemsValidator;
|
|
|
|
|
|
|
2012-04-29 20:16:31 -01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
///
|
|
|
|
|
|
/// </summary>
|
2012-04-28 13:57:04 -01:00
|
|
|
|
protected CustomTreeControl TreePickerControl;
|
|
|
|
|
|
|
2012-04-29 20:16:31 -01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
///
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
protected Repeater SelectedValues;
|
2012-04-28 13:57:04 -01:00
|
|
|
|
|
2012-04-29 20:16:31 -01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
///
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
protected HiddenField PickedValue;
|
2012-04-28 13:57:04 -01:00
|
|
|
|
|
2012-04-29 20:16:31 -01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
///
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
protected HtmlGenericControl RightColumn;
|
2012-04-28 13:57:04 -01:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region public Properties
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// gets/sets the value based on an array of IDs selected
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string[] SelectedIds
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
List<string> val = new List<string>();
|
|
|
|
|
|
var splitVals = PickedValue.Value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
|
|
//this will make sure only the node count specified is saved
|
|
|
|
|
|
//into umbraco, even if people try to hack the front end for whatever reason.
|
|
|
|
|
|
if (MaxNodeCount >= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (var i = 0; i < splitVals.Length; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (i < MaxNodeCount)
|
|
|
|
|
|
{
|
|
|
|
|
|
val.Add(splitVals[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
else break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
val = splitVals.ToList();
|
|
|
|
|
|
}
|
|
|
|
|
|
return val.ToArray();
|
|
|
|
|
|
}
|
|
|
|
|
|
set
|
|
|
|
|
|
{
|
|
|
|
|
|
XmlValue = ConvertToXDocument(value);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// get/set the value for the selected nodes in xml format
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public XDocument XmlValue
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
return ConvertToXDocument(SelectedIds);
|
|
|
|
|
|
}
|
|
|
|
|
|
set
|
|
|
|
|
|
{
|
|
|
|
|
|
if (value == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
SelectedValues.DataSource = null;
|
|
|
|
|
|
PickedValue.Value = "";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
//set the data source for the repeater and hidden field
|
|
|
|
|
|
var nodes = value.Descendants("nodeId");
|
|
|
|
|
|
SelectedValues.DataSource = nodes;
|
|
|
|
|
|
PickedValue.Value = string.Join(",", nodes.Select(x => x.Value).ToArray());
|
2012-04-29 20:16:31 -01:00
|
|
|
|
}
|
2012-04-28 13:57:04 -01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The property name being edited with the current data editor. This is used for the min items validation statement.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string PropertyName { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The tree type alias to render
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string TreeToRender { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// An xpath filter to match nodes that will be disabled from being clicked
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string XPathFilter { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The minimum amount of nodes that can be selected
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public int MinNodeCount { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The maximum amount of nodes that can be selected
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public int MaxNodeCount { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The start node id
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public int StartNodeId { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The start node selection type
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public NodeSelectionType StartNodeSelectionType { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The xpath expression type to select the start node when the StartNodeSelectionType is XPath
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public XPathExpressionType StartNodeXPathExpressionType { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The XPath expression to use to determine the start node when the StartNodeSelectionType is XPath
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string StartNodeXPathExpression { get; set; }
|
|
|
|
|
|
|
2012-04-29 20:16:31 -01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets or sets a value indicating whether [show tool tips].
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <value><c>true</c> if [show tool tips]; otherwise, <c>false</c>.</value>
|
|
|
|
|
|
/// <remarks>Shows/Hides the tooltip info bubble.</remarks>
|
2012-04-28 13:57:04 -01:00
|
|
|
|
public bool ShowToolTips { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The XPathFilterType to match
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public XPathFilterType XPathFilterMatchType { get; set; }
|
|
|
|
|
|
|
2012-04-29 20:16:31 -01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets or sets a value indicating whether [show thumbnails for media].
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <value>
|
|
|
|
|
|
/// <c>true</c> if [show thumbnails for media]; otherwise, <c>false</c>.
|
|
|
|
|
|
/// </value>
|
|
|
|
|
|
/// <remarks>Whether or not to show thumbnails for media</remarks>
|
2012-04-28 13:57:04 -01:00
|
|
|
|
public bool ShowThumbnailsForMedia { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// A list of media type names that can have thumbnails (i.e. 'image')
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string[] MediaTypesWithThumbnails { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// This is set by the data type and allows us to save a cookie value
|
|
|
|
|
|
/// for persistence for the data type.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public int DataTypeDefinitionId { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The height of the tree control box in pixels
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public int ControlHeight { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2012-04-29 20:16:31 -01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Initialize the control, make sure children are created
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="e">An <see cref="T:System.EventArgs"/> object that contains the event data.</param>
|
2012-04-28 13:57:04 -01:00
|
|
|
|
protected override void OnInit(EventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
base.OnInit(e);
|
|
|
|
|
|
|
|
|
|
|
|
EnsureChildControls();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Add the resources (sytles/scripts)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="e"></param>
|
|
|
|
|
|
protected override void OnLoad(EventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
base.OnLoad(e);
|
|
|
|
|
|
|
|
|
|
|
|
//add the js/css required
|
2012-04-29 20:16:31 -01:00
|
|
|
|
this.RegisterEmbeddedClientResource("umbraco.editorControls.MultiNodeTreePicker.MultiNodePickerStyles.css", umbraco.cms.businesslogic.datatype.ClientDependencyType.Css);
|
|
|
|
|
|
this.RegisterEmbeddedClientResource("umbraco.editorControls.MultiNodeTreePicker.MultiNodePickerScripts.js", umbraco.cms.businesslogic.datatype.ClientDependencyType.Javascript);
|
2012-04-28 13:57:04 -01:00
|
|
|
|
|
|
|
|
|
|
//update the tree type (we need to do this each time because i don't think view state works with these controls)
|
|
|
|
|
|
switch (TreeToRender)
|
|
|
|
|
|
{
|
|
|
|
|
|
case "media":
|
|
|
|
|
|
TreePickerControl.TreeType = "FilteredMediaTree";
|
|
|
|
|
|
TreePickerControl.App = "media";
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "content":
|
|
|
|
|
|
default:
|
|
|
|
|
|
TreePickerControl.TreeType = "FilteredContentTree";
|
|
|
|
|
|
TreePickerControl.App = "content";
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (Page.IsPostBack)
|
|
|
|
|
|
{
|
|
|
|
|
|
//since it is a post back, bind the data source to the view state values
|
|
|
|
|
|
XmlValue = ConvertToXDocument(SelectedIds);
|
|
|
|
|
|
}
|
2012-04-29 20:16:31 -01:00
|
|
|
|
|
2012-04-28 13:57:04 -01:00
|
|
|
|
//bind the repeater if theres a data source, or if there's no datasource but this is a postback (i.e. nodes deleted)
|
|
|
|
|
|
if (SelectedValues.DataSource != null || Page.IsPostBack)
|
|
|
|
|
|
{
|
|
|
|
|
|
SelectedValues.DataBind();
|
|
|
|
|
|
}
|
2012-04-29 20:16:31 -01:00
|
|
|
|
|
2012-04-28 13:57:04 -01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Creates the child controls for this control
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
protected override void CreateChildControls()
|
|
|
|
|
|
{
|
|
|
|
|
|
base.CreateChildControls();
|
|
|
|
|
|
|
|
|
|
|
|
EnsureChildControls();
|
|
|
|
|
|
|
|
|
|
|
|
//create the tree control
|
|
|
|
|
|
TreePickerControl = new CustomTreeControl
|
|
|
|
|
|
{
|
|
|
|
|
|
ID = "TreePicker",
|
|
|
|
|
|
IsDialog = true,
|
|
|
|
|
|
ShowContextMenu = false,
|
|
|
|
|
|
DialogMode = TreeDialogModes.id,
|
|
|
|
|
|
Height = Unit.Pixel(ControlHeight),
|
|
|
|
|
|
StartNodeID = StartNodeId
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//create the hidden field
|
|
|
|
|
|
PickedValue = new HiddenField { ID = "PickedValue" };
|
|
|
|
|
|
|
|
|
|
|
|
//create the right column
|
|
|
|
|
|
RightColumn = new HtmlGenericControl("div") { ID = "RightColumn" };
|
|
|
|
|
|
RightColumn.Attributes.Add("class", "right propertypane");
|
2012-04-29 20:16:31 -01:00
|
|
|
|
|
2012-04-28 13:57:04 -01:00
|
|
|
|
//create the repeater
|
|
|
|
|
|
SelectedValues = new Repeater
|
|
|
|
|
|
{
|
|
|
|
|
|
//EnableViewState = false,
|
|
|
|
|
|
ID = "SelectedValues",
|
|
|
|
|
|
ItemTemplate = new SelectedItemsTemplate()
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
SelectedValues.ItemDataBound += SelectedValues_ItemDataBound;
|
|
|
|
|
|
|
|
|
|
|
|
//add the repeater to the right column
|
|
|
|
|
|
RightColumn.Controls.Add(SelectedValues);
|
|
|
|
|
|
|
|
|
|
|
|
MinItemsValidator = new CustomValidator()
|
|
|
|
|
|
{
|
|
|
|
|
|
ID = "MinItemsValidator",
|
|
|
|
|
|
ErrorMessage =
|
|
|
|
|
|
string.Format(MNTPResources.Val_MinItemsInvalid, MinNodeCount)
|
|
|
|
|
|
};
|
|
|
|
|
|
MinItemsValidator.ServerValidate += new ServerValidateEventHandler(MinItemsValidator_ServerValidate);
|
|
|
|
|
|
|
|
|
|
|
|
//add the controls
|
|
|
|
|
|
this.Controls.Add(MinItemsValidator);
|
|
|
|
|
|
this.Controls.Add(TreePickerControl);
|
|
|
|
|
|
this.Controls.Add(PickedValue);
|
|
|
|
|
|
this.Controls.Add(RightColumn);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-04-29 20:16:31 -01:00
|
|
|
|
|
|
|
|
|
|
|
2012-04-28 13:57:04 -01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Ensure the repeater is data bound
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public override void DataBind()
|
2012-04-29 20:16:31 -01:00
|
|
|
|
{
|
2012-04-28 13:57:04 -01:00
|
|
|
|
base.DataBind();
|
|
|
|
|
|
SelectedValues.DataBind();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void MinItemsValidator_ServerValidate(object source, ServerValidateEventArgs args)
|
|
|
|
|
|
{
|
|
|
|
|
|
args.IsValid = true;
|
|
|
|
|
|
if (MinNodeCount > 0 && SelectedIds.Length < MinNodeCount)
|
|
|
|
|
|
{
|
|
|
|
|
|
args.IsValid = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Event handler for the selected node repeater.
|
|
|
|
|
|
/// This will fill in all of the text values, icons, etc.. for nodes based on their ID.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="sender"></param>
|
|
|
|
|
|
/// <param name="e"></param>
|
|
|
|
|
|
void SelectedValues_ItemDataBound(object sender, RepeaterItemEventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
var liSelectNode = (HtmlGenericControl)e.Item.FindControl("SelectedNodeListItem");
|
|
|
|
|
|
var lnkSelectNode = (HtmlAnchor)e.Item.FindControl("SelectedNodeLink");
|
|
|
|
|
|
var litSelectNodeName = (Literal)e.Item.FindControl("SelectedNodeText");
|
|
|
|
|
|
var infoButton = (HtmlAnchor)e.Item.FindControl("InfoButton");
|
|
|
|
|
|
|
|
|
|
|
|
//hide the info button if tooltips are hidden
|
|
|
|
|
|
if (!ShowToolTips)
|
|
|
|
|
|
{
|
|
|
|
|
|
infoButton.Style.Add(HtmlTextWriterStyle.Display, "none");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var thisNode = (XElement)e.Item.DataItem;
|
|
|
|
|
|
int thisNodeId;
|
|
|
|
|
|
if (int.TryParse(thisNode.Value, out thisNodeId))
|
|
|
|
|
|
{
|
|
|
|
|
|
umbraco.cms.businesslogic.Content loadedNode;
|
2012-04-29 20:16:31 -01:00
|
|
|
|
|
2012-04-28 13:57:04 -01:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
loadedNode = new umbraco.cms.businesslogic.Content(thisNodeId);
|
|
|
|
|
|
|
|
|
|
|
|
//add the node id
|
|
|
|
|
|
liSelectNode.Attributes["rel"] = thisNodeId.ToString();
|
|
|
|
|
|
//add the path to be referenced
|
|
|
|
|
|
liSelectNode.Attributes["umb:nodedata"] = loadedNode.Path;
|
|
|
|
|
|
lnkSelectNode.HRef = "javascript:void(0);";
|
|
|
|
|
|
litSelectNodeName.Text = loadedNode.Text;
|
|
|
|
|
|
|
|
|
|
|
|
if (loadedNode.IsTrashed)
|
|
|
|
|
|
{
|
|
|
|
|
|
//need to flag this to be removed which will be done after all items are data bound
|
|
|
|
|
|
liSelectNode.Attributes["rel"] = "trashed";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
//we need to set the icon
|
|
|
|
|
|
if (loadedNode.ContentTypeIcon.StartsWith(".spr"))
|
|
|
|
|
|
lnkSelectNode.Attributes["class"] += " " + loadedNode.ContentTypeIcon.TrimStart('.');
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
//it's a real icon, so make it a background image
|
|
|
|
|
|
lnkSelectNode.Style.Add(HtmlTextWriterStyle.BackgroundImage,
|
|
|
|
|
|
string.Format("url('{0}')", IconPath + loadedNode.ContentTypeIcon));
|
|
|
|
|
|
//set the nospr class since it's not a sprite
|
|
|
|
|
|
lnkSelectNode.Attributes["class"] += " noSpr";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//show the media preview if media and allowed
|
|
|
|
|
|
if (TreeToRender == "media" && ShowThumbnailsForMedia)
|
|
|
|
|
|
{
|
|
|
|
|
|
var imgPreview = (ImageViewer)e.Item.FindControl("ImgPreview");
|
|
|
|
|
|
//show the thubmnail controls
|
|
|
|
|
|
imgPreview.Visible = true;
|
2012-04-29 20:16:31 -01:00
|
|
|
|
|
2012-04-28 13:57:04 -01:00
|
|
|
|
//add the item class
|
|
|
|
|
|
var item = (HtmlGenericControl)e.Item.FindControl("Item");
|
|
|
|
|
|
item.Attributes["class"] += " thumb-item";
|
|
|
|
|
|
|
|
|
|
|
|
//item.Style.Add(HtmlTextWriterStyle.Height, "50px");
|
|
|
|
|
|
////make the content sit beside the item
|
|
|
|
|
|
//var inner = (HtmlGenericControl)e.Item.FindControl("InnerItem");
|
|
|
|
|
|
//inner.Style.Add(HtmlTextWriterStyle.Width, "224px");
|
|
|
|
|
|
|
|
|
|
|
|
//check if it's a thumbnail type element, we need to check both schemas
|
|
|
|
|
|
if (MediaTypesWithThumbnails.Select(x => x.ToUpper())
|
|
|
|
|
|
.Contains(loadedNode.ContentType.Alias.ToUpper()))
|
|
|
|
|
|
{
|
|
|
|
|
|
imgPreview.MediaId = thisNodeId;
|
|
|
|
|
|
imgPreview.DataBind();
|
|
|
|
|
|
}
|
2012-04-29 20:16:31 -01:00
|
|
|
|
}
|
2012-04-28 13:57:04 -01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (ArgumentException)
|
|
|
|
|
|
{
|
|
|
|
|
|
//the node no longer exists, so we display a msg
|
|
|
|
|
|
litSelectNodeName.Text = "<i>NODE NO LONGER EXISTS</i>";
|
2012-04-29 20:16:31 -01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2012-04-28 13:57:04 -01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// set the nodekey to the id of this datatype
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <remarks>
|
|
|
|
|
|
/// this is how get the xpath out of the cookie to know how the tree knows how to filter things.
|
|
|
|
|
|
/// generally the nodekey is used for a string id, but we'll use it for something different.
|
|
|
|
|
|
/// </remarks>
|
|
|
|
|
|
/// <param name="e"></param>
|
|
|
|
|
|
protected override void OnPreRender(EventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
base.OnPreRender(e);
|
|
|
|
|
|
|
|
|
|
|
|
TreePickerControl.NodeKey = this.DataTypeDefinitionId.ToString();
|
|
|
|
|
|
|
|
|
|
|
|
SavePersistentValuesForTree(XPathFilter);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-04-29 20:16:31 -01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Override render to control the exact output of what is rendered this includes instantiating the jquery plugin
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> object that receives the server control content.</param>
|
|
|
|
|
|
/// <remarks>
|
|
|
|
|
|
/// Generally i don't like to do this but there's a few div's, etc... to render so this makes more sense.
|
|
|
|
|
|
/// </remarks>
|
2012-04-28 13:57:04 -01:00
|
|
|
|
protected override void Render(HtmlTextWriter writer)
|
|
|
|
|
|
{
|
|
|
|
|
|
//<div class="multiTreePicker">
|
|
|
|
|
|
// <div class="header propertypane">
|
|
|
|
|
|
// <div>Select items</div>
|
|
|
|
|
|
// </div>
|
|
|
|
|
|
// <div class="left propertypane">
|
|
|
|
|
|
// <umb:tree runat="server" ID="TreePickerControl"
|
|
|
|
|
|
// CssClass="myTreePicker" Mode="Standard"
|
|
|
|
|
|
// DialogMode="id" ShowContextMenu="false"
|
|
|
|
|
|
// IsDialog="true" TreeType="content" />
|
|
|
|
|
|
// </div>
|
|
|
|
|
|
// <div class="right propertypane">
|
|
|
|
|
|
// </div>
|
|
|
|
|
|
//</div>
|
|
|
|
|
|
|
|
|
|
|
|
RenderTooltip(writer);
|
|
|
|
|
|
|
2012-04-29 20:16:31 -01:00
|
|
|
|
writer.AddAttribute("class", (!MinItemsValidator.IsValid ? "error " : "") + "multiNodePicker clearfix");
|
2012-04-28 13:57:04 -01:00
|
|
|
|
writer.AddAttribute("id", this.ClientID);
|
|
|
|
|
|
writer.RenderBeginTag(HtmlTextWriterTag.Div);
|
|
|
|
|
|
|
|
|
|
|
|
writer.AddAttribute("class", "header propertypane");
|
|
|
|
|
|
writer.RenderBeginTag(HtmlTextWriterTag.Div);
|
|
|
|
|
|
writer.RenderBeginTag(HtmlTextWriterTag.Div);
|
|
|
|
|
|
writer.Write("Select Items");
|
|
|
|
|
|
writer.RenderEndTag();
|
|
|
|
|
|
writer.RenderEndTag();
|
|
|
|
|
|
|
|
|
|
|
|
writer.AddAttribute("class", "left propertypane");
|
2012-04-29 20:16:31 -01:00
|
|
|
|
writer.AddStyleAttribute(HtmlTextWriterStyle.Height, ((ControlHeight + 10).ToString() + "px"));
|
2012-04-28 13:57:04 -01:00
|
|
|
|
writer.RenderBeginTag(HtmlTextWriterTag.Div);
|
|
|
|
|
|
//add the tree control here
|
|
|
|
|
|
TreePickerControl.RenderControl(writer);
|
|
|
|
|
|
writer.RenderEndTag();
|
|
|
|
|
|
|
2012-04-29 20:16:31 -01:00
|
|
|
|
RightColumn.RenderControl(writer);
|
2012-04-28 13:57:04 -01:00
|
|
|
|
|
|
|
|
|
|
//render the hidden field
|
|
|
|
|
|
PickedValue.RenderControl(writer);
|
|
|
|
|
|
|
|
|
|
|
|
writer.RenderEndTag(); //end multiNodePicker div
|
|
|
|
|
|
|
2012-04-29 20:16:31 -01:00
|
|
|
|
var tooltipAjaxUrl = IOHelper.ResolveUrl(SystemDirectories.Umbraco) + @"/plugins/MultiNodePicker/CustomTreeService.asmx/GetNodeInfo";
|
2012-04-28 13:57:04 -01:00
|
|
|
|
|
|
|
|
|
|
//add jquery window load event to create the js tree picker
|
|
|
|
|
|
var jsMethod = string.Format("jQuery('#{0}').MultiNodeTreePicker('{1}', {2}, '{3}', {4}, {5}, '{6}', '{7}');",
|
|
|
|
|
|
TreePickerControl.ClientID,
|
|
|
|
|
|
this.ClientID,
|
|
|
|
|
|
MaxNodeCount,
|
|
|
|
|
|
tooltipAjaxUrl,
|
|
|
|
|
|
ShowToolTips.ToString().ToLower(),
|
|
|
|
|
|
(TreeToRender == "media" && ShowThumbnailsForMedia).ToString().ToLower(),
|
|
|
|
|
|
IOHelper.ResolveUrl(SystemDirectories.Umbraco),
|
|
|
|
|
|
TreeToRender);
|
|
|
|
|
|
var js = "jQuery(window).load(function() { " + jsMethod + " });";
|
2012-04-29 20:16:31 -01:00
|
|
|
|
|
2012-04-28 13:57:04 -01:00
|
|
|
|
writer.WriteLine("<script type='text/javascript'>" + js + "</script>");
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-04-29 20:16:31 -01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// converts a list of Ids to the XDocument structure
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="val">The value.</param>
|
|
|
|
|
|
/// <returns></returns>
|
2012-04-28 13:57:04 -01:00
|
|
|
|
private XDocument ConvertToXDocument(IEnumerable<string> val)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (val.Count() > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
return new XDocument(new XElement("MultiNodePicker",
|
|
|
|
|
|
new XAttribute("type", TreeToRender),
|
2012-04-29 20:16:31 -01:00
|
|
|
|
val.Select(x => new XElement("nodeId", x.ToString()))));
|
2012-04-28 13:57:04 -01:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
//return null to support recursive values
|
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
|
|
//return an empty node set
|
|
|
|
|
|
//return new XDocument(new XElement("MultiNodePicker"));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// this will render the tooltip object on the page so long as another
|
|
|
|
|
|
/// one hasn't already been registered. There should only be one tooltip.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void RenderTooltip(HtmlTextWriter writer)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (this.Page.Items.Contains("MNTPTooltip"))
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//render the tooltip holder
|
|
|
|
|
|
//<div class="tooltip">
|
|
|
|
|
|
// <div class="throbber"></div>
|
|
|
|
|
|
// <div class="tooltipInfo"></div>
|
|
|
|
|
|
//</div>
|
|
|
|
|
|
//this.Page.Controls.AddAt(0, new LiteralControl("<div id='MNTPTooltip'><div class='throbber'></div><div class='tooltipInfo'></div></div>"));
|
|
|
|
|
|
writer.AddAttribute("id", "MNTPTooltip");
|
|
|
|
|
|
writer.RenderBeginTag(HtmlTextWriterTag.Div);
|
|
|
|
|
|
writer.AddAttribute("class", "throbber");
|
|
|
|
|
|
writer.RenderBeginTag(HtmlTextWriterTag.Div);
|
|
|
|
|
|
writer.RenderEndTag(); //end throbber
|
|
|
|
|
|
writer.AddAttribute("class", "tooltipInfo");
|
|
|
|
|
|
writer.RenderBeginTag(HtmlTextWriterTag.Div);
|
|
|
|
|
|
writer.RenderEndTag(); //end tooltipInfo
|
|
|
|
|
|
writer.RenderEndTag(); //end tooltipo
|
|
|
|
|
|
|
|
|
|
|
|
//ensure we add this to our page items so it's not duplicated
|
|
|
|
|
|
this.Page.Items.Add("MNTPTooltip", true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-04-29 20:16:31 -01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// This will update the multi-node tree picker data which is used to store
|
|
|
|
|
|
/// the xpath data and xpath match type for this control id.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="xpath">The xpath.</param>
|
|
|
|
|
|
/// <remarks>
|
|
|
|
|
|
/// This will save the data into a cookie and also into the request cookie. It must save
|
|
|
|
|
|
/// it to both locations in case the request cookie has been changed and the request cookie
|
|
|
|
|
|
/// is different than the response cookie.
|
|
|
|
|
|
/// </remarks>
|
2012-04-28 13:57:04 -01:00
|
|
|
|
private void SavePersistentValuesForTree(string xpath)
|
2012-04-29 20:16:31 -01:00
|
|
|
|
{
|
2012-04-28 13:57:04 -01:00
|
|
|
|
|
|
|
|
|
|
//create the output cookie with all of the values of the request cookie
|
|
|
|
|
|
|
|
|
|
|
|
var newCookie = HttpContext.Current.Response.Cookies[MNTP_DataType.PersistenceCookieName] ?? new HttpCookie(MNTP_DataType.PersistenceCookieName);
|
|
|
|
|
|
|
|
|
|
|
|
//store the xpath for this data type definition
|
|
|
|
|
|
newCookie.MntpAddXPathFilter(this.DataTypeDefinitionId, xpath);
|
|
|
|
|
|
//store the match type
|
|
|
|
|
|
newCookie.MntpAddXPathFilterType(this.DataTypeDefinitionId, XPathFilterMatchType);
|
|
|
|
|
|
//store the start node id
|
|
|
|
|
|
newCookie.MntpAddStartNodeId(this.DataTypeDefinitionId, StartNodeId);
|
|
|
|
|
|
//store the start node selection type
|
|
|
|
|
|
newCookie.MntpAddStartNodeSelectionType(this.DataTypeDefinitionId, StartNodeSelectionType);
|
|
|
|
|
|
//store the start node xpath expression type
|
|
|
|
|
|
newCookie.MntpAddStartNodeXPathExpressionType(this.DataTypeDefinitionId, StartNodeXPathExpressionType);
|
|
|
|
|
|
//store the start node xpath expression
|
|
|
|
|
|
newCookie.MntpAddStartNodeXPathExpression(this.DataTypeDefinitionId, StartNodeXPathExpression);
|
|
|
|
|
|
//store the current editing node if found
|
|
|
|
|
|
if (!string.IsNullOrEmpty(HttpContext.Current.Request["id"]))
|
|
|
|
|
|
{
|
|
|
|
|
|
var id = 0;
|
|
|
|
|
|
if (int.TryParse(HttpContext.Current.Request["id"], out id))
|
|
|
|
|
|
{
|
2012-04-29 20:16:31 -01:00
|
|
|
|
newCookie.MntpAddCurrentEditingNode(this.DataTypeDefinitionId, id);
|
2012-04-28 13:57:04 -01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HttpContext.Current.Response.Cookies.Add(newCookie);
|
|
|
|
|
|
|
|
|
|
|
|
//add it to the request cookies too, thus overriding any old data
|
|
|
|
|
|
if (HttpContext.Current.Request.Cookies[MNTP_DataType.PersistenceCookieName] != null && HttpContext.Current.Request.Cookies[MNTP_DataType.PersistenceCookieName].Values.Count > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
//remove the incoming one and replace with new one
|
|
|
|
|
|
HttpContext.Current.Request.Cookies.Remove(MNTP_DataType.PersistenceCookieName);
|
|
|
|
|
|
}
|
|
|
|
|
|
HttpContext.Current.Request.Cookies.Add(newCookie);
|
2012-04-29 20:16:31 -01:00
|
|
|
|
|
2012-04-28 13:57:04 -01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// A reference path to where the icons are actually stored as compared to where the tree themes folder is
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private static readonly string IconPath = IOHelper.ResolveUrl(SystemDirectories.Umbraco) + "/images/umbraco/";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|