diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/umbraco.servervariables.js b/src/Umbraco.Web.UI.Client/src/common/mocks/umbraco.servervariables.js
index 058a8366ae..d4fe75e80c 100644
--- a/src/Umbraco.Web.UI.Client/src/common/mocks/umbraco.servervariables.js
+++ b/src/Umbraco.Web.UI.Client/src/common/mocks/umbraco.servervariables.js
@@ -23,5 +23,10 @@ Umbraco.Sys.ServerVariables = {
"appPluginsPath" : "/App_Plugins",
"imageFileTypes": "jpeg,jpg,gif,bmp,png,tiff,tif"
},
+ umbracoPlugins: {
+ trees: [
+ { alias: "myTree", packageFolder: "MyPackage" }
+ ]
+ },
isDebuggingEnabled: true
};
\ No newline at end of file
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 eca8f70576..70d7dc577c 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
@@ -364,16 +364,19 @@ angular.module('umbraco.services')
//by convention we will look into the /views/{treetype}/{action}.html
// for example: /views/content/create.html
- //we will also check for a 'packageName' in metaData, if it exists, we'll look by convention in that folder
+ //we will also check for a 'packageName' for the current tree, if it exists then the convention will be:
// for example: /App_Plugins/{mypackage}/umbraco/{treetype}/create.html
- if (args.action.metaData["packageName"]) {
+ var treeAlias = treeService.getTreeAlias(args.node);
+ var packageTreeFolder = treeService.getTreePackageFolder(treeAlias);
- templateUrl = Umbraco.Sys.ServerVariables.umbracoSettings.appPluginsPath +
- "/umbraco/views/" + treeService.getTreeAlias(args.node) + "/" + args.action.alias + ".html";
+ if (packageTreeFolder) {
+ templateUrl = Umbraco.Sys.ServerVariables.umbracoSettings.appPluginsPath +
+ "/" + packageTreeFolder +
+ "/umbraco/" + treeAlias + "/" + args.action.alias + ".html";
}
else {
- templateUrl = "views/" + treeService.getTreeAlias(args.node) + "/" + args.action.alias + ".html";
+ templateUrl = "views/" + treeAlias + "/" + args.action.alias + ".html";
}
iframe = false;
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 f241ea6cab..b34cd72c79 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
@@ -58,6 +58,33 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc
}
},
+ /**
+ * @ngdoc method
+ * @name umbraco.services.treeService#getTreePackageFolder
+ * @methodOf umbraco.services.treeService
+ * @function
+ *
+ * @description
+ * Determines if the current tree is a plugin tree and if so returns the package folder it has declared
+ * so we know where to find it's views, otherwise it will just return undefined.
+ *
+ * @param {String} treeAlias The tree alias to check
+ */
+ getTreePackageFolder: function(treeAlias) {
+ //we determine this based on the server variables
+ if (Umbraco.Sys.ServerVariables.umbracoPlugins &&
+ Umbraco.Sys.ServerVariables.umbracoPlugins.trees &&
+ angular.isArray(Umbraco.Sys.ServerVariables.umbracoPlugins.trees)) {
+
+ var found = _.find(Umbraco.Sys.ServerVariables.umbracoPlugins.trees, function(item) {
+ return item.alias === treeAlias;
+ });
+
+ return found ? found.packageFolder : undefined;
+ }
+ return undefined;
+ },
+
/** clears the tree cache */
clearCache: function() {
treeArray = [];
diff --git a/src/Umbraco.Web.UI.Client/src/routes.js b/src/Umbraco.Web.UI.Client/src/routes.js
index 792fa23bd7..94ec046e81 100644
--- a/src/Umbraco.Web.UI.Client/src/routes.js
+++ b/src/Umbraco.Web.UI.Client/src/routes.js
@@ -84,21 +84,34 @@ app.config(function ($routeProvider) {
resolve: checkAuth(true)
})
.when('/:section/:tree/:method/:id', {
- templateUrl: function (rp) {
- if (!rp.tree || !rp.method) {
- return "views/common/dashboard.html";
+ //This allows us to dynamically change the template for this route since you cannot inject services into the templateUrl method.
+ template: "
",
+ //This controller will execute for this route, then we replace the template dynamnically based on the current tree.
+ controller: function ($scope, $route, $routeParams, treeService) {
+
+ if (!$routeParams.tree || !$routeParams.method) {
+ $scope.templateUrl = "views/common/dashboard.html";
}
-
- //TODO: Here we need to figure out if this route is for a package and if so then we need
+
+ // Here we need to figure out if this route is for a package tree and if so then we need
// to change it's convention view path to:
// /App_Plugins/{mypackage}/umbraco/{treetype}/{method}.html
+ // otherwise if it is a core tree we use the core paths:
+ // views/{treetype}/{method}.html
- //we don't need to put views into section folders since theoretically trees
- // could be moved among sections, we only need folders for specific trees.
+ var packageTreeFolder = treeService.getTreePackageFolder($routeParams.tree);
+
+ if (packageTreeFolder) {
+ $scope.templateUrl = Umbraco.Sys.ServerVariables.umbracoSettings.appPluginsPath +
+ "/" + packageTreeFolder +
+ "/umbraco/" + $routeParams.tree + "/" + $routeParams.method + ".html";
+ }
+ else {
+ $scope.templateUrl = 'views/' + $routeParams.tree + '/' + $routeParams.method + '.html';
+ }
- return 'views/' + rp.tree + '/' + rp.method + '.html';
- },
+ },
resolve: checkAuth(true)
})
.otherwise({ redirectTo: '/login' });
diff --git a/src/Umbraco.Web.UI.Client/test/unit/common/services/tree-service.spec.js b/src/Umbraco.Web.UI.Client/test/unit/common/services/tree-service.spec.js
index f5ed2935a4..230a5ad015 100644
--- a/src/Umbraco.Web.UI.Client/test/unit/common/services/tree-service.spec.js
+++ b/src/Umbraco.Web.UI.Client/test/unit/common/services/tree-service.spec.js
@@ -40,7 +40,23 @@ describe('tree service tests', function () {
beforeEach(inject(function ($injector) {
treeService = $injector.get('treeService');
- }));
+ }));
+
+ describe('lookup plugin based trees', function() {
+
+ it('can find a plugin based tree', function () {
+ //we know this exists in the mock umbraco server vars
+ var found = treeService.getTreePackageFolder("myTree");
+ expect(found).toBe("MyPackage");
+ });
+
+ it('returns undefined for a not found tree', function () {
+ //we know this exists in the mock umbraco server vars
+ var found = treeService.getTreePackageFolder("asdfasdf");
+ expect(found).not.toBeDefined();
+ });
+
+ });
describe('query existing node structure of the tree', function () {
diff --git a/src/Umbraco.Web.UI/App_Plugins/MyPackage/Trees/LegacyTestTree.cs b/src/Umbraco.Web.UI/App_Plugins/MyPackage/Trees/LegacyTestTree.cs
index 347c55503a..4971384529 100644
--- a/src/Umbraco.Web.UI/App_Plugins/MyPackage/Trees/LegacyTestTree.cs
+++ b/src/Umbraco.Web.UI/App_Plugins/MyPackage/Trees/LegacyTestTree.cs
@@ -30,9 +30,11 @@ namespace Umbraco.Web.UI.App_Plugins.MyPackage.Trees
}
protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings)
- {
- MenuItems.AddMenuItem(new MenuItem("create", "Create"));
- return MenuItems;
+ {
+ var menu = new MenuItemCollection();
+
+ menu.AddMenuItem(new MenuItem("create", "Create"));
+ return menu;
}
}
diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs
index 641d809ca7..dabbd6b12c 100644
--- a/src/Umbraco.Web/Editors/BackOfficeController.cs
+++ b/src/Umbraco.Web/Editors/BackOfficeController.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Text;
using System.Web.Mvc;
using Umbraco.Core.Configuration;
@@ -85,12 +86,45 @@ namespace Umbraco.Web.Editors
string.Join(",",UmbracoConfig.For.UmbracoSettings().Content.ImageFileTypes)},
}
},
+ {
+ "umbracoPlugins", new Dictionary
+ {
+ {"trees", GetTreePluginsMetaData()}
+ }
+ },
{ "isDebuggingEnabled", HttpContext.IsDebuggingEnabled }
};
return JavaScript(ServerVariablesParser.Parse(d));
}
+ private IEnumerable> GetTreePluginsMetaData()
+ {
+ var treeTypes = PluginManager.Current.ResolveAttributedTreeControllers();
+ //get all plugin trees with their attributes
+ var treesWithAttributes = treeTypes.Select(x => new
+ {
+ tree = x, attributes =
+ x.GetCustomAttributes(false)
+ }).ToArray();
+
+ var pluginTreesWithAttributes = treesWithAttributes
+ //don't resolve any tree decorated with CoreTreeAttribute
+ .Where(x => x.attributes.All(a => (a is CoreTreeAttribute) == false))
+ //we only care about trees with the PluginControllerAttribute
+ .Where(x => x.attributes.Any(a => a is PluginControllerAttribute))
+ .ToArray();
+
+ return (from p in pluginTreesWithAttributes
+ let treeAttr = p.attributes.OfType().Single()
+ let pluginAttr = p.attributes.OfType().Single()
+ select new Dictionary
+ {
+ {"alias", treeAttr.Alias}, {"packageFolder", pluginAttr.AreaName}
+ }).ToArray();
+
+ }
+
///
/// Returns the JavaScript blocks for any legacy trees declared
///
diff --git a/src/Umbraco.Web/PluginManagerExtensions.cs b/src/Umbraco.Web/PluginManagerExtensions.cs
index ab0846f530..bda4b3a95f 100644
--- a/src/Umbraco.Web/PluginManagerExtensions.cs
+++ b/src/Umbraco.Web/PluginManagerExtensions.cs
@@ -25,8 +25,7 @@ namespace Umbraco.Web
///
internal static IEnumerable 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(cacheResult: false);
+ return resolver.ResolveTypesWithAttribute();
}
internal static IEnumerable ResolveSurfaceControllers(this PluginManager resolver)
diff --git a/src/Umbraco.Web/Trees/ApplicationTreeRegistrar.cs b/src/Umbraco.Web/Trees/ApplicationTreeRegistrar.cs
index 2cdd43be38..6666936c70 100644
--- a/src/Umbraco.Web/Trees/ApplicationTreeRegistrar.cs
+++ b/src/Umbraco.Web/Trees/ApplicationTreeRegistrar.cs
@@ -7,6 +7,9 @@ using umbraco.businesslogic;
namespace Umbraco.Web.Trees
{
+ //TODO: Is there any way to get this to execute lazily when needed?
+ // i.e. When the back office loads so that this doesn't execute on startup for a content request.
+
///
/// A startup handler for putting the tree config in the config file based on attributes found
///
diff --git a/src/Umbraco.Web/Trees/ContentTreeController.cs b/src/Umbraco.Web/Trees/ContentTreeController.cs
index 080ca2a559..1d66121de2 100644
--- a/src/Umbraco.Web/Trees/ContentTreeController.cs
+++ b/src/Umbraco.Web/Trees/ContentTreeController.cs
@@ -22,6 +22,7 @@ namespace Umbraco.Web.Trees
[LegacyBaseTree(typeof(loadContent))]
[Tree(Constants.Applications.Content, Constants.Trees.Content, "Content")]
[PluginController("UmbracoTrees")]
+ [CoreTree]
public class ContentTreeController : ContentTreeControllerBase
{
protected override TreeNode CreateRootNode(FormDataCollection queryStrings)
diff --git a/src/Umbraco.Web/Trees/DataTypeTreeController.cs b/src/Umbraco.Web/Trees/DataTypeTreeController.cs
index 348fa88628..a5203da563 100644
--- a/src/Umbraco.Web/Trees/DataTypeTreeController.cs
+++ b/src/Umbraco.Web/Trees/DataTypeTreeController.cs
@@ -15,6 +15,7 @@ namespace Umbraco.Web.Trees
{
[Tree(Constants.Applications.Developer, Constants.Trees.DataTypes, "Data Types")]
[PluginController("UmbracoTrees")]
+ [CoreTree]
public class DataTypeTreeController : TreeController
{
protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings)
diff --git a/src/Umbraco.Web/Trees/MediaTreeController.cs b/src/Umbraco.Web/Trees/MediaTreeController.cs
index 8f806f980e..22fabe5da8 100644
--- a/src/Umbraco.Web/Trees/MediaTreeController.cs
+++ b/src/Umbraco.Web/Trees/MediaTreeController.cs
@@ -18,6 +18,7 @@ namespace Umbraco.Web.Trees
[LegacyBaseTree(typeof(loadMedia))]
[Tree(Constants.Applications.Media, Constants.Trees.Media, "Media")]
[PluginController("UmbracoTrees")]
+ [CoreTree]
public class MediaTreeController : ContentTreeControllerBase
{
protected override TreeNode CreateRootNode(FormDataCollection queryStrings)
diff --git a/src/Umbraco.Web/Trees/MemberTreeController.cs b/src/Umbraco.Web/Trees/MemberTreeController.cs
index 50c22812eb..8350662265 100644
--- a/src/Umbraco.Web/Trees/MemberTreeController.cs
+++ b/src/Umbraco.Web/Trees/MemberTreeController.cs
@@ -17,6 +17,7 @@ namespace Umbraco.Web.Trees
[LegacyBaseTree(typeof (loadMembers))]
[Tree(Constants.Applications.Members, Constants.Trees.Members, "Members")]
[PluginController("UmbracoTrees")]
+ [CoreTree]
public class MemberTreeController : TreeController
{
protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings)
diff --git a/src/Umbraco.Web/Trees/Menu/MenuItemCollection.cs b/src/Umbraco.Web/Trees/Menu/MenuItemCollection.cs
index 5e4f305848..bd71a8d595 100644
--- a/src/Umbraco.Web/Trees/Menu/MenuItemCollection.cs
+++ b/src/Umbraco.Web/Trees/Menu/MenuItemCollection.cs
@@ -12,7 +12,7 @@ namespace Umbraco.Web.Trees.Menu
[DataContract(Name = "menuItems", Namespace = "")]
public class MenuItemCollection
{
- private readonly string _packageFolderName;
+ //private readonly string _packageFolderName;
private readonly List