Merge branch '7.0.0' of https://github.com/umbraco/Umbraco-CMS into 7.0.0

This commit is contained in:
perploug
2013-11-12 10:05:46 +01:00
48 changed files with 560 additions and 450 deletions

View File

@@ -23,7 +23,8 @@ Umbraco.Sys.ServerVariables = {
umbracoSettings: {
"umbracoPath": "/umbraco",
"appPluginsPath" : "/App_Plugins",
"imageFileTypes": "jpeg,jpg,gif,bmp,png,tiff,tif"
"imageFileTypes": "jpeg,jpg,gif,bmp,png,tiff,tif",
"keepUserLoggedIn": true
},
umbracoPlugins: {
trees: [

View File

@@ -0,0 +1,53 @@
/**
* @ngdoc service
* @name umbraco.resources.currentUserResource
* @description Used for read/updates for the currently logged in user
*
*
**/
function currentUserResource($q, $http, umbRequestHelper) {
//the factory object returned
return {
/**
* @ngdoc method
* @name umbraco.resources.currentUserResource#changePassword
* @methodOf umbraco.resources.currentUserResource
*
* @description
* Changes the current users password
*
* @returns {Promise} resourcePromise object containing the user array.
*
*/
changePassword: function (changePasswordArgs) {
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"currentUserApiBaseUrl",
"PostChangePassword"),
changePasswordArgs),
'Failed to change password');
},
/**
* @ngdoc method
* @name umbraco.resources.currentUserResource#getMembershipProviderConfig
* @methodOf umbraco.resources.currentUserResource
*
* @description
* Gets the configuration of the user membership provider which is used to configure the change password form
*/
getMembershipProviderConfig: function () {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"currentUserApiBaseUrl",
"GetMembershipProviderConfig")),
'Failed to retreive membership provider config');
},
};
}
angular.module('umbraco.resources').factory('currentUserResource', currentUserResource);

View File

@@ -1,113 +0,0 @@
/**
* @ngdoc service
* @name umbraco.resources.userResource
* @description Retrives user data from the server, cannot be used for authentication, for this, use the user.service
*
*
**/
function userResource($q, $http, umbRequestHelper) {
//the factory object returned
return {
/**
* @ngdoc method
* @name umbraco.resources.userResource#getById
* @methodOf umbraco.resources.userResource
*
* @description
* Gets a user with a given id
*
* ##usage
* <pre>
* userResource.getById(1234)
* .then(function(ent) {
* var myUser = ent;
* alert('im here!');
* });
* </pre>
*
* @param {Int} id id of user to return
* @returns {Promise} resourcePromise object containing the user.
*
*/
getById: function (id) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"userApiBaseUrl",
"GetById",
[{ id: id }])),
'Failed to retreive user data for id ' + id);
},
/**
* @ngdoc method
* @name umbraco.resources.userResource#getAll
* @methodOf umbraco.resources.userResource
*
* @description
* Gets all users available on the system
*
* ##usage
* <pre>
* contentResource.getAll()
* .then(function(userArray) {
* var myUsers = userArray;
* alert('they are here!');
* });
* </pre>
*
* @returns {Promise} resourcePromise object containing the user array.
*
*/
getAll: function () {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"userApiBaseUrl",
"GetAll")),
'Failed to retreive all users');
},
/**
* @ngdoc method
* @name umbraco.resources.userResource#changePassword
* @methodOf umbraco.resources.userResource
*
* @description
* Changes the current users password
*
* @returns {Promise} resourcePromise object containing the user array.
*
*/
changePassword: function (changePasswordArgs) {
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"userApiBaseUrl",
"PostChangePassword"),
changePasswordArgs),
'Failed to change password');
},
/**
* @ngdoc method
* @name umbraco.resources.userResource#getMembershipProviderConfig
* @methodOf umbraco.resources.userResource
*
* @description
* Gets the configuration of the user membership provider which is used to configure the change password form
*/
getMembershipProviderConfig: function () {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"userApiBaseUrl",
"GetMembershipProviderConfig")),
'Failed to retreive membership provider config');
},
};
}
angular.module('umbraco.resources').factory('userResource', userResource);

View File

