diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/umbtree.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/umbtree.directive.js index a69f13677a..c044a1edd2 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/umbtree.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/umbtree.directive.js @@ -60,7 +60,6 @@ angular.module("umbraco.directives") if (scope.eventhandler) { $(scope.eventhandler).trigger(eventName, args); } - // $rootScope.$broadcast(eventName, args); } /** Method to load in the tree data */ @@ -93,7 +92,7 @@ angular.module("umbraco.directives") * When changing sections we don't want all of the tree-ndoes to do their 'leave' animations. */ scope.animation = function () { - if (enableDeleteAnimations && scope.tree.root.expanded) { + if (enableDeleteAnimations && scope.tree && scope.tree.root && scope.tree.root.expanded) { return { leave: 'tree-node-delete-leave' }; } else { @@ -126,8 +125,17 @@ angular.module("umbraco.directives") } }); - //initial change - loadTree(); + //When the user logs in + scope.$on("authenticated", function (evt, data) { + //populate the tree if the user has changed + if (data.lastUserId !== data.user.id) { + treeService.clearCache(); + scope.tree = null; + loadTree(); + } + }); + + }; } }; diff --git a/src/Umbraco.Web.UI.Client/src/common/security/interceptor.js b/src/Umbraco.Web.UI.Client/src/common/security/interceptor.js index fe77a4b26f..e995765d29 100644 --- a/src/Umbraco.Web.UI.Client/src/common/security/interceptor.js +++ b/src/Umbraco.Web.UI.Client/src/common/security/interceptor.js @@ -6,7 +6,9 @@ angular.module('umbraco.security.interceptor', ['umbraco.security.retryQueue']) // Intercept failed requests return promise.then(null, function (originalResponse) { + //A 401 means that the user is not logged in if (originalResponse.status === 401) { + // The request bounced because it was not authorized - add a new request to the retry queue promise = queue.pushRetryFn('unauthorized-server', function retryRequest() { // We must use $injector to get the $http service to prevent circular dependency diff --git a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js index a85cc8024c..03fd6b2946 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js @@ -55,8 +55,19 @@ function contentEditingHelper($location, $routeParams, notificationsService, ser for (var p in allOrigProps) { var newProp = getNewProp(allOrigProps[p].alias); if (newProp && !_.isEqual(allOrigProps[p].value, newProp.value)) { + //they have changed so set the origContent prop to the new one + var origVal = allOrigProps[p].value; allOrigProps[p].value = newProp.value; + + //instead of having a property editor $watch their expression to check if it has + // been updated, instead we'll check for the existence of a special method on their model + // and just call it. + if (angular.isFunction(allOrigProps[p].onValueChanged)) { + //send the newVal + oldVal + allOrigProps[p].onValueChanged(allOrigProps[p].value, origVal); + } + changed.push(allOrigProps[p]); } } 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 db8a892618..a2b8961879 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 @@ -8,8 +8,10 @@ * The tree service factory, used internally by the umbTree and umbTreeItem directives */ function treeService($q, treeResource, iconHelper, notificationsService, $rootScope) { - //implement this in local storage + + //TODO: implement this in local storage var treeArray = []; + var standardCssClass = 'icon umb-tree-icon sprTree'; return { @@ -58,6 +60,11 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc } }, + /** clears the tree cache */ + clearCache: function() { + treeArray = []; + }, + /** * @ngdoc method * @name umbraco.services.treeService#loadNodeChildren @@ -259,6 +266,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc return data; }); } + }; } diff --git a/src/Umbraco.Web.UI.Client/src/common/services/user.service.js b/src/Umbraco.Web.UI.Client/src/common/services/user.service.js index 54a5ae1999..626b11b98f 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/user.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/user.service.js @@ -2,6 +2,7 @@ angular.module('umbraco.services') .factory('userService', function ($rootScope, $q, $location, $log, securityRetryQueue, authResource, dialogService) { var currentUser = null; + var lastUserId = null; var loginDialog = null; // Redirect to the given url (defaults to '/') @@ -36,6 +37,16 @@ angular.module('umbraco.services') // Register a handler for when an item is added to the retry queue securityRetryQueue.onItemAddedCallbacks.push(function (retryItem) { if (securityRetryQueue.hasMore()) { + + //store the last user id and clear the user + if (currentUser && currentUser.id !== undefined) { + lastUserId = currentUser.id; + } + currentUser = null; + + //broadcast a global event that the user is no longer logged in + $rootScope.$broadcast("notAuthenticated"); + openLoginDialog(); } }); @@ -43,19 +54,27 @@ angular.module('umbraco.services') return { /** Returns a promise, sends a request to the server to check if the current cookie is authorized */ - isAuthenticated: function () { + isAuthenticated: function (args) { return authResource.isAuthenticated() .then(function(data) { //note, this can return null if they are not authenticated - if (!data) { + if (!data) { throw "Not authenticated"; } else { + + var result = { user: data, authenticated: true, lastUserId: lastUserId }; + + if (args.broadcastEvent) { + //broadcast a global event, will inform listening controllers to load in the user specific data + $rootScope.$broadcast("authenticated", result); + } + currentUser = data; currentUser.avatar = 'http://www.gravatar.com/avatar/' + data.emailHash + '?s=40&d=404'; - return { user: data, authenticated: true }; + return result; } }); }, @@ -65,16 +84,29 @@ angular.module('umbraco.services') return authResource.performLogin(login, password) .then(function (data) { + //when it's successful, return the user data currentUser = data; - return { user: data, authenticated: true }; + + var result = { user: data, authenticated: true, lastUserId: lastUserId }; + + //broadcast a global event + $rootScope.$broadcast("authenticated", result); + + return result; }); }, logout: function () { return authResource.performLogout() .then(function (data) { + + lastUserId = currentUser.id; currentUser = null; + + //broadcast a global event + $rootScope.$broadcast("notAuthenticated"); + openLoginDialog(); return null; }); diff --git a/src/Umbraco.Web.UI.Client/src/routes.js b/src/Umbraco.Web.UI.Client/src/routes.js index 603948984b..b3bd6791cd 100644 --- a/src/Umbraco.Web.UI.Client/src/routes.js +++ b/src/Umbraco.Web.UI.Client/src/routes.js @@ -53,8 +53,10 @@ app.config(function ($routeProvider) { }); -app.run(['userService', function (userService) { +app.run(['userService', '$log', '$rootScope', function (userService, $log, $rootScope) { + // Get the current user when the application starts // (in case they are still logged in from a previous session) - userService.isAuthenticated(); + + userService.isAuthenticated({broadcastEvent: true}); }]); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.controller.js index e42a2aa479..a4dd1f7507 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.controller.js @@ -15,7 +15,8 @@ $scope.today = weekday[d.getDay()]; $scope.errorMsg = ""; - $scope.loginSubmit = function (login, password) { + $scope.loginSubmit = function (login, password) { + //if the login and password are not empty we need to automatically // validate them - this is because if there are validation errors on the server // then the user has to change both username & password to resubmit which isn't ideal, @@ -32,20 +33,19 @@ userService.authenticate(login, password) .then(function (data) { - //We need to load in the legacy tree js. - legacyJsLoader.loadLegacyTreeJs($scope).then( - function(result) { - var iframe = $("#right"); - if(iframe){ - var url = decodeURIComponent($routeParams.url); - if(!url){ - url ="dashboard.aspx"; - } - iframe.attr("src", url); - } + + var iframe = $("#right"); + if (iframe) { + var url = decodeURIComponent($routeParams.url); + if (!url) { + url = "dashboard.aspx"; + } + iframe.attr("src", url); + } - $scope.submit(true); - }); + $scope.submit(true); + + }, function (reason) { $scope.errorMsg = reason.errorMsg; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/user.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/user.controller.js index a0b244c8f2..e683c831d1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/user.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/user.controller.js @@ -12,7 +12,7 @@ angular.module("umbraco") $scope.gotoHistory = function (link) { $location.path(link); - $scope.$apply() + $scope.$apply(); $scope.hide(); }; }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/main.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/main.controller.js index 476c9663c0..502a379aa9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/main.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/main.controller.js @@ -10,6 +10,7 @@ */ function MainController($scope, $routeParams, $rootScope, $timeout, $http, $log, notificationsService, userService, navigationService, legacyJsLoader) { + var legacyTreeJsLoaded = false; //detect if the current device is touch-enabled $scope.touchDevice = ("ontouchstart" in window || window.touch || window.navigator.msMaxTouchPoints===5 || window.DocumentTouch && document instanceof DocumentTouch); @@ -53,53 +54,48 @@ function MainController($scope, $routeParams, $rootScope, $timeout, $http, $log, $rootScope.$emit("closeDialogs", event); }; - //fetch the authorized status - userService.isAuthenticated() - .then(function (data) { - - //We need to load in the legacy tree js. + //when a user logs out or timesout + $scope.$on("notAuthenticated", function() { + + $scope.authenticated = null; + $scope.user = null; + + }); + + //when a user is authorized setup the data + $scope.$on("authenticated", function (evt, data) { + + //We need to load in the legacy tree js but only once no matter what user has logged in + if (!legacyTreeJsLoaded) { legacyJsLoader.loadLegacyTreeJs($scope).then( function (result) { + legacyTreeJsLoaded = true; + //TODO: We could wait for this to load before running the UI ? }); - - $scope.authenticated = data.authenticated; - $scope.user = data.user; + } + $scope.authenticated = data.authenticated; + $scope.user = data.user; -/* - var url = "http://www.gravatar.com/avatar/" + $scope.user.emailHash + ".json?404=404"; - $http.jsonp(url).then(function(response){ - $log.log("found: " + response); - }, function(data){ - $log.log(data); - }); -*/ + //var url = "http://www.gravatar.com/avatar/" + $scope.user.emailHash + ".json?404=404"; + //$http.jsonp(url).then(function(response){ + // $log.log("found: " + response); + //}, function(data){ + // $log.log(data); + //}); - /* - if($scope.user.avatar){ - $http.get($scope.user.avatar).then(function(){ - //alert($scope.user.avatar); - $scope.avatar = $scope.user.avatar; - }); - }*/ + //if($scope.user.avatar){ + // $http.get($scope.user.avatar).then(function(){ + // //alert($scope.user.avatar); + // $scope.avatar = $scope.user.avatar; + // }); + //} + + }); - - }, function (reason) { - notificationsService.error("An error occurred checking authentication."); - $scope.authenticated = false; - $scope.user = null; - }); } //register it -angular.module('umbraco').controller("Umbraco.MainController", MainController); - -/* -angular.module("umbraco").run(function(eventsService){ - eventsService.subscribe("Umbraco.Dialogs.ContentPickerController.Select", function(a, b){ - a.node.name = "wat"; - }); -}); -*/ \ No newline at end of file +angular.module('umbraco').controller("Umbraco.MainController", MainController); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/navigation.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/navigation.controller.js index bc88a561bb..2f12a2a40d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/navigation.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/navigation.controller.js @@ -15,9 +15,12 @@ function NavigationController($scope,$rootScope, $location, $log, $routeParams, // IMPORTANT: all properties assigned to this scope are generally available on the scope object on dialogs since // when we create a dialog we pass in this scope to be used for the dialog's scope instead of creating a new one. $scope.nav = navigationService; - $scope.routeParams = $routeParams; - $scope.$watch("routeParams.section", function (newVal, oldVal) { - $scope.currentSection = newVal; + + $scope.$watch(function () { + //watch the route parameters section + return $routeParams.section; + }, function(newVal, oldVal) { + $scope.currentSection = newVal; }); //trigger search with a hotkey: @@ -31,10 +34,16 @@ function NavigationController($scope,$rootScope, $location, $log, $routeParams, $scope.selectedId = navigationService.currentId; $scope.sections = navigationService.sections; - sectionResource.getSections() - .then(function(result) { - $scope.sections = result; - }); + //When the user logs in + $scope.$on("authenticated", function (evt, data) { + //populate their sections if the user has changed + if (data.lastUserId !== data.user.id) { + sectionResource.getSections() + .then(function (result) { + $scope.sections = result; + }); + } + }); //This reacts to clicks passed to the body element which emits a global call to close all dialogs $rootScope.$on("closeDialogs", function (event) { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/urllist/urllist.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/urllist/urllist.controller.js index 6dcd33847a..24a754b4f5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/urllist/urllist.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/urllist/urllist.controller.js @@ -12,12 +12,11 @@ angular.module('umbraco').controller("Umbraco.Editors.UrlListController", formatDisplayValue(); - //we need to put a watch on the real model because when it changes we have to update our renderModel - $scope.$watch("model.value", function (newVal, oldVal) { - if (newVal !== null && newVal !== undefined && newVal !== oldVal) { - //update the display val again - formatDisplayValue(); - } - }); + //here we declare a special method which will be called whenever the value has changed from the server + //this is instead of doing a watch on the model.value = faster + $scope.model.onValueChanged = function(newVal, oldVal) { + //update the display val again + formatDisplayValue(); + }; }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI/web.Template.Debug.config b/src/Umbraco.Web.UI/web.Template.Debug.config index a43ba6a739..1cc428f95a 100644 --- a/src/Umbraco.Web.UI/web.Template.Debug.config +++ b/src/Umbraco.Web.UI/web.Template.Debug.config @@ -46,6 +46,7 @@ +