Add user picker property editor (#7613)

This commit is contained in:
Bjarne Fyrstenborg
2020-07-23 16:08:24 +02:00
committed by GitHub
parent 2ca7cc9ccc
commit 41d1e33fc4
15 changed files with 247 additions and 37 deletions
@@ -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
* <pre>
* usersResource.getUsers([1,2,3])
* .then(function(data) {
* alert("It's here");
* });
* </pre>
*
* @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,
@@ -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;
}
}
@@ -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();
}
}
@@ -19,7 +19,7 @@
<umb-box>
<umb-box-content>
<div class="form-search" style="margin-bottom: 15px;">
<i class="icon-search"></i>
<i class="icon-search" aria-hidden="true"></i>
<input type="text"
ng-model="vm.usersOptions.filter"
ng-change="vm.searchUsers()"
@@ -78,7 +78,9 @@
action="vm.close()">
</umb-button>
<umb-button
ng-if="vm.multiPicker"
type="button"
disabled="model.selection.length === 0"
button-style="success"
label-key="general_submit"
action="vm.submit(model)">
@@ -14,6 +14,13 @@
</div>
<div class="umb-user-preview__actions">
<button type="button" class="umb-user-preview__action umb-user-preview__action--red btn-link" title="Remove" ng-if="allowRemove" ng-click="onRemove()"><localize key="general_remove">Remove</localize></button>
</div>
<button type="button"
class="umb-user-preview__action umb-user-preview__action--red btn-link"
title="Remove {{name}}"
ng-if="allowRemove"
ng-click="onRemove()">
<localize key="general_remove">Remove</localize>
<span class="sr-only">{{name}}</span>
</button>
<div>
</div>
@@ -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);
};
@@ -10,13 +10,11 @@
</umb-node-preview>
</div>
<a ng-show="model.config.multiPicker === true || renderModel.length === 0"
<button type="button" ng-show="model.config.multiPicker === true || renderModel.length === 0"
class="umb-node-preview-add"
href="#"
ng-click="openMemberPicker()"
prevent-default>
ng-click="openMemberPicker()">
<localize key="general_add">Add</localize>
</a>
</button>
<input type="hidden" name="mandatory" ng-model="model.mandatory" ng-required="model.validation.mandatory && !renderModel.length" />
@@ -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);
@@ -0,0 +1,21 @@
<div ng-controller="Umbraco.PropertyEditors.UserPickerController" class="umb-property-editor umb-userpicker">
<div>
<umb-user-preview
ng-repeat="user in renderModel"
name="user.name"
avatars="user.avatars"
allow-remove="allowRemove"
on-remove="remove($index)">
</umb-user-preview>
</div>
<button type="button" ng-show="model.config.multiPicker === true || renderModel.length === 0"
class="umb-node-preview-add"
ng-click="openUserPicker()">
<localize key="general_add">Add</localize>
</button>
<input type="hidden" name="mandatory" ng-model="model.mandatory" ng-required="model.validation.mandatory && !renderModel.length" />
</div>
@@ -469,6 +469,7 @@
<key alias="selectMemberType">Vælg medlemstype</key>
<key alias="selectNode">Vælg node</key>
<key alias="selectSections">Vælg sektioner</key>
<key alias="selectUser">Vælg bruger</key>
<key alias="selectUsers">Vælg brugere</key>
<key alias="noIconsFound">Ingen ikoner blev fundet</key>
<key alias="noMacroParams">Der er ingen parametre for denne makro</key>
@@ -491,6 +491,7 @@
<key alias="selectMemberType">Select member type</key>
<key alias="selectNode">Select node</key>
<key alias="selectSections">Select sections</key>
<key alias="selectUser">Select user</key>
<key alias="selectUsers">Select users</key>
<key alias="noIconsFound">No icons were found</key>
<key alias="noMacroParams">There are no parameters for this macro</key>
@@ -496,6 +496,7 @@
<key alias="selectMemberType">Select member type</key>
<key alias="selectNode">Select node</key>
<key alias="selectSections">Select sections</key>
<key alias="selectUser">Select user</key>
<key alias="selectUsers">Select users</key>
<key alias="noIconsFound">No icons were found</key>
<key alias="noMacroParams">There are no parameters for this macro</key>
@@ -178,6 +178,33 @@ namespace Umbraco.Web.Editors
return result;
}
/// <summary>
/// Get users by integer ids
/// </summary>
/// <param name="ids"></param>
/// <returns></returns>
[OutgoingEditorModelEvent]
[AdminUsersAuthorize]
public IEnumerable<UserDisplay> GetByIds([FromJsonPath]int[] ids)
{
if (ids == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
if (ids.Length == 0)
return Enumerable.Empty<UserDisplay>();
var users = Services.UserService.GetUsersById(ids);
if (users == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
var result = Mapper.MapEnumerable<IUser, UserDisplay>(users);
return result;
}
/// <summary>
/// Returns a paged users collection
/// </summary>
@@ -7,7 +7,8 @@ namespace Umbraco.Web.PropertyEditors
{
public override IDictionary<string, object> DefaultConfiguration => new Dictionary<string, object>
{
{"entityType", "User"}
{ "entityType", "User" },
{ "multiPicker", "0" }
};
}
}
}
@@ -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)]