@@ -5,7 +5,7 @@ angular.module('umbraco.services')
member.menuUrl = umbRequestHelper.getApiUrl("memberTreeBaseUrl", "GetMenu", [{ id: member.id }, { application: 'member' }]);
member.editorPath = "member/member/edit/" + (member.key ? member.key : member.id);
member.metaData = { treeAlias: "member" };
member.subTitle = member.additionalData.Email;
member.subTitle = member.metaData.Email;
}
function configureMediaResult(media)
@@ -19,7 +19,7 @@ angular.module('umbraco.services')
content.menuUrl = umbRequestHelper.getApiUrl("contentTreeBaseUrl", "GetMenu", [{ id: content.id }, { application: 'content' }]);
content.editorPath = "content/content/edit/" + content.id;
content.metaData = { treeAlias: "content" };
content.subTitle = content.additionalData.Url;
content.subTitle = content.metaData.Url;
}
return {

View File

@@ -9,7 +9,11 @@
*/
function treeService($q, treeResource, iconHelper, notificationsService, $rootScope) {
//TODO: implement this in local storage
//SD: Have looked at putting this in sessionStorage (not localStorage since that means you wouldn't be able to work
// in multiple tabs) - however our tree structure is cyclical, meaning a node has a reference to it's parent and it's children
// which you cannot serialize to sessionStorage. There's really no benefit of session storage except that you could refresh
// a tab and have the trees where they used to be - supposed that is kind of nice but would mean we'd have to store the parent
// as a nodeid reference instead of a variable with a getParent() method.
var treeCache = {};
var standardCssClass = 'icon umb-tree-icon sprTree';
@@ -36,13 +40,22 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc
_formatNodeDataForUseInUI: function (parentNode, treeNodes, section, level) {
//if no level is set, then we make it 1
var childLevel = (level ? level : 1);
//set the section if it's not already set
if (!parentNode.section) {
parentNode.section = section;
}
//create a method outside of the loop to return the parent - otherwise jshint blows up
var funcParent = function() {
return parentNode;
};
for (var i = 0; i < treeNodes.length; i++) {
treeNodes[i].level = childLevel;
treeNodes[i].parent = parentNode;
//create a function to get the parent node, we could assign the parent node but
// then we cannot serialize this entity because we have a cyclical reference.
// Instead we just make a function to return the parentNode.
treeNodes[i].parent = funcParent;
//set the section for each tree node - this allows us to reference this easily when accessing tree nodes
treeNodes[i].section = section;
@@ -108,7 +121,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc
return undefined;
},
/** clears the tree cache - with optional cacheKey and optional section */
/** clears the tree cache - with optional cacheKey, optional section or optional filter */
clearCache: function (args) {
//clear all if not specified
if (!args) {
@@ -122,6 +135,50 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc
treeCache = _.omit(treeCache, cacheKey);
}
}
else if (args.childrenOf) {
//if childrenOf is supplied a cacheKey must be supplied as well
if (!args.cacheKey) {
throw "args.cacheKey is required if args.childrenOf is supplied";
}
//this will clear out all children for the parentId passed in to this parameter, we'll
// do this by recursing and specifying a filter
var self = this;
this.clearCache({
cacheKey: args.cacheKey,
filter: function(cc) {
//get the new parent node from the tree cache
var parent = self.getDescendantNode(cc.root, args.childrenOf);
//clear it's children and set to not expanded
parent.children = null;
parent.expanded = false;
//return the cache to be saved
return cc;
}
});
}
else if (args.filter && angular.isFunction(args.filter)) {
//if a filter is supplied a cacheKey must be supplied as well
if (!args.cacheKey) {
throw "args.cacheKey is required if args.filter is supplied";
}
//if a filter is supplied the function needs to return the data to keep
var byKey = treeCache[args.cacheKey];
if (byKey) {
var result = args.filter(byKey);
if (result) {
//set the result to the filtered data
treeCache[args.cacheKey] = result;
}
else {
//remove the cache
treeCache = _.omit(treeCache, args.cacheKey);
}
}
}
else if (args.cacheKey) {
//if only the cache key is specified, then clear all cache starting with that key
var allKeys1 = _.keys(treeCache);
@@ -137,7 +194,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc
return k.endsWith("_" + args.section);
});
treeCache = _.omit(treeCache, toRemove2);
}
}
}
},
@@ -194,11 +251,11 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc
/** Removes a given tree node from the tree */
removeNode: function(treeNode) {
if (treeNode.parent == null) {
if (treeNode.parent() == null) {
throw "Cannot remove a node that doesn't have a parent";
}
//remove the current item from it's siblings
treeNode.parent.children.splice(treeNode.parent.children.indexOf(treeNode), 1);
treeNode.parent().children.splice(treeNode.parent().children.indexOf(treeNode), 1);
},
/** Removes all child nodes from a given tree node */
@@ -261,7 +318,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc
root = current;
}
else {
current = current.parent;
current = current.parent();
}
}
return root;
@@ -370,7 +427,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc
if (!node) {
throw "node cannot be null";
}
if (!node.parent) {
if (!node.parent()) {
throw "cannot reload a single node without a parent";
}
if (!node.section) {
@@ -382,7 +439,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc
//set the node to loading
node.loading = true;
this.getChildren({ node: node.parent, section: node.section }).then(function(data) {
this.getChildren({ node: node.parent(), section: node.section }).then(function(data) {
//ok, now that we have the children, find the node we're reloading
var found = _.find(data, function(item) {
@@ -390,14 +447,14 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc
});
if (found) {
//now we need to find the node in the parent.children collection to replace
var index = _.indexOf(node.parent.children, node);
var index = _.indexOf(node.parent().children, node);
//the trick here is to not actually replace the node - this would cause the delete animations
//to fire, instead we're just going to replace all the properties of this node.
_.extend(node.parent.children[index], found);
_.extend(node.parent().children[index], found);
//set the node to loading
node.parent.children[index].loading = false;
node.parent().children[index].loading = false;
//return
deferred.resolve(node.parent.children[index]);
deferred.resolve(node.parent().children[index]);
}
else {
deferred.reject();

View File

@@ -93,10 +93,28 @@ angular.module('umbraco.services')
//we are either timed out or very close to timing out so we need to show the login dialog.
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
angularHelper.safeApply($rootScope, function() {
userAuthExpired();
});
if (Umbraco.Sys.ServerVariables.umbracoSettings.keepUserLoggedIn !== true) {
angularHelper.safeApply($rootScope, function() {
userAuthExpired();
});
}
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
authResource.getRemainingTimeoutSeconds().then(function (result) {
setUserTimeoutInternal(result);
});
}
//recurse the countdown!
countdownUserTimeout();
}
}
}
}, 2000, //every 2 seconds

View File

@@ -1,5 +1,36 @@
/*Contains multiple services for various helper tasks */
/**
* @ngdoc function
* @name umbraco.services.umbSessionStorage
* @function
*
* @description
* Used to get/set things in browser sessionStorage but always prefixes keys with "umb_" and converts json vals so there is no overlap
* with any sessionStorage created by a developer.
*/
function umbSessionStorage($window) {
//gets the sessionStorage object if available, otherwise just uses a normal object
// - required for unit tests.
var storage = $window['sessionStorage'] ? $window['sessionStorage'] : {};
return {
get: function (key) {
console.log(storage);
console.log(storage["umb_" + key]);
return angular.fromJson(storage["umb_" + key]);
},
set : function(key, value) {
storage["umb_" + key] = angular.toJson(value);
}
};
}
angular.module('umbraco.services').factory('umbSessionStorage', umbSessionStorage);
/**
* @ngdoc function
* @name umbraco.services.legacyJsLoader

View File

@@ -4,7 +4,8 @@
.login-overlay {
width: 100%;
height: 100%;
background: @blackLight url(../img/application/logo.png) no-repeat 20px 30px fixed !important;
background: @blackLight url(../img/application/logo.png) no-repeat 25px 30px fixed !important;
background-size: 30px 30px !important;
color: @white;
position: absolute;
z-index: 2000;
@@ -12,6 +13,7 @@
left: 0px;
margin: 0 !Important;
padding: 0;
border-radius: 0
}
.login-overlay .umb-modalcolumn{

View File

@@ -298,7 +298,7 @@
.nav-pills .open .dropdown-toggle,
.nav > li.dropdown.open.active > a:hover,
.nav > li.dropdown.open.active > a:focus {
color: @white;
/*color: @white;*/
background-color: @grayLight;
border-color: @grayLight;
}

View File

@@ -42,7 +42,7 @@
position: relative;
}
.umb-panel-header .umb-headline {
.umb-panel-header .umb-headline, .umb-panel-header h1 {
font-size: 18px;
border: none;
background: none;
@@ -72,14 +72,7 @@
padding: 0px 0px 0px 20px;
}
.umb-panel-header h1 {
margin: 0;
font-size: 14px;
font-weight: 400;
color: @gray;
line-height: 1em;
width: 100%;
}
.umb-panel-header p {
margin:0px 20px;
}

View File

@@ -1,7 +1,7 @@
//used for the media picker dialog
angular.module("umbraco")
.controller("Umbraco.Dialogs.MediaPickerController",
function($scope, mediaResource, umbRequestHelper, entityResource, $log, imageHelper, eventsService) {
function($scope, mediaResource, umbRequestHelper, entityResource, $log, imageHelper, eventsService, treeService) {
var dialogOptions = $scope.$parent.dialogOptions;
$scope.options = {
@@ -21,6 +21,12 @@ angular.module("umbraco")
.addFolder($scope.newFolderName, $scope.options.formData.currentFolder)
.then(function(data) {
//we've added a new folder so lets clear the tree cache for that specific item
treeService.clearCache({
cacheKey: "__media", //this is the main media tree cache key
childrenOf: data.parentId //clear the children of the parent
});
$scope.gotoFolder(data.id);
});
}

View File

@@ -36,6 +36,7 @@ angular.module("umbraco")
$scope.user = user;
if ($scope.user) {
$scope.remainingAuthSeconds = $scope.user.remainingAuthSeconds;
$scope.canEditProfile = _.indexOf($scope.user.allowedSections, "users") > -1;
//set the timer
updateTimeout();
}

View File

@@ -19,8 +19,8 @@
<div class="umb-panel-body umb-scrollable">
<div class="tab-content umb-control-group">
<div class="umb-pane">
<div class="umb-pane" ng-if="canEditProfile">
<h5><localize key="user_yourProfile" /></h5>
<p>
<a

View File

@@ -39,7 +39,7 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.MoveController",
$scope.success = true;
//reloads the parent
navigationService.reloadNode(dialogOptions.currentNode.parent);
navigationService.reloadNode(dialogOptions.currentNode.parent());
//reloads the target
navigationService.syncTree({ tree: "content", path: path, forceReload: true });

View File

@@ -80,7 +80,7 @@ function MediaFolderBrowserDashboardController($rootScope, $scope, assetsService
angular.module("umbraco").controller("Umbraco.Dashboard.MediaFolderBrowserDashboardController", MediaFolderBrowserDashboardController);
function ChangePasswordDashboardController($scope, xmlhelper, $log, userResource, formHelper) {
function ChangePasswordDashboardController($scope, xmlhelper, $log, currentUserResource, formHelper) {
//create the initial model for change password property editor
$scope.changePasswordModel = {
@@ -91,7 +91,7 @@ function ChangePasswordDashboardController($scope, xmlhelper, $log, userResource
};
//go get the config for the membership provider and add it to the model
userResource.getMembershipProviderConfig().then(function(data) {
currentUserResource.getMembershipProviderConfig().then(function(data) {
$scope.changePasswordModel.config = data;
//ensure the hasPassword config option is set to true (the user of course has a password already assigned)
//this will ensure the oldPassword is shown so they can change it
@@ -105,7 +105,7 @@ function ChangePasswordDashboardController($scope, xmlhelper, $log, userResource
$scope.changePassword = function() {
if (formHelper.submitForm({ scope: $scope })) {
userResource.changePassword($scope.changePasswordModel.value).then(function(data) {
currentUserResource.changePassword($scope.changePasswordModel.value).then(function(data) {
//if the password has been reset, then update our model
if (data.value) {

View File

@@ -54,16 +54,6 @@ describe('tree service tests', function () {
describe('tree cache', function () {
//it('tree with section but no cache key does not cache', function () {
// treeService.getTree().then(function (data) {
// var cache = treeService._getTreeCache();
// expect(cache).toBeDefined();
// expect(cache["_content"]).toBeDefined();
// });
//});
it('does not cache with no args', function () {
var cache;
@@ -215,6 +205,91 @@ describe('tree service tests', function () {
cache = treeService._getTreeCache();
expect(_.keys(cache).length).toBe(0);
});
it('clears cache by key using a filter that returns null', function () {
var cache;
treeService.getTree({ section: "media", cacheKey: "_" }).then(function (d) {
treeService.getTree({ section: "content", cacheKey: "_" }).then(function (dd) {
cache = treeService._getTreeCache();
});
});
$rootScope.$digest();
$httpBackend.flush();
expect(_.keys(cache).length).toBe(2);
treeService.clearCache({
cacheKey: "__content",
filter: function(currentCache) {
return null;
}
});
cache = treeService._getTreeCache();
expect(_.keys(cache).length).toBe(1);
});
it('removes cache by key using a filter', function () {
var cache;
treeService.getTree({ section: "media", cacheKey: "_" }).then(function (d) {
treeService.getTree({ section: "content", cacheKey: "_" }).then(function (dd) {
cache = treeService._getTreeCache();
});
});
$rootScope.$digest();
$httpBackend.flush();
expect(_.keys(cache).length).toBe(2);
expect(cache.__content.root.children.length).toBe(4);
treeService.clearCache({
cacheKey: "__content",
filter: function (currentCache) {
var toRemove = treeService.getDescendantNode(currentCache.root, 1235);
toRemove.parent().children = _.without(toRemove.parent().children, toRemove);
return currentCache;
}
});
cache = treeService._getTreeCache();
expect(cache.__content.root.children.length).toBe(3);
});
it('removes cache children for a parent id specified', function () {
var cache;
treeService.getTree({ section: "content", cacheKey: "_" }).then(function (dd) {
treeService.loadNodeChildren({ node: dd.root.children[0] }).then(function () {
cache = treeService._getTreeCache();
});
});
$rootScope.$digest();
$httpBackend.flush();
expect(cache.__content.root.children.length).toBe(4);
expect(cache.__content.root.children[0].children.length).toBe(4);
treeService.clearCache({
cacheKey: "__content",
childrenOf: "1234"
});
cache = treeService._getTreeCache();
expect(cache.__content.root.children.length).toBe(4);
expect(cache.__content.root.children[0].children).toBeNull();
expect(cache.__content.root.children[0].expanded).toBe(false);
});
});