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 2b9e0c0fd5..91f00a36e3 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 @@ -295,6 +295,38 @@ "Failed to retrieve data for user " + userId); } + + /** + * @ngdoc method + * @name umbraco.resources.usersResource#getUsers + * @methodOf umbraco.resources.usersResource + * + * @description + * Gets users from ids + * + * ##usage + *
+          * usersResource.getUsers([1,2,3])
+          *    .then(function(data) {
+          *        alert("It's here");
+          *    });
+          * 
+ * + * @param {Array} userIds user ids. + * @returns {Promise} resourcePromise object containing the users array. + * + */ + function getUsers(userIds) { + + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "userApiBaseUrl", + "GetByIds", + { ids: userIds })), + "Failed to retrieve data for users " + userIds); + } + /** * @ngdoc method * @name umbraco.resources.usersResource#createUser @@ -481,6 +513,7 @@ setUserGroupsOnUsers: setUserGroupsOnUsers, getPagedResults: getPagedResults, getUser: getUser, + getUsers: getUsers, createUser: createUser, inviteUser: inviteUser, saveUser: saveUser, diff --git a/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-preview.less b/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-preview.less index f62f3afd37..d0dca54403 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-preview.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/users/umb-user-preview.less @@ -4,6 +4,11 @@ display: flex; box-sizing: border-box; border-bottom: 1px solid @gray-9; + flex-wrap: wrap; +} + +.umb-editor-wrapper .umb-user-preview { + .umb-property-editor--limit-width(); } .umb-user-preview:last-of-type { @@ -29,22 +34,26 @@ flex: 0 0 auto; display: flex; align-items: center; + margin-left: auto; } .umb-user-preview__action { + background: transparent; + padding: 0; + border: 0 none; margin-left: 5px; margin-right: 5px; font-size: 13px; font-weight: bold; - color: @gray-5; + color: @ui-action-type; } .umb-user-preview__action:hover { - color: @turquoise; + color: @ui-action-type-hover; text-decoration: none; opacity: 1; } .umb-user-preview__action--red:hover { color: @red; -} \ No newline at end of file +} diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/userpicker/userpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/userpicker/userpicker.controller.js index 2bd73a5558..a7021b2867 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/userpicker/userpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/userpicker/userpicker.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function UserPickerController($scope, usersResource, localizationService) { + function UserPickerController($scope, usersResource, localizationService, eventsService) { var vm = this; @@ -15,15 +15,18 @@ vm.submit = submit; vm.close = close; - ////////// + vm.multiPicker = $scope.model.multiPicker === false ? false : true; function onInit() { vm.loading = true; // set default title - if(!$scope.model.title) { - localizationService.localize("defaultdialogs_selectUsers").then(function(value){ + if (!$scope.model.title) { + + var labelKey = vm.multiPicker ? "defaultdialogs_selectUsers" : "defaultdialogs_selectUser"; + + localizationService.localize(labelKey).then(function(value){ $scope.model.title = value; }); } @@ -35,12 +38,11 @@ // get users getUsers(); - } function preSelect(selection, users) { - angular.forEach(selection, function(selected){ - angular.forEach(users, function(user){ + Utilities.forEach(selection, function(selected){ + Utilities.forEach(users, function(user){ if(selected.id === user.id) { user.selected = true; } @@ -50,22 +52,39 @@ function selectUser(user) { - if(!user.selected) { - + if (!user.selected) { user.selected = true; $scope.model.selection.push(user); - } else { - angular.forEach($scope.model.selection, function(selectedUser, index){ - if(selectedUser.id === user.id) { - user.selected = false; - $scope.model.selection.splice(index, 1); + if (user.selected) { + Utilities.forEach($scope.model.selection, function (selectedUser, index) { + if (selectedUser.id === user.id) { + user.selected = false; + $scope.model.selection.splice(index, 1); + } + }); + } else { + if (!vm.multiPicker) { + deselectAllUsers($scope.model.selection); } - }); - + eventsService.emit("dialogs.userPicker.select", user); + user.selected = true; + $scope.model.selection.push(user); + } } + if (!vm.multiPicker) { + submit($scope.model); + } + } + + function deselectAllUsers(users) { + for (var i = 0; i < users.length; i++) { + var user = users[i]; + user.selected = false; + } + users.length = 0; } var search = _.debounce(function () { @@ -95,7 +114,6 @@ preSelect($scope.model.selection, vm.users); vm.loading = false; - }); } @@ -105,14 +123,14 @@ } function submit(model) { - if($scope.model.submit) { + if ($scope.model.submit) { $scope.model.submit(model); } } function close() { - if($scope.model.close) { - $scope.model.close(); + if ($scope.model.close) { + $scope.model.close(); } } diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/userpicker/userpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/userpicker/userpicker.html index e39d693b47..5536ce38c2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/userpicker/userpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/userpicker/userpicker.html @@ -19,7 +19,7 @@
- -
+ +
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js index 95e595a97a..c3137360e2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js @@ -56,10 +56,9 @@ function memberPickerController($scope, entityResource, iconHelper, angularHelpe }; editorService.treePicker(memberPicker); - }; - $scope.remove =function(index){ + $scope.remove = function (index) { $scope.renderModel.splice(index, 1); }; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.html index d968f08d71..1db59f28e7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.html @@ -10,13 +10,11 @@ - + ng-click="openMemberPicker()"> Add - + diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/userpicker/userpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/userpicker/userpicker.controller.js new file mode 100644 index 0000000000..b7412970eb --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/userpicker/userpicker.controller.js @@ -0,0 +1,92 @@ +function userPickerController($scope, usersResource , iconHelper, editorService){ + + function trim(str, chr) { + var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^' + chr + '+|' + chr + '+$', 'g'); + return str.replace(rgxtrim, ''); + } + + $scope.renderModel = []; + $scope.allowRemove = true; + + var multiPicker = $scope.model.config.multiPicker && $scope.model.config.multiPicker !== '0' ? true : false; + + $scope.openUserPicker = function () { + + var currentSelection = []; + var userPicker = { + multiPicker: multiPicker, + selection: currentSelection, + submit: function (model) { + if (model.selection) { + _.each(model.selection, function (item, i) { + $scope.add(item); + }); + } + editorService.close(); + }, + close: function () { + editorService.close(); + } + }; + + editorService.userPicker(userPicker); + }; + + $scope.remove = function (index) { + $scope.renderModel.splice(index, 1); + }; + + $scope.add = function (item) { + var currIds = _.map($scope.renderModel, function (i) { + if ($scope.model.config.idType === "udi") { + return i.udi; + } + else { + return i.id; + } + }); + + var itemId = $scope.model.config.idType === "udi" ? item.udi : item.id; + + if (currIds.indexOf(itemId) < 0) { + item.icon = item.icon ? iconHelper.convertFromLegacyIcon(item.icon) : "icon-user"; + $scope.renderModel.push({ name: item.name, id: item.id, udi: item.udi, icon: item.icon, avatars: item.avatars }); + } + }; + + $scope.clear = function() { + $scope.renderModel = []; + }; + + var unsubscribe = $scope.$on("formSubmitting", function (ev, args) { + var currIds = _.map($scope.renderModel, function (i) { + if ($scope.model.config.idType === "udi") { + return i.udi; + } + else { + return i.id; + } + }); + $scope.model.value = trim(currIds.join(), ","); + }); + + //when the scope is destroyed we need to unsubscribe + $scope.$on('$destroy', function () { + unsubscribe(); + }); + + //load user data + var modelIds = $scope.model.value ? $scope.model.value.split(',') : []; + + // entityResource.getByIds doesn't support "User" and we would like to show avatars in umb-user-preview as well. + usersResource.getUsers(modelIds).then(function (data) { + _.each(data, function (item, i) { + // set default icon if it's missing + item.icon = item.icon ? iconHelper.convertFromLegacyIcon(item.icon) : "icon-user"; + $scope.renderModel.push({ name: item.name, id: item.id, udi: item.udi, icon: item.icon, avatars: item.avatars }); + }); + }); +} + + +angular.module('umbraco').controller("Umbraco.PropertyEditors.UserPickerController", userPickerController); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/userpicker/userpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/userpicker/userpicker.html new file mode 100644 index 0000000000..8345317eee --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/userpicker/userpicker.html @@ -0,0 +1,21 @@ +
+ +
+ + +
+ + + + + +
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index 59ce327d4d..85fcdee4fe 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -469,6 +469,7 @@ Vælg medlemstype Vælg node Vælg sektioner + Vælg bruger Vælg brugere Ingen ikoner blev fundet Der er ingen parametre for denne makro diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 287f079807..6bbb4b24c9 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -491,6 +491,7 @@ Select member type Select node Select sections + Select user Select users No icons were found There are no parameters for this macro diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index 7552b9eed9..dd088313de 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -496,6 +496,7 @@ Select member type Select node Select sections + Select user Select users No icons were found There are no parameters for this macro diff --git a/src/Umbraco.Web/Editors/UsersController.cs b/src/Umbraco.Web/Editors/UsersController.cs index c27d7c9f48..76c4228a01 100644 --- a/src/Umbraco.Web/Editors/UsersController.cs +++ b/src/Umbraco.Web/Editors/UsersController.cs @@ -178,6 +178,33 @@ namespace Umbraco.Web.Editors return result; } + /// + /// Get users by integer ids + /// + /// + /// + [OutgoingEditorModelEvent] + [AdminUsersAuthorize] + public IEnumerable GetByIds([FromJsonPath]int[] ids) + { + if (ids == null) + { + throw new HttpResponseException(HttpStatusCode.NotFound); + } + + if (ids.Length == 0) + return Enumerable.Empty(); + + var users = Services.UserService.GetUsersById(ids); + if (users == null) + { + throw new HttpResponseException(HttpStatusCode.NotFound); + } + + var result = Mapper.MapEnumerable(users); + return result; + } + /// /// Returns a paged users collection /// diff --git a/src/Umbraco.Web/PropertyEditors/UserPickerConfiguration.cs b/src/Umbraco.Web/PropertyEditors/UserPickerConfiguration.cs index f7bd0ceb7d..bf3cd197a3 100644 --- a/src/Umbraco.Web/PropertyEditors/UserPickerConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/UserPickerConfiguration.cs @@ -7,7 +7,8 @@ namespace Umbraco.Web.PropertyEditors { public override IDictionary DefaultConfiguration => new Dictionary { - {"entityType", "User"} + { "entityType", "User" }, + { "multiPicker", "0" } }; } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/PropertyEditors/UserPickerPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/UserPickerPropertyEditor.cs index daf574719a..32683dbbd0 100644 --- a/src/Umbraco.Web/PropertyEditors/UserPickerPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/UserPickerPropertyEditor.cs @@ -8,8 +8,8 @@ namespace Umbraco.Web.PropertyEditors { [DataEditor( Constants.PropertyEditors.Aliases.UserPicker, - "User picker", - "entitypicker", + "User Picker", + "userpicker", ValueType = ValueTypes.Integer, Group = Constants.PropertyEditors.Groups.People, Icon = Constants.Icons.User)]