This commit is contained in:
Sebastiaan Janssen
2018-03-15 10:02:20 +01:00
parent 05c38db54c
commit eac9785c45
11 changed files with 407 additions and 284 deletions

View File

@@ -42,7 +42,12 @@
"codemirror"
],
"sources": {
"moment": "bower_components/moment/min/moment-with-locales.js",
"moment": [
"bower_components/moment/min/moment.min.js",
"bower_components/moment/min/moment-with-locales.js",
"bower_components/moment/min/moment-with-locales.min.js",
"bower_components/moment/locale/*.js"
],
"underscore": [
"bower_components/underscore/underscore-min.js",
"bower_components/underscore/underscore-min.map"

View File

@@ -0,0 +1,40 @@
(function () {
'use strict';
function javascriptLibraryHelperService($q, $http, umbRequestHelper) {
var existingLocales = [];
function getSupportedLocalesForMoment() {
var deferred = $q.defer();
if (existingLocales.length === 0) {
umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"backOfficeAssetsApiBaseUrl",
"GetSupportedMomentLocales")),
'Failed to get cultures').then(function(locales) {
existingLocales = locales;
deferred.resolve(existingLocales);
});
} else {
deferred.resolve(existingLocales);
}
return deferred.promise;
}
var service = {
getSupportedLocalesForMoment: getSupportedLocalesForMoment
};
return service;
}
angular.module('umbraco.services').factory('javascriptLibraryHelperService', javascriptLibraryHelperService);
})();

View File

