From: Marcel Werk Date: Tue, 1 Jan 2019 18:03:09 +0000 (+0100) Subject: Improved flexibility of user item lists X-Git-Tag: 5.2.0_Alpha_1~390 X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=8faf6ea10ac894b87b3e357f5248f67b4fd7b716;p=GitHub%2FWoltLab%2FWCF.git Improved flexibility of user item lists --- diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/ItemList.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/ItemList.js index 9e0b6e7a6e..406fc01c0e 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/ItemList.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/ItemList.js @@ -81,6 +81,10 @@ define(['Core', 'Dictionary', 'Language', 'Dom/Traverse', 'EventKey', 'WoltLabSu callbackChange: null, // callback once the form is about to be submitted callbackSubmit: null, + // Callback for the custom shadow synchronization. + callbackSyncShadow: null, + // Callback to set values during the setup. + callbackSetupValues: null, // value may contain the placeholder `{$objectId}` submitFieldName: '' }, options); @@ -132,7 +136,13 @@ define(['Core', 'Dictionary', 'Language', 'Dom/Traverse', 'EventKey', 'WoltLabSu suggestion: suggestion }); - values = (data.values.length) ? data.values : values; + if (options.callbackSetupValues) { + values = options.callbackSetupValues(); + } + else { + values = (data.values.length) ? data.values : values; + } + if (Array.isArray(values)) { var value; for (var i = 0, length = values.length; i < length; i++) { @@ -162,7 +172,8 @@ define(['Core', 'Dictionary', 'Language', 'Dom/Traverse', 'EventKey', 'WoltLabSu elBySelAll('.item > span', data.list, function(span) { values.push({ objectId: ~~elData(span, 'object-id'), - value: span.textContent + value: span.textContent, + type: elData(span, 'type') }); }); @@ -239,10 +250,20 @@ define(['Core', 'Dictionary', 'Language', 'Dom/Traverse', 'EventKey', 'WoltLabSu element.addEventListener('keypress', _callbackKeyPress); element.addEventListener('keyup', _callbackKeyUp); element.addEventListener('paste', _callbackPaste); + var hasFocus = element === document.activeElement; + if (hasFocus) { + //noinspection JSUnresolvedFunction + element.blur(); + } element.addEventListener('blur', _callbackBlur); - element.parentNode.insertBefore(list, element); listItem.appendChild(element); + if (hasFocus) { + window.setTimeout(function() { + //noinspection JSUnresolvedFunction + element.focus(); + }, 1); + } if (options.maxLength !== -1) { elAttr(element, 'maxLength', options.maxLength); @@ -441,6 +462,7 @@ define(['Core', 'Dictionary', 'Language', 'Dom/Traverse', 'EventKey', 'WoltLabSu var content = elCreate('span'); content.className = 'content'; elData(content, 'object-id', value.objectId); + if (value.type) elData(content, 'type', value.type); content.textContent = value.value; var button = elCreate('a'); @@ -497,6 +519,9 @@ define(['Core', 'Dictionary', 'Language', 'Dom/Traverse', 'EventKey', 'WoltLabSu */ _syncShadow: function(data) { if (!data.options.isCSV) return null; + if (typeof data.options.callbackSyncShadow === 'function') { + return data.options.callbackSyncShadow(data); + } var value = '', values = this.getValues(data.element.id); for (var i = 0, length = values.length; i < length; i++) { diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/ItemList/User.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/ItemList/User.js index c3a40380e8..3327b04fd2 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/ItemList/User.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/ItemList/User.js @@ -22,6 +22,8 @@ define(['WoltLabSuite/Core/Ui/ItemList'], function(UiItemList) { * @exports WoltLabSuite/Core/Ui/ItemList/User */ return { + _shadowGroups: null, + /** * Initializes user suggestion support for an element. * @@ -29,16 +31,21 @@ define(['WoltLabSuite/Core/Ui/ItemList'], function(UiItemList) { * @param {object} options option list */ init: function(elementId, options) { + this._shadowGroups = null; + UiItemList.init(elementId, [], { ajax: { className: 'wcf\\data\\user\\UserAction', parameters: { data: { - includeUserGroups: ~~options.includeUserGroups + includeUserGroups: ~~options.includeUserGroups, + restrictUserGroupIDs: (Array.isArray(options.restrictUserGroupIDs) ? options.restrictUserGroupIDs : []) } } }, callbackChange: (typeof options.callbackChange === 'function' ? options.callbackChange : null), + callbackSyncShadow: options.csvPerType ? this._syncShadow.bind(this) : null, + callbackSetupValues: (typeof options.callbackSetupValues === 'function' ? options.callbackSetupValues : null), excludedSearchValues: (Array.isArray(options.excludedSearchValues) ? options.excludedSearchValues : []), isCSV: true, maxItems: ~~options.maxItems || -1, @@ -51,6 +58,27 @@ define(['WoltLabSuite/Core/Ui/ItemList'], function(UiItemList) { */ getValues: function(elementId) { return UiItemList.getValues(elementId); + }, + + _syncShadow: function(data) { + var values = this.getValues(data.element.id); + var users = [], groups = []; + + values.forEach(function(value) { + if (value.type && value.type === 'group') groups.push(value.objectId); + else users.push(value.value); + }); + + data.shadow.value = users.join(','); + if (!this._shadowGroups) { + this._shadowGroups = elCreate('input'); + this._shadowGroups.type = 'hidden'; + this._shadowGroups.name = data.shadow.name + 'GroupIDs'; + data.shadow.parentNode.insertBefore(this._shadowGroups, data.shadow); + } + this._shadowGroups.value = groups.join(','); + + return values; } }; }); diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Suggestion.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Suggestion.js index 037fc06f2b..8deb248bf9 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Suggestion.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Suggestion.js @@ -162,7 +162,8 @@ define(['Ajax', 'Core', 'Ui/SimpleDropdown'], function(Ajax, Core, UiSimpleDropd item = item.currentTarget.parentNode; } - this._options.callbackSelect(this._element.id, { objectId: elData(item.children[0], 'object-id'), value: item.textContent }); + var anchor = item.children[0]; + this._options.callbackSelect(this._element.id, { objectId: elData(anchor, 'object-id'), value: item.textContent, type: elData(anchor, 'type') }); if (isEvent) { this._element.focus(); @@ -239,6 +240,7 @@ define(['Ajax', 'Core', 'Ui/SimpleDropdown'], function(Ajax, Core, UiSimpleDropd anchor.textContent = item.label; } elData(anchor, 'object-id', item.objectID); + if (item.type) elData(anchor, 'type', item.type); anchor.addEventListener(WCF_CLICK_EVENT, this._select.bind(this)); listItem = elCreate('li'); diff --git a/wcfsetup/install/files/lib/data/user/UserAction.class.php b/wcfsetup/install/files/lib/data/user/UserAction.class.php index 0d73080229..24b50335ca 100644 --- a/wcfsetup/install/files/lib/data/user/UserAction.class.php +++ b/wcfsetup/install/files/lib/data/user/UserAction.class.php @@ -478,6 +478,7 @@ class UserAction extends AbstractDatabaseObjectAction implements IClipboardActio public function validateGetSearchResultList() { $this->readBoolean('includeUserGroups', false, 'data'); $this->readString('searchString', false, 'data'); + $this->readIntegerArray('restrictUserGroupIDs', false, 'data'); if (isset($this->parameters['data']['excludedSearchValues']) && !is_array($this->parameters['data']['excludedSearchValues'])) { throw new UserInputException('excludedSearchValues'); @@ -498,11 +499,16 @@ class UserAction extends AbstractDatabaseObjectAction implements IClipboardActio if ($this->parameters['data']['includeUserGroups']) { $accessibleGroups = UserGroup::getAccessibleGroups(); foreach ($accessibleGroups as $group) { + if (!empty($this->parameters['data']['restrictUserGroupIDs']) && !in_array($group->groupID, $this->parameters['data']['restrictUserGroupIDs'])) { + continue; + } + $groupName = $group->getName(); if (!in_array($groupName, $excludedSearchValues)) { $pos = mb_strripos($groupName, $searchString); if ($pos !== false && $pos == 0) { $list[] = [ + 'icon' => '', 'label' => $groupName, 'objectID' => $group->groupID, 'type' => 'group'