diff --git a/src/Umbraco.Core/DisposableTimer.cs b/src/Umbraco.Core/DisposableTimer.cs
index 5269244609..f60c3a5634 100644
--- a/src/Umbraco.Core/DisposableTimer.cs
+++ b/src/Umbraco.Core/DisposableTimer.cs
@@ -87,7 +87,7 @@ namespace Umbraco.Core
///
public static DisposableTimer DebugDuration(Type loggerType, string startMessage, string completeMessage)
{
- LogHelper.Info(loggerType, () => startMessage);
+ LogHelper.Debug(loggerType, () => startMessage);
return new DisposableTimer(x => LogHelper.Debug(loggerType, () => completeMessage + " (took " + x + "ms)"));
}
diff --git a/src/Umbraco.Core/ObjectResolution/LegacyTransientObjectsResolver.cs b/src/Umbraco.Core/ObjectResolution/LegacyTransientObjectsResolver.cs
index 582ec50b9e..27f2307bd2 100644
--- a/src/Umbraco.Core/ObjectResolution/LegacyTransientObjectsResolver.cs
+++ b/src/Umbraco.Core/ObjectResolution/LegacyTransientObjectsResolver.cs
@@ -33,7 +33,7 @@ namespace Umbraco.Core.ObjectResolution
/// TODO: However, it would make much more sense to do this and would speed up the application plus this would make the GetById method much easier.
///
protected LegacyTransientObjectsResolver(IEnumerable refreshers)
- : base(ObjectLifetimeScope.Transient) // false = new objects every time
+ : base(ObjectLifetimeScope.Transient) // new objects every time
{
foreach (var l in refreshers)
{
diff --git a/src/Umbraco.Web/DefaultPublishedContentStore.cs b/src/Umbraco.Web/DefaultPublishedContentStore.cs
index c1d79283a7..1b8dfddc7c 100644
--- a/src/Umbraco.Web/DefaultPublishedContentStore.cs
+++ b/src/Umbraco.Web/DefaultPublishedContentStore.cs
@@ -161,7 +161,10 @@ namespace Umbraco.Web
public bool HasContent(UmbracoContext umbracoContext)
{
- var node = GetXml(umbracoContext).SelectSingleNode(XPathStrings.RootDocuments);
+ var xml = GetXml(umbracoContext);
+ if (xml == null)
+ return false;
+ var node = xml.SelectSingleNode(XPathStrings.RootDocuments);
return node != null;
}
diff --git a/src/Umbraco.Web/UmbracoModule.cs b/src/Umbraco.Web/UmbracoModule.cs
index 5f84c387f6..8368563990 100644
--- a/src/Umbraco.Web/UmbracoModule.cs
+++ b/src/Umbraco.Web/UmbracoModule.cs
@@ -271,10 +271,11 @@ namespace Umbraco.Web
if (maybeDoc && GlobalSettings.IsReservedPathOrUrl(lpath))
maybeDoc = false;
- if (!maybeDoc)
- {
- LogHelper.Warn("Not a document");
- }
+ //NOTE: No need to warn, plus if we do we should log the document, as this message doesn't really tell us anything :)
+ //if (!maybeDoc)
+ //{
+ // LogHelper.Warn("Not a document");
+ //}
return maybeDoc;
}
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/TreeDefinitionCollection.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/TreeDefinitionCollection.cs
index e8aca83c1a..5d816ce07f 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/TreeDefinitionCollection.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/TreeDefinitionCollection.cs
@@ -33,6 +33,7 @@ namespace umbraco.cms.presentation.Trees
{
get
{
+ instance.EnsureTreesRegistered();
return instance;
}
}
diff --git a/src/umbraco.editorControls/MultiNodeTreePicker/MNTP_DataEditor.cs b/src/umbraco.editorControls/MultiNodeTreePicker/MNTP_DataEditor.cs
index a36b93e5f9..6e0a04320b 100644
--- a/src/umbraco.editorControls/MultiNodeTreePicker/MNTP_DataEditor.cs
+++ b/src/umbraco.editorControls/MultiNodeTreePicker/MNTP_DataEditor.cs
@@ -1,12 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Threading;
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.Core;
using umbraco.cms.presentation.Trees;
using umbraco.controls.Images;
using umbraco.controls.Tree;
@@ -17,679 +19,665 @@ using umbraco.IO;
namespace umbraco.editorControls.MultiNodeTreePicker
{
- ///
- /// The user interface to display to the content editor
- ///
- [ClientDependency(ClientDependencyType.Javascript, "ui/jqueryui.js", "UmbracoClient")]
- [ClientDependency(ClientDependencyType.Javascript, "ui/jquery.tooltip.min.js", "UmbracoClient")]
- [ClientDependency(ClientDependencyType.Javascript, "controls/Images/ImageViewer.js", "UmbracoRoot")]
- public class MNTP_DataEditor : Control, INamingContainer
- {
- #region Static Constructor
-
- ///
- /// This adds our filtered tree definition to the TreeDefinitionCollection at runtime
- /// instead of having to declare it in the database
- ///
- 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,
- "umbraco.editorControls",
- "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,
- "umbraco.editorControls",
- "MultiNodeTreePicker.FilteredMediaTree",
- contentTree.Tree.Action),
- contentTree.App);
-
- //add it to the collection at runtime
- TreeDefinitionCollection.Instance.Add(filteredContentTree);
- TreeDefinitionCollection.Instance.Add(filteredMediaTree);
- }
- }
- }
- }
-
- #endregion
-
- ///
- /// Initializes a new instance of the class.
- ///
- public MNTP_DataEditor()
- {
- this.MediaTypesWithThumbnails = new string[] { "image" };
- ShowThumbnailsForMedia = true;
- TreeToRender = "content";
- MaxNodeCount = -1;
- MinNodeCount = 0;
- StartNodeId = uQuery.RootNodeId;
- ShowToolTips = true;
- ControlHeight = 200;
- }
-
- #region Static members
- ///
- /// Used for locking code blocks
- ///
- private static readonly object m_Locker = new object();
- #endregion
-
- #region Protected members
-
- ///
- ///
- ///
- protected CustomValidator MinItemsValidator;
-
- ///
- ///
- ///
- protected CustomTreeControl TreePickerControl;
-
- ///
- ///
- ///
- protected Repeater SelectedValues;
-
- ///
- ///
- ///
- protected HiddenField PickedValue;
-
- ///
- ///
- ///
- protected HtmlGenericControl RightColumn;
- #endregion
-
- #region public Properties
-
- ///
- /// gets/sets the value based on an array of IDs selected
- ///
- public string[] SelectedIds
- {
- get
- {
- List val = new List();
- 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);
- }
- }
-
- ///
- /// get/set the value for the selected nodes in xml format
- ///
- 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());
- }
- }
- }
-
- ///
- /// The property name being edited with the current data editor. This is used for the min items validation statement.
- ///
- public string PropertyName { get; set; }
-
- ///
- /// The tree type alias to render
- ///
- public string TreeToRender { get; set; }
-
- ///
- /// An xpath filter to match nodes that will be disabled from being clicked
- ///
- public string XPathFilter { get; set; }
-
- ///
- /// The minimum amount of nodes that can be selected
- ///
- public int MinNodeCount { get; set; }
-
- ///
- /// The maximum amount of nodes that can be selected
- ///
- public int MaxNodeCount { get; set; }
-
- ///
- /// The start node id
- ///
- public int StartNodeId { get; set; }
-
- ///
- /// The start node selection type
- ///
- public NodeSelectionType StartNodeSelectionType { get; set; }
-
- ///
- /// The xpath expression type to select the start node when the StartNodeSelectionType is XPath
- ///
- public XPathExpressionType StartNodeXPathExpressionType { get; set; }
-
- ///
- /// The XPath expression to use to determine the start node when the StartNodeSelectionType is XPath
- ///
- public string StartNodeXPathExpression { get; set; }
-
- ///
- /// Gets or sets a value indicating whether [show tool tips].
- ///
- /// true if [show tool tips]; otherwise, false.
- /// Shows/Hides the tooltip info bubble.
- public bool ShowToolTips { get; set; }
-
- ///
- /// The XPathFilterType to match
- ///
- public XPathFilterType XPathFilterMatchType { get; set; }
-
- ///
- /// Gets or sets a value indicating whether [show thumbnails for media].
- ///
- ///
- /// true if [show thumbnails for media]; otherwise, false.
- ///
- /// Whether or not to show thumbnails for media
- public bool ShowThumbnailsForMedia { get; set; }
-
- ///
- /// A list of media type names that can have thumbnails (i.e. 'image')
- ///
- public string[] MediaTypesWithThumbnails { get; set; }
-
- ///
- /// This is set by the data type and allows us to save a cookie value
- /// for persistence for the data type.
- ///
- public int DataTypeDefinitionId { get; set; }
-
- ///
- /// The height of the tree control box in pixels
- ///
- public int ControlHeight { get; set; }
-
- #endregion
-
- ///
- /// Initialize the control, make sure children are created
- ///
- /// An object that contains the event data.
- protected override void OnInit(EventArgs e)
- {
- base.OnInit(e);
-
- EnsureChildControls();
- }
-
- ///
- /// Add the resources (sytles/scripts)
- ///
- ///
- protected override void OnLoad(EventArgs e)
- {
- base.OnLoad(e);
-
- //add the js/css required
- 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);
-
- //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);
- }
-
- //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();
- }
-
- }
-
- ///
- /// Creates the child controls for this control
- ///
- 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");
-
- //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);
- }
-
-
-
- ///
- /// Ensure the repeater is data bound
- ///
- public override void DataBind()
- {
- base.DataBind();
- SelectedValues.DataBind();
- }
-
- void MinItemsValidator_ServerValidate(object source, ServerValidateEventArgs args)
- {
- args.IsValid = true;
- if (MinNodeCount > 0 && SelectedIds.Length < MinNodeCount)
- {
- args.IsValid = false;
- }
- }
-
- ///
- /// Event handler for the selected node repeater.
- /// This will fill in all of the text values, icons, etc.. for nodes based on their ID.
- ///
- ///
- ///
- 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;
-
- 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;
-
- //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();
- }
- }
- }
-
- }
- catch (ArgumentException)
- {
- //the node no longer exists, so we display a msg
- litSelectNodeName.Text = "NODE NO LONGER EXISTS";
- }
- }
- }
-
- ///
- /// set the nodekey to the id of this datatype
- ///
- ///
- /// 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.
- ///
- ///
- protected override void OnPreRender(EventArgs e)
- {
- base.OnPreRender(e);
-
- TreePickerControl.NodeKey = this.DataTypeDefinitionId.ToString();
-
- SavePersistentValuesForTree(XPathFilter);
- }
-
- ///
- /// Override render to control the exact output of what is rendered this includes instantiating the jquery plugin
- ///
- /// The object that receives the server control content.
- ///
- /// Generally i don't like to do this but there's a few div's, etc... to render so this makes more sense.
- ///
- protected override void Render(HtmlTextWriter writer)
- {
- //
- //
- //
- //
- //
- //
- //
- //
-
- RenderTooltip(writer);
-
- writer.AddAttribute("class", (!MinItemsValidator.IsValid ? "error " : "") + "multiNodePicker clearfix");
- 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");
- writer.AddStyleAttribute(HtmlTextWriterStyle.Height, ((ControlHeight + 10).ToString() + "px"));
- writer.RenderBeginTag(HtmlTextWriterTag.Div);
- //add the tree control here
- TreePickerControl.RenderControl(writer);
- writer.RenderEndTag();
-
- RightColumn.RenderControl(writer);
-
- //render the hidden field
- PickedValue.RenderControl(writer);
-
- writer.RenderEndTag(); //end multiNodePicker div
-
- var tooltipAjaxUrl = IOHelper.ResolveUrl(SystemDirectories.Umbraco) + @"/controls/Tree/CustomTreeService.asmx/GetNodeInfo";
-
- //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 + " });";
-
- writer.WriteLine("");
-
- }
-
- ///
- /// converts a list of Ids to the XDocument structure
- ///
- /// The value.
- ///
- private XDocument ConvertToXDocument(IEnumerable val)
- {
- if (val.Count() > 0)
- {
- return new XDocument(new XElement("MultiNodePicker",
- new XAttribute("type", TreeToRender),
- val.Select(x => new XElement("nodeId", x.ToString()))));
- }
- else
- {
- //return null to support recursive values
- return null;
-
- //return an empty node set
- //return new XDocument(new XElement("MultiNodePicker"));
- }
- }
-
- ///
- /// 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.
- ///
- private void RenderTooltip(HtmlTextWriter writer)
- {
- if (this.Page.Items.Contains("MNTPTooltip"))
- {
- return;
- }
-
- //render the tooltip holder
- //
- //this.Page.Controls.AddAt(0, new LiteralControl(""));
- 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);
- }
-
- ///
- /// 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.
- ///
- /// The xpath.
- ///
- /// 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.
- ///
- private void SavePersistentValuesForTree(string xpath)
- {
-
- //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))
- {
- newCookie.MntpAddCurrentEditingNode(this.DataTypeDefinitionId, id);
- }
- }
-
- 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);
-
- }
-
- ///
- /// A reference path to where the icons are actually stored as compared to where the tree themes folder is
- ///
- private static readonly string IconPath = IOHelper.ResolveUrl(SystemDirectories.Umbraco) + "/images/umbraco/";
- }
+ ///
+ /// The user interface to display to the content editor
+ ///
+ [ClientDependency(ClientDependencyType.Javascript, "ui/jqueryui.js", "UmbracoClient")]
+ [ClientDependency(ClientDependencyType.Javascript, "ui/jquery.tooltip.min.js", "UmbracoClient")]
+ [ClientDependency(ClientDependencyType.Javascript, "controls/Images/ImageViewer.js", "UmbracoRoot")]
+ public class MNTP_DataEditor : Control, INamingContainer
+ {
+ #region Static Constructor
+
+ ///
+ /// This adds our filtered tree definition to the TreeDefinitionCollection at runtime
+ /// instead of having to declare it in the database
+ ///
+ static MNTP_DataEditor()
+ {
+ //NOTE: Before we had locking here but static ctors are always threadsafe
+
+ if (TreeDefinitionCollection.Instance.Any(x => x.TreeType == typeof (FilteredContentTree)))
+ return;
+
+
+ //need to add our tree definitions to the collection.
+
+ //find the content tree to duplicate
+ var contentTree = TreeDefinitionCollection.Instance.Single(x => x.Tree.Alias.ToUpper() == "CONTENT");
+ 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,
+ "umbraco.editorControls",
+ "MultiNodeTreePicker.FilteredContentTree",
+ contentTree.Tree.Action),
+ contentTree.App);
+
+ //find the media tree to duplicate
+ var mediaTree = TreeDefinitionCollection.Instance.Single(x => x.Tree.Alias.ToUpper() == "MEDIA");
+ 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,
+ "umbraco.editorControls",
+ "MultiNodeTreePicker.FilteredMediaTree",
+ contentTree.Tree.Action),
+ contentTree.App);
+
+ //add it to the collection at runtime
+ TreeDefinitionCollection.Instance.Add(filteredContentTree);
+ TreeDefinitionCollection.Instance.Add(filteredMediaTree);
+ }
+
+ #endregion
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MNTP_DataEditor()
+ {
+ this.MediaTypesWithThumbnails = new string[] { "image" };
+ ShowThumbnailsForMedia = true;
+ TreeToRender = "content";
+ MaxNodeCount = -1;
+ MinNodeCount = 0;
+ StartNodeId = uQuery.RootNodeId;
+ ShowToolTips = true;
+ ControlHeight = 200;
+ }
+
+
+
+ #region Protected members
+
+ ///
+ ///
+ ///
+ protected CustomValidator MinItemsValidator;
+
+ ///
+ ///
+ ///
+ protected CustomTreeControl TreePickerControl;
+
+ ///
+ ///
+ ///
+ protected Repeater SelectedValues;
+
+ ///
+ ///
+ ///
+ protected HiddenField PickedValue;
+
+ ///
+ ///
+ ///
+ protected HtmlGenericControl RightColumn;
+ #endregion
+
+ #region public Properties
+
+ ///
+ /// gets/sets the value based on an array of IDs selected
+ ///
+ public string[] SelectedIds
+ {
+ get
+ {
+ List val = new List();
+ 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);
+ }
+ }
+
+ ///
+ /// get/set the value for the selected nodes in xml format
+ ///
+ 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());
+ }
+ }
+ }
+
+ ///
+ /// The property name being edited with the current data editor. This is used for the min items validation statement.
+ ///
+ public string PropertyName { get; set; }
+
+ ///
+ /// The tree type alias to render
+ ///
+ public string TreeToRender { get; set; }
+
+ ///
+ /// An xpath filter to match nodes that will be disabled from being clicked
+ ///
+ public string XPathFilter { get; set; }
+
+ ///
+ /// The minimum amount of nodes that can be selected
+ ///
+ public int MinNodeCount { get; set; }
+
+ ///
+ /// The maximum amount of nodes that can be selected
+ ///
+ public int MaxNodeCount { get; set; }
+
+ ///
+ /// The start node id
+ ///
+ public int StartNodeId { get; set; }
+
+ ///
+ /// The start node selection type
+ ///
+ public NodeSelectionType StartNodeSelectionType { get; set; }
+
+ ///
+ /// The xpath expression type to select the start node when the StartNodeSelectionType is XPath
+ ///
+ public XPathExpressionType StartNodeXPathExpressionType { get; set; }
+
+ ///
+ /// The XPath expression to use to determine the start node when the StartNodeSelectionType is XPath
+ ///
+ public string StartNodeXPathExpression { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether [show tool tips].
+ ///
+ /// true if [show tool tips]; otherwise, false.
+ /// Shows/Hides the tooltip info bubble.
+ public bool ShowToolTips { get; set; }
+
+ ///
+ /// The XPathFilterType to match
+ ///
+ public XPathFilterType XPathFilterMatchType { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether [show thumbnails for media].
+ ///
+ ///
+ /// true if [show thumbnails for media]; otherwise, false.
+ ///
+ /// Whether or not to show thumbnails for media
+ public bool ShowThumbnailsForMedia { get; set; }
+
+ ///
+ /// A list of media type names that can have thumbnails (i.e. 'image')
+ ///
+ public string[] MediaTypesWithThumbnails { get; set; }
+
+ ///
+ /// This is set by the data type and allows us to save a cookie value
+ /// for persistence for the data type.
+ ///
+ public int DataTypeDefinitionId { get; set; }
+
+ ///
+ /// The height of the tree control box in pixels
+ ///
+ public int ControlHeight { get; set; }
+
+ #endregion
+
+ ///
+ /// Initialize the control, make sure children are created
+ ///
+ /// An object that contains the event data.
+ protected override void OnInit(EventArgs e)
+ {
+ base.OnInit(e);
+
+ EnsureChildControls();
+ }
+
+ ///
+ /// Add the resources (sytles/scripts)
+ ///
+ ///
+ protected override void OnLoad(EventArgs e)
+ {
+ base.OnLoad(e);
+
+ //add the js/css required
+ 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);
+
+ //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);
+ }
+
+ //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();
+ }
+
+ }
+
+ ///
+ /// Creates the child controls for this control
+ ///
+ 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");
+
+ //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);
+ }
+
+
+
+ ///
+ /// Ensure the repeater is data bound
+ ///
+ public override void DataBind()
+ {
+ base.DataBind();
+ SelectedValues.DataBind();
+ }
+
+ void MinItemsValidator_ServerValidate(object source, ServerValidateEventArgs args)
+ {
+ args.IsValid = true;
+ if (MinNodeCount > 0 && SelectedIds.Length < MinNodeCount)
+ {
+ args.IsValid = false;
+ }
+ }
+
+ ///
+ /// Event handler for the selected node repeater.
+ /// This will fill in all of the text values, icons, etc.. for nodes based on their ID.
+ ///
+ ///
+ ///
+ 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;
+
+ 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;
+
+ //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();
+ }
+ }
+ }
+
+ }
+ catch (ArgumentException)
+ {
+ //the node no longer exists, so we display a msg
+ litSelectNodeName.Text = "NODE NO LONGER EXISTS";
+ }
+ }
+ }
+
+ ///
+ /// set the nodekey to the id of this datatype
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ protected override void OnPreRender(EventArgs e)
+ {
+ base.OnPreRender(e);
+
+ TreePickerControl.NodeKey = this.DataTypeDefinitionId.ToString();
+
+ SavePersistentValuesForTree(XPathFilter);
+ }
+
+ ///
+ /// Override render to control the exact output of what is rendered this includes instantiating the jquery plugin
+ ///
+ /// The object that receives the server control content.
+ ///
+ /// Generally i don't like to do this but there's a few div's, etc... to render so this makes more sense.
+ ///
+ protected override void Render(HtmlTextWriter writer)
+ {
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+
+ RenderTooltip(writer);
+
+ writer.AddAttribute("class", (!MinItemsValidator.IsValid ? "error " : "") + "multiNodePicker clearfix");
+ 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");
+ writer.AddStyleAttribute(HtmlTextWriterStyle.Height, ((ControlHeight + 10).ToString() + "px"));
+ writer.RenderBeginTag(HtmlTextWriterTag.Div);
+ //add the tree control here
+ TreePickerControl.RenderControl(writer);
+ writer.RenderEndTag();
+
+ RightColumn.RenderControl(writer);
+
+ //render the hidden field
+ PickedValue.RenderControl(writer);
+
+ writer.RenderEndTag(); //end multiNodePicker div
+
+ var tooltipAjaxUrl = IOHelper.ResolveUrl(SystemDirectories.Umbraco) + @"/controls/Tree/CustomTreeService.asmx/GetNodeInfo";
+
+ //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 + " });";
+
+ writer.WriteLine("");
+
+ }
+
+ ///
+ /// converts a list of Ids to the XDocument structure
+ ///
+ /// The value.
+ ///
+ private XDocument ConvertToXDocument(IEnumerable val)
+ {
+ if (val.Count() > 0)
+ {
+ return new XDocument(new XElement("MultiNodePicker",
+ new XAttribute("type", TreeToRender),
+ val.Select(x => new XElement("nodeId", x.ToString()))));
+ }
+ else
+ {
+ //return null to support recursive values
+ return null;
+
+ //return an empty node set
+ //return new XDocument(new XElement("MultiNodePicker"));
+ }
+ }
+
+ ///
+ /// 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.
+ ///
+ private void RenderTooltip(HtmlTextWriter writer)
+ {
+ if (this.Page.Items.Contains("MNTPTooltip"))
+ {
+ return;
+ }
+
+ //render the tooltip holder
+ //
+ //this.Page.Controls.AddAt(0, new LiteralControl(""));
+ 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);
+ }
+
+ ///
+ /// 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.
+ ///
+ /// The xpath.
+ ///
+ /// 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.
+ ///
+ private void SavePersistentValuesForTree(string xpath)
+ {
+
+ //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))
+ {
+ newCookie.MntpAddCurrentEditingNode(this.DataTypeDefinitionId, id);
+ }
+ }
+
+ 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);
+
+ }
+
+ ///
+ /// A reference path to where the icons are actually stored as compared to where the tree themes folder is
+ ///
+ private static readonly string IconPath = IOHelper.ResolveUrl(SystemDirectories.Umbraco) + "/images/umbraco/";
+ }
}