@@ -1,285 +1,324 @@
angular.module('umbraco.services')
.factory('userService', function ($rootScope, eventsService, $q, $location, $log, securityRetryQueue, authResource, dialogService, $timeout, angularHelper, $http) {
.factory('userService', function ($rootScope, eventsService, $q, $location, $log, securityRetryQueue, authResource, assetsService, dialogService, $timeout, angularHelper, $http, javascriptLibraryHelperService) {
var currentUser = null;
var lastUserId = null;
var loginDialog = null;
var currentUser = null;
var lastUserId = null;
var loginDialog = null;
//this tracks the last date/time that the user's remainingAuthSeconds was updated from the server
// this is used so that we know when to go and get the user's remaining seconds directly.
var lastServerTimeoutSet = null;
//this tracks the last date/time that the user's remainingAuthSeconds was updated from the server
// this is used so that we know when to go and get the user's remaining seconds directly.
var lastServerTimeoutSet = null;
function openLoginDialog(isTimedOut) {
if (!loginDialog) {
loginDialog = dialogService.open({
function openLoginDialog(isTimedOut) {
if (!loginDialog) {
loginDialog = dialogService.open({
//very special flag which means that global events cannot close this dialog
manualClose: true,
//very special flag which means that global events cannot close this dialog
manualClose: true,
template: 'views/common/dialogs/login.html',
modalClass: "login-overlay",
animation: "slide",
show: true,
callback: onLoginDialogClose,
dialogData: {
isTimedOut: isTimedOut
}
});
}
}
function onLoginDialogClose(success) {
loginDialog = null;
if (success) {
securityRetryQueue.retryAll(currentUser.name);
}
else {
securityRetryQueue.cancelAll();
$location.path('/');
}
}
/**
This methods will set the current user when it is resolved and
will then start the counter to count in-memory how many seconds they have
remaining on the auth session
*/
function setCurrentUser(usr) {
if (!usr.remainingAuthSeconds) {
throw "The user object is invalid, the remainingAuthSeconds is required.";
}
currentUser = usr;
lastServerTimeoutSet = new Date();
//start the timer
countdownUserTimeout();
}
/**
Method to count down the current user's timeout seconds,
this will continually count down their current remaining seconds every 5 seconds until
there are no more seconds remaining.
*/
function countdownUserTimeout() {
$timeout(function () {
if (currentUser) {
//countdown by 5 seconds since that is how long our timer is for.
currentUser.remainingAuthSeconds -= 5;
//if there are more than 30 remaining seconds, recurse!
if (currentUser.remainingAuthSeconds > 30) {
//we need to check when the last time the timeout was set from the server, if
// it has been more than 30 seconds then we'll manually go and retrieve it from the
// server - this helps to keep our local countdown in check with the true timeout.
if (lastServerTimeoutSet != null) {
var now = new Date();
var seconds = (now.getTime() - lastServerTimeoutSet.getTime()) / 1000;
if (seconds > 30) {
//first we'll set the lastServerTimeoutSet to null - this is so we don't get back in to this loop while we
// wait for a response from the server otherwise we'll be making double/triple/etc... calls while we wait.
lastServerTimeoutSet = null;
//now go get it from the server
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
angularHelper.safeApply($rootScope, function () {
authResource.getRemainingTimeoutSeconds().then(function (result) {
setUserTimeoutInternal(result);
});
template: 'views/common/dialogs/login.html',
modalClass: "login-overlay",
animation: "slide",
show: true,
callback: onLoginDialogClose,
dialogData: {
isTimedOut: isTimedOut
}
});
}
}
}
//recurse the countdown!
countdownUserTimeout();
}
else {
function onLoginDialogClose(success) {
loginDialog = null;
//we are either timed out or very close to timing out so we need to show the login dialog.
if (Umbraco.Sys.ServerVariables.umbracoSettings.keepUserLoggedIn !== true) {
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
angularHelper.safeApply($rootScope, function () {
try {
//NOTE: We are calling this again so that the server can create a log that the timeout has expired, we
// don't actually care about this result.
authResource.getRemainingTimeoutSeconds();
}
finally {
userAuthExpired();
}
});
if (success) {
securityRetryQueue.retryAll(currentUser.name);
}
else {
//we've got less than 30 seconds remaining so let's check the server
if (lastServerTimeoutSet != null) {
//first we'll set the lastServerTimeoutSet to null - this is so we don't get back in to this loop while we
// wait for a response from the server otherwise we'll be making double/triple/etc... calls while we wait.
lastServerTimeoutSet = null;
//now go get it from the server
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
angularHelper.safeApply($rootScope, function () {
authResource.getRemainingTimeoutSeconds().then(function (result) {
setUserTimeoutInternal(result);
});
});
}
//recurse the countdown!
countdownUserTimeout();
securityRetryQueue.cancelAll();
$location.path('/');
}
}
}
}, 5000, //every 5 seconds
false); //false = do NOT execute a digest for every iteration
}
/** Called to update the current user's timeout */
function setUserTimeoutInternal(newTimeout) {
var asNumber = parseFloat(newTimeout);
if (!isNaN(asNumber) && currentUser && angular.isNumber(asNumber)) {
currentUser.remainingAuthSeconds = newTimeout;
lastServerTimeoutSet = new Date();
}
}
/** resets all user data, broadcasts the notAuthenticated event and shows the login dialog */
function userAuthExpired(isLogout) {
//store the last user id and clear the user
if (currentUser && currentUser.id !== undefined) {
lastUserId = currentUser.id;
}
if (currentUser) {
currentUser.remainingAuthSeconds = 0;
}
lastServerTimeoutSet = null;
currentUser = null;
//broadcast a global event that the user is no longer logged in
eventsService.emit("app.notAuthenticated");
openLoginDialog(isLogout === undefined ? true : !isLogout);
}
// Register a handler for when an item is added to the retry queue
securityRetryQueue.onItemAddedCallbacks.push(function (retryItem) {
if (securityRetryQueue.hasMore()) {
userAuthExpired();
}
});
return {
/** Internal method to display the login dialog */
_showLoginDialog: function () {
openLoginDialog();
},
/** Returns a promise, sends a request to the server to check if the current cookie is authorized */
isAuthenticated: function () {
//if we've got a current user then just return true
if (currentUser) {
var deferred = $q.defer();
deferred.resolve(true);
return deferred.promise;
/**
This methods will set the current user when it is resolved and
will then start the counter to count in-memory how many seconds they have
remaining on the auth session
*/
function setCurrentUser(usr) {
if (!usr.remainingAuthSeconds) {
throw "The user object is invalid, the remainingAuthSeconds is required.";
}
currentUser = usr;
lastServerTimeoutSet = new Date();
//start the timer
countdownUserTimeout();
}
return authResource.isAuthenticated();
},
/** Returns a promise, sends a request to the server to validate the credentials */
authenticate: function (login, password) {
/**
Method to count down the current user's timeout seconds,
this will continually count down their current remaining seconds every 5 seconds until
there are no more seconds remaining.
*/
function countdownUserTimeout() {
return authResource.performLogin(login, password)
.then(this.setAuthenticationSuccessful);
},
setAuthenticationSuccessful: function (data) {
$timeout(function () {
//when it's successful, return the user data
setCurrentUser(data);
if (currentUser) {
//countdown by 5 seconds since that is how long our timer is for.
currentUser.remainingAuthSeconds -= 5;
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "credentials" };
//if there are more than 30 remaining seconds, recurse!
if (currentUser.remainingAuthSeconds > 30) {
//broadcast a global event
eventsService.emit("app.authenticated", result);
return result;
},
//we need to check when the last time the timeout was set from the server, if
// it has been more than 30 seconds then we'll manually go and retrieve it from the
// server - this helps to keep our local countdown in check with the true timeout.
if (lastServerTimeoutSet != null) {
var now = new Date();
var seconds = (now.getTime() - lastServerTimeoutSet.getTime()) / 1000;
/** Logs the user out
*/
logout: function () {
if (seconds > 30) {
return authResource.performLogout()
.then(function (data) {
userAuthExpired();
//done!
return null;
});
},
//first we'll set the lastServerTimeoutSet to null - this is so we don't get back in to this loop while we
// wait for a response from the server otherwise we'll be making double/triple/etc... calls while we wait.
lastServerTimeoutSet = null;
/** Refreshes the current user data with the data stored for the user on the server and returns it */
refreshCurrentUser: function() {
var deferred = $q.defer();
//now go get it from the server
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
angularHelper.safeApply($rootScope, function () {
authResource.getRemainingTimeoutSeconds().then(function (result) {
setUserTimeoutInternal(result);
});
});
}
}
authResource.getCurrentUser()
.then(function (data) {
//recurse the countdown!
countdownUserTimeout();
}
else {
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "implicit" };
setCurrentUser(data);
//we are either timed out or very close to timing out so we need to show the login dialog.
if (Umbraco.Sys.ServerVariables.umbracoSettings.keepUserLoggedIn !== true) {
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
angularHelper.safeApply($rootScope, function () {
try {
//NOTE: We are calling this again so that the server can create a log that the timeout has expired, we
// don't actually care about this result.
authResource.getRemainingTimeoutSeconds();
}
finally {
userAuthExpired();
}
});
}
else {
//we've got less than 30 seconds remaining so let's check the server
deferred.resolve(currentUser);
}, function () {
//it failed, so they are not logged in
deferred.reject();
});
if (lastServerTimeoutSet != null) {
//first we'll set the lastServerTimeoutSet to null - this is so we don't get back in to this loop while we
// wait for a response from the server otherwise we'll be making double/triple/etc... calls while we wait.
lastServerTimeoutSet = null;
return deferred.promise;
},
//now go get it from the server
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
angularHelper.safeApply($rootScope, function () {
authResource.getRemainingTimeoutSeconds().then(function (result) {
setUserTimeoutInternal(result);
});
});
}
/** Returns the current user object in a promise */
getCurrentUser: function (args) {
var deferred = $q.defer();
//recurse the countdown!
countdownUserTimeout();
if (!currentUser) {
authResource.getCurrentUser()
.then(function (data) {
}
}
}
}, 5000, //every 5 seconds
false); //false = do NOT execute a digest for every iteration
}
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "implicit" };
/** Called to update the current user's timeout */
function setUserTimeoutInternal(newTimeout) {
if (args && args.broadcastEvent) {
//broadcast a global event, will inform listening controllers to load in the user specific data
var asNumber = parseFloat(newTimeout);
if (!isNaN(asNumber) && currentUser && angular.isNumber(asNumber)) {
currentUser.remainingAuthSeconds = newTimeout;
lastServerTimeoutSet = new Date();
}
}
/** resets all user data, broadcasts the notAuthenticated event and shows the login dialog */
function userAuthExpired(isLogout) {
//store the last user id and clear the user
if (currentUser && currentUser.id !== undefined) {
lastUserId = currentUser.id;
}
if (currentUser) {
currentUser.remainingAuthSeconds = 0;
}
lastServerTimeoutSet = null;
currentUser = null;
//broadcast a global event that the user is no longer logged in
eventsService.emit("app.notAuthenticated");
openLoginDialog(isLogout === undefined ? true : !isLogout);
}
// Register a handler for when an item is added to the retry queue
securityRetryQueue.onItemAddedCallbacks.push(function (retryItem) {
if (securityRetryQueue.hasMore()) {
userAuthExpired();
}
});
return {
/** Internal method to display the login dialog */
_showLoginDialog: function () {
openLoginDialog();
},
/** Returns a promise, sends a request to the server to check if the current cookie is authorized */
isAuthenticated: function () {
//if we've got a current user then just return true
if (currentUser) {
var deferred = $q.defer();
deferred.resolve(true);
return deferred.promise;
}
return authResource.isAuthenticated();
},
/** Returns a promise, sends a request to the server to validate the credentials */
authenticate: function (login, password) {
return authResource.performLogin(login, password)
.then(this.setAuthenticationSuccessful);
},
setAuthenticationSuccessful: function (data) {
//when it's successful, return the user data
setCurrentUser(data);
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "credentials" };
//broadcast a global event
eventsService.emit("app.authenticated", result);
}
return result;
},
setCurrentUser(data);
/** Logs the user out
*/
logout: function () {
deferred.resolve(currentUser);
}, function () {
//it failed, so they are not logged in
deferred.reject();
});
return authResource.performLogout()
.then(function (data) {
userAuthExpired();
//done!
return null;
});
},
}
else {
deferred.resolve(currentUser);
}
/** Refreshes the current user data with the data stored for the user on the server and returns it */
refreshCurrentUser: function () {
var deferred = $q.defer();
return deferred.promise;
},
authResource.getCurrentUser()
.then(function (data) {
/** Called whenever a server request is made that contains a x-umb-user-seconds response header for which we can update the user's remaining timeout seconds */
setUserTimeout: function (newTimeout) {
setUserTimeoutInternal(newTimeout);
}
};
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "implicit" };
});
setCurrentUser(data);
deferred.resolve(currentUser);
}, function () {
//it failed, so they are not logged in
deferred.reject();
});
return deferred.promise;
},
/** Returns the current user object in a promise */
getCurrentUser: function (args) {
var deferred = $q.defer();
if (!currentUser) {
authResource.getCurrentUser()
.then(function (data) {
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "implicit" };
if (args && args.broadcastEvent) {
//broadcast a global event, will inform listening controllers to load in the user specific data
eventsService.emit("app.authenticated", result);
}
setCurrentUser(data);
deferred.resolve(currentUser);
}, function () {
//it failed, so they are not logged in
deferred.reject();
});
}
else {
deferred.resolve(currentUser);
}
return deferred.promise;
},
/** Loads the Moment.js Locale for the current user. */
loadMomentLocaleForCurrentUser: function () {
var deferred = $q.defer();
function loadLocales(currentUser, supportedLocales) {
var locale = currentUser.locale.toLowerCase();
if (locale !== 'en-us') {
var localeUrls = [];
if (supportedLocales.indexOf(locale + '.js') > -1) {
localeUrls.push('lib/moment/' + locale + '.js');
}
if (locale.indexOf('-') > -1) {
var majorLocale = locale.split('-')[0] + '.js';
if (supportedLocales.indexOf(majorLocale) > -1) {
localeUrls.push('lib/moment/' + majorLocale);
}
}
assetsService.load(localeUrls).then(function () {
deferred.resolve(localeUrls);
});
} else {
deferred.resolve(['']);
}
}
var promises = {
currentUser: this.getCurrentUser(),
supportedLocales: javascriptLibraryHelperService.getSupportedLocalesForMoment()
}
$q.all(promises).then((values) => {
loadLocales(values.currentUser, values.supportedLocales);
});
return deferred.promise;
},
/** Called whenever a server request is made that contains a x-umb-user-seconds response header for which we can update the user's remaining timeout seconds */
setUserTimeout: function (newTimeout) {
setUserTimeoutInternal(newTimeout);
}
};
});

