Implements: U4-2937 A tree item should be able to have a default action
This commit is contained in:
@@ -31,7 +31,12 @@ angular.module('umbraco.mocks').
|
||||
{ seperator: true, name: "Empty Recycle Bin", cssclass: "trash", alias: "emptyrecyclebin", metaData: {} }
|
||||
];
|
||||
|
||||
return [200, menu, null];
|
||||
var result = {
|
||||
menuItems: menu,
|
||||
defaultAlias: "create"
|
||||
};
|
||||
|
||||
return [200, result, null];
|
||||
}
|
||||
|
||||
function returnChildren(status, data, headers) {
|
||||
@@ -54,10 +59,10 @@ angular.module('umbraco.mocks').
|
||||
}
|
||||
|
||||
var children = [
|
||||
{ name: "child-of-" + section, childNodesUrl: url, id: level + "" + 1234, icon: "icon-document", children: [], expanded: false, hasChildren: true, level: level, defaultAction: action, menuUrl: menuUrl },
|
||||
{ name: "random-name-" + section, childNodesUrl: url, id: level + "" + 1235, icon: "icon-document", children: [], expanded: false, hasChildren: true, level: level, defaultAction: action, menuUrl: menuUrl },
|
||||
{ name: "random-name-" + section, childNodesUrl: url, id: level + "" + 1236, icon: "icon-document", children: [], expanded: false, hasChildren: true, level: level, defaultAction: action, menuUrl: menuUrl },
|
||||
{ name: "random-name-" + section, childNodesUrl: url, id: level + "" + 1237, icon: "icon-document", routePath: "common/legacy/1237?p=" + encodeURI("developer/contentType.aspx?idequal1234"), children: [], expanded: false, hasChildren: true, level: level, defaultAction: action, menuUrl: menuUrl }
|
||||
{ name: "child-of-" + section, childNodesUrl: url, id: level + "" + 1234, icon: "icon-document", children: [], expanded: false, hasChildren: true, level: level, menuUrl: menuUrl },
|
||||
{ name: "random-name-" + section, childNodesUrl: url, id: level + "" + 1235, icon: "icon-document", children: [], expanded: false, hasChildren: true, level: level, menuUrl: menuUrl },
|
||||
{ name: "random-name-" + section, childNodesUrl: url, id: level + "" + 1236, icon: "icon-document", children: [], expanded: false, hasChildren: true, level: level, menuUrl: menuUrl },
|
||||
{ name: "random-name-" + section, childNodesUrl: url, id: level + "" + 1237, icon: "icon-document", routePath: "common/legacy/1237?p=" + encodeURI("developer/contentType.aspx?idequal1234"), children: [], expanded: false, hasChildren: true, level: level, menuUrl: menuUrl }
|
||||
];
|
||||
|
||||
return [200, children, null];
|
||||
@@ -69,10 +74,10 @@ angular.module('umbraco.mocks').
|
||||
}
|
||||
|
||||
var children = [
|
||||
{ name: "Textstring", childNodesUrl: null, id: 10, icon: "icon-document", children: [], expanded: false, hasChildren: false, level: 1, defaultAction: null, menuUrl: null },
|
||||
{ name: "Multiple textstring", childNodesUrl: null, id: 11, icon: "icon-document", children: [], expanded: false, hasChildren: false, level: 1, defaultAction: null, menuUrl: null },
|
||||
{ name: "Yes/No", childNodesUrl: null, id: 12, icon: "icon-document", children: [], expanded: false, hasChildren: false, level: 1, defaultAction: null, menuUrl: null },
|
||||
{ name: "Rich Text Editor", childNodesUrl: null, id: 13, icon: "icon-document", children: [], expanded: false, hasChildren: false, level: 1, defaultAction: null, menuUrl: null }
|
||||
{ name: "Textstring", childNodesUrl: null, id: 10, icon: "icon-document", children: [], expanded: false, hasChildren: false, level: 1, menuUrl: null },
|
||||
{ name: "Multiple textstring", childNodesUrl: null, id: 11, icon: "icon-document", children: [], expanded: false, hasChildren: false, level: 1, menuUrl: null },
|
||||
{ name: "Yes/No", childNodesUrl: null, id: 12, icon: "icon-document", children: [], expanded: false, hasChildren: false, level: 1, menuUrl: null },
|
||||
{ name: "Rich Text Editor", childNodesUrl: null, id: 13, icon: "icon-document", children: [], expanded: false, hasChildren: false, level: 1, menuUrl: null }
|
||||
];
|
||||
|
||||
return [200, children, null];
|
||||
@@ -112,10 +117,10 @@ angular.module('umbraco.mocks').
|
||||
name: "content",
|
||||
id: -1,
|
||||
children: [
|
||||
{ name: "My website", id: 1234, childNodesUrl: url, icon: "icon-home", children: [], expanded: false, hasChildren: true, level: 1, defaultAction: "create", menuUrl: menuUrl },
|
||||
{ name: "Components", id: 1235, childNodesUrl: url, icon: "icon-document", children: [], expanded: false, hasChildren: true, level: 1, defaultAction: "create", menuUrl: menuUrl },
|
||||
{ name: "Archieve", id: 1236, childNodesUrl: url, icon: "icon-document", children: [], expanded: false, hasChildren: true, level: 1, defaultAction: "create", menuUrl: menuUrl },
|
||||
{ name: "Recycle Bin", id: -20, childNodesUrl: url, icon: "icon-trash", routePath: section + "/recyclebin", children: [], expanded: false, hasChildren: true, level: 1, defaultAction: "create", menuUrl: menuUrl }
|
||||
{ name: "My website", id: 1234, childNodesUrl: url, icon: "icon-home", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
|
||||
{ name: "Components", id: 1235, childNodesUrl: url, icon: "icon-document", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
|
||||
{ name: "Archieve", id: 1236, childNodesUrl: url, icon: "icon-document", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
|
||||
{ name: "Recycle Bin", id: -20, childNodesUrl: url, icon: "icon-trash", routePath: section + "/recyclebin", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl }
|
||||
],
|
||||
expanded: true,
|
||||
hasChildren: true,
|
||||
@@ -130,10 +135,10 @@ angular.module('umbraco.mocks').
|
||||
name: "media",
|
||||
id: -1,
|
||||
children: [
|
||||
{ name: "random-name-" + section, childNodesUrl: url, id: 1234, icon: "icon-home", defaultAction: "create", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
|
||||
{ name: "random-name-" + section, childNodesUrl: url, id: 1235, icon: "icon-folder-close", defaultAction: "create", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
|
||||
{ name: "random-name-" + section, childNodesUrl: url, id: 1236, icon: "icon-folder-close", defaultAction: "create", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
|
||||
{ name: "random-name-" + section, childNodesUrl: url, id: 1237, icon: "icon-folder-close", defaultAction: "create", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl }
|
||||
{ name: "random-name-" + section, childNodesUrl: url, id: 1234, icon: "icon-home", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
|
||||
{ name: "random-name-" + section, childNodesUrl: url, id: 1235, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
|
||||
{ name: "random-name-" + section, childNodesUrl: url, id: 1236, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
|
||||
{ name: "random-name-" + section, childNodesUrl: url, id: 1237, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl }
|
||||
],
|
||||
expanded: true,
|
||||
hasChildren: true,
|
||||
@@ -189,10 +194,10 @@ angular.module('umbraco.mocks').
|
||||
name: "randomTree",
|
||||
id: -1,
|
||||
children: [
|
||||
{ name: "random-name-" + section, childNodesUrl: url, id: 1234, icon: "icon-home", defaultAction: "create", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
|
||||
{ name: "random-name-" + section, childNodesUrl: url, id: 1235, icon: "icon-folder-close", defaultAction: "create", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
|
||||
{ name: "random-name-" + section, childNodesUrl: url, id: 1236, icon: "icon-folder-close", defaultAction: "create", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
|
||||
{ name: "random-name-" + section, childNodesUrl: url, id: 1237, icon: "icon-folder-close", defaultAction: "create", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl }
|
||||
{ name: "random-name-" + section, childNodesUrl: url, id: 1234, icon: "icon-home", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
|
||||
{ name: "random-name-" + section, childNodesUrl: url, id: 1235, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
|
||||
{ name: "random-name-" + section, childNodesUrl: url, id: 1236, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
|
||||
{ name: "random-name-" + section, childNodesUrl: url, id: 1237, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl }
|
||||
],
|
||||
expanded: true,
|
||||
hasChildren: true,
|
||||
|
||||
@@ -191,48 +191,48 @@ angular.module('umbraco.services')
|
||||
var deferred = $q.defer();
|
||||
var self = this;
|
||||
|
||||
if (args.event !== undefined && args.node.defaultAction && !args.event.altKey) {
|
||||
|
||||
treeService.getMenuItemByAlias({ treeNode: args.node, menuItemAlias: args.node.defaultAction })
|
||||
.then(function(result) {
|
||||
|
||||
if (!result) {
|
||||
throw "No menu item found with alias " + args.node.defaultAction;
|
||||
}
|
||||
|
||||
self.ui.currentNode = args.node;
|
||||
|
||||
//ensure the current dialog is cleared before creating another!
|
||||
if (self.ui.currentDialog) {
|
||||
dialogService.close(self.ui.currentDialog);
|
||||
}
|
||||
|
||||
var dialog = self.showDialog({
|
||||
scope: args.scope,
|
||||
node: args.node,
|
||||
action: result,
|
||||
section: self.ui.currentSection
|
||||
treeService.getMenu({ treeNode: args.node })
|
||||
.then(function(data) {
|
||||
|
||||
//check for a default
|
||||
if (data.defaultAlias) {
|
||||
var found = _.find(data.menuItems, function(item) {
|
||||
return item.alias = data.defaultAlias;
|
||||
});
|
||||
if (found) {
|
||||
|
||||
self.ui.currentNode = args.node;
|
||||
//ensure the current dialog is cleared before creating another!
|
||||
if (self.ui.currentDialog) {
|
||||
dialogService.close(self.ui.currentDialog);
|
||||
}
|
||||
|
||||
//return the dialog this is opening.
|
||||
deferred.resolve(dialog);
|
||||
});
|
||||
}
|
||||
else {
|
||||
setMode("menu");
|
||||
var dialog = self.showDialog({
|
||||
scope: args.scope,
|
||||
node: args.node,
|
||||
action: found,
|
||||
section: self.ui.currentSection
|
||||
});
|
||||
|
||||
treeService.getMenu({ treeNode: args.node })
|
||||
.then(function(data) {
|
||||
ui.actions = data;
|
||||
});
|
||||
//return the dialog this is opening.
|
||||
deferred.resolve(dialog);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.ui.currentNode = args.node;
|
||||
this.ui.dialogTitle = args.node.name;
|
||||
|
||||
//we're not opening a dialog, return null.
|
||||
deferred.resolve(null);
|
||||
}
|
||||
//there is no default or we couldn't find one so just continue showing the menu
|
||||
|
||||
setMode("menu");
|
||||
|
||||
ui.actions = data.menuItems;
|
||||
|
||||
ui.currentNode = args.node;
|
||||
ui.dialogTitle = args.node.name;
|
||||
|
||||
//we're not opening a dialog, return null.
|
||||
deferred.resolve(null);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.services.treeService#getMenuItemByAlias
|
||||
* @name umbraco.services.treeService#loadNodeChildren
|
||||
* @methodOf umbraco.services.treeService
|
||||
* @function
|
||||
*
|
||||
@@ -237,39 +237,6 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.services.treeService#getMenuItemByAlias
|
||||
* @methodOf umbraco.services.treeService
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Attempts to return a tree node's menu item based on the alias supplied, otherwise returns null.
|
||||
* @param {object} args An arguments object
|
||||
* @param {object} args.treeNode The tree node to get the menu item for
|
||||
* @param {object} args.menuItemAlias The menu item alias to attempt to find
|
||||
*/
|
||||
getMenuItemByAlias: function (args) {
|
||||
|
||||
if (!args) {
|
||||
throw "args cannot be null";
|
||||
}
|
||||
if (!args.treeNode) {
|
||||
throw "args.treeNode cannot be null";
|
||||
}
|
||||
if (!args.menuItemAlias) {
|
||||
throw "args.menuItemAlias cannot be null";
|
||||
}
|
||||
|
||||
return this.getMenu(args)
|
||||
.then(function (menuItems) {
|
||||
//try to find the node with the alias
|
||||
return _.find(menuItems, function(item) {
|
||||
return item.alias === args.menuItemAlias;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/** Gets the children from the server for a given node */
|
||||
getChildren: function (args) {
|
||||
|
||||
@@ -283,12 +250,6 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc
|
||||
var section = args.section || 'content';
|
||||
var treeItem = args.node;
|
||||
|
||||
//hack to have create as default content action
|
||||
var action;
|
||||
if(section === "content"){
|
||||
action = "create";
|
||||
}
|
||||
|
||||
var self = this;
|
||||
|
||||
return treeResource.loadNodes({ section: section, node: treeItem })
|
||||
|
||||
@@ -11,17 +11,17 @@ describe('tree service tests', function () {
|
||||
id: -1,
|
||||
children: [
|
||||
{
|
||||
name: "My website", id: 1234, childNodesUrl: url, icon: "icon-home", expanded: false, hasChildren: true, level: 1, defaultAction: "create", menuUrl: menuUrl,
|
||||
name: "My website", id: 1234, childNodesUrl: url, icon: "icon-home", expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl,
|
||||
children: [
|
||||
{ name: "random-name-1", childNodesUrl: url, id: 11, icon: "icon-home", defaultAction: "create", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
|
||||
{ name: "random-name-2", childNodesUrl: url, id: 12, icon: "icon-folder-close", defaultAction: "create", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
|
||||
{ name: "random-name-3", childNodesUrl: url, id: 13, icon: "icon-folder-close", defaultAction: "create", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
|
||||
{ name: "random-name-4", childNodesUrl: url, id: 14, icon: "icon-folder-close", defaultAction: "create", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl }
|
||||
{ name: "random-name-1", childNodesUrl: url, id: 11, icon: "icon-home", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
|
||||
{ name: "random-name-2", childNodesUrl: url, id: 12, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
|
||||
{ name: "random-name-3", childNodesUrl: url, id: 13, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
|
||||
{ name: "random-name-4", childNodesUrl: url, id: 14, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl }
|
||||
]
|
||||
},
|
||||
{ name: "Components", id: 1235, childNodesUrl: url, icon: "icon-cogs", children: [], expanded: false, hasChildren: true, level: 1, defaultAction: "create", menuUrl: menuUrl },
|
||||
{ name: "Archieve", id: 1236, childNodesUrl: url, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, defaultAction: "create", menuUrl: menuUrl },
|
||||
{ name: "Recycle Bin", id: -20, childNodesUrl: url, icon: "icon-trash", routePath: "content/recyclebin", children: [], expanded: false, hasChildren: true, level: 1, defaultAction: "create", menuUrl: menuUrl }
|
||||
{ name: "Components", id: 1235, childNodesUrl: url, icon: "icon-cogs", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
|
||||
{ name: "Archieve", id: 1236, childNodesUrl: url, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
|
||||
{ name: "Recycle Bin", id: -20, childNodesUrl: url, icon: "icon-trash", routePath: "content/recyclebin", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl }
|
||||
],
|
||||
expanded: true,
|
||||
hasChildren: true,
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Umbraco.Web
|
||||
internal static IEnumerable<Type> ResolveAttributedTreeControllers(this PluginManager resolver)
|
||||
{
|
||||
//don't cache the result of this because it is only used once during app startup, caching will just add a bit more mem overhead for no reason
|
||||
return resolver.ResolveTypesWithAttribute<TreeApiController, TreeAttribute>(cacheResult: false);
|
||||
return resolver.ResolveTypesWithAttribute<TreeController, TreeAttribute>(cacheResult: false);
|
||||
}
|
||||
|
||||
internal static IEnumerable<Type> ResolveSurfaceControllers(this PluginManager resolver)
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Umbraco.Web.Trees
|
||||
{
|
||||
//get reference to all TreeApiControllers
|
||||
var controllerTrees = UmbracoApiControllerResolver.Current.RegisteredUmbracoApiControllers
|
||||
.Where(TypeHelper.IsTypeAssignableFrom<TreeApiController>)
|
||||
.Where(TypeHelper.IsTypeAssignableFrom<TreeController>)
|
||||
.ToArray();
|
||||
|
||||
//find the one we're looking for
|
||||
@@ -47,7 +47,7 @@ namespace Umbraco.Web.Trees
|
||||
}
|
||||
var foundControllerTree = foundControllerTreeAttempt.Result;
|
||||
//instantiate it, since we are proxying, we need to setup the instance with our current context
|
||||
var instance = (TreeApiController)DependencyResolver.Current.GetService(foundControllerTree);
|
||||
var instance = (TreeController)DependencyResolver.Current.GetService(foundControllerTree);
|
||||
instance.ControllerContext = controllerContext;
|
||||
instance.Request = controllerContext.Request;
|
||||
//return the root
|
||||
@@ -67,7 +67,7 @@ namespace Umbraco.Web.Trees
|
||||
var foundControllerTree = foundControllerTreeAttempt.Result;
|
||||
|
||||
//instantiate it, since we are proxying, we need to setup the instance with our current context
|
||||
var instance = (TreeApiController)DependencyResolver.Current.GetService(foundControllerTree);
|
||||
var instance = (TreeController)DependencyResolver.Current.GetService(foundControllerTree);
|
||||
instance.ControllerContext = controllerContext;
|
||||
instance.Request = controllerContext.Request;
|
||||
//return it's data
|
||||
|
||||
@@ -54,9 +54,6 @@ namespace Umbraco.Web.Trees
|
||||
{
|
||||
node = base.CreateRootNode(queryStrings);
|
||||
}
|
||||
|
||||
//InjectLegacyTreeCompatibility(node, queryStrings);
|
||||
|
||||
return node;
|
||||
|
||||
}
|
||||
@@ -81,7 +78,7 @@ namespace Umbraco.Web.Trees
|
||||
{
|
||||
var e = (UmbracoEntity)entity;
|
||||
|
||||
var allowedUserOptions = GetUserMenuItemsForNode(e);
|
||||
var allowedUserOptions = GetAllowedUserMenuItemsForNode(e);
|
||||
if (CanUserAccessNode(e, allowedUserOptions))
|
||||
{
|
||||
//TODO: if the node is of a specific type, don't list its children
|
||||
@@ -99,8 +96,6 @@ namespace Umbraco.Web.Trees
|
||||
e.ContentTypeIcon,
|
||||
hasChildren);
|
||||
|
||||
//InjectLegacyTreeCompatibility(node, queryStrings);
|
||||
|
||||
nodes.Add(node);
|
||||
}
|
||||
}
|
||||
@@ -109,10 +104,13 @@ namespace Umbraco.Web.Trees
|
||||
|
||||
protected override MenuItemCollection PerformGetMenuForNode(string id, FormDataCollection queryStrings)
|
||||
{
|
||||
var menu = new MenuItemCollection();
|
||||
|
||||
if (id == Constants.System.Root.ToInvariantString())
|
||||
{
|
||||
var menu = new MenuItemCollection();
|
||||
|
||||
//set the default to create
|
||||
menu.DefaultMenuAlias = ActionNew.Instance.Alias;
|
||||
|
||||
// we need to get the default permissions as you can't set permissions on the very root node
|
||||
var nodeActions = global::umbraco.BusinessLogic.Actions.Action.FromString(
|
||||
UmbracoUser.GetPermissions(Constants.System.Root.ToInvariantString()))
|
||||
@@ -123,19 +121,20 @@ namespace Umbraco.Web.Trees
|
||||
menu.AddMenuItem<ActionSort>();
|
||||
|
||||
//filter the standard items
|
||||
var allowedMenu = GetUserAllowedMenuItems(menu, nodeActions);
|
||||
FilterUserAllowedMenuItems(menu, nodeActions);
|
||||
|
||||
if (allowedMenu.Any())
|
||||
if (menu.MenuItems.Any())
|
||||
{
|
||||
allowedMenu.Last().SeperatorBefore = true;
|
||||
menu.MenuItems.Last().SeperatorBefore = true;
|
||||
}
|
||||
|
||||
// add default actions for *all* users
|
||||
allowedMenu.AddMenuItem<ActionRePublish>().ConvertLegacyMenuItem(null, "content", "content");
|
||||
allowedMenu.AddMenuItem<RefreshNode, ActionRefresh>(true);
|
||||
return allowedMenu;
|
||||
menu.AddMenuItem<ActionRePublish>().ConvertLegacyMenuItem(null, "content", "content");
|
||||
menu.AddMenuItem<RefreshNode, ActionRefresh>(true);
|
||||
return menu;
|
||||
}
|
||||
|
||||
|
||||
//return a normal node menu:
|
||||
int iid;
|
||||
if (int.TryParse(id, out iid) == false)
|
||||
@@ -148,9 +147,15 @@ namespace Umbraco.Web.Trees
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
return GetUserAllowedMenuItems(
|
||||
CreateAllowedActions(item),
|
||||
GetUserMenuItemsForNode(item));
|
||||
var nodeMenu = GetAllNodeMenuItems(item);
|
||||
var allowedMenuItems = GetAllowedUserMenuItemsForNode(item);
|
||||
|
||||
FilterUserAllowedMenuItems(nodeMenu, allowedMenuItems);
|
||||
|
||||
//set the default to create
|
||||
nodeMenu.DefaultMenuAlias = ActionNew.Instance.Alias;
|
||||
|
||||
return nodeMenu;
|
||||
}
|
||||
|
||||
protected override UmbracoObjectTypes UmbracoObjectType
|
||||
@@ -158,7 +163,12 @@ namespace Umbraco.Web.Trees
|
||||
get { return UmbracoObjectTypes.Document; }
|
||||
}
|
||||
|
||||
protected IEnumerable<MenuItem> CreateAllowedActions(IUmbracoEntity item)
|
||||
/// <summary>
|
||||
/// Returns a collection of all menu items that can be on a content node
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
protected MenuItemCollection GetAllNodeMenuItems(IUmbracoEntity item)
|
||||
{
|
||||
var menu = new MenuItemCollection();
|
||||
menu.AddMenuItem<ActionNew>();
|
||||
@@ -185,23 +195,5 @@ namespace Umbraco.Web.Trees
|
||||
return menu;
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// This is required so that the legacy tree dialog pickers and the legacy TreeControl.ascx stuff works with these new trees.
|
||||
///// </summary>
|
||||
///// <param name="node"></param>
|
||||
///// <param name="queryStrings"></param>
|
||||
///// <remarks>
|
||||
///// NOTE: That if developers create brand new trees using webapi controllers then they'll need to manually make it
|
||||
///// compatible with the legacy tree pickers, 99.9% of the time devs won't be doing that and once we make the new tree
|
||||
///// pickers and devs update their apps to be angularized it won't matter
|
||||
///// </remarks>
|
||||
//private void InjectLegacyTreeCompatibility(TreeNode node, FormDataCollection queryStrings)
|
||||
//{
|
||||
// if (IsDialog(queryStrings))
|
||||
// {
|
||||
// node.AdditionalData["legacyDialogAction"] = "javascript:openContent(" + node.NodeId + ");";
|
||||
// }
|
||||
//}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ using umbraco.BusinessLogic.Actions;
|
||||
|
||||
namespace Umbraco.Web.Trees
|
||||
{
|
||||
public abstract class ContentTreeControllerBase : TreeApiController
|
||||
public abstract class ContentTreeControllerBase : TreeController
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the
|
||||
@@ -99,19 +99,27 @@ namespace Umbraco.Web.Trees
|
||||
/// <summary>
|
||||
/// Based on the allowed actions, this will filter the ones that the current user is allowed
|
||||
/// </summary>
|
||||
/// <param name="allMenuItems"></param>
|
||||
/// <param name="menuWithAllItems"></param>
|
||||
/// <param name="userAllowedMenuItems"></param>
|
||||
/// <returns></returns>
|
||||
protected MenuItemCollection GetUserAllowedMenuItems(IEnumerable<MenuItem> allMenuItems, IEnumerable<MenuItem> userAllowedMenuItems)
|
||||
protected void FilterUserAllowedMenuItems(MenuItemCollection menuWithAllItems, IEnumerable<MenuItem> userAllowedMenuItems)
|
||||
{
|
||||
var userAllowedActions = userAllowedMenuItems.Where(x => x.Action != null).Select(x => x.Action).ToArray();
|
||||
return new MenuItemCollection(allMenuItems.Where(
|
||||
a => (a.Action == null
|
||||
|| a.Action.CanBePermissionAssigned == false
|
||||
|| (a.Action.CanBePermissionAssigned && userAllowedActions.Contains(a.Action)))));
|
||||
|
||||
var notAllowed = menuWithAllItems.MenuItems.Where(
|
||||
a => (a.Action != null
|
||||
&& a.Action.CanBePermissionAssigned
|
||||
&& (a.Action.CanBePermissionAssigned == false || userAllowedActions.Contains(a.Action) == false)))
|
||||
.ToArray();
|
||||
|
||||
//remove the ones that aren't allowed.
|
||||
foreach (var m in notAllowed)
|
||||
{
|
||||
menuWithAllItems.RemoveMenuItem(m);
|
||||
}
|
||||
}
|
||||
|
||||
internal MenuItemCollection GetUserMenuItemsForNode(IUmbracoEntity dd)
|
||||
internal IEnumerable<MenuItem> GetAllowedUserMenuItemsForNode(IUmbracoEntity dd)
|
||||
{
|
||||
var actions = global::umbraco.BusinessLogic.Actions.Action.FromString(UmbracoUser.GetPermissions(dd.Path));
|
||||
|
||||
@@ -119,7 +127,7 @@ namespace Umbraco.Web.Trees
|
||||
if (dd.CreatorId == UmbracoUser.Id && actions.Contains(ActionDelete.Instance) == false)
|
||||
actions.Add(ActionDelete.Instance);
|
||||
|
||||
return new MenuItemCollection(actions.Select(x => new MenuItem(x)));
|
||||
return actions.Select(x => new MenuItem(x));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Umbraco.Web.Trees
|
||||
{
|
||||
[Tree(Constants.Applications.Developer, Constants.Trees.DataTypes, "Data Types")]
|
||||
[PluginController("UmbracoTrees")]
|
||||
public class DataTypeTreeController : TreeApiController
|
||||
public class DataTypeTreeController : TreeController
|
||||
{
|
||||
protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings)
|
||||
{
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace Umbraco.Web.Trees
|
||||
var legacyAtt = controllerAttempt.Result.GetCustomAttribute<LegacyBaseTreeAttribute>(false);
|
||||
if (legacyAtt == null)
|
||||
{
|
||||
LogHelper.Warn<LegacyTreeDataConverter>("Cannot render tree: " + appTree.Alias + ". Cannot render a " + typeof(TreeApiController) + " tree type with the legacy web services unless attributed with " + typeof(LegacyBaseTreeAttribute));
|
||||
LogHelper.Warn<LegacyTreeDataConverter>("Cannot render tree: " + appTree.Alias + ". Cannot render a " + typeof(TreeController) + " tree type with the legacy web services unless attributed with " + typeof(LegacyBaseTreeAttribute));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ namespace Umbraco.Web.Trees
|
||||
if (t is ContextMenuSeperator && numAdded > 0)
|
||||
{
|
||||
//store the index for which the seperator should be placed
|
||||
seperators.Add(collection.Count());
|
||||
seperators.Add(collection.MenuItems.Count());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -164,12 +164,12 @@ namespace Umbraco.Web.Trees
|
||||
numAdded++;
|
||||
}
|
||||
}
|
||||
var length = collection.Count();
|
||||
var length = collection.MenuItems.Count();
|
||||
foreach (var s in seperators)
|
||||
{
|
||||
if (length >= s)
|
||||
{
|
||||
collection.ElementAt(s).SeperatorBefore = true;
|
||||
collection.MenuItems.ElementAt(s).SeperatorBefore = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,9 @@ namespace Umbraco.Web.Trees
|
||||
{
|
||||
var menu = new MenuItemCollection();
|
||||
|
||||
//set the default
|
||||
menu.DefaultMenuAlias = ActionNew.Instance.Alias;
|
||||
|
||||
if (id == Constants.System.Root.ToInvariantString())
|
||||
{
|
||||
// root actions
|
||||
|
||||
@@ -9,12 +9,14 @@ using umbraco.interfaces;
|
||||
namespace Umbraco.Web.Trees.Menu
|
||||
{
|
||||
|
||||
[CollectionDataContract(Name = "menuItems", Namespace = "")]
|
||||
public class MenuItemCollection : IEnumerable<MenuItem>
|
||||
[DataContract(Name = "menuItems", Namespace = "")]
|
||||
public class MenuItemCollection
|
||||
{
|
||||
private readonly List<MenuItem> _menuItems;
|
||||
|
||||
public MenuItemCollection()
|
||||
{
|
||||
|
||||
_menuItems = new List<MenuItem>();
|
||||
}
|
||||
|
||||
public MenuItemCollection(IEnumerable<MenuItem> items)
|
||||
@@ -22,7 +24,20 @@ namespace Umbraco.Web.Trees.Menu
|
||||
_menuItems = new List<MenuItem>(items);
|
||||
}
|
||||
|
||||
private readonly List<MenuItem> _menuItems = new List<MenuItem>();
|
||||
/// <summary>
|
||||
/// Sets the default menu item alias to be shown when the menu is launched - this is optional and if not set then the menu will just be shown normally.
|
||||
/// </summary>
|
||||
[DataMember(Name = "defaultAlias")]
|
||||
public string DefaultMenuAlias { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of menu items
|
||||
/// </summary>
|
||||
[DataMember(Name = "menuItems")]
|
||||
public IEnumerable<MenuItem> MenuItems
|
||||
{
|
||||
get { return _menuItems; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a menu item
|
||||
@@ -37,6 +52,15 @@ namespace Umbraco.Web.Trees.Menu
|
||||
return item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a menu item
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
public void RemoveMenuItem(MenuItem item)
|
||||
{
|
||||
_menuItems.Remove(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a menu item
|
||||
/// </summary>
|
||||
@@ -167,14 +191,5 @@ namespace Umbraco.Web.Trees.Menu
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<MenuItem> GetEnumerator()
|
||||
{
|
||||
return _menuItems.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,319 +1,319 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net.Http.Formatting;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Web.Trees.Menu;
|
||||
using Umbraco.Web.WebApi;
|
||||
using Umbraco.Web.WebApi.Filters;
|
||||
|
||||
namespace Umbraco.Web.Trees
|
||||
{
|
||||
/// <summary>
|
||||
/// The base controller for all tree requests
|
||||
/// </summary>
|
||||
public abstract class TreeApiController : UmbracoAuthorizedApiController
|
||||
{
|
||||
private readonly TreeAttribute _attribute;
|
||||
|
||||
/// <summary>
|
||||
/// Remove the xml formatter... only support JSON!
|
||||
/// </summary>
|
||||
/// <param name="controllerContext"></param>
|
||||
protected override void Initialize(global::System.Web.Http.Controllers.HttpControllerContext controllerContext)
|
||||
{
|
||||
base.Initialize(controllerContext);
|
||||
controllerContext.Configuration.Formatters.Remove(controllerContext.Configuration.Formatters.XmlFormatter);
|
||||
}
|
||||
|
||||
protected TreeApiController()
|
||||
{
|
||||
//Locate the tree attribute
|
||||
var treeAttributes = GetType()
|
||||
.GetCustomAttributes(typeof(TreeAttribute), false)
|
||||
.OfType<TreeAttribute>()
|
||||
.ToArray();
|
||||
|
||||
if (treeAttributes.Any() == false)
|
||||
{
|
||||
throw new InvalidOperationException("The Tree controller is missing the " + typeof(TreeAttribute).FullName + " attribute");
|
||||
}
|
||||
|
||||
//assign the properties of this object to those of the metadata attribute
|
||||
_attribute = treeAttributes.First();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The method called to render the contents of the tree structure
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="queryStrings">
|
||||
/// All of the query string parameters passed from jsTree
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// We are allowing an arbitrary number of query strings to be pased in so that developers are able to persist custom data from the front-end
|
||||
/// to the back end to be used in the query for model data.
|
||||
/// </remarks>
|
||||
protected abstract TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the menu structure for the node
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <returns></returns>
|
||||
protected abstract MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings);
|
||||
|
||||
/// <summary>
|
||||
/// The name to display on the root node
|
||||
/// </summary>
|
||||
public virtual string RootNodeDisplayName
|
||||
{
|
||||
get { return _attribute.Title; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current tree alias from the attribute assigned to it.
|
||||
/// </summary>
|
||||
public string TreeAlias
|
||||
{
|
||||
get { return _attribute.Alias; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the root node for the tree
|
||||
/// </summary>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <returns></returns>
|
||||
[HttpQueryStringFilter("queryStrings")]
|
||||
public TreeNode GetRootNode(FormDataCollection queryStrings)
|
||||
{
|
||||
if (queryStrings == null) queryStrings = new FormDataCollection("");
|
||||
var node = CreateRootNode(queryStrings);
|
||||
|
||||
//add the tree alias to the root
|
||||
node.AdditionalData.Add("treeAlias", TreeAlias);
|
||||
|
||||
AddQueryStringsToAdditionalData(node, queryStrings);
|
||||
|
||||
//check if the tree is searchable and add that to the meta data as well
|
||||
if (this is ISearchableTree)
|
||||
{
|
||||
node.AdditionalData.Add("searchable", "true");
|
||||
}
|
||||
|
||||
//now update all data based on some of the query strings, like if we are running in dialog mode
|
||||
if (IsDialog(queryStrings))
|
||||
{
|
||||
node.RoutePath = "#";
|
||||
}
|
||||
|
||||
OnRootNodeRendering(this, new TreeNodeRenderingEventArgs(node, queryStrings));
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The action called to render the contents of the tree structure
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="queryStrings">
|
||||
/// All of the query string parameters passed from jsTree
|
||||
/// </param>
|
||||
/// <returns>JSON markup for jsTree</returns>
|
||||
/// <remarks>
|
||||
/// We are allowing an arbitrary number of query strings to be pased in so that developers are able to persist custom data from the front-end
|
||||
/// to the back end to be used in the query for model data.
|
||||
/// </remarks>
|
||||
[HttpQueryStringFilter("queryStrings")]
|
||||
public TreeNodeCollection GetNodes(string id, FormDataCollection queryStrings)
|
||||
{
|
||||
if (queryStrings == null) queryStrings = new FormDataCollection("");
|
||||
var nodes = GetTreeNodes(id, queryStrings);
|
||||
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
AddQueryStringsToAdditionalData(node, queryStrings);
|
||||
}
|
||||
|
||||
//now update all data based on some of the query strings, like if we are running in dialog mode
|
||||
if (IsDialog((queryStrings)))
|
||||
{
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
node.RoutePath = "#";
|
||||
}
|
||||
}
|
||||
|
||||
//raise the event
|
||||
OnTreeNodesRendering(this, new TreeNodesRenderingEventArgs(nodes, queryStrings));
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The action called to render the menu for a tree node
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <returns></returns>
|
||||
[HttpQueryStringFilter("queryStrings")]
|
||||
public MenuItemCollection GetMenu(string id, FormDataCollection queryStrings)
|
||||
{
|
||||
if (queryStrings == null) queryStrings = new FormDataCollection("");
|
||||
return GetMenuForNode(id, queryStrings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to create a root model for a tree
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual TreeNode CreateRootNode(FormDataCollection queryStrings)
|
||||
{
|
||||
var rootNodeAsString = Constants.System.Root.ToString(CultureInfo.InvariantCulture);
|
||||
var currApp = queryStrings.GetValue<string>(TreeQueryStringParameters.Application);
|
||||
|
||||
var node = new TreeNode(
|
||||
rootNodeAsString,
|
||||
Url.GetTreeUrl(GetType(), rootNodeAsString, queryStrings),
|
||||
Url.GetMenuUrl(GetType(), rootNodeAsString, queryStrings))
|
||||
{
|
||||
HasChildren = true,
|
||||
RoutePath = currApp,
|
||||
Title = RootNodeDisplayName
|
||||
};
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The AdditionalData of a node is always populated with the query string data, this method performs this
|
||||
/// operation and ensures that special values are not inserted or that duplicate keys are not added.
|
||||
/// </summary>
|
||||
/// <param name="node"></param>
|
||||
/// <param name="queryStrings"></param>
|
||||
protected void AddQueryStringsToAdditionalData(TreeNode node, FormDataCollection queryStrings)
|
||||
{
|
||||
foreach (var q in queryStrings.Where(x => node.AdditionalData.ContainsKey(x.Key) == false))
|
||||
{
|
||||
node.AdditionalData.Add(q.Key, q.Value);
|
||||
}
|
||||
}
|
||||
|
||||
#region Create TreeNode methods
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to create tree nodes
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <param name="title"></param>
|
||||
/// <returns></returns>
|
||||
public TreeNode CreateTreeNode(string id, FormDataCollection queryStrings, string title)
|
||||
{
|
||||
var jsonUrl = Url.GetTreeUrl(GetType(), id, queryStrings);
|
||||
var menuUrl = Url.GetMenuUrl(GetType(), id, queryStrings);
|
||||
var node = new TreeNode(id, jsonUrl, menuUrl) { Title = title };
|
||||
return node;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to create tree nodes
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <param name="title"></param>
|
||||
/// <param name="icon"></param>
|
||||
/// <returns></returns>
|
||||
public TreeNode CreateTreeNode(string id, FormDataCollection queryStrings, string title, string icon)
|
||||
{
|
||||
var jsonUrl = Url.GetTreeUrl(GetType(), id, queryStrings);
|
||||
var menuUrl = Url.GetMenuUrl(GetType(), id, queryStrings);
|
||||
var node = new TreeNode(id, jsonUrl, menuUrl) { Title = title, Icon = icon };
|
||||
return node;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to create tree nodes
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <param name="title"></param>
|
||||
/// <param name="routePath"></param>
|
||||
/// <param name="icon"></param>
|
||||
/// <returns></returns>
|
||||
public TreeNode CreateTreeNode(string id, FormDataCollection queryStrings, string title, string icon, string routePath)
|
||||
{
|
||||
var jsonUrl = Url.GetTreeUrl(GetType(), id, queryStrings);
|
||||
var menuUrl = Url.GetMenuUrl(GetType(), id, queryStrings);
|
||||
var node = new TreeNode(id, jsonUrl, menuUrl) { Title = title, RoutePath = routePath, Icon = icon };
|
||||
return node;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to create tree nodes and automatically generate the json url
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <param name="title"></param>
|
||||
/// <param name="icon"></param>
|
||||
/// <param name="hasChildren"></param>
|
||||
/// <returns></returns>
|
||||
public TreeNode CreateTreeNode(string id, FormDataCollection queryStrings, string title, string icon, bool hasChildren)
|
||||
{
|
||||
var treeNode = CreateTreeNode(id, queryStrings, title, icon);
|
||||
treeNode.HasChildren = hasChildren;
|
||||
return treeNode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to create tree nodes and automatically generate the json url
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <param name="title"></param>
|
||||
/// <param name="routePath"></param>
|
||||
/// <param name="hasChildren"></param>
|
||||
/// <param name="icon"></param>
|
||||
/// <returns></returns>
|
||||
public TreeNode CreateTreeNode(string id, FormDataCollection queryStrings, string title, string icon, bool hasChildren, string routePath)
|
||||
{
|
||||
var treeNode = CreateTreeNode(id, queryStrings, title, icon);
|
||||
treeNode.HasChildren = hasChildren;
|
||||
treeNode.RoutePath = routePath;
|
||||
return treeNode;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Query String parameter helpers
|
||||
|
||||
/// <summary>
|
||||
/// If the request is for a dialog mode tree
|
||||
/// </summary>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <returns></returns>
|
||||
protected bool IsDialog(FormDataCollection queryStrings)
|
||||
{
|
||||
return queryStrings.GetValue<bool>(TreeQueryStringParameters.DialogMode);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public static event EventHandler<TreeNodesRenderingEventArgs> TreeNodesRendering;
|
||||
|
||||
private static void OnTreeNodesRendering(TreeApiController instance, TreeNodesRenderingEventArgs e)
|
||||
{
|
||||
var handler = TreeNodesRendering;
|
||||
if (handler != null) handler(instance, e);
|
||||
}
|
||||
|
||||
public static event EventHandler<TreeNodeRenderingEventArgs> RootNodeRendering;
|
||||
|
||||
private static void OnRootNodeRendering(TreeApiController instance, TreeNodeRenderingEventArgs e)
|
||||
{
|
||||
var handler = RootNodeRendering;
|
||||
if (handler != null) handler(instance, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net.Http.Formatting;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Web.Trees.Menu;
|
||||
using Umbraco.Web.WebApi;
|
||||
using Umbraco.Web.WebApi.Filters;
|
||||
|
||||
namespace Umbraco.Web.Trees
|
||||
{
|
||||
/// <summary>
|
||||
/// The base controller for all tree requests
|
||||
/// </summary>
|
||||
public abstract class TreeController : UmbracoAuthorizedApiController
|
||||
{
|
||||
private readonly TreeAttribute _attribute;
|
||||
|
||||
/// <summary>
|
||||
/// Remove the xml formatter... only support JSON!
|
||||
/// </summary>
|
||||
/// <param name="controllerContext"></param>
|
||||
protected override void Initialize(global::System.Web.Http.Controllers.HttpControllerContext controllerContext)
|
||||
{
|
||||
base.Initialize(controllerContext);
|
||||
controllerContext.Configuration.Formatters.Remove(controllerContext.Configuration.Formatters.XmlFormatter);
|
||||
}
|
||||
|
||||
protected TreeController()
|
||||
{
|
||||
//Locate the tree attribute
|
||||
var treeAttributes = GetType()
|
||||
.GetCustomAttributes(typeof(TreeAttribute), false)
|
||||
.OfType<TreeAttribute>()
|
||||
.ToArray();
|
||||
|
||||
if (treeAttributes.Any() == false)
|
||||
{
|
||||
throw new InvalidOperationException("The Tree controller is missing the " + typeof(TreeAttribute).FullName + " attribute");
|
||||
}
|
||||
|
||||
//assign the properties of this object to those of the metadata attribute
|
||||
_attribute = treeAttributes.First();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The method called to render the contents of the tree structure
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="queryStrings">
|
||||
/// All of the query string parameters passed from jsTree
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// We are allowing an arbitrary number of query strings to be pased in so that developers are able to persist custom data from the front-end
|
||||
/// to the back end to be used in the query for model data.
|
||||
/// </remarks>
|
||||
protected abstract TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the menu structure for the node
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <returns></returns>
|
||||
protected abstract MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings);
|
||||
|
||||
/// <summary>
|
||||
/// The name to display on the root node
|
||||
/// </summary>
|
||||
public virtual string RootNodeDisplayName
|
||||
{
|
||||
get { return _attribute.Title; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current tree alias from the attribute assigned to it.
|
||||
/// </summary>
|
||||
public string TreeAlias
|
||||
{
|
||||
get { return _attribute.Alias; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the root node for the tree
|
||||
/// </summary>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <returns></returns>
|
||||
[HttpQueryStringFilter("queryStrings")]
|
||||
public TreeNode GetRootNode(FormDataCollection queryStrings)
|
||||
{
|
||||
if (queryStrings == null) queryStrings = new FormDataCollection("");
|
||||
var node = CreateRootNode(queryStrings);
|
||||
|
||||
//add the tree alias to the root
|
||||
node.AdditionalData.Add("treeAlias", TreeAlias);
|
||||
|
||||
AddQueryStringsToAdditionalData(node, queryStrings);
|
||||
|
||||
//check if the tree is searchable and add that to the meta data as well
|
||||
if (this is ISearchableTree)
|
||||
{
|
||||
node.AdditionalData.Add("searchable", "true");
|
||||
}
|
||||
|
||||
//now update all data based on some of the query strings, like if we are running in dialog mode
|
||||
if (IsDialog(queryStrings))
|
||||
{
|
||||
node.RoutePath = "#";
|
||||
}
|
||||
|
||||
OnRootNodeRendering(this, new TreeNodeRenderingEventArgs(node, queryStrings));
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The action called to render the contents of the tree structure
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="queryStrings">
|
||||
/// All of the query string parameters passed from jsTree
|
||||
/// </param>
|
||||
/// <returns>JSON markup for jsTree</returns>
|
||||
/// <remarks>
|
||||
/// We are allowing an arbitrary number of query strings to be pased in so that developers are able to persist custom data from the front-end
|
||||
/// to the back end to be used in the query for model data.
|
||||
/// </remarks>
|
||||
[HttpQueryStringFilter("queryStrings")]
|
||||
public TreeNodeCollection GetNodes(string id, FormDataCollection queryStrings)
|
||||
{
|
||||
if (queryStrings == null) queryStrings = new FormDataCollection("");
|
||||
var nodes = GetTreeNodes(id, queryStrings);
|
||||
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
AddQueryStringsToAdditionalData(node, queryStrings);
|
||||
}
|
||||
|
||||
//now update all data based on some of the query strings, like if we are running in dialog mode
|
||||
if (IsDialog((queryStrings)))
|
||||
{
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
node.RoutePath = "#";
|
||||
}
|
||||
}
|
||||
|
||||
//raise the event
|
||||
OnTreeNodesRendering(this, new TreeNodesRenderingEventArgs(nodes, queryStrings));
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The action called to render the menu for a tree node
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <returns></returns>
|
||||
[HttpQueryStringFilter("queryStrings")]
|
||||
public MenuItemCollection GetMenu(string id, FormDataCollection queryStrings)
|
||||
{
|
||||
if (queryStrings == null) queryStrings = new FormDataCollection("");
|
||||
return GetMenuForNode(id, queryStrings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to create a root model for a tree
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual TreeNode CreateRootNode(FormDataCollection queryStrings)
|
||||
{
|
||||
var rootNodeAsString = Constants.System.Root.ToString(CultureInfo.InvariantCulture);
|
||||
var currApp = queryStrings.GetValue<string>(TreeQueryStringParameters.Application);
|
||||
|
||||
var node = new TreeNode(
|
||||
rootNodeAsString,
|
||||
Url.GetTreeUrl(GetType(), rootNodeAsString, queryStrings),
|
||||
Url.GetMenuUrl(GetType(), rootNodeAsString, queryStrings))
|
||||
{
|
||||
HasChildren = true,
|
||||
RoutePath = currApp,
|
||||
Title = RootNodeDisplayName
|
||||
};
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The AdditionalData of a node is always populated with the query string data, this method performs this
|
||||
/// operation and ensures that special values are not inserted or that duplicate keys are not added.
|
||||
/// </summary>
|
||||
/// <param name="node"></param>
|
||||
/// <param name="queryStrings"></param>
|
||||
protected void AddQueryStringsToAdditionalData(TreeNode node, FormDataCollection queryStrings)
|
||||
{
|
||||
foreach (var q in queryStrings.Where(x => node.AdditionalData.ContainsKey(x.Key) == false))
|
||||
{
|
||||
node.AdditionalData.Add(q.Key, q.Value);
|
||||
}
|
||||
}
|
||||
|
||||
#region Create TreeNode methods
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to create tree nodes
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <param name="title"></param>
|
||||
/// <returns></returns>
|
||||
public TreeNode CreateTreeNode(string id, FormDataCollection queryStrings, string title)
|
||||
{
|
||||
var jsonUrl = Url.GetTreeUrl(GetType(), id, queryStrings);
|
||||
var menuUrl = Url.GetMenuUrl(GetType(), id, queryStrings);
|
||||
var node = new TreeNode(id, jsonUrl, menuUrl) { Title = title };
|
||||
return node;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to create tree nodes
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <param name="title"></param>
|
||||
/// <param name="icon"></param>
|
||||
/// <returns></returns>
|
||||
public TreeNode CreateTreeNode(string id, FormDataCollection queryStrings, string title, string icon)
|
||||
{
|
||||
var jsonUrl = Url.GetTreeUrl(GetType(), id, queryStrings);
|
||||
var menuUrl = Url.GetMenuUrl(GetType(), id, queryStrings);
|
||||
var node = new TreeNode(id, jsonUrl, menuUrl) { Title = title, Icon = icon };
|
||||
return node;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to create tree nodes
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <param name="title"></param>
|
||||
/// <param name="routePath"></param>
|
||||
/// <param name="icon"></param>
|
||||
/// <returns></returns>
|
||||
public TreeNode CreateTreeNode(string id, FormDataCollection queryStrings, string title, string icon, string routePath)
|
||||
{
|
||||
var jsonUrl = Url.GetTreeUrl(GetType(), id, queryStrings);
|
||||
var menuUrl = Url.GetMenuUrl(GetType(), id, queryStrings);
|
||||
var node = new TreeNode(id, jsonUrl, menuUrl) { Title = title, RoutePath = routePath, Icon = icon };
|
||||
return node;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to create tree nodes and automatically generate the json url
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <param name="title"></param>
|
||||
/// <param name="icon"></param>
|
||||
/// <param name="hasChildren"></param>
|
||||
/// <returns></returns>
|
||||
public TreeNode CreateTreeNode(string id, FormDataCollection queryStrings, string title, string icon, bool hasChildren)
|
||||
{
|
||||
var treeNode = CreateTreeNode(id, queryStrings, title, icon);
|
||||
treeNode.HasChildren = hasChildren;
|
||||
return treeNode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to create tree nodes and automatically generate the json url
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <param name="title"></param>
|
||||
/// <param name="routePath"></param>
|
||||
/// <param name="hasChildren"></param>
|
||||
/// <param name="icon"></param>
|
||||
/// <returns></returns>
|
||||
public TreeNode CreateTreeNode(string id, FormDataCollection queryStrings, string title, string icon, bool hasChildren, string routePath)
|
||||
{
|
||||
var treeNode = CreateTreeNode(id, queryStrings, title, icon);
|
||||
treeNode.HasChildren = hasChildren;
|
||||
treeNode.RoutePath = routePath;
|
||||
return treeNode;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Query String parameter helpers
|
||||
|
||||
/// <summary>
|
||||
/// If the request is for a dialog mode tree
|
||||
/// </summary>
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <returns></returns>
|
||||
protected bool IsDialog(FormDataCollection queryStrings)
|
||||
{
|
||||
return queryStrings.GetValue<bool>(TreeQueryStringParameters.DialogMode);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public static event EventHandler<TreeNodesRenderingEventArgs> TreeNodesRendering;
|
||||
|
||||
private static void OnTreeNodesRendering(TreeController instance, TreeNodesRenderingEventArgs e)
|
||||
{
|
||||
var handler = TreeNodesRendering;
|
||||
if (handler != null) handler(instance, e);
|
||||
}
|
||||
|
||||
public static event EventHandler<TreeNodeRenderingEventArgs> RootNodeRendering;
|
||||
|
||||
private static void OnRootNodeRendering(TreeController instance, TreeNodeRenderingEventArgs e)
|
||||
{
|
||||
var handler = RootNodeRendering;
|
||||
if (handler != null) handler(instance, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -428,7 +428,7 @@
|
||||
<Compile Include="Trees\Menu\RefreshNode.cs" />
|
||||
<Compile Include="Trees\SearchResultItem.cs" />
|
||||
<Compile Include="Trees\SectionRootNode.cs" />
|
||||
<Compile Include="Trees\TreeApiController.cs" />
|
||||
<Compile Include="Trees\TreeController.cs" />
|
||||
<Compile Include="Trees\ApplicationTreeExtensions.cs" />
|
||||
<Compile Include="Trees\TreeAttribute.cs" />
|
||||
<Compile Include="Trees\TreeNode.cs" />
|
||||
|
||||
Reference in New Issue
Block a user