From 72495b3eaf2a34cd87653a4b5ee79996f7e9beda Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 10 Aug 2017 17:25:16 +1000 Subject: [PATCH] U4-10227 Users section should start in the user tree --- .../application/umbsections.directive.js | 12 ++- src/Umbraco.Web.UI.Client/src/routes.js | 33 ++++--- .../Editors/DashboardController.cs | 51 +--------- src/Umbraco.Web/Editors/DashboardHelper.cs | 96 +++++++++++++++++++ src/Umbraco.Web/Editors/SectionController.cs | 44 ++++++++- .../Models/ContentEditing/Section.cs | 7 ++ .../Trees/ApplicationTreeController.cs | 2 +- src/Umbraco.Web/Umbraco.Web.csproj | 1 + 8 files changed, 178 insertions(+), 68 deletions(-) create mode 100644 src/Umbraco.Web/Editors/DashboardHelper.cs diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbsections.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbsections.directive.js index 5ce3eac8a0..6183151c2a 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbsections.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbsections.directive.js @@ -154,8 +154,16 @@ function sectionsDirective($timeout, $window, navigationService, treeService, se } navigationService.hideSearch(); - navigationService.showTree(section.alias); - $location.path("/" + section.alias); + navigationService.showTree(section.alias); + + //in some cases the section will have a custom route path specified, if there is one we'll use it + if (section.routePath) { + $location.path(section.routePath); + } + else { + $location.path(section.alias); + } + }; scope.sectionDblClick = function(section){ diff --git a/src/Umbraco.Web.UI.Client/src/routes.js b/src/Umbraco.Web.UI.Client/src/routes.js index 69e6779a15..7eafd7106d 100644 --- a/src/Umbraco.Web.UI.Client/src/routes.js +++ b/src/Umbraco.Web.UI.Client/src/routes.js @@ -98,14 +98,25 @@ app.config(function ($routeProvider) { resolve: doLogout() }) .when('/:section', { - templateUrl: function (rp) { - if (rp.section.toLowerCase() === "default" || rp.section.toLowerCase() === "umbraco" || rp.section === "") - { - rp.section = "content"; + //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 can execute some code in order to set the template Url + controller: function ($scope, $route, $routeParams, $location) { + if ($routeParams.section.toLowerCase() === "default" || $routeParams.section.toLowerCase() === "umbraco" || $routeParams.section === "") { + $routeParams.section = "content"; } + + //TODO: Here we could run some extra logic to check if the dashboard we are navigating + //to has any content to show and if not it could redirect to the first tree root path. + //BUT! this would mean that we'd need a server side call to check this data.... + //Instead we already have this data in the sections returned from the sectionResource but we + //don't want to cache data in a resource so we'd have to create a sectionService which would rely + //on the userService, then we update the umbsections.directive to use the sectionService and when the + //sectionService requests the sections, it caches the result against the current user. Then we can + //use the sectionService here to do the redirection. - rp.url = "dashboard.aspx?app=" + rp.section; - return 'views/common/dashboard.html'; + $routeParams.url = "dashboard.aspx?app=" + $routeParams.section; + $scope.templateUrl = 'views/common/dashboard.html'; }, resolve: canRoute(true) }) @@ -121,15 +132,11 @@ app.config(function ($routeProvider) { }) .when('/:section/:tree/:method', { templateUrl: function (rp) { + + //if there is no method registered for this then show the dashboard if (!rp.method) return "views/common/dashboard.html"; - - //NOTE: This current isn't utilized by anything but does open up some cool opportunities for - // us since we'll be able to have specialized views for individual sections which is something - // we've never had before. So could utilize this for a new dashboard model when we get native - // angular dashboards working. Perhaps a normal section dashboard would list out the registered - // dashboards (as tabs if we wanted) and each tab could actually be a route link to one of these views? - + return ('views/' + rp.tree + '/' + rp.method + '.html'); }, resolve: canRoute(true) diff --git a/src/Umbraco.Web/Editors/DashboardController.cs b/src/Umbraco.Web/Editors/DashboardController.cs index e4c94965b7..55286d99b4 100644 --- a/src/Umbraco.Web/Editors/DashboardController.cs +++ b/src/Umbraco.Web/Editors/DashboardController.cs @@ -19,7 +19,6 @@ using Umbraco.Core.Logging; namespace Umbraco.Web.Editors { - //we need to fire up the controller like this to enable loading of remote css directly from this controller [PluginController("UmbracoApi")] [ValidationFilter] @@ -124,54 +123,8 @@ namespace Umbraco.Web.Editors [ValidateAngularAntiForgeryToken] public IEnumerable> GetDashboard(string section) { - var tabs = new List>(); - var i = 1; - - // The dashboard config can contain more than one area inserted by a package. - foreach( var dashboardSection in UmbracoConfig.For.DashboardSettings().Sections.Where(x => x.Areas.Contains(section))) - { - //we need to validate access to this section - if (DashboardSecurity.AuthorizeAccess(dashboardSection, Security.CurrentUser, Services.SectionService) == false) - continue; - - //User is authorized - foreach (var tab in dashboardSection.Tabs) - { - //we need to validate access to this tab - if (DashboardSecurity.AuthorizeAccess(tab, Security.CurrentUser, Services.SectionService) == false) - continue; - - var dashboardControls = new List(); - - foreach (var control in tab.Controls) - { - if (DashboardSecurity.AuthorizeAccess(control, Security.CurrentUser, Services.SectionService) == false) - continue; - - var dashboardControl = new DashboardControl(); - var controlPath = control.ControlPath.Trim(); - dashboardControl.Path = IOHelper.FindFile(controlPath); - if (controlPath.ToLowerInvariant().EndsWith(".ascx".ToLowerInvariant())) - dashboardControl.ServerSide = true; - - dashboardControls.Add(dashboardControl); - } - - tabs.Add(new Tab - { - Id = i, - Alias = tab.Caption.ToSafeAlias(), - IsActive = i == 1, - Label = tab.Caption, - Properties = dashboardControls - }); - - i++; - } - } - - //In case there are no tabs or a user doesn't have access the empty tabs list is returned - return tabs; + var dashboardHelper = new DashboardHelper(Services.SectionService); + return dashboardHelper.GetDashboard(section, Security.CurrentUser); } } } diff --git a/src/Umbraco.Web/Editors/DashboardHelper.cs b/src/Umbraco.Web/Editors/DashboardHelper.cs new file mode 100644 index 0000000000..8b43335ebe --- /dev/null +++ b/src/Umbraco.Web/Editors/DashboardHelper.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core; +using Umbraco.Core.Configuration; +using Umbraco.Core.IO; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Services; +using Umbraco.Web.Models.ContentEditing; + +namespace Umbraco.Web.Editors +{ + internal class DashboardHelper + { + private readonly ISectionService _sectionService; + + public DashboardHelper(ISectionService sectionService) + { + if (sectionService == null) throw new ArgumentNullException("sectionService"); + _sectionService = sectionService; + } + + /// + /// Returns the dashboard models per section for the current user and it's access + /// + /// + /// + public IDictionary>> GetDashboards(IUser currentUser) + { + var result = new Dictionary>>(); + foreach (var section in _sectionService.GetSections()) + { + result[section.Alias] = GetDashboard(section.Alias, currentUser); + } + return result; + } + + /// + /// Returns the dashboard model for the given section based on the current user and it's access + /// + /// + /// + /// + public IEnumerable> GetDashboard(string section, IUser currentUser) + { + var tabs = new List>(); + var i = 1; + + // The dashboard config can contain more than one area inserted by a package. + foreach (var dashboardSection in UmbracoConfig.For.DashboardSettings().Sections.Where(x => x.Areas.Contains(section))) + { + //we need to validate access to this section + if (DashboardSecurity.AuthorizeAccess(dashboardSection, currentUser, _sectionService) == false) + continue; + + //User is authorized + foreach (var tab in dashboardSection.Tabs) + { + //we need to validate access to this tab + if (DashboardSecurity.AuthorizeAccess(tab, currentUser, _sectionService) == false) + continue; + + var dashboardControls = new List(); + + foreach (var control in tab.Controls) + { + if (DashboardSecurity.AuthorizeAccess(control, currentUser, _sectionService) == false) + continue; + + var dashboardControl = new DashboardControl(); + var controlPath = control.ControlPath.Trim(); + dashboardControl.Path = IOHelper.FindFile(controlPath); + if (controlPath.ToLowerInvariant().EndsWith(".ascx".ToLowerInvariant())) + dashboardControl.ServerSide = true; + + dashboardControls.Add(dashboardControl); + } + + tabs.Add(new Tab + { + Id = i, + Alias = tab.Caption.ToSafeAlias(), + IsActive = i == 1, + Label = tab.Caption, + Properties = dashboardControls + }); + + i++; + } + } + + //In case there are no tabs or a user doesn't have access the empty tabs list is returned + return tabs; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Editors/SectionController.cs b/src/Umbraco.Web/Editors/SectionController.cs index cdb5b29b7b..6563e18b17 100644 --- a/src/Umbraco.Web/Editors/SectionController.cs +++ b/src/Umbraco.Web/Editors/SectionController.cs @@ -5,6 +5,8 @@ using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; using System.Linq; +using System.Net.Http.Formatting; +using Umbraco.Web.Trees; namespace Umbraco.Web.Editors { @@ -15,9 +17,45 @@ namespace Umbraco.Web.Editors public class SectionController : UmbracoAuthorizedJsonController { public IEnumerable
GetSections() - { - var sections = Services.SectionService.GetAllowedSections(UmbracoUser.Id); - return sections.Select(Mapper.Map); + { + + var sections = Services.SectionService.GetAllowedSections(Security.GetUserId()); + + var sectionModels = sections.Select(Mapper.Map).ToArray(); + + //Check if there are empty dashboards or dashboards that will end up empty based on the current user's access + //and add the meta data about them + var dashboardHelper = new DashboardHelper(Services.SectionService); + //this is a bit nasty since we'll be proxying via the app tree controller but we sort of have to do that + //since tree's by nature are controllers and require request contextual data. + var appTreeController = new ApplicationTreeController + { + ControllerContext = ControllerContext + }; + var dashboards = dashboardHelper.GetDashboards(Security.CurrentUser); + //now we can add metadata for each section so that the UI knows if there's actually anything at all to render for + //a dashboard for a given section, then the UI can deal with it accordingly (i.e. redirect to the first tree) + foreach (var section in sectionModels) + { + var hasDashboards = false; + IEnumerable> dashboardsForSection; + if (dashboards.TryGetValue(section.Alias, out dashboardsForSection)) + { + if (dashboardsForSection.Any()) + hasDashboards = true; + } + + if (hasDashboards == false) + { + //get the first tree in the section and get it's root node route path + var sectionTrees = appTreeController.GetApplicationTrees(section.Alias, null, null).Result; + section.RoutePath = sectionTrees.IsContainer == false + ? sectionTrees.RoutePath + : sectionTrees.Children[0].RoutePath; + } + } + + return sectionModels; } public IEnumerable
GetAllSections() diff --git a/src/Umbraco.Web/Models/ContentEditing/Section.cs b/src/Umbraco.Web/Models/ContentEditing/Section.cs index b0c1839297..06ece10ddf 100644 --- a/src/Umbraco.Web/Models/ContentEditing/Section.cs +++ b/src/Umbraco.Web/Models/ContentEditing/Section.cs @@ -23,6 +23,13 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "alias")] public string Alias { get; set; } + + /// + /// In some cases a custom route path can be specified so that when clicking on a section it goes to this + /// path instead of the normal dashboard path + /// + [DataMember(Name = "routePath")] + public string RoutePath { get; set; } } } diff --git a/src/Umbraco.Web/Trees/ApplicationTreeController.cs b/src/Umbraco.Web/Trees/ApplicationTreeController.cs index c66cb1d22c..dcec993dfe 100644 --- a/src/Umbraco.Web/Trees/ApplicationTreeController.cs +++ b/src/Umbraco.Web/Trees/ApplicationTreeController.cs @@ -40,7 +40,7 @@ namespace Umbraco.Web.Trees var rootId = Constants.System.Root.ToString(CultureInfo.InvariantCulture); //find all tree definitions that have the current application alias - var appTrees = ApplicationContext.Current.Services.ApplicationTreeService.GetApplicationTrees(application, onlyInitialized).ToArray(); + var appTrees = Services.ApplicationTreeService.GetApplicationTrees(application, onlyInitialized).ToArray(); if (string.IsNullOrEmpty(tree) == false || appTrees.Length == 1) { diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 14ba427bbb..9046c92f48 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -320,6 +320,7 @@ +