View File

@@ -18,23 +18,26 @@ app.run(['userService', '$log', '$rootScope', '$location', 'queryStrings', 'navi
eventsService.on("app.authenticated", function(evt, data) {
assetsService._loadInitAssets().then(function() {
//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);
}
// Loads the user's locale settings for Moment.
userService.loadMomentLocaleForCurrentUser().then(function() {
//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(){
appReady(data);
});
}, function(){
appReady(data);
});
});
});

View File

@@ -157,26 +157,24 @@ angular.module("umbraco")
.controller('Umbraco.PropertyEditors.FileUploadController', fileUploadController)
.run(function(mediaHelper, umbRequestHelper, assetsService){
if (mediaHelper && mediaHelper.registerFileResolver) {
assetsService.load(["lib/moment/moment-with-locales.js"]).then(
function () {
//NOTE: The 'entity' can be either a normal media entity or an "entity" returned from the entityResource
// they contain different data structures so if we need to query against it we need to be aware of this.
mediaHelper.registerFileResolver("Umbraco.UploadField", function(property, entity, thumbnail){
if (thumbnail) {
if (mediaHelper.detectIfImageByExtension(property.value)) {
//get default big thumbnail from image processor
var thumbnailUrl = property.value + "?rnd=" + moment(entity.updateDate).format("YYYYMMDDHHmmss") + "&width=500&animationprocessmode=first";
return thumbnailUrl;
}
else {
return null;
}
}
else {
return property.value;
}
});
//NOTE: The 'entity' can be either a normal media entity or an "entity" returned from the entityResource
// they contain different data structures so if we need to query against it we need to be aware of this.
mediaHelper.registerFileResolver("Umbraco.UploadField", function(property, entity, thumbnail){
if (thumbnail) {
if (mediaHelper.detectIfImageByExtension(property.value)) {
//get default big thumbnail from image processor
var thumbnailUrl = property.value + "?rnd=" + moment(entity.updateDate).format("YYYYMMDDHHmmss") + "&width=500&animationprocessmode=first";
return thumbnailUrl;
}
else {
return null;
}
}
);
else {
return property.value;
}
});
}
});

View File

@@ -99,4 +99,4 @@ module.exports = function (config) {
'karma-phantomjs-launcher'
]
});
};
};