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);
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++) {
elBySelAll('.item > span', data.list, function(span) {
values.push({
objectId: ~~elData(span, 'object-id'),
- value: span.textContent
+ value: span.textContent,
+ type: elData(span, 'type')
});
});
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);
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');
*/
_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++) {
* @exports WoltLabSuite/Core/Ui/ItemList/User
*/
return {
+ _shadowGroups: null,
+
/**
* Initializes user suggestion support for an element.
*
* @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,
*/
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;
}
};
});
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();
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');
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');
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' => '<span class="icon icon16 fa-users"></span>',
'label' => $groupName,
'objectID' => $group->groupID,
'type' => 'group'