From 82c784d56047ef8b4a9a30f582c8f735ea181abc Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 27 Sep 2013 15:19:39 +1000 Subject: [PATCH] Most of the member tree working, have the member editor rendering data --- src/Umbraco.Core/Constants-Applications.cs | 5 + .../src/common/resources/member.resource.js | 131 ++++++++++++++++++ .../src/views/member/delete.html | 12 ++ .../src/views/member/edit.html | 40 ++++++ .../views/member/member.delete.controller.js | 32 +++++ .../views/member/member.edit.controller.js | 88 ++++++++++++ src/Umbraco.Web.UI/config/trees.config | 2 +- .../Editors/BackOfficeController.cs | 3 +- .../Editors/ContentControllerBase.cs | 2 +- src/Umbraco.Web/Editors/MediaController.cs | 1 + src/Umbraco.Web/Editors/MemberController.cs | 96 +++++-------- .../Models/ContentEditing/MemberDisplay.cs | 20 +++ .../ContentEditing/MemberEntityBasic.cs | 21 --- .../Models/Mapping/MemberModelMapper.cs | 88 ++++++++++++ src/Umbraco.Web/Trees/MediaTreeController.cs | 80 +++++++++++ src/Umbraco.Web/Umbraco.Web.csproj | 3 +- .../umbraco/Trees/loadMembers.cs | 2 +- 17 files changed, 541 insertions(+), 85 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/common/resources/member.resource.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/member/delete.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/member/edit.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/member/member.delete.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/member/member.edit.controller.js create mode 100644 src/Umbraco.Web/Models/ContentEditing/MemberDisplay.cs delete mode 100644 src/Umbraco.Web/Models/ContentEditing/MemberEntityBasic.cs create mode 100644 src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs diff --git a/src/Umbraco.Core/Constants-Applications.cs b/src/Umbraco.Core/Constants-Applications.cs index 9a910e131a..4ae32d1b2d 100644 --- a/src/Umbraco.Core/Constants-Applications.cs +++ b/src/Umbraco.Core/Constants-Applications.cs @@ -53,6 +53,11 @@ /// public const string Content = "content"; + /// + /// alias for the media tree. + /// + public const string Members = "member"; + /// /// alias for the media tree. /// diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/member.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/member.resource.js new file mode 100644 index 0000000000..f5f32037ad --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/resources/member.resource.js @@ -0,0 +1,131 @@ +/** + * @ngdoc service + * @name umbraco.resources.memberResource + * @description Loads in data for members + **/ +function memberResource($q, $http, umbDataFormatter, umbRequestHelper) { + + /** internal method process the saving of data and post processing the result */ + function saveMember(content, action, files) { + return umbRequestHelper.postSaveContent( + umbRequestHelper.getApiUrl( + "memberApiBaseUrl", + "PostSave"), + content, action, files); + } + + return { + + + /** + * @ngdoc method + * @name umbraco.resources.memberResource#getByLogin + * @methodOf umbraco.resources.memberResource + * + * @description + * Gets a member item with a given id + * + * ##usage + *
+         * memberResource.getByLogin("tom")
+         *    .then(function(member) {
+         *        var mymember = member; 
+         *        alert('its here!');
+         *    });
+         * 
+ * + * @param {Int} id id of member item to return + * @returns {Promise} resourcePromise object containing the member item. + * + */ + getByLogin: function (loginName) { + + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "memberApiBaseUrl", + "GetByLogin", + [{ loginName: loginName }])), + 'Failed to retreive data for member id ' + loginName); + }, + + /** + * @ngdoc method + * @name umbraco.resources.memberResource#deleteByLogin + * @methodOf umbraco.resources.memberResource + * + * @description + * Deletes a member item with a given id + * + * ##usage + *
+         * memberResource.deleteByLogin(1234)
+         *    .then(function() {
+         *        alert('its gone!');
+         *    });
+         * 
+ * + * @param {Int} id id of member item to delete + * @returns {Promise} resourcePromise object. + * + */ + deleteByLogin: function (id) { + return umbRequestHelper.resourcePromise( + $http.delete( + umbRequestHelper.getApiUrl( + "memberApiBaseUrl", + "DeleteById", + [{ id: id }])), + 'Failed to delete item ' + id); + }, + + /** + * @ngdoc method + * @name umbraco.resources.memberResource#getScaffold + * @methodOf umbraco.resources.memberResource + * + * @description + * Returns a scaffold of an empty member item, given the id of the member item to place it underneath and the member type alias. + * + * - Member Type alias must be provided so umbraco knows which properties to put on the member scaffold + * + * The scaffold is used to build editors for member that has not yet been populated with data. + * + * ##usage + *
+         * memberResource.getScaffold('client')
+         *    .then(function(scaffold) {
+         *        var myDoc = scaffold;
+         *        myDoc.name = "My new member item"; 
+         *
+         *        memberResource.save(myDoc, true)
+         *            .then(function(member){
+         *                alert("Retrieved, updated and saved again");
+         *            });
+         *    });
+         * 
+ * + * @param {String} alias membertype alias to base the scaffold on + * @returns {Promise} resourcePromise object containing the member scaffold. + * + */ + getScaffold: function (alias) { + + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "memberApiBaseUrl", + "GetEmpty", + [{ contentTypeAlias: alias }])), + 'Failed to retreive data for empty member item type ' + alias); + + }, + + /** saves or updates a member object */ + save: function (member, isNew, files) { + return saveMember(member, "save" + (isNew ? "New" : ""), files); + } + }; +} + +angular.module('umbraco.resources').factory('memberResource', memberResource); diff --git a/src/Umbraco.Web.UI.Client/src/views/member/delete.html b/src/Umbraco.Web.UI.Client/src/views/member/delete.html new file mode 100644 index 0000000000..c4d931b53b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/member/delete.html @@ -0,0 +1,12 @@ +
+
+ +

