From 2872d0b0a2ec82bc0237450686ea2769a7fea610 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 17 Apr 2018 14:43:17 +1000 Subject: [PATCH] Fixes tree init logic when user isn't logged in, moves sequential promise chaining to a helper funciton, updates the route promise chaining to actual chain. --- .../common/services/angularhelper.service.js | 37 ++++++++++++- .../src/controllers/navigation.controller.js | 29 ++-------- src/Umbraco.Web.UI.Client/src/init.js | 39 +++++++------- src/Umbraco.Web.UI.Client/src/routes.js | 54 +++++++++---------- .../treepicker/treepicker.controller.js | 24 ++------- .../application/umb-navigation.html | 2 +- .../Umbraco/Views/AuthorizeUpgrade.cshtml | 5 +- .../umbraco/Views/Default.cshtml | 2 +- .../Editors/BackOfficeController.cs | 17 ++---- src/Umbraco.Web/Editors/BackOfficeModel.cs | 17 ++++++ src/Umbraco.Web/Umbraco.Web.csproj | 1 + 11 files changed, 117 insertions(+), 110 deletions(-) create mode 100644 src/Umbraco.Web/Editors/BackOfficeModel.cs diff --git a/src/Umbraco.Web.UI.Client/src/common/services/angularhelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/angularhelper.service.js index fb4d8216e3..24db2e5989 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/angularhelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/angularhelper.service.js @@ -8,7 +8,42 @@ */ function angularHelper($log, $q) { return { - + + /** + * Execute a list of promises sequentially. Unlike $q.all which executes all promises at once, this will execute them in sequence. + * @param {} promises + * @returns {} + */ + executeSequentialPromises: function (promises) { + + //this is sequential promise chaining, it's not pretty but we need to do it this way. + //$q.all doesn't execute promises in sequence but that's what we want to do here. + + if (!angular.isArray(promises)) { + throw "promises must be an array"; + } + + //now execute them in sequence... sorry there's no other good way to do it with angular promises + var j = 0; + function pExec(promise) { + j++; + return promise.then(function (data) { + if (j === promises.length) { + return $q.when(data); //exit + } + else { + return pExec(promises[j]); //recurse + } + }); + } + if (promises.length > 0) { + return pExec(promises[0]); //start the promise chain + } + else { + return $q.when(true); // just exit, no promises to execute + } + }, + /** * @ngdoc function * @name safeApply diff --git a/src/Umbraco.Web.UI.Client/src/controllers/navigation.controller.js b/src/Umbraco.Web.UI.Client/src/controllers/navigation.controller.js index d82c6d4307..88896285e2 100644 --- a/src/Umbraco.Web.UI.Client/src/controllers/navigation.controller.js +++ b/src/Umbraco.Web.UI.Client/src/controllers/navigation.controller.js @@ -286,10 +286,6 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar //reload the tree with it's updated querystring args $scope.treeApi.load($scope.currentSection).then(function () { - //this is sequential promise chaining, it's not pretty but we need to do it this way. $q.all doesn't execute promises in - //sequence but that's what we need to do here - - //re-sync to currently edited node var currNode = appState.getTreeState("selectedNode"); //create the list of promises @@ -299,26 +295,11 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar var path = treeService.getPath(currNode); promises.push($scope.treeApi.syncTree({ path: path, activate: true })); } - for (var i = 0; i < expandedPaths.length; i++) { - promises.push($scope.treeApi.syncTree({ path: expandedPaths[i], activate: false, forceReload: true })); - } - - //now execute them in sequence... sorry there's no other good way to do it with angular promises - var j = 0; - function pExec(promise) { - j++; - promise.then(function (data) { - if (j === promises.length) { - return $q.when(data); //exit - } - else { - return pExec(promises[j]); //recurse - } - }); - } - if (promises.length > 0) { - pExec(promises[0]); //start the promise chain - } + //for (var i = 0; i < expandedPaths.length; i++) { + // promises.push($scope.treeApi.syncTree({ path: expandedPaths[i], activate: false, forceReload: true })); + //} + //execute them sequentially + angularHelper.executeSequentialPromises(promises); }); }); diff --git a/src/Umbraco.Web.UI.Client/src/init.js b/src/Umbraco.Web.UI.Client/src/init.js index 666fcfdf5d..7fb35a4cd0 100644 --- a/src/Umbraco.Web.UI.Client/src/init.js +++ b/src/Umbraco.Web.UI.Client/src/init.js @@ -16,28 +16,29 @@ app.run(['userService', '$q', '$log', '$rootScope', '$location', 'queryStrings', /** Listens for authentication and checks if our required assets are loaded, if/once they are we'll broadcast a ready event */ eventsService.on("app.authenticated", function (evt, data) { - - $q.all([ - assetsService._loadInitAssets(), - userService.loadMomentLocaleForCurrentUser(), - tourService.registerAllTours() - ]).then(function () { - //Register all of the tours on the server - tourService.registerAllTours().then(function () { - appReady(data); + assetsService._loadInitAssets().then(function() { + $q.all([ + userService.loadMomentLocaleForCurrentUser(), + tourService.registerAllTours() + ]).then(function () { - // Auto start intro tour - tourService.getTourByAlias("umbIntroIntroduction").then(function (introTour) { - // start intro tour if it hasn't been completed or disabled - if (introTour && introTour.disabled !== true && introTour.completed !== true) { - tourService.startTour(introTour); - } + //Register all of the tours on the server + tourService.registerAllTours().then(function () { + appReady(data); + + // Auto start intro tour + tourService.getTourByAlias("umbIntroIntroduction").then(function (introTour) { + // start intro tour if it hasn't been completed or disabled + if (introTour && introTour.disabled !== true && introTour.completed !== true) { + tourService.startTour(introTour); + } + }); + + }, function () { + appAuthenticated = true; + appReady(data); }); - - }, function () { - appAuthenticated = true; - appReady(data); }); }); diff --git a/src/Umbraco.Web.UI.Client/src/routes.js b/src/Umbraco.Web.UI.Client/src/routes.js index 3fc4d3f78e..5f660cf212 100644 --- a/src/Umbraco.Web.UI.Client/src/routes.js +++ b/src/Umbraco.Web.UI.Client/src/routes.js @@ -1,54 +1,56 @@ app.config(function ($routeProvider) { - - /** This checks if the user is authenticated for a route and what the isRequired is set to. - Depending on whether isRequired = true, it first check if the user is authenticated and will resolve successfully + + /** + * This determines if the route can continue depending on authentication and initialization requirements + * @param {boolean} authRequired If true, it first checks if the user is authenticated and will resolve successfully otherwise the route will fail and the $routeChangeError event will execute, in that handler we will redirect to the rejected - path that is resolved from this method and prevent default (prevent the route from executing) */ - var canRoute = function(isRequired) { + path that is resolved from this method and prevent default (prevent the route from executing) + * @param {boolean} navRequired if true, the route can only continue once the main navigation is ready + * @returns {promise} + */ + var canRoute = function(authRequired) { return { /** Checks that the user is authenticated, then ensures that are requires assets are loaded */ isAuthenticatedAndReady: function ($q, userService, $route, assetsService, appState) { - var deferred = $q.defer(); - + //don't need to check if we've redirected to login and we've already checked auth if (!$route.current.params.section && ($route.current.params.check === false || $route.current.params.check === "false")) { - deferred.resolve(true); - return deferred.promise; + return $q.when(true); } - userService.isAuthenticated() + return userService.isAuthenticated() .then(function () { - assetsService._loadInitAssets().then(function() { + return assetsService._loadInitAssets().then(function () { //This could be the first time has loaded after the user has logged in, in this case // we need to broadcast the authenticated event - this will be handled by the startup (init) // handler to set/broadcast the ready state var broadcast = appState.getGlobalState("isReady") !== true; - userService.getCurrentUser({ broadcastEvent: broadcast }).then(function (user) { + return userService.getCurrentUser({ broadcastEvent: broadcast }).then(function (user) { //is auth, check if we allow or reject - if (isRequired) { - + if (authRequired) { + //This checks the current section and will force a redirect to 'content' as the default if ($route.current.params.section.toLowerCase() === "default" || $route.current.params.section.toLowerCase() === "umbraco" || $route.current.params.section === "") { $route.current.params.section = "content"; - } + } // U4-5430, Benjamin Howarth // We need to change the current route params if the user only has access to a single section // To do this we need to grab the current user's allowed sections, then reject the promise with the correct path. if (user.allowedSections.indexOf($route.current.params.section) > -1) { //this will resolve successfully so the route will continue - deferred.resolve(true); + return $q.when(true); } else { - deferred.reject({ path: "/" + user.allowedSections[0] }); + return $q.reject({ path: "/" + user.allowedSections[0] }); } } else { - deferred.reject({ path: "/" }); + return $q.reject({ path: "/" }); } }); @@ -56,17 +58,17 @@ app.config(function ($routeProvider) { }, function () { //not auth, check if we allow or reject - if (isRequired) { + if (authRequired) { //the check=false is checked above so that we don't have to make another http call to check //if they are logged in since we already know they are not. - deferred.reject({ path: "/login/false" }); + return $q.reject({ path: "/login/false" }); } else { //this will resolve successfully so the route will continue - deferred.resolve(true); + return $q.when(true); } }); - return deferred.promise; + } }; }; @@ -75,15 +77,13 @@ app.config(function ($routeProvider) { var doLogout = function() { return { isLoggedOut: function ($q, userService) { - var deferred = $q.defer(); - userService.logout().then(function () { + return userService.logout().then(function () { //success so continue - deferred.resolve(true); + return $q.when(true); }, function() { //logout failed somehow ? we'll reject with the login page i suppose - deferred.reject({ path: "/login/false" }); + return $q.reject({ path: "/login/false" }); }); - return deferred.promise; } } } diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.controller.js index 2180db1c64..c5ad32e0dc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.controller.js @@ -198,32 +198,14 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController", $timeout(function () { //reload the tree with it's updated querystring args vm.dialogTreeApi.load(vm.section).then(function () { - - //this is sequential promise chaining, it's not pretty but we need to do it this way. $q.all doesn't execute promises in - //sequence but that's what we need to do here - + //create the list of promises var promises = []; for (var i = 0; i < expandedPaths.length; i++) { promises.push(vm.dialogTreeApi.syncTree({ path: expandedPaths[i], activate: false, forceReload: true })); - } - - //now execute them in sequence... sorry there's no other good way to do it with angular promises - var j = 0; - function pExec(promise) { - j++; - promise.then(function (data) { - if (j === promises.length) { - return $q.when(data); //exit - } - else { - return pExec(promises[j]); //recurse - } - }); - } - if (promises.length > 0) { - pExec(promises[0]); //start the promise chain } + //execute them sequentially + angularHelper.executeSequentialPromises(promises); }); }); }; diff --git a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html index 0dd3851c6d..a470bfe307 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html @@ -16,7 +16,7 @@ -
+
@{ Layout = null; @@ -25,7 +26,7 @@ - + @@ -59,7 +60,7 @@ redirectUrl = Url.Action("AuthorizeUpgrade", "BackOffice") }); } - @Html.BareMinimumServerVariablesScript(Url, externalLoginUrl, Model.Features) + @Html.BareMinimumServerVariablesScript(Url, externalLoginUrl, Model.Features, Model.GlobalSettings)