Skip to content

Commit 434d681

Browse files
Vincent Petryblizzz
authored andcommitted
Backport of Users page lazy multiselect group dropdowns #1128 to stable9
Users page lazy multiselect group dropdowns Instead of pre-rendering all multiselects with lots of group entries, the current groups are now displayed as simple labels. Behind the labels there is a pencil icon like for other fields. When clicking the pencil icon, the dropdown will be spawned and will open itself. Upon closing of the dropdown, the label comes back with the updated selection and the dropdown is destroyed. Extra non-available groups also in list Fix group sorting in user list group selection
1 parent d2d1508 commit 434d681

File tree

7 files changed

+143
-119
lines changed

7 files changed

+143
-119
lines changed

core/js/multiselect.js

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
'onuncheck':false,
3333
'minWidth': 'default;'
3434
};
35-
var slideDuration = 200;
35+
var slideDuration = 0;
3636
$(this).attr('data-msid', multiSelectId);
3737
$.extend(settings,options);
3838
$.each(this.children(),function(i,option) {
@@ -75,6 +75,26 @@
7575

7676
var self = this;
7777
self.menuDirection = 'down';
78+
79+
function closeDropDown() {
80+
if(!button.parent().data('preventHide')) {
81+
// How can I save the effect in a var?
82+
if(self.menuDirection === 'down') {
83+
button.parent().children('ul').slideUp(slideDuration,function() {
84+
button.parent().children('ul').remove();
85+
button.removeClass('active down');
86+
$(self).trigger($.Event('dropdownclosed', settings));
87+
});
88+
} else {
89+
button.parent().children('ul').fadeOut(slideDuration,function() {
90+
button.parent().children('ul').remove();
91+
button.removeClass('active up');
92+
$(self).trigger($.Event('dropdownclosed', settings));
93+
});
94+
}
95+
}
96+
}
97+
7898
button.click(function(event){
7999

80100
var button=$(this);
@@ -83,21 +103,20 @@
83103
button.parent().children('ul').slideUp(slideDuration,function() {
84104
button.parent().children('ul').remove();
85105
button.removeClass('active down');
106+
$(self).trigger($.Event('dropdownclosed', settings));
86107
});
87108
} else {
88109
button.parent().children('ul').fadeOut(slideDuration,function() {
89110
button.parent().children('ul').remove();
90111
button.removeClass('active up');
112+
$(self).trigger($.Event('dropdownclosed', settings));
91113
});
92114
}
93115
return;
94116
}
117+
// tell other lists to shut themselves
95118
var lists=$('ul.multiselectoptions');
96-
lists.slideUp(slideDuration,function(){
97-
lists.remove();
98-
$('div.multiselect').removeClass('active');
99-
button.addClass('active');
100-
});
119+
lists.trigger($.Event('shut'));
101120
button.addClass('active');
102121
event.stopPropagation();
103122
var options=$(this).parent().next().children();
@@ -309,29 +328,16 @@
309328
list.detach().insertBefore($(this));
310329
list.addClass('up');
311330
button.addClass('up');
312-
list.fadeIn();
331+
list.show();
313332
self.menuDirection = 'up';
314333
}
315334
list.click(function(event) {
316335
event.stopPropagation();
317336
});
337+
list.one('shut', closeDropDown);
318338
});
319-
$(window).click(function() {
320-
if(!button.parent().data('preventHide')) {
321-
// How can I save the effect in a var?
322-
if(self.menuDirection === 'down') {
323-
button.parent().children('ul').slideUp(slideDuration,function() {
324-
button.parent().children('ul').remove();
325-
button.removeClass('active down');
326-
});
327-
} else {
328-
button.parent().children('ul').fadeOut(slideDuration,function() {
329-
button.parent().children('ul').remove();
330-
button.removeClass('active up');
331-
});
332-
}
333-
}
334-
});
339+
340+
$(window).click(closeDropDown);
335341

336342
return span;
337343
};

settings/css/settings.css

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,15 @@ span.usersLastLoginTooltip { white-space: nowrap; }
194194
color: #000000;
195195
}
196196

