From 4b68d1950c5a8fca15fcbbdbb9f8ceb3999bffd7 Mon Sep 17 00:00:00 2001
From: Per Ploug
askjdkasj lasjd
" }, - { alias: "textarea", label: "textarea", view: "umbraco.textarea", value: "ajsdka sdjkds", config: { rows: 4 } }, - { alias: "map", label: "Map", view: "umbraco.googlemaps", value: "37.4419,-122.1419", config: { mapType: "ROADMAP", zoom: 4 } }, - { alias: "media", label: "Media picker", view: "umbraco.mediapicker", value: "" }, - { alias: "content", label: "Content picker", view: "umbraco.contentpicker", value: "" } - ] - }, - { - label: "Sample Editor", - alias: "tab02", - id: 2, - properties: [ - { alias: "datepicker", label: "Datepicker", view: "umbraco.datepicker", config: { rows: 7 } }, - { alias: "tags", label: "Tags", view: "umbraco.tags", value: ""} - ] - }, - { - label: "Grid", - alias: "tab03", - id: 3, - properties: [ - { alias: "grid", label: "Grid", view: "umbraco.grid", controller: "umbraco.grid", value: "test", hideLabel: true } - ] - },{ - label: "WIP", - alias: "tab04", - id: 4, - properties: [ - { alias: "tes", label: "Stuff", view: "umbraco.test", controller: "umbraco.embeddedcontent", value: "", - - config: { - fields: [ - { alias: "embedded", label: "Embbeded", view: "umbraco.textstring", value: ""}, - { alias: "embedded2", label: "Embbeded 2", view: "umbraco.contentpicker", value: ""}, - { alias: "embedded3", label: "Embbeded 3", view: "umbraco.textarea", value: ""}, - { alias: "embedded4", label: "Embbeded 4", view: "umbraco.datepicker", value: ""} - ] - } - } - ] - } - - - ] - }; - - // return undefined; - - return content; + return deferred.promise; }, //returns an empty content object which can be persistent on the content service @@ -131,7 +131,7 @@ angular.module('umbraco.mocks.resources') var _id = 0; for (var i = 0; i < collection.take; i++) { _id = (parentId + i) * options.offset; - var cnt = this.getContent(_id); + var cnt = this.getById(_id); //here we fake filtering if(options.filter !== ''){ diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/media.resource.js b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/media.resource.js index 9eddb304d8..7fe28ecab1 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/media.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/media.resource.js @@ -1,13 +1,19 @@ angular.module('umbraco.mocks.resources') -.factory('mediaResource', function () { +.factory('mediaResource', function ($q) { var mediaArray = []; return { rootMedia: function(){ - return [ - {id: 1234, src: "/Media/boston.jpg", thumbnail: "/Media/boston.jpg" }, - {src: "/Media/bird.jpg", thumbnail: "/Media/bird.jpg" }, - {src: "/Media/frog.jpg", thumbnail: "/Media/frog.jpg" } - ]; + + var deferred = $q.defer(); + + deferred.resolve( + [ + {id: 1234, src: "/Media/boston.jpg", thumbnail: "/Media/boston.jpg" }, + {src: "/Media/bird.jpg", thumbnail: "/Media/bird.jpg" }, + {src: "/Media/frog.jpg", thumbnail: "/Media/frog.jpg" } + ]); + + return deferred.promise; } }; }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/tree.resource.js b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/tree.resource.js index 0158f0a8ad..2489631ea2 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/tree.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/tree.resource.js @@ -21,10 +21,10 @@ function treeResource($q) { } return [ - { name: "child-of-" + treeItem.name, id: iLevel + "" + 1234, icon: "icon-file-alt", view: section + "/edit/" + iLevel + "" + 1234, children: [], expanded: false, level: iLevel, defaultAction: action }, - { name: "random-name-" + section, id: iLevel + "" + 1235, icon: "icon-file-alt", view: section + "/edit/" + iLevel + "" + 1235, children: [], expanded: false, level: iLevel, defaultAction: action }, - { name: "random-name-" + section, id: iLevel + "" + 1236, icon: "icon-file-alt", view: section + "/edit/" + iLevel + "" + 1236, children: [], expanded: false, level: iLevel, defaultAction: action }, - { name: "random-name-" + section, id: iLevel + "" + 1237, icon: "icon-file-alt", view: "common/legacy/1237?p=" + encodeURI("developer/contentType.aspx?idequal1234"), children: [], expanded: false, level: iLevel, defaultAction: action } + { name: "child-of-" + treeItem.name, id: iLevel + "" + 1234, icon: "icon-file-alt", view: section + "/edit/" + iLevel + "" + 1234, children: [], expanded: false, hasChildren: true, level: iLevel, defaultAction: action }, + { name: "random-name-" + section, id: iLevel + "" + 1235, icon: "icon-file-alt", view: section + "/edit/" + iLevel + "" + 1235, children: [], expanded: false, hasChildren: true, level: iLevel, defaultAction: action }, + { name: "random-name-" + section, id: iLevel + "" + 1236, icon: "icon-file-alt", view: section + "/edit/" + iLevel + "" + 1236, children: [], expanded: false, hasChildren: true, level: iLevel, defaultAction: action }, + { name: "random-name-" + section, id: iLevel + "" + 1237, icon: "icon-file-alt", view: "common/legacy/1237?p=" + encodeURI("developer/contentType.aspx?idequal1234"), children: [], expanded: false, hasChildren: true, level: iLevel, defaultAction: action } ]; } @@ -36,7 +36,7 @@ function treeResource($q) { var section = options.section || 'content'; var cacheKey = options.cachekey || ''; - cacheKey += "_" + section; + cacheKey += "_" + section; if (treeArray[cacheKey] !== undefined){ return treeArray[cacheKey]; @@ -46,59 +46,39 @@ function treeResource($q) { switch(section){ case "content": - t = { - name: section, - alias: section, - - children: [ - { name: "My website", id: 1234, icon: "icon-home", view: section + "/edit/" + 1234, children: [], expanded: false, level: 1, defaultAction: "create" }, - { name: "Components", id: 1235, icon: "icon-cogs", view: section + "/edit/" + 1235, children: [], expanded: false, level: 1, defaultAction: "create" }, - { name: "Archieve", id: 1236, icon: "icon-folder-close", view: section + "/edit/" + 1236, children: [], expanded: false, level: 1, defaultAction: "create" }, - { name: "Recycle Bin", id: 1237, icon: "icon-trash", view: section + "/trash/view/", children: [], expanded: false, level: 1, defaultAction: "create" } - ] - }; + t = [ + { name: "My website", id: 1234, icon: "icon-home", view: section + "/edit/" + 1234, children: [], expanded: false, hasChildren: true, level: 1, defaultAction: "create" }, + { name: "Components", id: 1235, icon: "icon-cogs", view: section + "/edit/" + 1235, children: [], expanded: false, hasChildren: true, level: 1, defaultAction: "create" }, + { name: "Archieve", id: 1236, icon: "icon-folder-close", view: section + "/edit/" + 1236, children: [], expanded: false, hasChildren: true, level: 1, defaultAction: "create" }, + { name: "Recycle Bin", id: 1237, icon: "icon-trash", view: section + "/trash/view/", children: [], expanded: false, hasChildren: true, level: 1, defaultAction: "create" } + ]; break; case "developer": - t = { - name: section, - alias: section, - - children: [ - { name: "Data types", id: 1234, icon: "icon-folder-close", view: section + "/edit/" + 1234, children: [], expanded: false, level: 1 }, - { name: "Macros", id: 1235, icon: "icon-folder-close", view: section + "/edit/" + 1235, children: [], expanded: false, level: 1 }, - { name: "Pacakges", id: 1236, icon: "icon-folder-close", view: section + "/edit/" + 1236, children: [], expanded: false, level: 1 }, - { name: "XSLT Files", id: 1237, icon: "icon-folder-close", view: section + "/edit/" + 1237, children: [], expanded: false, level: 1 }, - { name: "Razor Files", id: 1237, icon: "icon-folder-close", view: section + "/edit/" + 1237, children: [], expanded: false, level: 1 } - ] - }; + t = [ + { name: "Data types", id: 1234, icon: "icon-folder-close", view: section + "/edit/" + 1234, children: [], expanded: false, hasChildren: true, level: 1 }, + { name: "Macros", id: 1235, icon: "icon-folder-close", view: section + "/edit/" + 1235, children: [], expanded: false, hasChildren: true, level: 1 }, + { name: "Pacakges", id: 1236, icon: "icon-folder-close", view: section + "/edit/" + 1236, children: [], expanded: false, hasChildren: true, level: 1 }, + { name: "XSLT Files", id: 1237, icon: "icon-folder-close", view: section + "/edit/" + 1237, children: [], expanded: false, hasChildren: true, level: 1 }, + { name: "Razor Files", id: 1237, icon: "icon-folder-close", view: section + "/edit/" + 1237, children: [], expanded: false, hasChildren: true, level: 1 } + ]; break; case "settings": - t = { - name: section, - alias: section, - - children: [ - { name: "Stylesheets", id: 1234, icon: "icon-folder-close", view: section + "/edit/" + 1234, children: [], expanded: false, level: 1 }, - { name: "Templates", id: 1235, icon: "icon-folder-close", view: section + "/edit/" + 1235, children: [], expanded: false, level: 1 }, - { name: "Dictionary", id: 1236, icon: "icon-folder-close", view: section + "/edit/" + 1236, children: [], expanded: false, level: 1 }, - { name: "Media types", id: 1237, icon: "icon-folder-close", view: section + "/edit/" + 1237, children: [], expanded: false, level: 1 }, - { name: "Document types", id: 1237, icon: "icon-folder-close", view: section + "/edit/" + 1237, children: [], expanded: false, level: 1 } - ] - }; + t = [ + { name: "Stylesheets", id: 1234, icon: "icon-folder-close", view: section + "/edit/" + 1234, children: [], expanded: false, hasChildren: true, level: 1 }, + { name: "Templates", id: 1235, icon: "icon-folder-close", view: section + "/edit/" + 1235, children: [], expanded: false, hasChildren: true, level: 1 }, + { name: "Dictionary", id: 1236, icon: "icon-folder-close", view: section + "/edit/" + 1236, children: [], expanded: false, hasChildren: true, level: 1 }, + { name: "Media types", id: 1237, icon: "icon-folder-close", view: section + "/edit/" + 1237, children: [], expanded: false, hasChildren: true, level: 1 }, + { name: "Document types", id: 1237, icon: "icon-folder-close", view: section + "/edit/" + 1237, children: [], expanded: false, hasChildren: true, level: 1 } + ]; break; default: - t = { - name: section, - alias: section, - - children: [ - { name: "random-name-" + section, id: 1234, icon: "icon-home", defaultAction: "create", view: section + "/edit/" + 1234, children: [], expanded: false, level: 1 }, - { name: "random-name-" + section, id: 1235, icon: "icon-folder-close", defaultAction: "create", view: section + "/edit/" + 1235, children: [], expanded: false, level: 1 }, - { name: "random-name-" + section, id: 1236, icon: "icon-folder-close", defaultAction: "create", view: section + "/edit/" + 1236, children: [], expanded: false, level: 1 }, - { name: "random-name-" + section, id: 1237, icon: "icon-folder-close", defaultAction: "create", view: section + "/edit/" + 1237, children: [], expanded: false, level: 1 } - ] - }; + t = [ + { name: "random-name-" + section, id: 1234, icon: "icon-home", defaultAction: "create", view: section + "/edit/" + 1234, children: [], expanded: false, hasChildren: true, level: 1 }, + { name: "random-name-" + section, id: 1235, icon: "icon-folder-close", defaultAction: "create", view: section + "/edit/" + 1235, children: [], expanded: false, hasChildren: true, level: 1 }, + { name: "random-name-" + section, id: 1236, icon: "icon-folder-close", defaultAction: "create", view: section + "/edit/" + 1236, children: [], expanded: false, hasChildren: true, level: 1 }, + { name: "random-name-" + section, id: 1237, icon: "icon-folder-close", defaultAction: "create", view: section + "/edit/" + 1237, children: [], expanded: false, hasChildren: true, level: 1 } + ]; break; } @@ -121,7 +101,6 @@ function treeResource($q) { var deferred = $q.defer(); var data = _getChildren(options); - deferred.resolve(data); return deferred.promise; } diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js index 24cdc63116..c33e6aacf8 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js @@ -52,7 +52,7 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { deferred.reject('Failed to retreive data for content id ' + id); }); - return deferred.promise; + return deferred.promise; }, getByIds: function (ids) { diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/media.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/media.resource.js index 51373415a8..2673bbd343 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/media.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/media.resource.js @@ -121,7 +121,7 @@ function mediaResource($q, $http, umbDataFormatter, umbRequestHelper) { /** saves or updates a media object */ saveMedia: function (media, isNew, files) { return saveMediaItem(media, "save" + (isNew ? "New" : ""), files); - }, + } }; } diff --git a/src/Umbraco.Web.UI.Client/src/common/security/_module.js b/src/Umbraco.Web.UI.Client/src/common/security/_module.js new file mode 100644 index 0000000000..93b0f58c1d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/security/_module.js @@ -0,0 +1,4 @@ +// Based loosely around work by Witold Szczerba - https://github.com/witoldsz/angular-http-auth +angular.module('umbraco.security', [ + 'umbraco.security.service', + 'umbraco.security.interceptor']); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/security/interceptor.js b/src/Umbraco.Web.UI.Client/src/common/security/interceptor.js new file mode 100644 index 0000000000..892b46fe24 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/security/interceptor.js @@ -0,0 +1,23 @@ +angular.module('umbraco.security.interceptor', ['umbraco.security.retryQueue']) + +// This http interceptor listens for authentication failures +.factory('securityInterceptor', ['$injector', 'securityRetryQueue', function($injector, queue) { + return function(promise) { + // Intercept failed requests + return promise.then(null, function(originalResponse) { + 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 + return $injector.get('$http')(originalResponse.config); + }); + } + return promise; + }); + }; +}]) + +// We have to add the interceptor to the queue as a string because the interceptor depends upon service instances that are not available in the config block. +.config(['$httpProvider', function($httpProvider) { + $httpProvider.responseInterceptors.push('securityInterceptor'); +}]); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/security/retryqueue.js b/src/Umbraco.Web.UI.Client/src/common/security/retryqueue.js new file mode 100644 index 0000000000..f734eecb87 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/security/retryqueue.js @@ -0,0 +1,68 @@ +angular.module('umbraco.security.retryQueue', []) + +// This is a generic retry queue for security failures. Each item is expected to expose two functions: retry and cancel. +.factory('securityRetryQueue', ['$q', '$log', function($q, $log) { + var retryQueue = []; + var service = { + // The security service puts its own handler in here! + onItemAddedCallbacks: [], + + hasMore: function() { + return retryQueue.length > 0; + }, + push: function(retryItem) { + retryQueue.push(retryItem); + // Call all the onItemAdded callbacks + angular.forEach(service.onItemAddedCallbacks, function(cb) { + try { + cb(retryItem); + } catch(e) { + $log.error('securityRetryQueue.push(retryItem): callback threw an error' + e); + } + }); + }, + pushRetryFn: function(reason, retryFn) { + // The reason parameter is optional + if ( arguments.length === 1) { + retryFn = reason; + reason = undefined; + } + + // The deferred object that will be resolved or rejected by calling retry or cancel + var deferred = $q.defer(); + var retryItem = { + reason: reason, + retry: function() { + // Wrap the result of the retryFn into a promise if it is not already + $q.when(retryFn()).then(function(value) { + // If it was successful then resolve our deferred + deferred.resolve(value); + }, function(value) { + // Othewise reject it + deferred.reject(value); + }); + }, + cancel: function() { + // Give up on retrying and reject our deferred + deferred.reject(); + } + }; + service.push(retryItem); + return deferred.promise; + }, + retryReason: function() { + return service.hasMore() && retryQueue[0].reason; + }, + cancelAll: function() { + while(service.hasMore()) { + retryQueue.shift().cancel(); + } + }, + retryAll: function() { + while(service.hasMore()) { + retryQueue.shift().retry(); + } + } + }; + return service; +}]); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/security/security.service.js b/src/Umbraco.Web.UI.Client/src/common/security/security.service.js new file mode 100644 index 0000000000..bc6406d744 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/security/security.service.js @@ -0,0 +1,114 @@ +// Based loosely around work by Witold Szczerba - https://github.com/witoldsz/angular-http-auth +angular.module('umbraco.security.service', [ + 'umbraco.security.retryQueue', // Keeps track of failed requests that need to be retried once the user logs in + 'umbraco.services' +]) + +.factory('security', ['$http', '$q', '$location', 'securityRetryQueue', 'dialogService', function($http, $q, $location, queue, $dialog) { + + // Redirect to the given url (defaults to '/') + function redirect(url) { + url = url || '/'; + $location.path(url); + } + + // Login form dialog stuff + var loginDialog = null; + + function openLoginDialog() { + if ( loginDialog ) { + throw new Error('Trying to open a dialog that is already open!'); + } + + //loginDialog = $dialog.dialog(); + //loginDialog.open('security/login/form.tpl.html', 'LoginFormController').then(onLoginDialogClose); + } + function closeLoginDialog(success) { + if (loginDialog) { + loginDialog.close(success); + } + } + + function onLoginDialogClose(success) { + loginDialog = null; + if ( success ) { + queue.retryAll(); + } else { + queue.cancelAll(); + redirect(); + } + } + + // Register a handler for when an item is added to the retry queue + queue.onItemAddedCallbacks.push(function(retryItem) { + if ( queue.hasMore() ) { + service.showLogin(); + } + }); + + // The public API of the service + var service = { + + // Get the first reason for needing a login + getLoginReason: function() { + return queue.retryReason(); + }, + + // Show the modal login dialog + showLogin: function() { + openLoginDialog(); + }, + + // Attempt to authenticate a user by the given email and password + login: function(email, password) { + var request = $http.post('/login', {email: email, password: password}); + return request.then(function(response) { + service.currentUser = response.data.user; + if ( service.isAuthenticated() ) { + closeLoginDialog(true); + } + }); + }, + + // Give up trying to login and clear the retry queue + cancelLogin: function() { + closeLoginDialog(false); + redirect(); + }, + + // Logout the current user and redirect + logout: function(redirectTo) { + $http.post('/logout').then(function() { + service.currentUser = null; + redirect(redirectTo); + }); + }, + + // Ask the backend to see if a user is already authenticated - this may be from a previous session. + requestCurrentUser: function() { + if ( service.isAuthenticated() ) { + return $q.when(service.currentUser); + } else { + return $http.get('/current-user').then(function(response) { + service.currentUser = response.data.user; + return service.currentUser; + }); + } + }, + + // Information about the current user + currentUser: null, + + // Is the current user authenticated? + isAuthenticated: function(){ + return !!service.currentUser; + }, + + // Is the current user an adminstrator? + isAdmin: function() { + return !!(service.currentUser && service.currentUser.admin); + } + }; + + return service; +}]); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/services/scriptloader.service.js b/src/Umbraco.Web.UI.Client/src/common/services/scriptloader.service.js new file mode 100644 index 0000000000..6e4f04364b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/services/scriptloader.service.js @@ -0,0 +1,19 @@ +//script loader wrapping around 3rd party loader +angular.module('umbraco.services') +.factory('scriptLoader', function ($q) { + + return { + load: function (pathArray) { + var deferred = $q.defer(); + + yepnope({ + load: pathArray, + complete: function () { + deferred.resolve(true); + } + }); + + return deferred.promise; + } + }; +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/services/scripts.js b/src/Umbraco.Web.UI.Client/src/common/services/scripts.js deleted file mode 100644 index e4300f8a80..0000000000 --- a/src/Umbraco.Web.UI.Client/src/common/services/scripts.js +++ /dev/null @@ -1 +0,0 @@ -//script loader wrapping around 3rd party loader \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/services/utill.service.js b/src/Umbraco.Web.UI.Client/src/common/services/utill.service.js index d6391f0c9d..741d00d55f 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/utill.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/utill.service.js @@ -12,9 +12,9 @@ function umbImageHelper() { if (!options && !options.imageModel && !options.scope) { throw "The options objet does not contain the required parameters: imageModel, scope"; } - if (options.imageModel.contentTypeAlias.toLowerCase() == "image") { + if (options.imageModel.contentTypeAlias.toLowerCase() === "image") { var imageProp = _.find(options.imageModel.properties, function (item) { - return item.alias == 'umbracoFile'; + return item.alias === 'umbracoFile'; }); var imageVal; //Legacy images will be saved as a string, not an array so we will convert the legacy values @@ -40,7 +40,7 @@ function umbImageHelper() { } var imagePropVal = this.getImagePropertyVaue(options); - if (imagePropVal != "") { + if (imagePropVal !== "") { return this.getThumbnailFromPath(imagePropVal); } return ""; diff --git a/src/Umbraco.Web.UI.Client/src/index.html b/src/Umbraco.Web.UI.Client/src/index.html index d08d84172e..3d8f30376a 100644 --- a/src/Umbraco.Web.UI.Client/src/index.html +++ b/src/Umbraco.Web.UI.Client/src/index.html @@ -146,7 +146,12 @@ + + + +