diff --git a/src/Umbraco.Core/Models/UserExtensions.cs b/src/Umbraco.Core/Models/UserExtensions.cs
index 4894be7b87..6636259c92 100644
--- a/src/Umbraco.Core/Models/UserExtensions.cs
+++ b/src/Umbraco.Core/Models/UserExtensions.cs
@@ -1,6 +1,9 @@
using System;
using System.Globalization;
using System.Linq;
+using System.Net;
+using Umbraco.Core.Cache;
+using Umbraco.Core.IO;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Services;
@@ -8,6 +11,69 @@ namespace Umbraco.Core.Models
{
public static class UserExtensions
{
+ ///
+ /// Tries to lookup the user's gravatar to see if the endpoint can be reached, if so it returns the valid URL
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// A list of 5 different sized avatar URLs
+ ///
+ internal static string[] GetCurrentUserAvatarUrls(this IUser user, IUserService userService, ICacheProvider staticCache)
+ {
+ if (user.Avatar.IsNullOrWhiteSpace())
+ {
+ var gravatarHash = user.Email.ToMd5();
+ var gravatarUrl = "https://www.gravatar.com/avatar/" + gravatarHash;
+
+ //try gravatar
+ var gravatarAccess = staticCache.GetCacheItem("UserAvatar" + user.Id, () =>
+ {
+ // Test if we can reach this URL, will fail when there's network or firewall errors
+ var request = (HttpWebRequest)WebRequest.Create(gravatarUrl);
+ // Require response within 10 seconds
+ request.Timeout = 10000;
+ try
+ {
+ using ((HttpWebResponse)request.GetResponse()) { }
+ }
+ catch (Exception)
+ {
+ // There was an HTTP or other error, return an null instead
+ return false;
+ }
+ return true;
+ });
+
+ if (gravatarAccess)
+ {
+ return new[]
+ {
+ gravatarUrl + "?s=30&d=mm",
+ gravatarUrl + "?s=60&d=mm",
+ gravatarUrl + "?s=90&d=mm",
+ gravatarUrl + "?s=150&d=mm",
+ gravatarUrl + "?s=300&d=mm"
+ };
+ }
+
+ return null;
+ }
+
+ //use the custom avatar
+ var avatarUrl = FileSystemProviderManager.Current.MediaFileSystem.GetUrl(user.Avatar);
+ return new[]
+ {
+ avatarUrl + "?width=30&height=30&mode=crop",
+ avatarUrl + "?width=60&height=60&mode=crop",
+ avatarUrl + "?width=90&height=90&mode=crop",
+ avatarUrl + "?width=150&height=150&mode=crop",
+ avatarUrl + "?width=300&height=300&mode=crop"
+ };
+
+ }
+
///
/// Returns the culture info associated with this user, based on the language they're assigned to in the back office
///
diff --git a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs
index 4f3fb1b6e7..296985f084 100644
--- a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs
@@ -269,7 +269,8 @@ namespace Umbraco.Core.Persistence.Repositories
{"lastLoginDate", "LastLoginDate"},
{"failedLoginAttempts", "FailedPasswordAttempts"},
{"createDate", "CreateDate"},
- {"updateDate", "UpdateDate"}
+ {"updateDate", "UpdateDate"},
+ {"avatar", "Avatar"}
};
//create list of properties that have changed
diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/users.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/users.resource.js
index c70729246f..c08fbb0cdf 100644
--- a/src/Umbraco.Web.UI.Client/src/common/resources/users.resource.js
+++ b/src/Umbraco.Web.UI.Client/src/common/resources/users.resource.js
@@ -11,6 +11,17 @@
function usersResource($http, umbRequestHelper, $q, umbDataFormatter) {
+ function clearAvatar(userId) {
+
+ return umbRequestHelper.resourcePromise(
+ $http.post(
+ umbRequestHelper.getApiUrl(
+ "userApiBaseUrl",
+ "PostClearAvatar",
+ { id: userId })),
+ 'Failed to clear the user avatar ' + userId);
+ }
+
function disableUsers(userIds) {
if (!userIds) {
throw "userIds not specified";
@@ -188,7 +199,8 @@
createUser: createUser,
saveUser: saveUser,
getUserGroup: getUserGroup,
- getUserGroups: getUserGroups
+ getUserGroups: getUserGroups,
+ clearAvatar: clearAvatar
};
return resource;
diff --git a/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js b/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js
index 1504abf7c1..f17fefcac8 100644
--- a/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js
@@ -60,7 +60,7 @@ function MainController($scope, $rootScope, $location, $routeParams, $timeout, $
$scope.authenticated = data.authenticated;
$scope.user = data.user;
- updateChecker.check().then(function(update) {
+ updateChecker.check().then(function (update) {
if (update && update !== "null") {
if (update.type !== "None") {
var notification = {
@@ -87,7 +87,7 @@ function MainController($scope, $rootScope, $location, $routeParams, $timeout, $
}
//if this is a new login (i.e. the user entered credentials), then clear out local storage - could contain sensitive data
- if (data.loginType === "credentials") {
+ if (data.loginType === "credentials") {
localStorageService.clearAll();
}
@@ -96,30 +96,15 @@ function MainController($scope, $rootScope, $location, $routeParams, $timeout, $
tmhDynamicLocale.set($scope.user.locale);
}
- if ($scope.user.emailHash) {
+ if ($scope.user.avatars) {
- //let's attempt to load the avatar, it might not exist or we might not have
- // internet access, well get an empty string back
- $http.get(umbRequestHelper.getApiUrl("gravatarApiBaseUrl", "GetCurrentUserGravatarUrl"))
- .then(
- function successCallback(response) {
- // if we can't download the gravatar for some reason, an null gets returned, we cannot do anything
- if (response.data !== "null") {
- if ($scope.user && $scope.user.emailHash) {
- var avatarBaseUrl = "https://www.gravatar.com/avatar/";
- var hash = $scope.user.emailHash;
+ $scope.avatar = [];
+ if (angular.isArray($scope.user.avatars)) {
+ for (var i = 0; i < $scope.user.avatars.length; i++) {
+ $scope.avatar.push({ value: $scope.user.avatars[i] });
+ }
+ }
- $scope.avatar = [
- { value: avatarBaseUrl + hash + ".jpg?s=30&d=mm" },
- { value: avatarBaseUrl + hash + ".jpg?s=60&d=mm" },
- { value: avatarBaseUrl + hash + ".jpg?s=90&d=mm" }
- ];
- }
- }
-
- }, function errorCallback(response) {
- //cannot load it from the server so we cannot do anything
- });
}
}));
@@ -143,7 +128,7 @@ function MainController($scope, $rootScope, $location, $routeParams, $timeout, $
//register it
angular.module('umbraco').controller("Umbraco.MainController", MainController).
- config(function (tmhDynamicLocaleProvider) {
- //Set url for locale files
- tmhDynamicLocaleProvider.localeLocationPattern('lib/angular/1.1.5/i18n/angular-locale_{{locale}}.js');
- });
+ config(function (tmhDynamicLocaleProvider) {
+ //Set url for locale files
+ tmhDynamicLocaleProvider.localeLocationPattern('lib/angular/1.1.5/i18n/angular-locale_{{locale}}.js');
+ });
diff --git a/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.controller.js b/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.controller.js
index 9ce2506e76..b540df524c 100644
--- a/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.controller.js
@@ -96,7 +96,7 @@
}
} else if (evt.Message) {
- file.serverErrorMessage = evt.Message;
+ vm.zipFile.serverErrorMessage = evt.Message;
}
}
});
diff --git a/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js b/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js
index c69fe3ad6e..888b14b93f 100644
--- a/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js
@@ -1,7 +1,7 @@
(function () {
"use strict";
- function UserEditController($scope, $timeout, $location, $routeParams, usersResource, contentEditingHelper, localizationService, notificationsService) {
+ function UserEditController($scope, $timeout, $location, $routeParams, usersResource, contentEditingHelper, localizationService, notificationsService, mediaHelper, Upload, umbRequestHelper) {
var vm = this;
var localizeSaving = localizationService.localize("general_saving");
@@ -9,6 +9,7 @@
vm.page = {};
vm.user = {};
vm.breadcrumbs = [];
+ vm.avatarFile = {};
vm.goToPage = goToPage;
vm.openUserGroupPicker = openUserGroupPicker;
@@ -18,8 +19,10 @@
vm.disableUser = disableUser;
vm.resetPassword = resetPassword;
vm.getUserStateType = getUserStateType;
- vm.changeAvatar = changeAvatar;
+ vm.clearAvatar = clearAvatar;
vm.save = save;
+ vm.maxFileSize = Umbraco.Sys.ServerVariables.umbracoSettings.maxFileSize + "KB"
+ vm.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes);
function init() {
@@ -31,7 +34,7 @@
makeBreadcrumbs(vm.user);
vm.loading = false;
});
-
+
}
function save() {
@@ -47,7 +50,7 @@
// when server side validation fails - as opposed to content where we are capable of saving the content
// item if server side validation fails
redirectOnFailure: false,
- rebindCallback: function (orignal, saved) {}
+ rebindCallback: function (orignal, saved) { }
}).then(function (saved) {
vm.user = saved;
@@ -72,17 +75,17 @@
selection: vm.user.userGroups,
closeButtonLabel: "Cancel",
show: true,
- submit: function(model) {
+ submit: function (model) {
// apply changes
- if(model.selection) {
+ if (model.selection) {
vm.user.userGroups = model.selection;
}
vm.userGroupPicker.show = false;
vm.userGroupPicker = null;
},
- close: function(oldModel) {
+ close: function (oldModel) {
// rollback on close
- if(oldModel.selection) {
+ if (oldModel.selection) {
vm.user.userGroups = oldModel.selection;
}
vm.userGroupPicker.show = false;
@@ -97,14 +100,14 @@
view: "contentpicker",
multiPicker: true,
show: true,
- submit: function(model) {
- if(model.selection) {
+ submit: function (model) {
+ if (model.selection) {
vm.user.startNodesContent = model.selection;
}
vm.contentPicker.show = false;
vm.contentPicker = null;
},
- close: function(oldModel) {
+ close: function (oldModel) {
vm.contentPicker.show = false;
vm.contentPicker = null;
}
@@ -120,14 +123,14 @@
entityType: "media",
multiPicker: true,
show: true,
- submit: function(model) {
- if(model.selection) {
+ submit: function (model) {
+ if (model.selection) {
vm.user.startNodesMedia = model.selection;
}
vm.contentPicker.show = false;
vm.contentPicker = null;
},
- close: function(oldModel) {
+ close: function (oldModel) {
vm.contentPicker.show = false;
vm.contentPicker = null;
}
@@ -144,6 +147,13 @@
function resetPassword() {
alert("reset password");
+ }
+
+ function clearAvatar() {
+ // get user
+ usersResource.clearAvatar(vm.user.id).then(function (data) {
+ vm.user.avatars = data;
+ });
}
function getUserStateType(state) {
@@ -157,10 +167,63 @@
}
}
- function changeAvatar() {
- alert("change avatar");
+ $scope.changeAvatar = function (files, event) {
+ if (files && files.length > 0) {
+ upload(files[0]);
+ }
+ };
+
+ function upload(file) {
+
+ Upload.upload({
+ url: umbRequestHelper.getApiUrl("userApiBaseUrl", "PostSetAvatar", { id: vm.user.id }),
+ fields: {},
+ file: file
+ }).progress(function (evt) {
+
+ //TODO: Do progress, etc...
+ // set uploading status on file
+ vm.avatarFile.uploadStatus = "uploading";
+
+ }).success(function (data, status, headers, config) {
+
+ // set done status on file
+ vm.avatarFile.uploadStatus = "done";
+
+ vm.user.avatars = data;
+
+ }).error(function (evt, status, headers, config) {
+
+ // set status done
+ vm.avatarFile.uploadStatus = "error";
+
+ // If file not found, server will return a 404 and display this message
+ if (status === 404) {
+ vm.avatarFile.serverErrorMessage = "File not found";
+ }
+ else if (status == 400) {
+ //it's a validation error
+ vm.avatarFile.serverErrorMessage = evt.message;
+ }
+ else {
+ //it's an unhandled error
+ //if the service returns a detailed error
+ if (evt.InnerException) {
+ vm.avatarFile.serverErrorMessage = evt.InnerException.ExceptionMessage;
+
+ //Check if its the common "too large file" exception
+ if (evt.InnerException.StackTrace && evt.InnerException.StackTrace.indexOf("ValidateRequestEntityLength") > 0) {
+ vm.avatarFile.serverErrorMessage = "File too large to upload";
+ }
+
+ } else if (evt.Message) {
+ vm.avatarFile.serverErrorMessage = evt.Message;
+ }
+ }
+ });
}
+
function makeBreadcrumbs() {
vm.breadcrumbs = [
{
@@ -173,7 +236,7 @@
}
];
}
-
+
init();
}
diff --git a/src/Umbraco.Web.UI.Client/src/views/users/user.html b/src/Umbraco.Web.UI.Client/src/views/users/user.html
index a703a45139..0c6ded037a 100644
--- a/src/Umbraco.Web.UI.Client/src/views/users/user.html
+++ b/src/Umbraco.Web.UI.Client/src/views/users/user.html
@@ -119,18 +119,30 @@