197+
#newuser .groups {
198+
display: inline;
199+
}
200+
201+
#newuser .groupsListContainer.hidden,
202+
#userlist .groupsListContainer.hidden {
203+
display: none;
204+
}
205+
197206
tr:hover>td.password>span, tr:hover>td.displayName>span { margin:0; cursor:pointer; }
198207
tr:hover>td.remove>a, tr:hover>td.password>img,tr:hover>td.displayName>img, tr:hover>td.quota>img { visibility:visible; cursor:pointer; }
199208
td.remove {

settings/js/users/groups.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,10 +136,6 @@ GroupList = {
136136
var addedGroup = result.groupname;
137137
UserList.availableGroups = $.unique($.merge(UserList.availableGroups, [addedGroup]));
138138
GroupList.addGroup(result.groupname);
139-
140-
$('.groupsselect, .subadminsselect')
141-
.append($('<option>', { value: result.groupname })
142-
.text(result.groupname));
143139
}
144140
GroupList.toggleAddGroup();
145141
}).fail(function(result) {

settings/js/users/users.js

Lines changed: 96 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,6 @@ var UserList = {
5757
var $tr = $userListBody.find('tr:first-child').clone();
5858
// this removes just the `display:none` of the template row
5959
$tr.removeAttr('style');
60-
var subAdminsEl;
61-
var subAdminSelect;
62-
var groupsSelect;
6360

6461
/**
6562
* Avatar or placeholder
@@ -86,32 +83,17 @@ var UserList = {
8683
$tr.find('td.mailAddress > .action').tooltip({placement: 'top'});
8784
$tr.find('td.password > .action').tooltip({placement: 'top'});
8885

86+
8987
/**
9088
* groups and subadmins
9189
*/
92-
// make them look like the multiselect buttons
93-
// until they get time to really get initialized
94-
groupsSelect = $('<select multiple="multiple" class="groupsselect multiselect button" data-placehoder="Groups" title="' + t('settings', 'no group') + '"></select>')
95-
.data('username', user.name)
96-
.data('user-groups', user.groups);
97-
if ($tr.find('td.subadmins').length > 0) {
98-
subAdminSelect = $('<select multiple="multiple" class="subadminsselect multiselect button" data-placehoder="subadmins" title="' + t('settings', 'no group') + '">')
99-
.data('username', user.name)
100-
.data('user-groups', user.groups)
101-
.data('subadmin', user.subadmin);
102-
$tr.find('td.subadmins').empty();
103-
}
104-
$.each(this.availableGroups, function (i, group) {
105-
groupsSelect.append($('<option value="' + escapeHTML(group) + '">' + escapeHTML(group) + '</option>'));
106-
if (typeof subAdminSelect !== 'undefined' && group !== 'admin') {
107-
subAdminSelect.append($('<option value="' + escapeHTML(group) + '">' + escapeHTML(group) + '</option>'));
108-
}
109-
});
110-
$tr.find('td.groups').empty().append(groupsSelect);
111-
subAdminsEl = $tr.find('td.subadmins');
112-
if (subAdminsEl.length > 0) {
113-
subAdminsEl.append(subAdminSelect);
114-
}
90+
var $tdGroups = $tr.find('td.groups');
91+
this._updateGroupListLabel($tdGroups, user.groups);
92+
$tdGroups.find('.action').tooltip({placement: 'top'});
93+
94+
var $tdSubadmins = $tr.find('td.subadmins');
95+
this._updateGroupListLabel($tdSubadmins, user.subadmin);
96+
$tdSubadmins.find('.action').tooltip({placement: 'top'});
11597

11698
/**
11799
* remove action
@@ -195,10 +177,6 @@ var UserList = {
195177
// defer init so the user first sees the list appear more quickly
196178
window.setTimeout(function(){
197179
$quotaSelect.singleSelect();
198-
UserList.applyGroupSelect(groupsSelect);
199-
if (subAdminSelect) {
200-
UserList.applySubadminSelect(subAdminSelect);
201-
}
202180
}, 0);
203181
return $tr;
204182
},
@@ -319,7 +297,7 @@ var UserList = {
319297
},
320298
markRemove: function(uid) {
321299
var $tr = UserList.getRow(uid);
322-
var groups = $tr.find('.groups .groupsselect').val();
300+
var groups = $tr.find('.groups').data('groups');
323301
for(var i in groups) {
324302
var gid = groups[i];
325303
var $li = GroupList.getGroupLI(gid);
@@ -334,7 +312,7 @@ var UserList = {
334312
},
335313
undoRemove: function(uid) {
336314
var $tr = UserList.getRow(uid);
337-
var groups = $tr.find('.groups .groupsselect').val();
315+
var groups = $tr.find('.groups').data('groups');
338316
for(var i in groups) {
339317
var gid = groups[i];
340318
var $li = GroupList.getGroupLI(gid);
@@ -435,19 +413,9 @@ var UserList = {
435413
});
436414
},
437415

438-
applyGroupSelect: function (element) {
439-
var checked = [];
416+
applyGroupSelect: function (element, user, checked) {
440417
var $element = $(element);
441-
var user = UserList.getUID($element);
442418

443-
if ($element.data('user-groups')) {
444-
if (typeof $element.data('user-groups') === 'string') {
445-
checked = $element.data('user-groups').split(", ");
446-
}
447-
else {
448-
checked = $element.data('user-groups');
449-
}
450-
}
451419
var checkHandler = null;
452420
if(user) { // Only if in a user row, and not the #newusergroups select
453421
checkHandler = function (group) {
@@ -487,13 +455,6 @@ var UserList = {
487455
};
488456
}
489457
var addGroup = function (select, group) {
490-
$('select[multiple]').each(function (index, element) {
491-
$element = $(element);
492-
if ($element.find('option').filterAttr('value', group).length === 0 &&
493-
select.data('msid') !== $element.data('msid')) {
494-
$element.append('<option value="' + escapeHTML(group) + '">' + escapeHTML(group) + '</option>');
495-
}
496-
});
497458
GroupList.addGroup(escapeHTML(group));
498459
};
499460
var label;
@@ -514,19 +475,8 @@ var UserList = {
514475
});
515476
},
516477

517-
applySubadminSelect: function (element) {
518-
var checked = [];
478+
applySubadminSelect: function (element, user, checked) {
519479
var $element = $(element);
520-
var user = UserList.getUID($element);
521-
522-
if ($element.data('subadmin')) {
523-
if (typeof $element.data('subadmin') === 'string') {
524-
checked = $element.data('subadmin').split(", ");
525-
}
526-
else {
527-
checked = $element.data('subadmin');
528-
}
529-
}
530480
var checkHandler = function (group) {
531481
if (group === 'admin') {
532482
return false;
@@ -542,15 +492,7 @@ var UserList = {
542492
);
543493
};
544494

545-
var addSubAdmin = function (group) {
546-
$('select[multiple]').each(function (index, element) {
547-
if ($(element).find('option').filterAttr('value', group).length === 0) {
548-
$(element).append('<option value="' + escapeHTML(group) + '">' + escapeHTML(group) + '</option>');
549-
}
550-
});
551-
};
552495
$element.multiSelect({
553-
createCallback: addSubAdmin,
554496
createText: null,
555497
checked: checked,
556498
oncheck: checkHandler,
@@ -599,6 +541,76 @@ var UserList = {
599541
}
600542
}
601543
);
544+
},
545+
546+
/**
547+
* Creates a temporary jquery.multiselect selector on the given group field
548+
*/
549+
_triggerGroupEdit: function($td, isSubadminSelect) {
550+
var $groupsListContainer = $td.find('.groupsListContainer');
551+
var placeholder = $groupsListContainer.attr('data-placeholder') || t('settings', 'no group');
552+
var user = UserList.getUID($td);
553+
var checked = $td.data('groups') || [];
554+
var extraGroups = [].concat(checked);
555+
556+
$td.find('.multiselectoptions').remove();
557+
558+
// jquery.multiselect can only work with select+options in DOM ? We'll give jquery.multiselect what it wants...
559+
var $groupsSelect;
560+
if (isSubadminSelect) {
561+
$groupsSelect = $('<select multiple="multiple" class="groupsselect multiselect button" title="' + placeholder + '"></select>');
562+
} else {
563+
$groupsSelect = $('<select multiple="multiple" class="subadminsselect multiselect button" title="' + placeholder + '"></select>')
564+
}
565+
566+
function createItem(group) {
567+
if (isSubadminSelect && group === 'admin') {
568+
// can't become subadmin of "admin" group
569+
return;
570+
}
571+
$groupsSelect.append($('<option value="' + escapeHTML(group) + '">' + escapeHTML(group) + '</option>'));
572+
}
573+
574+
$.each(this.availableGroups, function (i, group) {
575+
// some new groups might be selected but not in the available groups list yet
576+
var extraIndex = extraGroups.indexOf(group);
577+
if (extraIndex >= 0) {
578+
// remove extra group as it was found
579+
extraGroups.splice(extraIndex, 1);
580+
}
581+
createItem(group);
582+
});
583+
$.each(extraGroups, function (i, group) {
584+
createItem(group);
585+
});
586+
587+
$td.append($groupsSelect);
588+
589+
if (isSubadminSelect) {
590+
UserList.applySubadminSelect($groupsSelect, user, checked);
591+
} else {
592+
UserList.applyGroupSelect($groupsSelect, user, checked);
593+
}
594+
595+
$groupsListContainer.addClass('hidden');
596+
$td.find('.multiselect:not(.groupsListContainer):first').click();
597+
$groupsSelect.on('dropdownclosed', function(e) {
598+
$groupsSelect.remove();
599+
$td.find('.multiselect:not(.groupsListContainer)').parent().remove();
600+
$td.find('.multiselectoptions').remove();
601+
$groupsListContainer.removeClass('hidden');
602+
UserList._updateGroupListLabel($td, e.checked);
603+
});
604+
},
605+
606+
/**
607+
* Updates the groups list td with the given groups selection
608+
*/
609+
_updateGroupListLabel: function($td, groups) {
610+
var placeholder = $td.find('.groupsListContainer').attr('data-placeholder');
611+
var $groupsEl = $td.find('.groupsList');
612+
$groupsEl.text(groups.join(', ') || placeholder || t('settings', 'no group'));
613+
$td.data('groups', groups);
602614
}
603615
};
604616

@@ -623,13 +635,6 @@ $(document).ready(function () {
623635
// TODO: move other init calls inside of initialize
624636
UserList.initialize($('#userlist'));
625637

626-
$('.groupsselect').each(function (index, element) {
627-
UserList.applyGroupSelect(element);
628-
});
629-
$('.subadminsselect').each(function (index, element) {
630-
UserList.applySubadminSelect(element);
631-
});
632-
633638
$userListBody.on('click', '.password', function (event) {
634639
event.stopPropagation();
635640

@@ -758,11 +763,24 @@ $(document).ready(function () {
758763
});
759764
});
760765

766+
$('#newuser .groupsListContainer').on('click', function (event) {
767+
event.stopPropagation();
768+
var $div = $(this).closest('.groups');
769+
UserList._triggerGroupEdit($div);
770+
});
771+
$userListBody.on('click', '.groups .groupsListContainer, .subadmins .groupsListContainer', function (event) {
772+
event.stopPropagation();
773+
var $td = $(this).closest('td');
774+
var isSubadminSelect = $td.hasClass('subadmins');
775+
UserList._triggerGroupEdit($td, isSubadminSelect);
776+
});
777+
761778
// init the quota field select box after it is shown the first time
762779
$('#app-settings').one('show', function() {
763780
$(this).find('#default_quota').singleSelect().on('change', UserList.onQuotaSelect);
764781
});
765782

783+
UserList._updateGroupListLabel($('#newuser .groups'), []);
766784
$('#newuser').submit(function (event) {
767785
event.preventDefault();
768786
var username = $('#newusername').val();
@@ -798,7 +816,7 @@ $(document).ready(function () {
798816
}
799817

800818
promise.then(function() {
801-
var groups = $('#newusergroups').val() || [];
819+
var groups = $('#newuser .groups').data('groups') || [];
802820
$.post(
803821
OC.generateUrl('/settings/users/users'),
804822
{

0 commit comments

Comments
 (0)