diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs
index 33a5730444..7f8cc95a13 100644
--- a/src/Umbraco.Core/StringExtensions.cs
+++ b/src/Umbraco.Core/StringExtensions.cs
@@ -28,6 +28,33 @@ namespace Umbraco.Core
[UmbracoWillObsolete("Do not use this constants. See IShortStringHelper.CleanStringForSafeAliasJavaScriptCode.")]
public const string UmbracoInvalidFirstCharacters = "01234567890";
+ ///
+ /// This will append the query string to the URL
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// This methods ensures that the resulting URL is structured correctly, that there's only one '?' and that things are
+ /// delimited properly with '&'
+ ///
+ internal static string AppendQueryStringToUrl(this string url, params string[] queryStrings)
+ {
+ //remove any prefixed '&' or '?'
+ for (var i = 0; i < queryStrings.Length; i++)
+ {
+ queryStrings[i] = queryStrings[i].TrimStart('?', '&').TrimEnd('&');
+ }
+
+ var nonEmpty = queryStrings.Where(x => !x.IsNullOrWhiteSpace()).ToArray();
+
+ if (url.Contains("?"))
+ {
+ return url + string.Join("&", nonEmpty).EnsureStartsWith('&');
+ }
+ return url + string.Join("&", nonEmpty).EnsureStartsWith('?');
+ }
+
///
/// Encrypt the string using the MachineKey in medium trust
///
diff --git a/src/Umbraco.Web.UI.Client/docs/src/using-promises-resources.md b/src/Umbraco.Web.UI.Client/docs/src/using-promises-resources.md
new file mode 100644
index 0000000000..ad84149dd3
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/docs/src/using-promises-resources.md
@@ -0,0 +1,103 @@
+#Using AngularJS Promises and Umbraco Resources
+
+##Promises in Umbraco Resources
+
+All Umbraco resource methods utilize a helper method:
+
+ angularHelper.resourcePromise
+
+This method accepts 2 arguments:
+
+* The angular HttpPromise which is created with a call to $http.get (post, etc..)
+* The error message that is bubbled to the UI when the http call fails
+
+Here's an example of the usage in an Umbraco resource. This example is the method of the treeResource that fetches data to display the menu for a tree node:
+
+ /** Loads in the data to display the nodes menu */
+ loadMenu: function (node) {
+
+ return angularHelper.resourcePromise(
+ $http.get(getTreeMenuUrl(node)),
+ "Failed to retreive data for a node's menu " + node.id);
+ }
+
+HTTP error handling is performed automatically inside of the `angularHelper.resourcePromise` and inside of Umbraco's response interceptors.
+
+##Consuming Umbraco resources
+
+When consuming Umbraco resources, a normal angular promise will be returned based on the above `angularHelper.resourcePromise`. The success callback will always receive the RAW json data from the server and the error callback will always receive an object containing these properties:
+
+* erroMsg = the error message that can be used to show in the UI
+* data = the original data object used to create the promise
+
+Error handling will be done automatically in the Umbraco resource. Http error handling should not be done during the consumption of an Umbraco resource.
+
+###Simple example
+
+An simple example of consuming an Umbraco resource:
+
+ treeResource.loadMenu(treeItem.node)
+ .then(function(data) {
+ scope.menu = data;
+ });
+
+###Transforming result data
+
+Sometimes the consumption of an Umbraco resource needs to return a promise itself. This is required in some circumstances such as:
+
+The data from a result of an http resource might need to be transformed into something usable in the UI so a Service may need to call a resource, transform the result and continue to return it's own promise (since everything happens async).
+
+This is actually very simple to do, the Service (or whatever is consuming the resource) just returns the result of their 'then' call. Example - this example is the `getActions` method of the `treeService` that consumes the treeResource, transforms the result and continues to return it's own promise:
+
+ getActions: function(treeItem, section) {
+
+ return treeResource.loadMenu(treeItem.node)
+ .then(function(data) {
+ //need to convert the icons to new ones
+ for (var i = 0; i < data.length; i++) {
+ data[i].cssclass = iconHelper.convertFromLegacyIcon(data[i].cssclass);
+ }
+ return data;
+ });
+ }
+
+Notice that this is just returning the call to 'then' which will return a promise that resolves the data from it's return statement.
+
+###Error hanlding
+
+Ok, what about error handling ? This is really simple as well, we just add an additional method to the .then call. A simple example:
+
+ treeResource.loadMenu(treeItem.node)
+ .then(function(data) {
+ scope.menu = data;
+ }, function(err) {
+ //display the error
+ notificationsService.error(err.errorMsg);
+ });
+
+###Error handling when transforming result data
+
+This is one of those things that is important to note! If you need to return a custom promise based on the result of an Umbraco resource (like the example above in Transforming result data) then you will need to 'throw' an error if you want to 'bubble' the error to the handler of your custom promise.
+
+The good news is, this is very simple to do, example:
+
+ getActions: function(treeItem, section) {
+
+ return treeResource.loadMenu(treeItem.node)
+ .then(function(data) {
+ //need to convert the icons to new ones
+ for (var i = 0; i < data.length; i++) {
+ data[i].cssclass = iconHelper.convertFromLegacyIcon(data[i].cssclass);
+ }
+ return data;
+ }, function(err) {
+ //display the error
+ notificationsService.error(err.errorMsg);
+
+ //since we want the handler of this promise to be notified of this error
+ // we just need to rethrow it:
+ throw err;
+ });
+ }
+
+The next thing that is important to note is that **you don't have to do anything** if you don't want to do anything with the error but still want the error bubbled up to your promises handlers. So for example, if you are expecting the handler of this promise to handle the error and display something in the UI, just leave out the function(err) callback which would look exactly the same as the example for 'Transforming result data'
\ No newline at end of file
diff --git a/src/Umbraco.Web.UI.Client/lib/umbraco/compat.js b/src/Umbraco.Web.UI.Client/lib/umbraco/compat.js
index 2d0d9edf7b..c88da6339b 100644
--- a/src/Umbraco.Web.UI.Client/lib/umbraco/compat.js
+++ b/src/Umbraco.Web.UI.Client/lib/umbraco/compat.js
@@ -1,32 +1,42 @@
/* contains random bits and pieces we neede to make the U6 UI behave */
-jQuery(document).ready(function () {
- scaleScrollables("body");
+
+Umbraco.Sys.registerNamespace("Umbraco.Application.LegacyHelper");
+
+(function ($) {
- jQuery(window).bind("resize", function () {
- scaleScrollables("body");
- });
- jQuery("body").click(function(event) {
- var el = event.target.nodeName;
- var pEl = event.target.parentElement.nodeName;
+ $(document).ready(function () {
+ Umbraco.Application.LegacyHelper.scaleScrollables("body");
- if(el != "A" && el != "BUTTON" && pEl != "A" && pEl != "BUTTON"){
- UmbClientMgr.closeModalWindow(undefined);
- }
- });
-});
-
-
-
-function scaleScrollables(selector){
- jQuery(".umb-scrollable").each(function () {
- var el = jQuery(this);
- var totalOffset = 0;
- var offsety = el.data("offset-y");
-
- if (offsety != undefined)
- totalOffset += offsety;
-
- el.height($(window).height() - (el.offset().top + totalOffset));
+ $(window).bind("resize", function () {
+ Umbraco.Application.LegacyHelper.scaleScrollables("body");
});
-}
\ No newline at end of file
+
+ $("body").click(function (event) {
+ var el = event.target.nodeName;
+ var pEl = event.target.parentElement.nodeName;
+
+ if (el != "A" && el != "BUTTON" && pEl != "A" && pEl != "BUTTON") {
+ UmbClientMgr.closeModalWindow(undefined);
+ }
+ });
+ });
+
+ /** Static helper class */
+ Umbraco.Application.LegacyHelper = {
+
+ scaleScrollables: function (selector) {
+ $(".umb-scrollable").each(function() {
+ var el = jQuery(this);
+ var totalOffset = 0;
+ var offsety = el.data("offset-y");
+
+ if (offsety != undefined)
+ totalOffset += offsety;
+
+ el.height($(window).height() - (el.offset().top + totalOffset));
+ });
+ }
+ };
+
+})(jQuery);
\ No newline at end of file
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/umbtree.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/umbtree.directive.js
index 2504d82160..f543c3c3c5 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/umbtree.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/umbtree.directive.js
@@ -45,22 +45,20 @@ angular.module("umbraco.directives")
return function (scope, element, attrs, controller) {
- function loadTree(){
- if(scope.section){
+ function loadTree() {
+ if (scope.section) {
- $q.when(treeService.getTree({ section: scope.section, cachekey: scope.cachekey }))
- .then(function (data) {
- //set the data once we have it
- scope.tree = data;
- }, function (reason) {
+ //use $q.when because a promise OR raw data might be returned.
- notificationsService.error("Tree Error", reason);
- return;
- });
-
- // scope.tree = treeService.getTree({section:scope.section, cachekey: scope.cachekey});
- }
- }
+ $q.when(treeService.getTree({ section: scope.section, cachekey: scope.cachekey }))
+ .then(function(data) {
+ //set the data once we have it
+ scope.tree = data;
+ }, function(reason) {
+ notificationsService.error("Tree Error", reason);
+ });
+ }
+ }
//watch for section changes
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/umbtreeitem.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/umbtreeitem.directive.js
index ca921d128d..6aeac0796c 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/umbtreeitem.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/umbtreeitem.directive.js
@@ -115,7 +115,6 @@ angular.module("umbraco.directives")
//tell notications about the error
notificationsService.error(reason);
- return;
});
}
};
@@ -135,4 +134,4 @@ angular.module("umbraco.directives")
element.append(newElement);
}
};
-});
\ No newline at end of file
+});
diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/tree.mocks.js b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/tree.mocks.js
index 11b2b24d24..3f648ea8f6 100644
--- a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/tree.mocks.js
+++ b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/tree.mocks.js
@@ -3,7 +3,8 @@ angular.module('umbraco.mocks').
'use strict';
function getMenuItems() {
- return [
+
+ var menu = [
{ name: "Create", cssclass: "plus", alias: "create" },
{ seperator: true, name: "Delete", cssclass: "remove", alias: "delete" },
@@ -23,6 +24,8 @@ angular.module('umbraco.mocks').
{ seperator: true, name: "Reload", cssclass: "refresh", alias: "users" }
];
+
+ return [200, menu, null];
}
function returnChildren(status, data, headers) {
@@ -32,6 +35,8 @@ angular.module('umbraco.mocks').
var level = mocksUtills.getParameterByName(data, "level")+1;
var url = "/umbraco/UmbracoTrees/ApplicationTreeApi/GetChildren?treeType=" + section + "&id=1234&level=" + level;
+ var menuUrl = "/umbraco/UmbracoTrees/ApplicationTreeApi/GetMenu?treeType=" + section + "&id=1234&parentId=456";
+
//hack to have create as default content action
var action;
if (section === "content") {
@@ -39,10 +44,10 @@ angular.module('umbraco.mocks').
}
var children = [
- { name: "child-of-" + section, childNodesUrl:url, id: level + "" + 1234, icon: "icon-file-alt", view: section + "/edit/" + level + "" + 1234, children: [], expanded: false, hasChildren: true, level: level, defaultAction: action, menu: getMenuItems() },
- { name: "random-name-" + section, childNodesUrl:url, id: level + "" + 1235, icon: "icon-file-alt", view: section + "/edit/" + level + "" + 1235, children: [], expanded: false, hasChildren: true, level: level, defaultAction: action, menu: getMenuItems() },
- { name: "random-name-" + section, childNodesUrl:url, id: level + "" + 1236, icon: "icon-file-alt", view: section + "/edit/" + level + "" + 1236, children: [], expanded: false, hasChildren: true, level: level, defaultAction: action, menu: getMenuItems() },
- { name: "random-name-" + section, childNodesUrl:url, id: level + "" + 1237, icon: "icon-file-alt", view: "common/legacy/1237?p=" + encodeURI("developer/contentType.aspx?idequal1234"), children: [], expanded: false, hasChildren: true, level: level, defaultAction: action, menu: getMenuItems() }
+ { name: "child-of-" + section, childNodesUrl: url, id: level + "" + 1234, icon: "icon-file-alt", view: section + "/edit/" + level + "" + 1234, children: [], expanded: false, hasChildren: true, level: level, defaultAction: action, menuUrl: menuUrl },
+ { name: "random-name-" + section, childNodesUrl: url, id: level + "" + 1235, icon: "icon-file-alt", view: section + "/edit/" + level + "" + 1235, children: [], expanded: false, hasChildren: true, level: level, defaultAction: action, menuUrl: menuUrl },
+ { name: "random-name-" + section, childNodesUrl: url, id: level + "" + 1236, icon: "icon-file-alt", view: section + "/edit/" + level + "" + 1236, children: [], expanded: false, hasChildren: true, level: level, defaultAction: action, menuUrl: menuUrl },
+ { name: "random-name-" + section, childNodesUrl: url, id: level + "" + 1237, icon: "icon-file-alt", view: "common/legacy/1237?p=" + encodeURI("developer/contentType.aspx?idequal1234"), children: [], expanded: false, hasChildren: true, level: level, defaultAction: action, menuUrl: menuUrl }
];
return [200, children, null];
@@ -51,42 +56,43 @@ angular.module('umbraco.mocks').
function returnApplicationTrees(status, data, headers) {
var section = mocksUtills.getParameterByName(data, "application");
var url = "/umbraco/UmbracoTrees/ApplicationTreeApi/GetChildren?treeType=" + section + "&id=1234&level=1";
+ var menuUrl = "/umbraco/UmbracoTrees/ApplicationTreeApi/GetMenu?treeType=" + section + "&id=1234&parentId=456";
var t;
switch (section) {
case "content":
t = [
- { name: "My website", id: 1234, childNodesUrl:url, icon: "icon-home", view: section + "/edit/" + 1234, children: [], expanded: false, hasChildren: true, level: 1, defaultAction: "create", menu: getMenuItems() },
- { name: "Components", id: 1235, childNodesUrl:url, icon: "icon-cogs", view: section + "/edit/" + 1235, children: [], expanded: false, hasChildren: true, level: 1, defaultAction: "create", menu: getMenuItems() },
- { name: "Archieve", id: 1236, childNodesUrl:url, icon: "icon-folder-close", view: section + "/edit/" + 1236, children: [], expanded: false, hasChildren: true, level: 1, defaultAction: "create", menu: getMenuItems() },
- { name: "Recycle Bin", id: 1237, childNodesUrl:url, icon: "icon-trash", view: section + "/trash/view/", children: [], expanded: false, hasChildren: true, level: 1, defaultAction: "create", menu: getMenuItems() }
+ { name: "My website", id: 1234, childNodesUrl: url, icon: "icon-home", view: section + "/edit/" + 1234, children: [], expanded: false, hasChildren: true, level: 1, defaultAction: "create", menuUrl: menuUrl },
+ { name: "Components", id: 1235, childNodesUrl: url, icon: "icon-cogs", view: section + "/edit/" + 1235, children: [], expanded: false, hasChildren: true, level: 1, defaultAction: "create", menuUrl: menuUrl },
+ { name: "Archieve", id: 1236, childNodesUrl: url, icon: "icon-folder-close", view: section + "/edit/" + 1236, children: [], expanded: false, hasChildren: true, level: 1, defaultAction: "create", menuUrl: menuUrl },
+ { name: "Recycle Bin", id: 1237, childNodesUrl: url, icon: "icon-trash", view: section + "/trash/view/", children: [], expanded: false, hasChildren: true, level: 1, defaultAction: "create", menuUrl: menuUrl }
];
break;
case "developer":
t = [
- { name: "Data types", childNodesUrl:url, id: 1234, icon: "icon-folder-close", view: section + "/edit/" + 1234, children: [], expanded: false, hasChildren: true, level: 1, menu: getMenuItems() },
- { name: "Macros", childNodesUrl:url, id: 1235, icon: "icon-folder-close", view: section + "/edit/" + 1235, children: [], expanded: false, hasChildren: true, level: 1, menu: getMenuItems() },
- { name: "Pacakges", childNodesUrl:url, id: 1236, icon: "icon-folder-close", view: section + "/edit/" + 1236, children: [], expanded: false, hasChildren: true, level: 1, menu: getMenuItems() },
- { name: "XSLT Files", childNodesUrl:url, id: 1237, icon: "icon-folder-close", view: section + "/edit/" + 1237, children: [], expanded: false, hasChildren: true, level: 1, menu: getMenuItems() },
- { name: "Razor Files", childNodesUrl:url, id: 1237, icon: "icon-folder-close", view: section + "/edit/" + 1237, children: [], expanded: false, hasChildren: true, level: 1, menu: getMenuItems() }
+ { name: "Data types", childNodesUrl: url, id: 1234, icon: "icon-folder-close", view: section + "/edit/" + 1234, children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
+ { name: "Macros", childNodesUrl: url, id: 1235, icon: "icon-folder-close", view: section + "/edit/" + 1235, children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
+ { name: "Pacakges", childNodesUrl: url, id: 1236, icon: "icon-folder-close", view: section + "/edit/" + 1236, children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
+ { name: "XSLT Files", childNodesUrl: url, id: 1237, icon: "icon-folder-close", view: section + "/edit/" + 1237, children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
+ { name: "Razor Files", childNodesUrl: url, id: 1237, icon: "icon-folder-close", view: section + "/edit/" + 1237, children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl }
];
break;
case "settings":
t = [
- { name: "Stylesheets", childNodesUrl:url, id: 1234, icon: "icon-folder-close", view: section + "/edit/" + 1234, children: [], expanded: false, hasChildren: true, level: 1, menu: getMenuItems() },
- { name: "Templates", childNodesUrl:url, id: 1235, icon: "icon-folder-close", view: section + "/edit/" + 1235, children: [], expanded: false, hasChildren: true, level: 1, menu: getMenuItems() },
- { name: "Dictionary", childNodesUrl:url, id: 1236, icon: "icon-folder-close", view: section + "/edit/" + 1236, children: [], expanded: false, hasChildren: true, level: 1, menu: getMenuItems() },
- { name: "Media types", childNodesUrl:url, id: 1237, icon: "icon-folder-close", view: section + "/edit/" + 1237, children: [], expanded: false, hasChildren: true, level: 1, menu: getMenuItems() },
- { name: "Document types", childNodesUrl:url, id: 1237, icon: "icon-folder-close", view: section + "/edit/" + 1237, children: [], expanded: false, hasChildren: true, level: 1, menu: getMenuItems() }
+ { name: "Stylesheets", childNodesUrl: url, id: 1234, icon: "icon-folder-close", view: section + "/edit/" + 1234, children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
+ { name: "Templates", childNodesUrl: url, id: 1235, icon: "icon-folder-close", view: section + "/edit/" + 1235, children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
+ { name: "Dictionary", childNodesUrl: url, id: 1236, icon: "icon-folder-close", view: section + "/edit/" + 1236, children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
+ { name: "Media types", childNodesUrl: url, id: 1237, icon: "icon-folder-close", view: section + "/edit/" + 1237, children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
+ { name: "Document types", childNodesUrl: url, id: 1237, icon: "icon-folder-close", view: section + "/edit/" + 1237, children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl }
];
break;
default:
t = [
- { name: "random-name-" + section, childNodesUrl:url, id: 1234, icon: "icon-home", defaultAction: "create", view: section + "/edit/" + 1234, children: [], expanded: false, hasChildren: true, level: 1, menu: getMenuItems() },
- { name: "random-name-" + section, childNodesUrl:url, id: 1235, icon: "icon-folder-close", defaultAction: "create", view: section + "/edit/" + 1235, children: [], expanded: false, hasChildren: true, level: 1, menu: getMenuItems() },
- { name: "random-name-" + section, childNodesUrl:url, id: 1236, icon: "icon-folder-close", defaultAction: "create", view: section + "/edit/" + 1236, children: [], expanded: false, hasChildren: true, level: 1, menu: getMenuItems() },
- { name: "random-name-" + section, childNodesUrl:url, id: 1237, icon: "icon-folder-close", defaultAction: "create", view: section + "/edit/" + 1237, children: [], expanded: false, hasChildren: true, level: 1, menu: getMenuItems() }
+ { name: "random-name-" + section, childNodesUrl: url, id: 1234, icon: "icon-home", defaultAction: "create", view: section + "/edit/" + 1234, children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
+ { name: "random-name-" + section, childNodesUrl: url, id: 1235, icon: "icon-folder-close", defaultAction: "create", view: section + "/edit/" + 1235, children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
+ { name: "random-name-" + section, childNodesUrl: url, id: 1236, icon: "icon-folder-close", defaultAction: "create", view: section + "/edit/" + 1236, children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
+ { name: "random-name-" + section, childNodesUrl: url, id: 1237, icon: "icon-folder-close", defaultAction: "create", view: section + "/edit/" + 1237, children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl }
];
break;
}
@@ -105,7 +111,11 @@ angular.module('umbraco.mocks').
$httpBackend
.whenGET(mocksUtills.urlRegex('/umbraco/UmbracoTrees/ApplicationTreeApi/GetChildren'))
- .respond(returnChildren);
+ .respond(returnChildren);
+
+ $httpBackend
+ .whenGET(mocksUtills.urlRegex('/umbraco/UmbracoTrees/ApplicationTreeApi/GetMenu'))
+ .respond(getMenuItems);
}
};
diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/auth.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/auth.resource.js
index 3c761bb581..c61ce5aefe 100644
--- a/src/Umbraco.Web.UI.Client/src/common/resources/auth.resource.js
+++ b/src/Umbraco.Web.UI.Client/src/common/resources/auth.resource.js
@@ -46,16 +46,18 @@ function authResource($q, $http, umbDataFormatter, umbRequestHelper) {
//send the data
$http.get(getIsAuthUrl()).
success(function (data, status, headers, config) {
- deferred.resolve(data);
+
+ //204 - means the current user is not-authorized, the result was empty.
+ if (status === 204) {
+ //if it's unauthorized it just means we are not authenticated so we'll just return null
+ deferred.reject(null);
+ }
+ else {
+ deferred.resolve(data);
+ }
}).
error(function (data, status, headers, config) {
- if (status === 401) {
- //if it's unauthorized it just means we are not authenticated so we'll just return null
- deferred.resolve(null);
- }
- else {
- deferred.reject('Server call failed for checking authorization');
- }
+ deferred.reject('Server call failed for checking authorization');
});
return deferred.promise;
diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/tree.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/tree.resource.js
index 3622fc5b08..441d8788ab 100644
--- a/src/Umbraco.Web.UI.Client/src/common/resources/tree.resource.js
+++ b/src/Umbraco.Web.UI.Client/src/common/resources/tree.resource.js
@@ -3,23 +3,40 @@
* @name umbraco.resources.treeResource
* @description Loads in data for trees
**/
-function treeResource($q, $http) {
+function treeResource($q, $http, angularHelper) {
/** internal method to get the tree app url */
function getTreeAppUrl(section) {
return Umbraco.Sys.ServerVariables.treeApplicationApiBaseUrl + "GetApplicationTrees?application=" + section;
}
-
+
/** internal method to get the tree node's children url */
function getTreeNodesUrl(node) {
- if (!node.childNodesUrl){
+ if (!node.childNodesUrl) {
throw "No childNodesUrl property found on the tree node, cannot load child nodes";
}
return node.childNodesUrl;
}
+ /** internal method to get the tree menu url */
+ function getTreeMenuUrl(node) {
+ if (!node.menuUrl) {
+ throw "No menuUrl property found on the tree node, cannot load menu";
+ }
+ return node.menuUrl;
+ }
+
//the factory object returned
return {
+
+ /** Loads in the data to display the nodes menu */
+ loadMenu: function (node) {
+
+ return angularHelper.resourcePromise(
+ $http.get(getTreeMenuUrl(node)),
+ "Failed to retreive data for a node's menu " + node.id);
+ },
+
/** Loads in the data to display the nodes for an application */
loadApplication: function (options) {
@@ -27,19 +44,11 @@ function treeResource($q, $http) {
throw "The object specified for does not contain a 'section' property";
}
- var deferred = $q.defer();
-
- //go and get the tree data
- $http.get(getTreeAppUrl(options.section)).
- success(function (data, status, headers, config) {
- deferred.resolve(data);
- }).
- error(function (data, status, headers, config) {
- deferred.reject('Failed to retreive data for application tree ' + options.section);
- });
-
- return deferred.promise;
+ return angularHelper.resourcePromise(
+ $http.get(getTreeAppUrl(options.section)),
+ 'Failed to retreive data for application tree ' + options.section);
},
+
/** Loads in the data to display the child nodes for a given node */
loadNodes: function (options) {
@@ -47,19 +56,9 @@ function treeResource($q, $http) {
throw "The options parameter object does not contain the required properties: 'node' and 'section'";
}
- var deferred = $q.defer();
-
- //go and get the tree data
- $http.get(getTreeNodesUrl(options.node)).
- success(function (data, status, headers, config) {
- deferred.resolve(data);
- }).
- error(function (data, status, headers, config) {
- deferred.reject('Failed to retreive data for child nodes ' + options.node.nodeId);
- });
-
- return deferred.promise;
-
+ return angularHelper.resourcePromise(
+ $http.get(getTreeNodesUrl(options.node)),
+ 'Failed to retreive data for child nodes ' + options.node.nodeId);
}
};
}
diff --git a/src/Umbraco.Web.UI.Client/src/common/security/security.service.js b/src/Umbraco.Web.UI.Client/src/common/security/security.service.js
index fce6964e49..4e4f2adc77 100644
--- a/src/Umbraco.Web.UI.Client/src/common/security/security.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/security/security.service.js
@@ -38,11 +38,11 @@ angular.module('umbraco.security.service', [
callback: onLoginDialogClose});
}
- function closeLoginDialog(success) {
- if (loginDialog) {
- loginDialog = null;
- }
- }
+ //function closeLoginDialog(success) {
+ // if (loginDialog) {
+ // loginDialog = null;
+ // }
+ //}
function onLoginDialogClose(success) {
loginDialog = null;
@@ -55,44 +55,46 @@ angular.module('umbraco.security.service', [
}
}
+ //TODO: Clean this class up since most of this doesn't do anything!
+
// The public API of the service
var service = {
- // Get the first reason for needing a login
- getLoginReason: function() {
- return queue.retryReason();
- },
+ //// Get the first reason for needing a login
+ //getLoginReason: function() {
+ // return queue.retryReason();
+ //},
// Show the modal login dialog
showLogin: function() {
openLoginDialog();
- },
+ },
+
+ //// Attempt to authenticate a user by the given email and password
+ //login: function(email, password) {
+ // var request = $http.post('/login', {email: email, password: password});
+ // return request.then(function(response) {
+ // service.currentUser = response.data.user;
+ // if ( service.isAuthenticated() ) {
+ // closeLoginDialog(true);
+ // }
+ // });
+ //},
- // Attempt to authenticate a user by the given email and password
- login: function(email, password) {
- var request = $http.post('/login', {email: email, password: password});
- return request.then(function(response) {
- service.currentUser = response.data.user;
- if ( service.isAuthenticated() ) {
- closeLoginDialog(true);
- }
- });
- },
+ //// Give up trying to login and clear the retry queue
+ //cancelLogin: function() {
+ // closeLoginDialog(false);
+ // redirect();
+ //},
- // Give up trying to login and clear the retry queue
- cancelLogin: function() {
- closeLoginDialog(false);
- redirect();
- },
-
- // Logout the current user and redirect
- logout: function(redirectTo) {
- $http.post('/logout').then(function() {
- service.currentUser = null;
- redirect(redirectTo);
- });
- },
+ //// Logout the current user and redirect
+ //logout: function(redirectTo) {
+ // $http.post('/logout').then(function() {
+ // service.currentUser = null;
+ // redirect(redirectTo);
+ // });
+ //},
// Ask the backend to see if a user is already authenticated - this may be from a previous session.
requestCurrentUser: function() {
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js
index 7e2f035c05..69d78ea38f 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js
@@ -1,294 +1,301 @@
-/**
- * @ngdoc service
- * @name umbraco.services.navigationService
- *
- * @requires $rootScope
- * @requires $routeParams
- * @requires $log
- * @requires $location
- * @requires dialogService
- * @requires treeService
- * @requires sectionResource
- *
- * @description
- * Service to handle the main application navigation. Responsible for invoking the tree
- * Section navigation and search, and maintain their state for the entire application lifetime
- *
- */
-
- angular.module('umbraco.services')
- .factory('navigationService', function ($rootScope, $routeParams, $log, $location, dialogService, treeService, sectionResource) {
-
- var currentSection = $routeParams.section;
- var currentId = $routeParams.id;
- var currentNode;
- var ui = {};
-
- var _sections = sectionResource.getSections();
-
- function setMode(mode){
- switch(mode)
- {
- case 'tree':
- ui.showNavigation = true;
- ui.showContextMenu = false;
- ui.showContextMenuDialog = false;
- ui.stickyNavigation = false;
-
- $("#search-form input").focus();
- break;
- case 'menu':
- ui.showNavigation = true;
- ui.showContextMenu = true;
- ui.showContextMenuDialog = false;
- ui.stickyNavigation = true;
- break;
- case 'dialog':
- ui.stickyNavigation = true;
- ui.showNavigation = true;
- ui.showContextMenu = false;
- ui.showContextMenuDialog = true;
- break;
- case 'search':
- ui.stickyNavigation = false;
- ui.showNavigation = true;
- ui.showContextMenu = false;
- ui.showSearchResults = true;
- ui.showContextMenuDialog = false;
- break;
- default:
- ui.showNavigation = false;
- ui.showContextMenu = false;
- ui.showContextMenuDialog = false;
- ui.showSearchResults = false;
- ui.stickyNavigation = false;
- break;
- }
- }
-
- return {
- currentNode: currentNode,
- mode: "default",
- ui: ui,
- sections: _sections,
-
- /**
- * @ngdoc method
- * @name umbraco.services.navigationService#load
- * @methodOf umbraco.services.navigationService
- *
- * @description
- * Shows the legacy iframe and loads in the content based on the source url
- * @param {String} source The URL to load into the iframe
- */
- loadLegacyIFrame: function (source) {
- $location.path("/framed/" + encodeURIComponent(source));
- },
-
- /**
- * @ngdoc method
- * @name umbraco.services.navigationService#changeSection
- * @methodOf umbraco.services.navigationService
- *
- * @description
- * Changes the active section to a given section alias
- * If the navigation is 'sticky' this will load the associated tree
- * and load the dashboard related to the section
- * @param {string} sectionAlias The alias of the section
- */
- changeSection: function(sectionAlias){
- if(this.ui.stickyNavigation){
- setMode("default-opensection");
- this.ui.currentSection = selectedSection;
- this.showTree(selectedSection);
- }
-
- $location.path(sectionAlias);
- },
-
- /**
- * @ngdoc method
- * @name umbraco.services.navigationService#showTree
- * @methodOf umbraco.services.navigationService
- *
- * @description
- * Shows the tree for a given tree alias but turning on the containing dom element
- * only changes if the section is different from the current one
- * @param {string} sectionAlias The alias of the section the tree should load data from
- */
- showTree: function(sectionAlias){
- if(!this.ui.stickyNavigation && sectionAlias !== this.ui.currentTree){
- this.ui.currentTree = sectionAlias;
- setMode("tree");
- }
- },
-
- /**
- * @ngdoc method
- * @name umbraco.services.navigationService#hideTree
- * @methodOf umbraco.services.navigationService
- *
- * @description
- * Hides the tree by hiding the containing dom element
- */
- hideTree: function(){
- if(!this.ui.stickyNavigation){
- $log.log("hide tree");
- this.ui.currentTree = "";
- setMode("default-hidesectiontree");
- }
- },
-
- /**
- * @ngdoc method
- * @name umbraco.services.navigationService#showMenu
- * @methodOf umbraco.services.navigationService
- *
- * @description
- * Hides the tree by hiding the containing dom element
- * @param {Event} event the click event triggering the method, passed from the DOM element
- */
- showMenu: function (event, args) {
- if(args.event !== undefined && args.node.defaultAction && !args.event.altKey){
- //hack for now, it needs the complete action object to, so either include in tree item json
- //or lookup in service...
- var act = {
- alias: args.node.defaultAction,
- name: args.node.defaultAction
- };
-
- this.ui.currentNode = args.node;
- this.showDialog({
- scope: args.scope,
- node: args.node,
- action: act,
- section: this.ui.currentTree
- });
- }
- else {
- setMode("menu");
- ui.actions = treeService.getActions({node: args.node, section: this.ui.currentTree});
-
-
- this.ui.currentNode = args.node;
- this.ui.dialogTitle = args.node.name;
- }
- },
-
- /**
- * @ngdoc method
- * @name umbraco.services.navigationService#hideMenu
- * @methodOf umbraco.services.navigationService
- *
- * @description
- * Hides the menu by hiding the containing dom element
- */
- hideMenu: function () {
- var selectedId = $routeParams.id;
- this.ui.currentNode = undefined;
- this.ui.actions = [];
- setMode("tree");
- },
-
- /**
- * @ngdoc method
- * @name umbraco.services.navigationService#showUserDialog
- * @methodOf umbraco.services.navigationService
- *
- * @description
- * Opens the user dialog, next to the sections navigation
- * template is located in views/common/dialogs/user.html
- */
- showUserDialog: function(){
- var d = dialogService.open(
- {
- template: "views/common/dialogs/user.html",
- modalClass: "umb-modal-left",
- show: true
- });
-
- },
- /**
- * @ngdoc method
- * @name umbraco.services.navigationService#showDialog
- * @methodOf umbraco.services.navigationService
- *
- * @description
- * Opens a dialog, for a given action on a given tree node
- * uses the dialogServicet to inject the selected action dialog
- * into #dialog div.umb-panel-body
- * the path to the dialog view is determined by:
- * "views/" + current tree + "/" + action alias + ".html"
- * @param {Object} args arguments passed to the function
- * @param {Scope} args.scope current scope passed to the dialog
- * @param {Object} args.action the clicked action containing `name` and `alias`
- */
- showDialog: function (args) {
- setMode("dialog");
-
- var scope = args.scope || $rootScope.$new();
- scope.currentNode = args.node;
-
- //this.currentNode = item;
- this.ui.dialogTitle = args.action.name;
-
- var templateUrl = "views/" + this.ui.currentTree + "/" + args.action.alias + ".html";
- var d = dialogService.open(
- {
- container: $("#dialog div.umb-panel-body"),
- scope: scope,
- template: templateUrl
- });
- },
-
- /**
- * @ngdoc method
- * @name umbraco.services.navigationService#hideDialog
- * @methodOf umbraco.services.navigationService
- *
- * @description
- * hides the currently open dialog
- */
- hideDialog: function() {
- this.showMenu(undefined, {node: this.ui.currentNode});
- },
- /**
- * @ngdoc method
- * @name umbraco.services.navigationService#showSearch
- * @methodOf umbraco.services.navigationService
- *
- * @description
- * shows the search pane
- */
- showSearch: function() {
- setMode("search");
- },
- /**
- * @ngdoc method
- * @name umbraco.services.navigationService#hideSearch
- * @methodOf umbraco.services.navigationService
- *
- * @description
- * hides the search pane
- */
- hideSearch: function() {
- setMode("default-hidesearch");
- },
- /**
- * @ngdoc method
- * @name umbraco.services.navigationService#hideNavigation
- * @methodOf umbraco.services.navigationService
- *
- * @description
- * hides any open navigation panes and resets the tree, actions and the currently selected node
- */
- hideNavigation: function(){
- this.ui.currentTree = "";
- this.ui.actions = [];
- this.ui.currentNode = undefined;
-
- setMode("default");
- }
- };
-
- });
+/**
+ * @ngdoc service
+ * @name umbraco.services.navigationService
+ *
+ * @requires $rootScope
+ * @requires $routeParams
+ * @requires $log
+ * @requires $location
+ * @requires dialogService
+ * @requires treeService
+ * @requires sectionResource
+ *
+ * @description
+ * Service to handle the main application navigation. Responsible for invoking the tree
+ * Section navigation and search, and maintain their state for the entire application lifetime
+ *
+ */
+
+ angular.module('umbraco.services')
+ .factory('navigationService', function ($rootScope, $routeParams, $log, $location, dialogService, treeService, sectionResource) {
+
+ var currentSection = $routeParams.section;
+ var currentId = $routeParams.id;
+ var currentNode;
+ var ui = {};
+
+ var _sections = sectionResource.getSections();
+
+ function setMode(mode){
+ switch(mode)
+ {
+ case 'tree':
+ ui.showNavigation = true;
+ ui.showContextMenu = false;
+ ui.showContextMenuDialog = false;
+ ui.stickyNavigation = false;
+
+ $("#search-form input").focus();
+ break;
+ case 'menu':
+ ui.showNavigation = true;
+ ui.showContextMenu = true;
+ ui.showContextMenuDialog = false;
+ ui.stickyNavigation = true;
+ break;
+ case 'dialog':
+ ui.stickyNavigation = true;
+ ui.showNavigation = true;
+ ui.showContextMenu = false;
+ ui.showContextMenuDialog = true;
+ break;
+ case 'search':
+ ui.stickyNavigation = false;
+ ui.showNavigation = true;
+ ui.showContextMenu = false;
+ ui.showSearchResults = true;
+ ui.showContextMenuDialog = false;
+ break;
+ default:
+ ui.showNavigation = false;
+ ui.showContextMenu = false;
+ ui.showContextMenuDialog = false;
+ ui.showSearchResults = false;
+ ui.stickyNavigation = false;
+ break;
+ }
+ }
+
+ return {
+ currentNode: currentNode,
+ mode: "default",
+ ui: ui,
+ sections: _sections,
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.navigationService#load
+ * @methodOf umbraco.services.navigationService
+ *
+ * @description
+ * Shows the legacy iframe and loads in the content based on the source url
+ * @param {String} source The URL to load into the iframe
+ */
+ loadLegacyIFrame: function (source) {
+ $location.path("/framed/" + encodeURIComponent(source));
+ },
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.navigationService#changeSection
+ * @methodOf umbraco.services.navigationService
+ *
+ * @description
+ * Changes the active section to a given section alias
+ * If the navigation is 'sticky' this will load the associated tree
+ * and load the dashboard related to the section
+ * @param {string} sectionAlias The alias of the section
+ */
+ changeSection: function(sectionAlias){
+ if(this.ui.stickyNavigation){
+ setMode("default-opensection");
+ this.ui.currentSection = selectedSection;
+ this.showTree(selectedSection);
+ }
+
+ $location.path(sectionAlias);
+ },
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.navigationService#showTree
+ * @methodOf umbraco.services.navigationService
+ *
+ * @description
+ * Shows the tree for a given tree alias but turning on the containing dom element
+ * only changes if the section is different from the current one
+ * @param {string} sectionAlias The alias of the section the tree should load data from
+ */
+ showTree: function(sectionAlias){
+ if(!this.ui.stickyNavigation && sectionAlias !== this.ui.currentTree){
+ this.ui.currentTree = sectionAlias;
+ setMode("tree");
+ }
+ },
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.navigationService#hideTree
+ * @methodOf umbraco.services.navigationService
+ *
+ * @description
+ * Hides the tree by hiding the containing dom element
+ */
+ hideTree: function(){
+ if(!this.ui.stickyNavigation){
+ $log.log("hide tree");
+ this.ui.currentTree = "";
+ setMode("default-hidesectiontree");
+ }
+ },
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.navigationService#showMenu
+ * @methodOf umbraco.services.navigationService
+ *
+ * @description
+ * Hides the tree by hiding the containing dom element
+ * @param {Event} event the click event triggering the method, passed from the DOM element
+ */
+ showMenu: function (event, args) {
+ if(args.event !== undefined && args.node.defaultAction && !args.event.altKey){
+ //hack for now, it needs the complete action object to, so either include in tree item json
+ //or lookup in service...
+ var act = {
+ alias: args.node.defaultAction,
+ name: args.node.defaultAction
+ };
+
+ this.ui.currentNode = args.node;
+ this.showDialog({
+ scope: args.scope,
+ node: args.node,
+ action: act,
+ section: this.ui.currentTree
+ });
+ }
+ else {
+ setMode("menu");
+
+ treeService.getActions({ node: args.node, section: this.ui.currentTree })
+ .then(function(data) {
+ ui.actions = data;
+ }, function (err) {
+ //display the error
+ notificationsService.error(err.errorMsg);
+ });
+
+
+ this.ui.currentNode = args.node;
+ this.ui.dialogTitle = args.node.name;
+ }
+ },
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.navigationService#hideMenu
+ * @methodOf umbraco.services.navigationService
+ *
+ * @description
+ * Hides the menu by hiding the containing dom element
+ */
+ hideMenu: function () {
+ var selectedId = $routeParams.id;
+ this.ui.currentNode = undefined;
+ this.ui.actions = [];
+ setMode("tree");
+ },
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.navigationService#showUserDialog
+ * @methodOf umbraco.services.navigationService
+ *
+ * @description
+ * Opens the user dialog, next to the sections navigation
+ * template is located in views/common/dialogs/user.html
+ */
+ showUserDialog: function(){
+ var d = dialogService.open(
+ {
+ template: "views/common/dialogs/user.html",
+ modalClass: "umb-modal-left",
+ show: true
+ });
+
+ },
+ /**
+ * @ngdoc method
+ * @name umbraco.services.navigationService#showDialog
+ * @methodOf umbraco.services.navigationService
+ *
+ * @description
+ * Opens a dialog, for a given action on a given tree node
+ * uses the dialogServicet to inject the selected action dialog
+ * into #dialog div.umb-panel-body
+ * the path to the dialog view is determined by:
+ * "views/" + current tree + "/" + action alias + ".html"
+ * @param {Object} args arguments passed to the function
+ * @param {Scope} args.scope current scope passed to the dialog
+ * @param {Object} args.action the clicked action containing `name` and `alias`
+ */
+ showDialog: function (args) {
+ setMode("dialog");
+
+ var scope = args.scope || $rootScope.$new();
+ scope.currentNode = args.node;
+
+ //this.currentNode = item;
+ this.ui.dialogTitle = args.action.name;
+
+ var templateUrl = "views/" + this.ui.currentTree + "/" + args.action.alias + ".html";
+ var d = dialogService.open(
+ {
+ container: $("#dialog div.umb-panel-body"),
+ scope: scope,
+ template: templateUrl
+ });
+ },
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.navigationService#hideDialog
+ * @methodOf umbraco.services.navigationService
+ *
+ * @description
+ * hides the currently open dialog
+ */
+ hideDialog: function() {
+ this.showMenu(undefined, {node: this.ui.currentNode});
+ },
+ /**
+ * @ngdoc method
+ * @name umbraco.services.navigationService#showSearch
+ * @methodOf umbraco.services.navigationService
+ *
+ * @description
+ * shows the search pane
+ */
+ showSearch: function() {
+ setMode("search");
+ },
+ /**
+ * @ngdoc method
+ * @name umbraco.services.navigationService#hideSearch
+ * @methodOf umbraco.services.navigationService
+ *
+ * @description
+ * hides the search pane
+ */
+ hideSearch: function() {
+ setMode("default-hidesearch");
+ },
+ /**
+ * @ngdoc method
+ * @name umbraco.services.navigationService#hideNavigation
+ * @methodOf umbraco.services.navigationService
+ *
+ * @description
+ * hides any open navigation panes and resets the tree, actions and the currently selected node
+ */
+ hideNavigation: function(){
+ this.ui.currentTree = "";
+ this.ui.actions = [];
+ this.ui.currentNode = undefined;
+
+ setMode("default");
+ }
+ };
+
+ });
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/scriptloader.service.js b/src/Umbraco.Web.UI.Client/src/common/services/scriptloader.service.js
index 871f370211..183b153152 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/scriptloader.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/scriptloader.service.js
@@ -1,29 +1,45 @@
//script loader wrapping around 3rd party loader
angular.module('umbraco.services')
-.factory('scriptLoader', function ($q) {
+.factory('scriptLoader', function ($q, angularHelper) {
return {
load: function (pathArray, scope) {
var deferred = $q.defer();
- yepnope({
- load: pathArray,
- complete: function () {
-
- //if a scope is supplied then we need to make a digest here because
- // deferred only executes in a digest. This might be required if we
- // are doing a load script after an http request or some other async call.
- if (!scope) {
- deferred.resolve(true);
- }
- else {
- scope.$apply(function () {
- deferred.resolve(true);
- });
- }
- }
+ var nonEmpty = _.reject(pathArray, function(item) {
+ return item === undefined || item === "";
});
+ //don't load anything if there's nothing to load
+ if (nonEmpty.length > 0) {
+ yepnope({
+ load: pathArray,
+ complete: function() {
+
+ //if a scope is supplied then we need to make a digest here because
+ // deferred only executes in a digest. This might be required if we
+ // are doing a load script after an http request or some other async call.
+ if (!scope) {
+ deferred.resolve(true);
+ }
+ else {
+ angularHelper.safeApply(scope, function () {
+ deferred.resolve(true);
+ });
+ }
+ }
+ });
+ }
+ else {
+ if (!scope) {
+ deferred.resolve(true);
+ }
+ else {
+ angularHelper.safeApply(scope, function () {
+ deferred.resolve(true);
+ });
+ }
+ }
return deferred.promise;
}
};
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js
index 0da797bf26..f7f35adffb 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js
@@ -33,44 +33,40 @@ function treeService($q, treeResource, iconHelper) {
var cacheKey = options.cachekey || '';
cacheKey += "_" + section;
- var deferred = $q.defer();
-
//return the cache if it exists
if (treeArray[cacheKey] !== undefined){
return treeArray[cacheKey];
}
-
- treeResource.loadApplication(options)
- .then(function (data) {
- //this will be called once the tree app data has loaded
- var result = {
- name: section,
- alias: section,
- children: data
- };
- //ensure the view is added to each tree node
- ensureLevelAndView(result.children, section);
- //cache this result
- //TODO: We'll need to un-cache this in many circumstances
- treeArray[cacheKey] = result;
- //return the data result as promised
- deferred.resolve(treeArray[cacheKey]);
- }, function (reason) {
- //bubble up the rejection
- deferred.reject(reason);
- return;
- });
-
- return deferred.promise;
+
+ return treeResource.loadApplication(options)
+ .then(function(data) {
+ //this will be called once the tree app data has loaded
+ var result = {
+ name: section,
+ alias: section,
+ children: data
+ };
+ //ensure the view is added to each tree node
+ ensureLevelAndView(result.children, section);
+ //cache this result
+ //TODO: We'll need to un-cache this in many circumstances
+ treeArray[cacheKey] = result;
+ //return the data result as promised
+ //deferred.resolve(treeArray[cacheKey]);
+ return treeArray[cacheKey];
+ });
},
getActions: function(treeItem, section) {
- //need to convert the icons to new ones
- for (var i = 0; i < treeItem.node.menu.length; i++) {
- treeItem.node.menu[i].cssclass = iconHelper.convertFromLegacyIcon(treeItem.node.menu[i].cssclass);
- }
- return treeItem.node.menu;
+ return treeResource.loadMenu(treeItem.node)
+ .then(function(data) {
+ //need to convert the icons to new ones
+ for (var i = 0; i < data.length; i++) {
+ data[i].cssclass = iconHelper.convertFromLegacyIcon(data[i].cssclass);
+ }
+ return data;
+ });
},
getChildren: function (options) {
@@ -95,20 +91,12 @@ function treeService($q, treeResource, iconHelper) {
throw "No node defined";
}
- var deferred = $q.defer();
-
- treeResource.loadNodes( {section: section, node:treeItem} )
- .then(function (data) {
- //now that we have the data, we need to add the level property to each item and the view
- ensureLevelAndView(data, section, treeItem.level + 1);
- deferred.resolve(data);
- }, function (reason) {
- //bubble up the rejection
- deferred.reject(reason);
- return;
- });
-
- return deferred.promise;
+ return treeResource.loadNodes({ section: section, node: treeItem })
+ .then(function(data) {
+ //now that we have the data, we need to add the level property to each item and the view
+ ensureLevelAndView(data, section, treeItem.level + 1);
+ return data;
+ });
}
};
}
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/user.service.js b/src/Umbraco.Web.UI.Client/src/common/services/user.service.js
index 87cde95012..0af24cf1d4 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/user.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/user.service.js
@@ -15,7 +15,7 @@ angular.module('umbraco.services')
currentUser.avatar = 'http://www.gravatar.com/avatar/' + data.emailHash + '?s=40';
//note, this can return null if they are not authenticated
- deferred.resolve({ user: data, authenticated: data == null ? false : true });
+ deferred.resolve({ user: data, authenticated: true });
},
function(reason) {
deferred.reject(reason);
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/utill.service.js b/src/Umbraco.Web.UI.Client/src/common/services/utill.service.js
index 095be663a8..de049239c1 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/utill.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/utill.service.js
@@ -1,5 +1,27 @@
/*Contains multiple services for various helper tasks */
+
+/**
+ * @ngdoc function
+ * @name umbraco.services.angularHelper
+ * @function
+ *
+ * @description
+ * Some angular helper/extension methods
+ */
+function legacyJsLoader(scriptLoader) {
+ return {
+
+ /** Called to load in the legacy tree js which is required on startup if a user is logged in or
+ after login, but cannot be called until they are authenticated which is why it needs to be lazy loaded. */
+ loadLegacyTreeJs: function(scope) {
+ return scriptLoader.load([Umbraco.Sys.ServerVariables.legacyTreeJs], scope);
+ }
+ };
+}
+
+angular.module('umbraco.services').factory('legacyJsLoader', legacyJsLoader);
+
/**
* @ngdoc service
* @name umbraco.services.angularHelper
@@ -8,9 +30,34 @@
* @description
* Some angular helper/extension methods
*/
-function angularHelper($log) {
+function angularHelper($log, $q) {
return {
+ resourcePromise: function (httpPromise, errorMsg) {
+ var deferred = $q.defer();
+
+ httpPromise.success(function (data, status, headers, config) {
+
+ //when it's successful, just return the data
+ deferred.resolve(data);
+
+ }).error(function(data, status, headers, config) {
+
+ //when there's an erorr...
+ // TODO: Deal with the error in a global way
+
+ //return an error object including the error message for UI
+ deferred.reject({
+ errorMsg: errorMsg,
+ data: data
+ });
+
+ });
+
+ return deferred.promise;
+
+ },
+
/**
* @ngdoc function
* @name safeApply
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.controller.js
index 998a0cce03..5dc4f51628 100644
--- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.controller.js
@@ -1,4 +1,4 @@
-angular.module("umbraco").controller("Umbraco.Dialogs.LoginController", function ($scope, userService) {
+angular.module("umbraco").controller("Umbraco.Dialogs.LoginController", function ($scope, userService, legacyJsLoader) {
/**
* @ngdoc function
@@ -17,7 +17,15 @@
userService.authenticate(login, password)
.then(function (data) {
+
+ //We need to load in the legacy tree js.
+ legacyJsLoader.loadLegacyTreeJs($scope).then(
+ function(result) {
+ //TODO: We could wait for this to load before running the UI ?
+ });
+
$scope.submit(data);
+
}, function (reason) {
alert(reason);
});
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/main.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/main.controller.js
index 4d661a3321..710e7ce619 100644
--- a/src/Umbraco.Web.UI.Client/src/views/common/main.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/common/main.controller.js
@@ -8,7 +8,7 @@
* The main application controller
*
*/
-function MainController($scope, $routeParams, $rootScope, $timeout, notificationsService, userService, navigationService) {
+function MainController($scope, $routeParams, $rootScope, $timeout, notificationsService, userService, navigationService, legacyJsLoader) {
//set default properties
$scope.authenticated = null; //the null is important because we do an explicit bool check on this in the view
@@ -41,8 +41,16 @@ function MainController($scope, $routeParams, $rootScope, $timeout, notification
//fetch the authorized status
userService.isAuthenticated()
.then(function (data) {
+
+ //We need to load in the legacy tree js.
+ legacyJsLoader.loadLegacyTreeJs($scope).then(
+ function (result) {
+ //TODO: We could wait for this to load before running the UI ?
+ });
+
$scope.authenticated = data.authenticated;
$scope.user = data.user;
+
}, function (reason) {
notificationsService.error("An error occurred checking authentication.");
$scope.authenticated = false;
diff --git a/src/Umbraco.Web.UI/umbraco/Views/Default.cshtml b/src/Umbraco.Web.UI/umbraco/Views/Default.cshtml
index 2c68aa95c5..4650a0eb27 100644
--- a/src/Umbraco.Web.UI/umbraco/Views/Default.cshtml
+++ b/src/Umbraco.Web.UI/umbraco/Views/Default.cshtml
@@ -11,7 +11,6 @@
- @**@
@@ -19,32 +18,6 @@
- @**@
@@ -64,9 +37,6 @@
@*Currently this needs to be loaded before anything*@
-
- @*Now we need to load in some legacy stuff*@
-
@*And finally we can load in our angular app*@
diff --git a/src/Umbraco.Web/Editors/AuthenticationController.cs b/src/Umbraco.Web/Editors/AuthenticationController.cs
index de201e6e8d..505d12253d 100644
--- a/src/Umbraco.Web/Editors/AuthenticationController.cs
+++ b/src/Umbraco.Web/Editors/AuthenticationController.cs
@@ -44,7 +44,10 @@ namespace Umbraco.Web.Editors
UmbracoContext.Security.GetUserId(UmbracoContext.Security.UmbracoUserContextId));
return _userModelMapper.ToUserDetail(user);
}
- throw new HttpResponseException(HttpStatusCode.Unauthorized);
+
+ //don't return not-authorized because this method is here to check if the current user is authorized.
+ // if they are not, then we just return no content.
+ throw new HttpResponseException(HttpStatusCode.NoContent);
}
public UserDetail PostLogin(string username, string password)
diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs
index 7d4968e429..d10dfb8c48 100644
--- a/src/Umbraco.Web/Editors/BackOfficeController.cs
+++ b/src/Umbraco.Web/Editors/BackOfficeController.cs
@@ -56,6 +56,7 @@ namespace Umbraco.Web.Editors
var d = new Dictionary
{
{"umbracoPath", GlobalSettings.Path},
+ {"legacyTreeJs", Url.Action("LegacyTreeJs", "BackOffice")},
{"contentApiBaseUrl", Url.GetUmbracoApiService("PostSave").TrimEnd("PostSave")},
{"mediaApiBaseUrl", Url.GetUmbracoApiService("GetRootMedia").TrimEnd("GetRootMedia")},
{"sectionApiBaseUrl", Url.GetUmbracoApiService("GetSections").TrimEnd("GetSections")},
@@ -72,6 +73,7 @@ namespace Umbraco.Web.Editors
/// Returns the JavaScript blocks for any legacy trees declared
///
///
+ [UmbracoAuthorize]
public JavaScriptResult LegacyTreeJs()
{
var javascript = new StringBuilder();
diff --git a/src/Umbraco.Web/Mvc/UmbracoAuthorizeAttribute.cs b/src/Umbraco.Web/Mvc/UmbracoAuthorizeAttribute.cs
index 19615706d6..084ef39b62 100644
--- a/src/Umbraco.Web/Mvc/UmbracoAuthorizeAttribute.cs
+++ b/src/Umbraco.Web/Mvc/UmbracoAuthorizeAttribute.cs
@@ -52,12 +52,15 @@ namespace Umbraco.Web.Mvc
}
///
- /// Override to throw exception instead of returning a 401 result
+ /// Override to to ensure no redirect occurs
///
///
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
- throw new HttpException((int)global::System.Net.HttpStatusCode.Unauthorized, "You must login to view this resource.");
+ filterContext.Result = (ActionResult)new HttpUnauthorizedResult("You must login to view this resource.");
+
+ //DON'T do a FormsAuth redirect... argh!! thankfully we're running .Net 4.5 :)
+ filterContext.RequestContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;
}
}
diff --git a/src/Umbraco.Web/Trees/ApplicationTreeExtensions.cs b/src/Umbraco.Web/Trees/ApplicationTreeExtensions.cs
index 283f8216db..860214adf8 100644
--- a/src/Umbraco.Web/Trees/ApplicationTreeExtensions.cs
+++ b/src/Umbraco.Web/Trees/ApplicationTreeExtensions.cs
@@ -74,17 +74,28 @@ namespace Umbraco.Web.Trees
}
internal static Attempt TryGetRootNodeFromLegacyTree(this ApplicationTree appTree, FormDataCollection formCollection, UrlHelper urlHelper)
+ {
+ var xmlTreeNodeAttempt = TryGetRootXmlNodeFromLegacyTree(appTree, formCollection, urlHelper);
+ if (xmlTreeNodeAttempt.Success == false)
+ {
+ return new Attempt(xmlTreeNodeAttempt.Error);
+ }
+ return new Attempt(true,
+ LegacyTreeDataConverter.ConvertFromLegacy(xmlTreeNodeAttempt.Result.NodeID, xmlTreeNodeAttempt.Result, urlHelper, isRoot: true));
+ }
+
+ internal static Attempt TryGetRootXmlNodeFromLegacyTree(this ApplicationTree appTree, FormDataCollection formCollection, UrlHelper urlHelper)
{
var treeDefAttempt = appTree.TryGetLegacyTreeDef();
if (treeDefAttempt.Success == false)
{
- return new Attempt(treeDefAttempt.Error);
+ return new Attempt(treeDefAttempt.Error);
}
var treeDef = treeDefAttempt.Result;
var bTree = treeDef.CreateInstance();
var treeParams = new LegacyTreeParams(formCollection);
bTree.SetTreeParameters(treeParams);
- return new Attempt(true, LegacyTreeDataConverter.ConvertFromLegacy(bTree.RootNode, urlHelper));
+ return new Attempt(true, bTree.RootNode);
}
private static Attempt TryGetLegacyTreeDef(this ApplicationTree appTree)
@@ -98,11 +109,49 @@ namespace Umbraco.Web.Trees
internal static Attempt TryLoadFromLegacyTree(this ApplicationTree appTree, string id, FormDataCollection formCollection, UrlHelper urlHelper)
{
- var treeDefAttempt = appTree.TryGetLegacyTreeDef();
+ var xTreeAttempt = appTree.TryGetXmlTree(id, formCollection);
+ if (xTreeAttempt.Success == false)
+ {
+ return new Attempt(xTreeAttempt.Error);
+ }
+ return new Attempt(true, LegacyTreeDataConverter.ConvertFromLegacy(id, xTreeAttempt.Result, urlHelper));
+ }
+
+ internal static Attempt TryGetMenuFromLegacyTreeRootNode(this ApplicationTree appTree, FormDataCollection formCollection, UrlHelper urlHelper)
+ {
+ var rootAttempt = appTree.TryGetRootXmlNodeFromLegacyTree(formCollection, urlHelper);
+ if (rootAttempt.Success == false)
+ {
+ return new Attempt(rootAttempt.Error);
+ }
+
+ var result = LegacyTreeDataConverter.ConvertFromLegacyMenu(rootAttempt.Result);
+ return new Attempt(true, result);
+ }
+
+ internal static Attempt TryGetMenuFromLegacyTreeNode(this ApplicationTree appTree, string parentId, string nodeId, FormDataCollection formCollection, UrlHelper urlHelper)
+ {
+ var xTreeAttempt = appTree.TryGetXmlTree(parentId, formCollection);
+ if (xTreeAttempt.Success == false)
+ {
+ return new Attempt(xTreeAttempt.Error);
+ }
+
+ var result = LegacyTreeDataConverter.ConvertFromLegacyMenu(nodeId, xTreeAttempt.Result);
+ if (result == null)
+ {
+ return new Attempt(new ApplicationException("Could not find the node with id " + nodeId + " in the collection of nodes contained with parent id " + parentId));
+ }
+ return new Attempt(true, result);
+ }
+
+ private static Attempt TryGetXmlTree(this ApplicationTree appTree, string id, FormDataCollection formCollection)
+ {
+ var treeDefAttempt = appTree.TryGetLegacyTreeDef();
if (treeDefAttempt.Success == false)
{
- return new Attempt(treeDefAttempt.Error);
- }
+ return new Attempt(treeDefAttempt.Error);
+ }
var treeDef = treeDefAttempt.Result;
//This is how the legacy trees worked....
var bTree = treeDef.CreateInstance();
@@ -122,8 +171,7 @@ namespace Umbraco.Web.Trees
var xTree = new XmlTree();
bTree.SetTreeParameters(treeParams);
bTree.Render(ref xTree);
-
- return new Attempt(true, LegacyTreeDataConverter.ConvertFromLegacy(xTree, urlHelper));
+ return new Attempt(true, xTree);
}
}
diff --git a/src/Umbraco.Web/Trees/LegacyTreeApiController.cs b/src/Umbraco.Web/Trees/LegacyTreeApiController.cs
index 8f6fb8a1da..b93afdb7b6 100644
--- a/src/Umbraco.Web/Trees/LegacyTreeApiController.cs
+++ b/src/Umbraco.Web/Trees/LegacyTreeApiController.cs
@@ -2,6 +2,8 @@
using System.Globalization;
using System.Linq;
using System.Net.Http.Formatting;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Web.Mvc;
using Umbraco.Web.WebApi;
@@ -24,6 +26,62 @@ namespace Umbraco.Web.Trees
///
[HttpQueryStringFilter("queryStrings")]
public TreeNodeCollection GetNodes(string id, FormDataCollection queryStrings)
+ {
+ var tree = GetTree(queryStrings);
+ var attempt = tree.TryLoadFromLegacyTree(id, queryStrings, Url);
+ if (attempt.Success == false)
+ {
+ var msg = "Could not render tree " + queryStrings.GetRequiredString("treeType") + " for node id " + id;
+ LogHelper.Error(msg, attempt.Error);
+ throw new ApplicationException(msg);
+ }
+
+ return attempt.Result;
+ }
+
+ ///
+ /// This will return the menu item collection for the tree node with the specified Id
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// Due to the nature of legacy trees this means that we need to lookup the parent node, render
+ /// the TreeNodeCollection and then find the node we're looking for and render it's menu.
+ ///
+ [HttpQueryStringFilter("queryStrings")]
+ public MenuItemCollection GetMenu(string id, FormDataCollection queryStrings)
+ {
+ //get the parent id from the query strings
+ var parentId = queryStrings.GetRequiredString("parentId");
+ var tree = GetTree(queryStrings);
+
+ //if the id and the parentId are both -1 then we need to get the menu for the root node
+ if (id == "-1" && parentId == "-1")
+ {
+ var attempt = tree.TryGetMenuFromLegacyTreeRootNode(queryStrings, Url);
+ if (attempt.Success == false)
+ {
+ var msg = "Could not render menu for root node for treeType " + queryStrings.GetRequiredString("treeType");
+ LogHelper.Error(msg, attempt.Error);
+ throw new ApplicationException(msg);
+ }
+ return attempt.Result;
+ }
+ else
+ {
+ var attempt = tree.TryGetMenuFromLegacyTreeNode(parentId, id, queryStrings, Url);
+ if (attempt.Success == false)
+ {
+ var msg = "Could not render menu for treeType " + queryStrings.GetRequiredString("treeType") + " for node id " + parentId;
+ LogHelper.Error(msg, attempt.Error);
+ throw new ApplicationException(msg);
+ }
+ return attempt.Result;
+ }
+ }
+
+ private ApplicationTree GetTree(FormDataCollection queryStrings)
{
//need to ensure we have a tree type
var treeType = queryStrings.GetRequiredString("treeType");
@@ -31,14 +89,7 @@ namespace Umbraco.Web.Trees
var tree = Services.ApplicationTreeService.GetByAlias(treeType);
if (tree == null)
throw new InvalidOperationException("No tree found with alias " + treeType);
-
- var attempt = tree.TryLoadFromLegacyTree(id, queryStrings, Url);
- if (attempt.Success == false)
- {
- throw new ApplicationException("Could not render tree " + treeType + " for node id " + id);
- }
-
- return attempt.Result;
+ return tree;
}
}
diff --git a/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs b/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs
index af12749def..d57759bf0d 100644
--- a/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs
+++ b/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs
@@ -1,8 +1,10 @@
using System;
+using System.Collections.Generic;
using System.Linq;
using System.Web.Http.Routing;
using Umbraco.Core;
using umbraco.BusinessLogic.Actions;
+using umbraco.cms.helpers;
using umbraco.cms.presentation.Trees;
using umbraco.controls.Tree;
using umbraco.interfaces;
@@ -15,21 +17,87 @@ namespace Umbraco.Web.Trees
internal class LegacyTreeDataConverter
{
- internal static TreeNode ConvertFromLegacy(XmlTreeNode xmlTreeNode, UrlHelper urlHelper)
+ ///
+ /// Gets the menu item collection from a legacy tree node based on it's parent node's child collection
+ ///
+ /// The node id
+ /// The node collection that contains the node id
+ ///
+ internal static MenuItemCollection ConvertFromLegacyMenu(string nodeId, XmlTree xmlTree)
+ {
+ var xmlTreeNode = xmlTree.treeCollection.FirstOrDefault(x => x.NodeID == nodeId);
+ if (xmlTreeNode == null)
+ {
+ return null;
+ }
+
+ return ConvertFromLegacyMenu(xmlTreeNode);
+ }
+
+ ///
+ /// Gets the menu item collection from a legacy tree node
+ ///
+ ///
+ ///
+ internal static MenuItemCollection ConvertFromLegacyMenu(XmlTreeNode xmlTreeNode)
+ {
+ var collection = new MenuItemCollection();
+
+ var menuItems = xmlTreeNode.Menu.ToArray();
+ var numAdded = 0;
+ var seperators = new List();
+ foreach (var t in menuItems)
+ {
+ if (t is ContextMenuSeperator && numAdded > 0)
+ {
+ //store the index for which the seperator should be placed
+ seperators.Add(collection.Count());
+ }
+ else
+ {
+ collection.AddMenuItem(t);
+ numAdded++;
+ }
+ }
+ var length = collection.Count();
+ foreach (var s in seperators)
+ {
+ if (length >= s)
+ {
+ collection.ElementAt(s).Seperator = true;
+ }
+ }
+
+ return collection;
+ }
+
+ internal static TreeNode ConvertFromLegacy(string parentId, XmlTreeNode xmlTreeNode, UrlHelper urlHelper, bool isRoot = false)
{
// /umbraco/tree.aspx?rnd=d0d0ff11a1c347dabfaa0fc75effcc2a&id=1046&treeType=content&contextMenu=false&isDialog=false
//we need to convert the node source to our legacy tree controller
- var source = urlHelper.GetUmbracoApiService("GetNodes");
+ var childNodesSource = urlHelper.GetUmbracoApiService("GetNodes");
+
+ var childQuery = (xmlTreeNode.Source.IsNullOrWhiteSpace() || xmlTreeNode.Source.IndexOf('?') == -1)
+ ? ""
+ : xmlTreeNode.Source.Substring(xmlTreeNode.Source.IndexOf('?'));
+
//append the query strings
- var query = xmlTreeNode.Source.IsNullOrWhiteSpace()
- ? new string[] { }
- : xmlTreeNode.Source.Split(new[] { '?' }, StringSplitOptions.RemoveEmptyEntries);
- source += query.Length > 1 ? query[1].EnsureStartsWith('?') : "";
+ childNodesSource = childNodesSource.AppendQueryStringToUrl(childQuery);
+
+ //for the menu source we need to detect if this is a root node since we'll need to set the parentId and id to -1
+ // for which we'll handle correctly on the server side.
+ var menuSource = urlHelper.GetUmbracoApiService("GetMenu");
+ menuSource = menuSource.AppendQueryStringToUrl(new[]
+ {
+ "id=" + (isRoot ? "-1" : xmlTreeNode.NodeID),
+ "treeType=" + xmlTreeNode.TreeType,
+ "parentId=" + (isRoot ? "-1" : parentId)
+ });
//TODO: Might need to add stuff to additional attributes
-
- var node = new TreeNode(xmlTreeNode.NodeID, source)
+
+ var node = new TreeNode(xmlTreeNode.NodeID, childNodesSource, menuSource)
{
HasChildren = xmlTreeNode.HasChildren,
Icon = xmlTreeNode.Icon,
@@ -44,36 +112,18 @@ namespace Umbraco.Web.Trees
{
node.OnClickCallback = xmlTreeNode.Action;
}
-
- var menuItems = xmlTreeNode.Menu.ToArray();
- var numAdded = 0;
- foreach (var t in menuItems)
- {
- if (t is ContextMenuSeperator && numAdded > 0)
- {
- //if it is a seperator, then update the previous menu item that we've added to be flagged
- //with a seperator
- node.Menu.ElementAt(numAdded - 1).Seperator = true;
- }
- else
- {
- node.AddMenuItem(t);
- numAdded++;
- }
- }
-
return node;
}
- internal static TreeNodeCollection ConvertFromLegacy(XmlTree xmlTree, UrlHelper urlHelper)
+ internal static TreeNodeCollection ConvertFromLegacy(string parentId, XmlTree xmlTree, UrlHelper urlHelper)
{
//TODO: Once we get the editor URL stuff working we'll need to figure out how to convert
// that over to use the old school ui.xml stuff for these old trees and however the old menu items worked.
var collection = new TreeNodeCollection();
foreach (var x in xmlTree.treeCollection)
- {
- collection.Add(ConvertFromLegacy(x, urlHelper));
+ {
+ collection.Add(ConvertFromLegacy(parentId, x, urlHelper));
}
return collection;
}
diff --git a/src/Umbraco.Web/Trees/MenuItem.cs b/src/Umbraco.Web/Trees/MenuItem.cs
index 37690d630e..3608a06319 100644
--- a/src/Umbraco.Web/Trees/MenuItem.cs
+++ b/src/Umbraco.Web/Trees/MenuItem.cs
@@ -1,6 +1,7 @@
using System.ComponentModel.DataAnnotations;
using System.Runtime.Serialization;
using umbraco.interfaces;
+using System.Collections.Generic;
namespace Umbraco.Web.Trees
{
@@ -9,7 +10,7 @@ namespace Umbraco.Web.Trees
{
public MenuItem()
{
-
+ AdditionalData = new Dictionary();
}
public MenuItem(IAction legacyMenu)
@@ -20,6 +21,13 @@ namespace Umbraco.Web.Trees
Icon = legacyMenu.Icon;
}
+ ///
+ /// A dictionary to support any additional meta data that should be rendered for the node which is
+ /// useful for custom action commands such as 'create', 'copy', etc...
+ ///
+ [DataMember(Name = "metaData")]
+ public Dictionary AdditionalData { get; private set; }
+
[DataMember(Name = "name", IsRequired = true)]
[Required]
public string Name { get; set; }
diff --git a/src/Umbraco.Web/Trees/MenuItemCollection.cs b/src/Umbraco.Web/Trees/MenuItemCollection.cs
new file mode 100644
index 0000000000..0b94eaa4e0
--- /dev/null
+++ b/src/Umbraco.Web/Trees/MenuItemCollection.cs
@@ -0,0 +1,100 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+using Umbraco.Core;
+using umbraco.interfaces;
+
+namespace Umbraco.Web.Trees
+{
+ [CollectionDataContract(Name = "menuItems", Namespace = "")]
+ public class MenuItemCollection : IEnumerable