diff --git a/src/Umbraco.Core/Constants-ObjectTypes.cs b/src/Umbraco.Core/Constants-ObjectTypes.cs
index 229bd1b7d7..97213ae935 100644
--- a/src/Umbraco.Core/Constants-ObjectTypes.cs
+++ b/src/Umbraco.Core/Constants-ObjectTypes.cs
@@ -89,6 +89,11 @@ namespace Umbraco.Core
///
public const string Media = "B796F64C-1F99-4FFB-B886-4BF4BC011A9C";
+ ///
+ /// Guid for a Document object.
+ ///
+ public static readonly Guid MediaGuid = new Guid(Media);
+
///
/// Guid for the Media Recycle Bin.
///
@@ -148,6 +153,11 @@ namespace Umbraco.Core
///
public const string Template = "6FBDE604-4178-42CE-A10B-8A2600A2F07D";
+ ///
+ /// Guid for a Template object.
+ ///
+ public static readonly Guid TemplateTypeGuid = new Guid(Template);
+
///
/// Guid for a Lock object.
///
diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj
index eb126eb68e..31317aae51 100644
--- a/src/Umbraco.Tests/Umbraco.Tests.csproj
+++ b/src/Umbraco.Tests/Umbraco.Tests.csproj
@@ -594,6 +594,7 @@
+
diff --git a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs
new file mode 100644
index 0000000000..5c0880ee6e
--- /dev/null
+++ b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs
@@ -0,0 +1,69 @@
+using System;
+using System.Linq;
+using System.Web;
+using Moq;
+using NUnit.Framework;
+using Umbraco.Core;
+using Umbraco.Core.Configuration.UmbracoSettings;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Models;
+using Umbraco.Core.Persistence.SqlSyntax;
+using Umbraco.Core.Profiling;
+using Umbraco.Core.Scoping;
+using Umbraco.Core.Services;
+using Umbraco.Tests.TestHelpers;
+using Umbraco.Web;
+using Umbraco.Web.Routing;
+using Umbraco.Web.Security;
+using Umbraco.Web.Templates;
+
+namespace Umbraco.Tests.Web
+{
+ [TestFixture]
+ public class TemplateUtilitiesTests
+ {
+ [TestCase("", "")]
+ [TestCase("hello href=\"{localLink:1234}\" world ", "hello href=\"/my-test-url\" world ")]
+ [TestCase("hello href=\"{localLink:umb://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ", "hello href=\"/my-test-url\" world ")]
+ //this one has an invalid char so won't match
+ [TestCase("hello href=\"{localLink:umb^://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ", "hello href=\"{localLink:umb^://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ")]
+ public void ParseLocalLinks(string input, string result)
+ {
+ var serviceCtxMock = MockHelper.GetMockedServiceContext();
+
+ //setup a mock entity service from the service context to return an integer for a GUID
+ var entityService = Mock.Get(serviceCtxMock.EntityService);
+ entityService.Setup(x => x.GetIdForKey(It.IsAny(), It.IsAny()))
+ .Returns((Guid id, UmbracoObjectTypes objType) =>
+ {
+ return Attempt.Succeed(1234);
+ });
+
+ //setup a mock url provider which we'll use fo rtesting
+ var testUrlProvider = new Mock();
+ testUrlProvider.Setup(x => x.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()))
+ .Returns((UmbracoContext umbCtx, int id, Uri url, UrlProviderMode mode) =>
+ {
+ return "/my-test-url";
+ });
+
+ using (var appCtx = new ApplicationContext(new DatabaseContext(new Mock().Object, Mock.Of(), Mock.Of(), "test"),
+ serviceCtxMock,
+ CacheHelper.CreateDisabledCacheHelper(),
+ new ProfilingLogger(Mock.Of(), Mock.Of())))
+ using (var umbCtx = UmbracoContext.EnsureContext(
+ Mock.Of(), appCtx, new Mock(null, null).Object,
+ //setup a quick mock of the WebRouting section
+ Mock.Of(section => section.WebRouting == Mock.Of(routingSection => routingSection.UrlProviderMode == "AutoLegacy")),
+ //pass in the custom url provider
+ new[]{ testUrlProvider.Object },
+ false))
+ {
+ var output = TemplateUtilities.ParseInternalLinks(input, umbCtx.UrlProvider);
+
+ Assert.AreEqual(result, output);
+ }
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracolink/plugin.min.js b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracolink/plugin.min.js
index deb0a3cf3e..3f936b0577 100644
--- a/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracolink/plugin.min.js
+++ b/src/Umbraco.Web.UI.Client/lib/tinymce/plugins/umbracolink/plugin.min.js
@@ -172,8 +172,17 @@ tinymce.PluginManager.add('umbracolink', function(editor) {
//locallink detection, we do this here, to avoid poluting the dialogservice
//so the dialog service can just expect to get a node-like structure
- if(currentTarget.url.indexOf("localLink:") > 0){
- currentTarget.id = currentTarget.url.substring(currentTarget.url.indexOf(":")+1,currentTarget.url.length-1);
+ if (currentTarget.url.indexOf("localLink:") > 0) {
+ var linkId = currentTarget.url.substring(currentTarget.url.indexOf(":") + 1, currentTarget.url.length - 1);
+ //we need to check if this is an INT or a UDI
+ var parsedIntId = parseInt(linkId, 10);
+ if (isNaN(parsedIntId)) {
+ //it's a UDI
+ currentTarget.udi = linkId;
+ }
+ else {
+ currentTarget.id = linkId;
+ }
}
}
@@ -213,7 +222,13 @@ tinymce.PluginManager.add('umbracolink', function(editor) {
//if we have an id, it must be a locallink:id, aslong as the isMedia flag is not set
if(data.id && (angular.isUndefined(data.isMedia) || !data.isMedia)){
- href = "/{localLink:" + data.id + "}";
+ if (target.udi) {
+ href = "/{localLink:" + target.udi + "}";
+ }
+ else {
+ //This shouldn't happen! but just in case we'll leave this here
+ href = "/{localLink:" + target.id + "}";
+ }
insertLink();
return;
}
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js
index cdc7d77ca4..8676ac5b34 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js
@@ -672,8 +672,17 @@ function tinyMceService(dialogService, $log, imageHelper, $http, $timeout, macro
//locallink detection, we do this here, to avoid poluting the dialogservice
//so the dialog service can just expect to get a node-like structure
- if(currentTarget.url.indexOf("localLink:") > 0){
- currentTarget.id = currentTarget.url.substring(currentTarget.url.indexOf(":")+1,currentTarget.url.length-1);
+ if (currentTarget.url.indexOf("localLink:") > 0) {
+ var linkId = currentTarget.url.substring(currentTarget.url.indexOf(":") + 1, currentTarget.url.length - 1);
+ //we need to check if this is an INT or a UDI
+ var parsedIntId = parseInt(linkId, 10);
+ if (isNaN(parsedIntId)) {
+ //it's a UDI
+ currentTarget.udi = linkId;
+ }
+ else {
+ currentTarget.id = linkId;
+ }
}
}
@@ -747,7 +756,14 @@ function tinyMceService(dialogService, $log, imageHelper, $http, $timeout, macro
//if we have an id, it must be a locallink:id, aslong as the isMedia flag is not set
if(target.id && (angular.isUndefined(target.isMedia) || !target.isMedia)){
- href = "/{localLink:" + target.id + "}";
+ if (target.udi) {
+ href = "/{localLink:" + target.udi + "}";
+ }
+ else {
+ //This shouldn't happen! but just in case we'll leave this here
+ href = "/{localLink:" + target.id + "}";
+ }
+
insertLink();
return;
}
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.controller.js
index 5d638444c7..e2f95b0478 100644
--- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.controller.js
@@ -26,17 +26,21 @@ angular.module("umbraco").controller("Umbraco.Overlays.LinkPickerController",
$scope.model.target = dialogOptions.currentTarget;
//if we have a node ID, we fetch the current node to build the form data
- if ($scope.model.target.id) {
+ if ($scope.model.target.id || $scope.model.target.udi) {
- if (!$scope.model.target.path) {
- entityResource.getPath($scope.model.target.id, "Document").then(function (path) {
+ //will be either a udi or an int
+ var id = $scope.model.target.udi ? $scope.model.target.udi : $scope.model.target.id;
+
+ if (!$scope.model.target.path) {
+
+ entityResource.getPath(id, "Document").then(function (path) {
$scope.model.target.path = path;
//now sync the tree to this path
$scope.dialogTreeEventHandler.syncTree({ path: $scope.model.target.path, tree: "content" });
});
}
- contentResource.getNiceUrl($scope.model.target.id).then(function (url) {
+ contentResource.getNiceUrl(id).then(function (url) {
$scope.model.target.url = url;
});
}
@@ -63,7 +67,8 @@ angular.module("umbraco").controller("Umbraco.Overlays.LinkPickerController",
$scope.currentNode = args.node;
$scope.currentNode.selected = true;
- $scope.model.target.id = args.node.id;
+ $scope.model.target.id = args.node.id;
+ $scope.model.target.udi = args.node.udi;
$scope.model.target.name = args.node.name;
if (args.node.id < 0) {
@@ -116,7 +121,8 @@ angular.module("umbraco").controller("Umbraco.Overlays.LinkPickerController",
submit: function(model) {
var media = model.selectedImages[0];
- $scope.model.target.id = media.id;
+ $scope.model.target.id = media.id;
+ $scope.model.target.udi = media.udi;
$scope.model.target.isMedia = true;
$scope.model.target.name = media.name;
$scope.model.target.url = mediaHelper.resolveFile(media);
diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs
index ed0ac95a2b..da5e0c3a3b 100644
--- a/src/Umbraco.Web/Editors/ContentController.cs
+++ b/src/Umbraco.Web/Editors/ContentController.cs
@@ -6,6 +6,7 @@ using System.Net;
using System.Net.Http;
using System.Text;
using System.Web.Http;
+using System.Web.Http.Controllers;
using System.Web.Http.ModelBinding;
using AutoMapper;
using Umbraco.Core;
@@ -35,7 +36,8 @@ namespace Umbraco.Web.Editors
/// access to ALL of the methods on this controller will need access to the content application.
///
[PluginController("UmbracoApi")]
- [UmbracoApplicationAuthorizeAttribute(Constants.Applications.Content)]
+ [UmbracoApplicationAuthorize(Constants.Applications.Content)]
+ [ContentControllerConfiguration]
public class ContentController : ContentControllerBase
{
///
@@ -55,6 +57,18 @@ namespace Umbraco.Web.Editors
{
}
+ ///
+ /// Configures this controller with a custom action selector
+ ///
+ private class ContentControllerConfigurationAttribute : Attribute, IControllerConfiguration
+ {
+ public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
+ {
+ controllerSettings.Services.Replace(typeof(IHttpActionSelector), new ParameterSwapControllerActionSelector(
+ new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetNiceUrl", "id", typeof(int), typeof(Guid), typeof(Udi))));
+ }
+ }
+
///
/// Return content for the specified ids
///
@@ -162,6 +176,34 @@ namespace Umbraco.Web.Editors
return response;
}
+ ///
+ /// Gets the Url for a given node ID
+ ///
+ ///
+ ///
+ public HttpResponseMessage GetNiceUrl(Guid id)
+ {
+ var url = Umbraco.UrlProvider.GetUrl(id);
+ var response = Request.CreateResponse(HttpStatusCode.OK);
+ response.Content = new StringContent(url, Encoding.UTF8, "application/json");
+ return response;
+ }
+
+ ///
+ /// Gets the Url for a given node ID
+ ///
+ ///
+ ///
+ public HttpResponseMessage GetNiceUrl(Udi id)
+ {
+ var guidUdi = id as GuidUdi;
+ if (guidUdi != null)
+ {
+ return GetNiceUrl(guidUdi.Guid);
+ }
+ throw new HttpResponseException(HttpStatusCode.NotFound);
+ }
+
///
/// Gets the children for the content id passed in
///
diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs
index 1bb0cdfdf6..48da53e263 100644
--- a/src/Umbraco.Web/Editors/EntityController.cs
+++ b/src/Umbraco.Web/Editors/EntityController.cs
@@ -46,7 +46,7 @@ namespace Umbraco.Web.Editors
//This is a special case, we'll accept a String here so that we can get page members when the special "all-members"
//id is passed in eventually we'll probably want to support GUID + Udi too
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetPagedChildren", "id", typeof(int), typeof(string)),
-
+ new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetPath", "id", typeof(int), typeof(Guid), typeof(Udi)),
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetById", "id", typeof(int), typeof(Guid), typeof(Udi)),
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetByIds", "ids", typeof(int[]), typeof(Guid[]), typeof(Udi[]))));
}
@@ -153,7 +153,36 @@ namespace Umbraco.Web.Editors
return foundContent.Path.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse);
}
-
+
+ ///
+ /// Gets the path for a given node ID
+ ///
+ ///
+ ///
+ ///
+ public IEnumerable GetPath(Guid id, UmbracoEntityTypes type)
+ {
+ var foundContent = GetResultForKey(id, type);
+
+ return foundContent.Path.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse);
+ }
+
+ ///
+ /// Gets the path for a given node ID
+ ///
+ ///
+ ///
+ ///
+ public IEnumerable GetPath(Udi id, UmbracoEntityTypes type)
+ {
+ var guidUdi = id as GuidUdi;
+ if (guidUdi != null)
+ {
+ return GetPath(guidUdi.Guid, type);
+ }
+ throw new HttpResponseException(HttpStatusCode.NotFound);
+ }
+
///
/// Gets the url of an entity
///
diff --git a/src/Umbraco.Web/Routing/UrlProvider.cs b/src/Umbraco.Web/Routing/UrlProvider.cs
index 2bb012d207..f237a7d886 100644
--- a/src/Umbraco.Web/Routing/UrlProvider.cs
+++ b/src/Umbraco.Web/Routing/UrlProvider.cs
@@ -5,6 +5,7 @@ using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Web.PublishedCache;
using Umbraco.Core;
+using Umbraco.Core.Models;
namespace Umbraco.Web.Routing
{
@@ -24,6 +25,7 @@ namespace Umbraco.Web.Routing
public UrlProvider(UmbracoContext umbracoContext, IWebRoutingSection routingSettings, IEnumerable urlProviders)
{
if (umbracoContext == null) throw new ArgumentNullException("umbracoContext");
+ if (routingSettings == null) throw new ArgumentNullException("routingSettings");
_umbracoContext = umbracoContext;
_urlProviders = urlProviders;
@@ -65,6 +67,72 @@ namespace Umbraco.Web.Routing
#region GetUrl
+ ///
+ /// Gets the url of a published content.
+ ///
+ /// The published content identifier.
+ /// The url for the published content.
+ ///
+ /// The url is absolute or relative depending on Mode and on the current url.
+ /// If the provider is unable to provide a url, it returns "#".
+ ///
+ public string GetUrl(Guid id)
+ {
+ var intId = _umbracoContext.Application.Services.EntityService.GetIdForKey(id, UmbracoObjectTypes.Document);
+ return GetUrl(intId.Success ? intId.Result : -1);
+ }
+
+ ///
+ /// Gets the nice url of a published content.
+ ///
+ /// The published content identifier.
+ /// A value indicating whether the url should be absolute in any case.
+ /// The url for the published content.
+ ///
+ /// The url is absolute or relative depending on Mode and on current, unless
+ /// absolute is true, in which case the url is always absolute.
+ /// If the provider is unable to provide a url, it returns "#".
+ ///
+ public string GetUrl(Guid id, bool absolute)
+ {
+ var intId = _umbracoContext.Application.Services.EntityService.GetIdForKey(id, UmbracoObjectTypes.Document);
+ return GetUrl(intId.Success ? intId.Result : -1, absolute);
+ }
+
+ ///
+ /// Gets the nice url of a published content.
+ ///
+ /// The published content id.
+ /// The current absolute url.
+ /// A value indicating whether the url should be absolute in any case.
+ /// The url for the published content.
+ ///
+ /// The url is absolute or relative depending on Mode and on current, unless
+ /// absolute is true, in which case the url is always absolute.
+ /// If the provider is unable to provide a url, it returns "#".
+ ///
+ public string GetUrl(Guid id, Uri current, bool absolute)
+ {
+ var intId = _umbracoContext.Application.Services.EntityService.GetIdForKey(id, UmbracoObjectTypes.Document);
+ return GetUrl(intId.Success ? intId.Result : -1, current, absolute);
+ }
+
+ ///
+ /// Gets the nice url of a published content.
+ ///
+ /// The published content identifier.
+ /// The url mode.
+ /// The url for the published content.
+ ///
+ /// The url is absolute or relative depending on mode and on the current url.
+ /// If the provider is unable to provide a url, it returns "#".
+ ///
+ public string GetUrl(Guid id, UrlProviderMode mode)
+ {
+ var intId = _umbracoContext.Application.Services.EntityService.GetIdForKey(id, UmbracoObjectTypes.Document);
+ return GetUrl(intId.Success ? intId.Result : -1, mode);
+ }
+
///
/// Gets the url of a published content.
///
diff --git a/src/Umbraco.Web/Templates/TemplateUtilities.cs b/src/Umbraco.Web/Templates/TemplateUtilities.cs
index c56d7b5b8a..a7e6738374 100644
--- a/src/Umbraco.Web/Templates/TemplateUtilities.cs
+++ b/src/Umbraco.Web/Templates/TemplateUtilities.cs
@@ -4,6 +4,7 @@ using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
+using Umbraco.Web.Routing;
namespace Umbraco.Web.Templates
{
@@ -39,11 +40,51 @@ namespace Umbraco.Web.Templates
/// Parses the string looking for the {localLink} syntax and updates them to their correct links.
///
///
+ ///
///
- public static string ParseInternalLinks(string text)
+ public static string ParseInternalLinks(string text, UrlProvider urlProvider)
{
- //TODO: Pass in an Umbraco context!!!!!!!! Don't rely on the singleton so things are more testable, better yet, pass in urlprovider, routing context, separately
+ if (urlProvider == null) throw new ArgumentNullException("urlProvider");
+ // Parse internal links
+ var tags = LocalLinkPattern.Matches(text);
+ foreach (Match tag in tags)
+ {
+ if (tag.Groups.Count > 0)
+ {
+ var id = tag.Groups[1].Value; //.Remove(tag.Groups[1].Value.Length - 1, 1);
+
+ //The id could be an int or a UDI
+ Udi udi;
+ if (Udi.TryParse(id, out udi))
+ {
+ var guidUdi = udi as GuidUdi;
+ if (guidUdi != null)
+ {
+ var newLink = urlProvider.GetUrl(guidUdi.Guid);
+ text = text.Replace(tag.Value, "href=\"" + newLink);
+ }
+ }
+ int intId;
+ if (int.TryParse(id, out intId))
+ {
+ var newLink = urlProvider.GetUrl(intId);
+ text = text.Replace(tag.Value, "href=\"" + newLink);
+ }
+ }
+ }
+
+ return text;
+ }
+
+ ///
+ /// Parses the string looking for the {localLink} syntax and updates them to their correct links.
+ ///
+ ///
+ ///
+ [Obsolete("Use the overload specifying all dependencies instead")]
+ public static string ParseInternalLinks(string text)
+ {
//don't attempt to proceed without a context as we cannot lookup urls without one
if (UmbracoContext.Current == null || UmbracoContext.Current.RoutingContext == null)
{
@@ -51,22 +92,14 @@ namespace Umbraco.Web.Templates
}
var urlProvider = UmbracoContext.Current.UrlProvider;
-
- // Parse internal links
- var tags = Regex.Matches(text, @"href=""[/]?(?:\{|\%7B)localLink:([0-9]+)(?:\}|\%7D)", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
- foreach (Match tag in tags)
- if (tag.Groups.Count > 0)
- {
- var id = tag.Groups[1].Value; //.Remove(tag.Groups[1].Value.Length - 1, 1);
- var newLink = urlProvider.GetUrl(int.Parse(id));
- text = text.Replace(tag.Value, "href=\"" + newLink);
- }
-
- return text;
+ return ParseInternalLinks(text, urlProvider);
}
-
+
// static compiled regex for faster performance
- private readonly static Regex ResolveUrlPattern = new Regex("(=[\"\']?)(\\W?\\~(?:.(?![\"\']?\\s+(?:\\S+)=|[>\"\']))+.)[\"\']?",
+ private static readonly Regex LocalLinkPattern = new Regex(@"href=""[/]?(?:\{|\%7B)localLink:([a-zA-Z0-9-://]+)(?:\}|\%7D)",
+ RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
+
+ private static readonly Regex ResolveUrlPattern = new Regex("(=[\"\']?)(\\W?\\~(?:.(?![\"\']?\\s+(?:\\S+)=|[>\"\']))+.)[\"\']?",
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
///
diff --git a/src/Umbraco.Web/Trees/ContentTreeController.cs b/src/Umbraco.Web/Trees/ContentTreeController.cs
index 9a5f90ff89..b0f5d59508 100644
--- a/src/Umbraco.Web/Trees/ContentTreeController.cs
+++ b/src/Umbraco.Web/Trees/ContentTreeController.cs
@@ -84,11 +84,10 @@ namespace Umbraco.Web.Trees
var isContainer = e.IsContainer(); // && (queryStrings.Get("isDialog") != "true");
var node = CreateTreeNode(
- e.Id.ToInvariantString(),
+ entity,
+ Constants.ObjectTypes.DocumentGuid,
parentId,
- queryStrings,
- e.Name,
- entity.ContentTypeIcon,
+ queryStrings,
entity.HasChildren && (isContainer == false));
node.AdditionalData.Add("contentType", entity.ContentTypeAlias);
diff --git a/src/Umbraco.Web/Trees/ContentTypeTreeController.cs b/src/Umbraco.Web/Trees/ContentTypeTreeController.cs
index 7de352de79..6df71341cd 100644
--- a/src/Umbraco.Web/Trees/ContentTypeTreeController.cs
+++ b/src/Umbraco.Web/Trees/ContentTypeTreeController.cs
@@ -47,7 +47,7 @@ namespace Umbraco.Web.Trees
.OrderBy(entity => entity.Name)
.Select(dt =>
{
- var node = CreateTreeNode(dt.Id.ToString(), id, queryStrings, dt.Name, "icon-item-arrangement",
+ var node = CreateTreeNode(dt, Constants.ObjectTypes.DocumentTypeGuid, id, queryStrings, "icon-item-arrangement",
//NOTE: Since 7.4+ child type creation is enabled by a config option. It defaults to on, but can be disabled if we decide to.
//We need this check to keep supporting sites where childs have already been created.
dt.HasChildren());
diff --git a/src/Umbraco.Web/Trees/DataTypeTreeController.cs b/src/Umbraco.Web/Trees/DataTypeTreeController.cs
index 35ae7cabfd..0baadaf6ca 100644
--- a/src/Umbraco.Web/Trees/DataTypeTreeController.cs
+++ b/src/Umbraco.Web/Trees/DataTypeTreeController.cs
@@ -37,7 +37,7 @@ namespace Umbraco.Web.Trees
.OrderBy(entity => entity.Name)
.Select(dt =>
{
- var node = CreateTreeNode(dt.Id.ToString(), id, queryStrings, dt.Name, "icon-folder", dt.HasChildren(), "");
+ var node = CreateTreeNode(dt, Constants.ObjectTypes.DataTypeGuid, id, queryStrings, "icon-folder", dt.HasChildren());
node.Path = dt.Path;
node.NodeType = "container";
//TODO: This isn't the best way to ensure a noop process for clicking a node but it works for now.
diff --git a/src/Umbraco.Web/Trees/MediaTreeController.cs b/src/Umbraco.Web/Trees/MediaTreeController.cs
index b08db4cacd..528080c35a 100644
--- a/src/Umbraco.Web/Trees/MediaTreeController.cs
+++ b/src/Umbraco.Web/Trees/MediaTreeController.cs
@@ -73,11 +73,10 @@ namespace Umbraco.Web.Trees
var isContainer = e.IsContainer(); // && (queryStrings.Get("isDialog") != "true");
var node = CreateTreeNode(
- e.Id.ToInvariantString(),
+ entity,
+ Constants.ObjectTypes.MediaGuid,
parentId,
- queryStrings,
- e.Name,
- entity.ContentTypeIcon,
+ queryStrings,
entity.HasChildren && (isContainer == false));
node.AdditionalData.Add("contentType", entity.ContentTypeAlias);
diff --git a/src/Umbraco.Web/Trees/MediaTypeTreeController.cs b/src/Umbraco.Web/Trees/MediaTypeTreeController.cs
index 31fb899f81..e116e43825 100644
--- a/src/Umbraco.Web/Trees/MediaTypeTreeController.cs
+++ b/src/Umbraco.Web/Trees/MediaTypeTreeController.cs
@@ -47,7 +47,7 @@ namespace Umbraco.Web.Trees
.OrderBy(entity => entity.Name)
.Select(dt =>
{
- var node = CreateTreeNode(dt.Id.ToString(), id, queryStrings, dt.Name, "icon-item-arrangement",
+ var node = CreateTreeNode(dt, Constants.ObjectTypes.MediaTypeGuid, id, queryStrings, "icon-item-arrangement",
//NOTE: Since 7.4+ child type creation is enabled by a config option. It defaults to on, but can be disabled if we decide to.
//We need this check to keep supporting sites where childs have already been created.
dt.HasChildren());
diff --git a/src/Umbraco.Web/Trees/MemberTreeController.cs b/src/Umbraco.Web/Trees/MemberTreeController.cs
index fa6eb64571..fe7aa8a689 100644
--- a/src/Umbraco.Web/Trees/MemberTreeController.cs
+++ b/src/Umbraco.Web/Trees/MemberTreeController.cs
@@ -79,7 +79,9 @@ namespace Umbraco.Web.Trees
queryStrings,
member.Name,
"icon-user",
- false);
+ false,
+ "",
+ Udi.Create(UmbracoObjectTypesExtensions.GetUdiType(Constants.ObjectTypes.MemberGuid), member.Key));
node.AdditionalData.Add("contentType", member.ContentTypeAlias);
node.AdditionalData.Add("isContainer", true);
diff --git a/src/Umbraco.Web/Trees/MemberTypeTreeController.cs b/src/Umbraco.Web/Trees/MemberTypeTreeController.cs
index d739c2a661..1e0c32a4b4 100644
--- a/src/Umbraco.Web/Trees/MemberTypeTreeController.cs
+++ b/src/Umbraco.Web/Trees/MemberTypeTreeController.cs
@@ -25,7 +25,7 @@ namespace Umbraco.Web.Trees
nodes.AddRange(
Services.MemberTypeService.GetAll()
.OrderBy(x => x.Name)
- .Select(dt => CreateTreeNode(dt.Id.ToString(), id, queryStrings, dt.Name, "icon-item-arrangement", false)));
+ .Select(dt => CreateTreeNode(dt, Constants.ObjectTypes.MemberTypeGuid, id, queryStrings, "icon-item-arrangement", false)));
return nodes;
}
diff --git a/src/Umbraco.Web/Trees/TemplatesTreeController.cs b/src/Umbraco.Web/Trees/TemplatesTreeController.cs
index 04458cbc20..b92e79fe7b 100644
--- a/src/Umbraco.Web/Trees/TemplatesTreeController.cs
+++ b/src/Umbraco.Web/Trees/TemplatesTreeController.cs
@@ -48,13 +48,14 @@ namespace Umbraco.Web.Trees
nodes.AddRange(found.Select(template => CreateTreeNode(
template.Id.ToString(CultureInfo.InvariantCulture),
//TODO: Fix parent ID stuff for templates
- "-1",
+ "-1",
queryStrings,
template.Name,
template.IsMasterTemplate ? "icon-newspaper" : "icon-newspaper-alt",
- template.IsMasterTemplate,
- GetEditorPath(template, queryStrings)
- )));
+ template.IsMasterTemplate,
+ GetEditorPath(template, queryStrings),
+ Udi.Create(UmbracoObjectTypesExtensions.GetUdiType(Constants.ObjectTypes.TemplateTypeGuid), template.Key)
+ )));
return nodes;
}
diff --git a/src/Umbraco.Web/Trees/TreeControllerBase.cs b/src/Umbraco.Web/Trees/TreeControllerBase.cs
index 91191a165d..751f83db16 100644
--- a/src/Umbraco.Web/Trees/TreeControllerBase.cs
+++ b/src/Umbraco.Web/Trees/TreeControllerBase.cs
@@ -8,6 +8,7 @@ using Umbraco.Web.Models.Trees;
using Umbraco.Web.WebApi;
using Umbraco.Web.WebApi.Filters;
using Umbraco.Core.Models;
+using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Web.Trees
{
@@ -229,6 +230,41 @@ namespace Umbraco.Web.Trees
return node;
}
+ ///
+ /// Helper method to create tree nodes and automatically generate the json url + UDI
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public TreeNode CreateTreeNode(UmbracoEntity entity, Guid entityObjectType, string parentId, FormDataCollection queryStrings, bool hasChildren)
+ {
+ var treeNode = CreateTreeNode(entity.Id.ToInvariantString(), parentId, queryStrings, entity.Name, entity.ContentTypeIcon);
+ treeNode.Udi = Udi.Create(UmbracoObjectTypesExtensions.GetUdiType(entityObjectType), entity.Key);
+ treeNode.HasChildren = hasChildren;
+ return treeNode;
+ }
+
+ ///
+ /// Helper method to create tree nodes and automatically generate the json url + UDI
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public TreeNode CreateTreeNode(IUmbracoEntity entity, Guid entityObjectType, string parentId, FormDataCollection queryStrings, string icon, bool hasChildren)
+ {
+ var treeNode = CreateTreeNode(entity.Id.ToInvariantString(), parentId, queryStrings, entity.Name, icon);
+ treeNode.Udi = Udi.Create(UmbracoObjectTypesExtensions.GetUdiType(entityObjectType), entity.Key);
+ treeNode.HasChildren = hasChildren;
+ return treeNode;
+ }
+
///
/// Helper method to create tree nodes and automatically generate the json url
///
@@ -265,6 +301,27 @@ namespace Umbraco.Web.Trees
return treeNode;
}
+ ///
+ /// Helper method to create tree nodes and automatically generate the json url + UDI
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public TreeNode CreateTreeNode(string id, string parentId, FormDataCollection queryStrings, string title, string icon, bool hasChildren, string routePath, Udi udi)
+ {
+ var treeNode = CreateTreeNode(id, parentId, queryStrings, title, icon);
+ treeNode.HasChildren = hasChildren;
+ treeNode.RoutePath = routePath;
+ treeNode.Udi = udi;
+ return treeNode;
+ }
+
#endregion
///