+ Are you sure you want to delete {{currentNode.name}} ? +

+ + + + +
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/member/edit.html b/src/Umbraco.Web.UI.Client/src/views/member/edit.html new file mode 100644 index 0000000000..221886e162 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/member/edit.html @@ -0,0 +1,40 @@ +
+ + + +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+ + + + +
+ + + + +
+ +
+
+
+
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/member/member.delete.controller.js b/src/Umbraco.Web.UI.Client/src/views/member/member.delete.controller.js new file mode 100644 index 0000000000..27d01f6a4a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/member/member.delete.controller.js @@ -0,0 +1,32 @@ +/** + * @ngdoc controller + * @name Umbraco.Editors.Member.DeleteController + * @function + * + * @description + * The controller for deleting content + */ +function MemberDeleteController($scope, memberResource, treeService, navigationService) { + + $scope.performDelete = function() { + + //mark it for deletion (used in the UI) + $scope.currentNode.loading = true; + + memberResource.deleteByLogin($scope.currentNode.id).then(function () { + $scope.currentNode.loading = false; + + //TODO: Need to sync tree, etc... + treeService.removeNode($scope.currentNode); + + navigationService.hideMenu(); + }); + + }; + + $scope.cancel = function() { + navigationService.hideDialog(); + }; +} + +angular.module("umbraco").controller("Umbraco.Editors.Member.DeleteController", MemberDeleteController); diff --git a/src/Umbraco.Web.UI.Client/src/views/member/member.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/member/member.edit.controller.js new file mode 100644 index 0000000000..4ff85a6f95 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/member/member.edit.controller.js @@ -0,0 +1,88 @@ +/** + * @ngdoc controller + * @name Umbraco.Editors.Member.EditController + * @function + * + * @description + * The controller for the member editor + */ +function MemberEditController($scope, $routeParams, $q, $timeout, $window, memberResource, notificationsService, angularHelper, serverValidationManager, contentEditingHelper, fileManager, editorContextService) { + + //initialize the file manager + fileManager.clearFiles(); + + if ($routeParams.create) { + //we are creating so get an empty member item + memberResource.getScaffold($routeParams.id, $routeParams.doctype) + .then(function(data) { + $scope.loaded = true; + $scope.content = data; + editorContextService.setContext($scope.content); + }); + } + else { + //we are editing so get the content item from the server + memberResource.getByLogin($routeParams.id) + .then(function(data) { + $scope.loaded = true; + $scope.content = data; + editorContextService.setContext($scope.content); + + //in one particular special case, after we've created a new item we redirect back to the edit + // route but there might be server validation errors in the collection which we need to display + // after the redirect, so we will bind all subscriptions which will show the server validation errors + // if there are any and then clear them so the collection no longer persists them. + serverValidationManager.executeAndClearAllSubscriptions(); + }); + } + + //TODO: Need to figure out a way to share the saving and event broadcasting with all editors! + + $scope.setStatus = function(status){ + //add localization + $scope.status = status; + $timeout(function(){ + $scope.status = undefined; + }, 2500); + }; + + $scope.save = function () { + var deferred = $q.defer(); + + $scope.setStatus("Saving..."); + $scope.$broadcast("saving", { scope: $scope }); + + var currentForm = angularHelper.getRequiredCurrentForm($scope); + + //don't continue if the form is invalid + if (currentForm.$invalid) return; + + serverValidationManager.reset(); + + memberResource.save($scope.content, $routeParams.create, fileManager.getFiles()) + .then(function (data) { + + contentEditingHelper.handleSuccessfulSave({ + scope: $scope, + newContent: data, + rebindCallback: contentEditingHelper.reBindChangedProperties($scope.content, data) + }); + + deferred.resolve(data); + + }, function (err) { + contentEditingHelper.handleSaveError({ + err: err, + allNewProps: contentEditingHelper.getAllProps(err.data), + allOrigProps: contentEditingHelper.getAllProps($scope.content) + }); + + deferred.reject(err); + }); + + return deferred.promise; + }; + +} + +angular.module("umbraco").controller("Umbraco.Editors.Member.EditController", MemberEditController); diff --git a/src/Umbraco.Web.UI/config/trees.config b/src/Umbraco.Web.UI/config/trees.config index e18f473875..58c3560e54 100644 --- a/src/Umbraco.Web.UI/config/trees.config +++ b/src/Umbraco.Web.UI/config/trees.config @@ -30,7 +30,7 @@ - + diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs index 30d9fc55ee..8c17fcc663 100644 --- a/src/Umbraco.Web/Editors/BackOfficeController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeController.cs @@ -66,7 +66,8 @@ namespace Umbraco.Web.Editors {"entityApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl("GetById")}, {"dataTypeApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl("GetById")}, {"dashboardApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl("GetDashboard")}, - {"logApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl("GetEntityLog")} + {"logApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl("GetEntityLog")}, + {"memberApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl("GetByLogin")}, } }, { diff --git a/src/Umbraco.Web/Editors/ContentControllerBase.cs b/src/Umbraco.Web/Editors/ContentControllerBase.cs index a7d603c562..6413173ce8 100644 --- a/src/Umbraco.Web/Editors/ContentControllerBase.cs +++ b/src/Umbraco.Web/Editors/ContentControllerBase.cs @@ -39,7 +39,7 @@ namespace Umbraco.Web.Editors { } - protected HttpResponseMessage HandleContentNotFound(int id, bool throwException = true) + protected HttpResponseMessage HandleContentNotFound(object id, bool throwException = true) { ModelState.AddModelError("id", string.Format("content with id: {0} was not found", id)); var errorResponse = Request.CreateErrorResponse( diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index 8d1dd5c3c6..b32b709df5 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -33,6 +33,7 @@ using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Editors { + /// /// This controller is decorated with the UmbracoApplicationAuthorizeAttribute which means that any user requesting /// access to ALL of the methods on this controller will need access to the media application. diff --git a/src/Umbraco.Web/Editors/MemberController.cs b/src/Umbraco.Web/Editors/MemberController.cs index aa07dc4923..a67f472f1a 100644 --- a/src/Umbraco.Web/Editors/MemberController.cs +++ b/src/Umbraco.Web/Editors/MemberController.cs @@ -3,17 +3,19 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; - +using System.Web.Http; +using AutoMapper; using Examine.LuceneEngine.SearchCriteria; using Examine.SearchCriteria; - -using umbraco.cms.businesslogic.member; +using Umbraco.Core.Models; +using Umbraco.Web.WebApi; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; using Examine; using System.Web.Security; +using Member = umbraco.cms.businesslogic.member.Member; namespace Umbraco.Web.Editors { @@ -23,71 +25,47 @@ namespace Umbraco.Web.Editors /// [PluginController("UmbracoApi")] [UmbracoApplicationAuthorizeAttribute(Constants.Applications.Members)] - public class MemberController : UmbracoAuthorizedJsonController + public class MemberController : ContentControllerBase { - public IEnumerable Search(string query) + /// + /// Constructor + /// + public MemberController() + : this(UmbracoContext.Current) + { + } + + /// + /// Constructor + /// + /// + public MemberController(UmbracoContext umbracoContext) + : base(umbracoContext) { - if (string.IsNullOrEmpty(query)) - throw new ArgumentException("A search query must be defined", "query"); + } - - //this will change when we add the new members API, but for now, we need to live with this setup + /// + /// Gets the content json for the member + /// + /// + /// + public MemberDisplay GetByLogin(string loginName) + { if (Member.InUmbracoMemberMode()) { - var internalSearcher = ExamineManager.Instance.SearchProviderCollection[Constants.Examine.InternalMemberSearcher]; - var criteria = internalSearcher.CreateSearchCriteria("member", BooleanOperation.Or); - var fields = new[] { "id", "__nodeName", "email" }; - var term = new[] { query.ToLower().Escape() }; - var operation = criteria.GroupedOr(fields, term).Compile(); - - var results = internalSearcher.Search(operation) - .Select(x => new MemberEntityBasic - { - Id = int.Parse(x["id"]), - Name = x["nodeName"], - Email = x["email"], - LoginName = x["loginName"], - Icon = ".icon-user" - }); - - return results; + var foundMember = Services.MemberService.GetByUsername(loginName); + if (foundMember == null) + { + HandleContentNotFound(loginName); + } + return Mapper.Map(foundMember); } else { - IEnumerable results; - - if (query.Contains("@")) - { - results = from MembershipUser x in Membership.FindUsersByEmail(query) - select - new MemberEntityBasic() - { - //how do we get ID? - Id = 0, - Email = x.Email, - LoginName = x.UserName, - Name = x.UserName, - Icon = "icon-user" - }; - } - else - { - results = from MembershipUser x in Membership.FindUsersByName(query + "%") - select - new MemberEntityBasic() - { - //how do we get ID? - Id = 0, - Email = x.Email, - LoginName = x.UserName, - Name = x.UserName, - Icon = "icon-user" - }; - } - - return results; + //TODO: Support this + throw new HttpResponseException(Request.CreateValidationErrorResponse("Editing member with a non-umbraco membership provider is currently not supported")); } + } - } } diff --git a/src/Umbraco.Web/Models/ContentEditing/MemberDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/MemberDisplay.cs new file mode 100644 index 0000000000..27a4eaca0f --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/MemberDisplay.cs @@ -0,0 +1,20 @@ +using System.Runtime.Serialization; +using Umbraco.Core.Models; + +namespace Umbraco.Web.Models.ContentEditing +{ + /// + /// A model representing a member to be displayed in the back office + /// + [DataContract(Name = "content", Namespace = "")] + public class MemberDisplay : ContentItemDisplayBase + { + + [DataMember(Name = "username")] + public string Username { get; set; } + + [DataMember(Name = "email")] + public string Email { get; set; } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/ContentEditing/MemberEntityBasic.cs b/src/Umbraco.Web/Models/ContentEditing/MemberEntityBasic.cs deleted file mode 100644 index d836cb71a4..0000000000 --- a/src/Umbraco.Web/Models/ContentEditing/MemberEntityBasic.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.Serialization; -using System.Text; -using System.Threading.Tasks; -using Umbraco.Core.Models.Validation; - -namespace Umbraco.Web.Models.ContentEditing -{ - public class MemberEntityBasic : EntityBasic - { - [DataMember(Name = "email", IsRequired = true)] - [RequiredForPersistence(AllowEmptyStrings = false, ErrorMessage = "Required")] - public string Email { get; set; } - - [DataMember(Name = "loginName", IsRequired = true)] - [RequiredForPersistence(AllowEmptyStrings = false, ErrorMessage = "Required")] - public string LoginName { get; set; } - } -} diff --git a/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs b/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs new file mode 100644 index 0000000000..d1689e6eb2 --- /dev/null +++ b/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs @@ -0,0 +1,88 @@ +using AutoMapper; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Mapping; +using Umbraco.Web.Models.ContentEditing; +using umbraco; + +namespace Umbraco.Web.Models.Mapping +{ + /// + /// Declares model mappings for members. + /// + internal class MemberModelMapper : MapperConfiguration + { + public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext) + { + //FROM IMember TO MediaItemDisplay + config.CreateMap() + .ForMember( + dto => dto.Owner, + expression => expression.ResolveUsing>()) + .ForMember( + dto => dto.Icon, + expression => expression.MapFrom(content => content.ContentType.Icon)) + .ForMember( + dto => dto.ContentTypeAlias, + expression => expression.MapFrom(content => content.ContentType.Alias)) + .ForMember( + dto => dto.ContentTypeName, + expression => expression.MapFrom(content => content.ContentType.Name)) + .ForMember(display => display.Properties, expression => expression.Ignore()) + .ForMember(display => display.Tabs, expression => expression.ResolveUsing()) + .AfterMap(MapGenericCustomProperties); + + //FROM IMember TO ContentItemBasic + config.CreateMap>() + .ForMember( + dto => dto.Owner, + expression => expression.ResolveUsing>()) + .ForMember( + dto => dto.Icon, + expression => expression.MapFrom(content => content.ContentType.Icon)) + .ForMember( + dto => dto.ContentTypeAlias, + expression => expression.MapFrom(content => content.ContentType.Alias)); + + //FROM IMember TO ContentItemDto + config.CreateMap>() + .ForMember( + dto => dto.Owner, + expression => expression.ResolveUsing>()); + } + + /// + /// Maps the generic tab with custom properties for content + /// + /// + /// + private static void MapGenericCustomProperties(IMember member, MemberDisplay display) + { + + TabsAndPropertiesResolver.MapGenericProperties( + member, display, + new ContentPropertyDisplay + { + Alias = string.Format("{0}login", Constants.PropertyEditors.InternalGenericPropertiesPrefix), + Label = ui.Text("login"), + Value = display.Username, + View = "textbox" + }, + new ContentPropertyDisplay + { + Alias = string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix), + Label = ui.Text("password"), + Value = "", + View = "changepassword" //TODO: Hard coding this because the templatepicker doesn't necessarily need to be a resolvable (real) property editor + }, + new ContentPropertyDisplay + { + Alias = string.Format("{0}template", Constants.PropertyEditors.InternalGenericPropertiesPrefix), + Label = ui.Text("general", "email"), + Value = display.Email, + View = "textbox" + }); + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Trees/MediaTreeController.cs b/src/Umbraco.Web/Trees/MediaTreeController.cs index 4c2a3dd3c0..ee2432bf15 100644 --- a/src/Umbraco.Web/Trees/MediaTreeController.cs +++ b/src/Umbraco.Web/Trees/MediaTreeController.cs @@ -1,8 +1,11 @@ using System; +using System.Globalization; using System.Linq; using System.Net; using System.Net.Http.Formatting; +using System.Web; using System.Web.Http; +using System.Web.Security; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Services; @@ -11,9 +14,86 @@ using Umbraco.Web.Trees.Menu; using umbraco; using umbraco.BusinessLogic.Actions; using Constants = Umbraco.Core.Constants; +using LegacyMember = umbraco.cms.businesslogic.member.Member; namespace Umbraco.Web.Trees { + [LegacyBaseTree(typeof (loadMembers))] + [Tree(Constants.Applications.Members, Constants.Trees.Members, "Members")] + [PluginController("UmbracoTrees")] + public class MemberTreeController : TreeController + { + protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings) + { + var nodes = new TreeNodeCollection(); + + if (id == Constants.System.Root.ToInvariantString()) + { + //list out all the letters + for (var i = 97; i < 123; i++) + { + var charString = ((char) i).ToString(CultureInfo.InvariantCulture); + nodes.Add(CreateTreeNode(charString, queryStrings, charString, "icon-folder-close", true)); + } + //list out 'Others' if the membership provider is umbraco + if (LegacyMember.InUmbracoMemberMode()) + { + nodes.Add(CreateTreeNode("others", queryStrings, "Others", "icon-folder-close", true)); + } + } + else + { + //if it is a letter + if (id.Length == 1 && char.IsLower(id, 0)) + { + if (LegacyMember.InUmbracoMemberMode()) + { + //get the members from our member data layer + nodes.AddRange( + LegacyMember.getMemberFromFirstLetter(id.ToCharArray()[0]) + .Select(m => CreateTreeNode(m.LoginName, queryStrings, m.Text, "icon-user"))); + } + else + { + //get the members from the provider + int total; + nodes.AddRange( + Membership.Provider.FindUsersByName(id + "%", 0, 9999, out total).Cast() + .Select(m => CreateTreeNode(m.UserName, queryStrings, m.UserName, "icon-user"))); + } + } + else if (id == "others") + { + //others will only show up when in umbraco membership mode + nodes.AddRange( + LegacyMember.getAllOtherMembers() + .Select(m => CreateTreeNode(m.Id.ToInvariantString(), queryStrings, m.Text, "icon-user"))); + } + } + return nodes; + } + + protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings) + { + var menu = new MenuItemCollection(); + + if (id == Constants.System.Root.ToInvariantString()) + { + //set default + menu.DefaultMenuAlias = ActionNew.Instance.Alias; + + // root actions + menu.AddMenuItem(); + menu.AddMenuItem(true); + return menu; + } + + menu.AddMenuItem(); + return menu; + } + } + + [LegacyBaseTree(typeof(loadMedia))] [Tree(Constants.Applications.Media, Constants.Trees.Media, "Media")] [PluginController("UmbracoTrees")] diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index a09ed7c6df..4b64498c0b 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -314,10 +314,12 @@ + + @@ -333,7 +335,6 @@ - diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadMembers.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadMembers.cs index 26e0a59b40..3e11a35f49 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadMembers.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadMembers.cs @@ -31,7 +31,7 @@ namespace umbraco /// /// Handles loading of the member application into the application tree /// - [Tree(Constants.Applications.Members, "member", "Members")] + [Obsolete("This is no longer used and will be removed from the codebase in the future")] public class loadMembers : BaseTree { public loadMembers(string application) : base(application) { }