diff --git a/api/js/etemplate/et2_widget_selectAccount.js b/api/js/etemplate/et2_widget_selectAccount.js
index a95ac75b06..7d0be43110 100644
--- a/api/js/etemplate/et2_widget_selectAccount.js
+++ b/api/js/etemplate/et2_widget_selectAccount.js
@@ -1,3 +1,4 @@
+"use strict";
/**
* EGroupware eTemplate2 - JS Select account widget
*
@@ -12,11 +13,28 @@
* @copyright Nathan Gray 2012
* @version $Id$
*/
-
+var __extends = (this && this.__extends) || (function () {
+ var extendStatics = function (d, b) {
+ extendStatics = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+ return extendStatics(d, b);
+ };
+ return function (d, b) {
+ extendStatics(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ };
+})();
+Object.defineProperty(exports, "__esModule", { value: true });
/*egw:uses
- et2_widget_link;
+ et2_widget_link;
*/
-
+var et2_widget_selectbox_1 = require("./et2_widget_selectbox");
+var et2_core_widget_1 = require("./et2_core_widget");
+var et2_core_inheritance_1 = require("./et2_core_inheritance");
+var et2_widget_link_1 = require("./et2_widget_link");
+var et2_widget_dialog_1 = require("./et2_widget_dialog");
/**
* Account selection widget
* Changes according to the user's account_selection preference
@@ -27,722 +45,572 @@
*
* Only primary_group and popup need anything different from a normal selectbox
*
- * @augments et2_selectbox
*/
-var et2_selectAccount = (function(){ "use strict"; return et2_selectbox.extend(
-{
- attributes: {
- 'account_type': {
- 'name': 'Account type',
- 'default': 'accounts',
- 'type': 'string',
- 'description': 'Limit type of accounts. One of {accounts,groups,both,owngroups}.'
- }
- },
-
- legacyOptions: ['empty_label','account_type'],
-
- account_types: ['accounts','groups','both','owngroups'],
-
- /**
- * Constructor
- *
- * @param _parent
- * @param _attrs
- * @memberOf et2_selectAccount
- * @returns
- */
- init: function(_parent, _attrs) {
-
- // Type in rows or somewhere else?
- if(jQuery.inArray(_attrs['empty_label'], this.account_types) > 0 && (
- jQuery.inArray(_attrs['account_type'], this.account_types) < 0 ||
- _attrs['account_type'] == this.attributes.account_type['default'])
- )
- {
- _attrs['account_type'] = _attrs['empty_label'];
- _attrs['empty_label'] = '';
- }
- if(jQuery.inArray(_attrs['account_type'], this.account_types) < 0)
- {
- this.egw().debug("warn", "Invalid account_type: %s Valid options:",_attrs['account_type'], this.account_types);
- }
-
- // Holder for search jQuery nodes
- this.search = null;
-
- // Reference to dialog
- this.dialog = null;
-
- // Reference to widget within dialog
- this.widgets = null;
-
- if(!_attrs.empty_label && !_attrs.readonly && _attrs.multiple)
- {
- _attrs.empty_label = this.egw().lang('Select user or group');
- }
-
- this._super.call(this, _parent, _attrs);
-
- // Allow certain widgets inside this one
- this.supportedWidgetClasses = [et2_link_entry];
- },
-
- destroy: function() {
- this._super.apply(this, arguments);
- },
-
- /**
- * Tell et2 widget framework where to go
- *
- * @param {object} _sender
- *
- getDOMNode: function(_sender) {
- if(this.search_widget != null && _sender == this.search_widget)
- {
- return this.search != null ? this.search[0] : this.search_widget._parent.getDOMNode();
- }
- return this._super.apply(this, arguments);
- },
- */
-
-
- /**
- * Single selection - override to add search button
- */
- createInputWidget: function()
- {
- var type = this.egw().preference('account_selection', 'common');
-
- switch(type)
- {
- case 'none':
- if(typeof egw.user('apps').admin == 'undefined')
- {
- this.options.select_options = {};
- break;
- }
- case 'selectbox':
- case 'groupmembers':
- default:
- this.options.select_options = this._get_accounts();
- break;
- }
-
- this._super.apply(this, arguments);
-
- // Add search button
- if(type == 'primary_group')
- {
- var button = jQuery(document.createElement("span"))
- .addClass("et2_clickable")
- .click(this, jQuery.proxy(function(e) {
- // Auto-expand
- if(this.options.expand_multiple_rows && !this.options.multiple)
- {
- this.set_multiple(true, this.options.expand_multiple_rows);
- }
-
- if(this.options.multiple)
- {
- this._open_multi_search(e);
- }
- else
- {
- this._open_search(e);
- }
- },this))
- .attr("title", egw.lang("popup with search"))
- .append('');
-
- this.getSurroundings().insertDOMNode(button[0]);
- }
- },
-
- /**
- * Multiple selection - override to add search button
- */
- createMultiSelect: function() {
-
- var type = this.egw().preference('account_selection', 'common');
- if(type == 'none' && typeof egw.user('apps').admin == 'undefined') return;
-
- this._super.apply(this, arguments);
-
- this.options.select_options = this._get_accounts();
-
- if(type == 'primary_group')
- {
- // Allow search 'inside' this widget
- this.supportedWidgetClasses = [et2_link_entry];
-
- // Add quick search - turn off multiple to get normal result list
- this.options.multiple = false;
- this._create_search();
-
- // Clear search box after select
- var old_select = this.search_widget.select;
- var self = this;
- this.search_widget.select = function(e, selected) {
- var current = self.getValue();
-
- // Fix ID as sent from server - must be numeric
- selected.item.value = parseInt(selected.item.value);
-
- // This one is important, it makes sure the option is there
- old_select.apply(this, arguments);
-
- // Add quick search selection into current selection
- current.push(selected.item.value);
-
- // Clear search
- this.search.val('');
-
- self.set_value(current);
- };
-
- // Put search results as a DOM sibling of the options, for proper display
- this.search_widget.search.on("autocompleteopen", jQuery.proxy(function() {
- this.search_widget.search.data("ui-autocomplete").menu.element
- .appendTo(this.node)
- .position({my: 'left top', at: 'left bottom', of: this.multiOptions.prev()});
- },this));
- this.search = jQuery(document.createElement("li"))
- .appendTo(this.multiOptions.prev().find('ul'));
- this.options.multiple = true;
-
- // Add search button
- var button = jQuery(document.createElement("li"))
- .addClass("et2_clickable")
- .click(this, this._open_multi_search)
- .attr("title", egw.lang("popup with search"))
- .append('');
- var type = this.egw().preference('account_selection', 'common');
-
- // Put it last so check/uncheck doesn't move around
- this.multiOptions.prev().find('ul')
- .append(button);
- }
- },
-
- /**
- * Override parent to make sure accounts are there as options.
- *
- * Depending on the widget's attributes and the user's preferences, not all selected
- * accounts may be in the cache as options, so we fetch the extras to make sure
- * we don't lose any.
- *
- * As fetching them might only work asynchron (if they are not yet loaded),
- * we have to call set_value again, once all labels have arrived from server.
- *
- * @param {string|array} _value
- */
- set_value: function(_value)
- {
- if(typeof _value == "string" && this.options.multiple && _value.match(this._is_multiple_regexp) !== null)
- {
- _value = _value.split(',');
- }
-
- if(_value)
- {
- var search = _value;
- if (!jQuery.isArray(search))
- {
- search = [_value];
- }
- var update_options = false;
- var num_calls = 0;
- var current_call = 0;
- for(var j = 0; j < search.length; j++)
- {
- var found = false;
-
- // Not having a value to look up causes an infinite loop
- if(!search[j] || search[j] === "0") continue;
-
- // Options are not indexed, so we must look
- for(var i = 0; !found && i < this.options.select_options.length; i++)
- {
- if (typeof this.options.select_options[i] != 'object')
- {
- egw.debug('warn',this.id + ' wrong option '+i+' this.options.select_options=', this.options.select_options);
- continue;
- }
- if(this.options.select_options[i].value == search[j]) found = true;
- }
- // We only look for numeric IDs, non-numeric IDs cause an exception
- if(!found && !isNaN(search[j]))
- {
- // Add it in
- var name = this.egw().link_title('api-accounts', search[j]);
- if (name) // was already cached on client-side
- {
- update_options = true;
- this.options.select_options.push({value: search[j], label:name});
- }
- else // not available: need to call set_value again, after all arrived from server
- {
- ++num_calls;
- // Add immediately with value as label, we'll replace later
- this._appendOptionElement(search[j],search[j]);
- this.egw().link_title('api-accounts', search[j], function(name)
- {
- if (++current_call >= num_calls) // only run last callback
- {
- // Update the label
- // Options are not indexed, so we must look
- for(var i = 0; i < this.widget.options.select_options.length; i++)
- {
- var opt = this.widget.options.select_options[i];
- if(opt && opt.value && opt.value == this.unknown && opt.label == this.unknown)
- {
- opt.label = name;
- this.widget.set_select_options(this.widget.options.select_options);
- break;
- }
- }
- this.widget.set_value(_value);
- }
- }, {widget: this, unknown: search[j]});
- }
- }
- }
- if(update_options)
- {
- this.set_select_options(this.options.select_options);
- }
- }
- this._super.apply(this, arguments);
- },
-
- /**
- * Get account info for select options from common client-side account cache
- *
- * @return {Array} select options
- */
- _get_accounts: function()
- {
- if (!jQuery.isArray(this.options.select_options))
- {
- var options = jQuery.extend({}, this.options.select_options);
- this.options.select_options = [];
- for(var key in options)
- {
- if (typeof options[key] == 'object')
- {
- if (typeof(options[key].key) == 'undefined')
- {
- options[key].value = key;
- }
- this.options.select_options.push(options[key]);
- }
- else
- {
- this.options.select_options.push({value: key, label: options[key]});
- }
- }
- }
- var type = this.egw().preference('account_selection', 'common');
- var accounts = [];
- // for primary_group we only display owngroups == own memberships, not other groups
- if (type == 'primary_group' && this.options.account_type != 'accounts')
- {
- if (this.options.account_type == 'both')
- {
- accounts = this.egw().accounts('accounts');
- }
- accounts = accounts.concat(this.egw().accounts('owngroups'));
- }
- else
- {
- accounts = this.egw().accounts(this.options.account_type);
- }
- return this.options.select_options.concat(accounts);
- },
-
- /**
- * Create & display a way to search & select a single account / group
- * Single selection is just link widget
- *
- * @param e event
- */
- _open_search: function(e) {
- var widget = e.data;
- var search = widget._create_search();
-
- // Selecting a single user closes the dialog, this only used if user cleared
- var ok_click = function() {
- widget.set_value([]);
- // Fire change event
- if(widget.input) widget.input.trigger("change");
- jQuery(this).dialog("close");
- };
- widget._create_dialog(search, ok_click);
- },
-
- /**
- * Create & display a way to search & select multiple accounts / groups
- *
- * @param e event
- */
- _open_multi_search: function(e) {
- var widget = e && e.data ? e.data : this;
- var table = widget.search = jQuery('
');
- table.css("width", "100%").css("height", "100%");
- var search_col = jQuery('#search_col',table);
- var select_col = jQuery('#selection_col',table);
-
- // Search / Selection
- search_col.append(widget._create_search());
-
- // Currently selected
- select_col.append(widget._create_selected());
-
- var ok_click = function() {
- jQuery(this).dialog("close");
- // Update widget with selected
- var ids = [];
- var data = {};
- jQuery('#'+widget.getInstanceManager().uniqueId + '_selected li',select_col).each(function() {
- var id = jQuery(this).attr("data-id");
- // Add to list
- ids.push(id);
-
- // Make sure option is there
- if(!widget.options.multiple && jQuery('input[id$="_opt_'+id+'"]',widget.multiOptions).length == 0)
- {
- widget._appendMultiOption(id,jQuery('label',this).text());
- }
- else if (widget.options.multiple && jQuery('option[value="'+id+'"]',widget.node).length == 0)
- {
- widget._appendOptionElement(id,jQuery('label',this).text());
- }
- });
-
- widget.set_value(ids);
-
- // Fire change event
- if(widget.input) widget.input.trigger("change");
- };
-
- var container = jQuery(document.createElement("div")).append(table);
- return widget._create_dialog(container, ok_click);
- },
-
- /**
- * Create / display popup with search / selection widgets
- *
- * @param {et2_dialog} widgets
- * @param {function} update_function
- */
- _create_dialog: function(widgets, update_function) {
- this.widgets = widgets;
- this.dialog = et2_dialog.show_dialog(false,
- '',
- this.options.label ? this.options.label : this.egw().lang('Select'),
- {},
- [{
- text: this.egw().lang("ok"),
- image: 'check',
- click: update_function
- },{
- text: this.egw().lang("cancel"),
- image: 'cancel'
- }]
- );
- this.dialog.set_dialog_type('');
- // Static size for easier layout
- this.dialog.div.dialog({width: "500", height: "370"});
-
- this.dialog.div.append(widgets.width('100%'));
- return widgets;
- },
-
- /**
- * Search is a link-entry widget, with some special display for multi-select
- */
- _create_search: function() {
- var self = this;
- var search = this.search = jQuery(document.createElement("div"));
-
- var search_widget = this.search_widget = et2_createWidget('link-entry', {
- 'only_app': 'api-accounts',
- 'query': function(request, response) {
- // Clear previous search results for multi-select
- if(!request.options)
- {
- search.find('#search_results').empty();
- }
- // Restrict to specified account type
- if(!request.options || !request.options.filter)
- {
- request.options = {account_type: self.options.account_type};
- }
- return true;
- },
- 'select': function(e, selected) {
- // Make sure option is there
- var already_there = false;
- var last_key = null;
- for(last_key in self.options.select_options)
- {
- var option = self.options.select_options[last_key];
- already_there = already_there || (typeof option.value != 'undefined' && option.value == selected.item.value);
- }
- if(!already_there)
- {
- self.options.select_options[parseInt(last_key)+1] = selected.item;
- self._appendOptionElement(selected.item.value, selected.item.label);
- }
- self.set_value(selected.item.value);
- if(self.dialog)
- {
- self.dialog.div.dialog("close");
- }
- // Fire change event
- if(self.input) self.input.trigger("change");
- return true;
- }
- }, this);
- // add it where we want it
- search.append(search_widget.getDOMNode());
-
- if(!this.options.multiple) return search;
-
- // Multiple is more complicated. It uses a custom display for results to
- // allow choosing multiples from a match
- var results = jQuery(document.createElement("ul"))
- .attr("id", "search_results")
- .css("height", "230px")
- .addClass("ui-multiselect-checkboxes ui-helper-reset");
- jQuery(document.createElement("div"))
- .addClass("et2_selectbox")
- .css("height", "100%")
- .append(results)
- .appendTo(search);
-
- // Override link-entry auto-complete for custom display
- // Don't show normal drop-down
- search_widget.search.data("ui-autocomplete")._suggest = function(items) {
- jQuery.each(items, function (index, item) {
- // Make sure value is numeric
- item.value = parseInt(item.value);
- self._add_search_result(results, item);
- });
- };
-
- return search;
- },
-
- /**
- * Add the selected result to the list of search results
- *
- * @param list
- * @param item
- */
- _add_search_result: function(list, item) {
-
- var node = null;
- var self = this;
-
- // Make sure value is numeric
- if(item.value) item.value = parseInt(item.value);
-
- // (containter of) Currently selected users / groups
- var selected = jQuery('#'+this.getInstanceManager().uniqueId + "_selected", this.widgets);
-
- // Group
- if(item.value && item.value < 0)
- {
- node = jQuery(document.createElement('ul'));
- // Add button to show users
- if(this.options.account_type != 'groups')
- {
- jQuery('')
- .css("float", "left")
- .appendTo(node)
- .click(function() {
- if(jQuery(this).hasClass("ui-icon-circlesmall-plus"))
- {
- jQuery(this).removeClass("ui-icon-circlesmall-plus")
- .addClass("ui-icon-circlesmall-minus");
-
- var group = jQuery(this).parent()
- .addClass("expanded");
-
- if(group.children("li").length == 0)
- {
- // Fetch group members
- self.search_widget.query({
- term:"",
- options: {filter:{group: item.value}},
- no_cache:true
- }, function(items) {
- jQuery(items).each(function(index,item) {
- self._add_search_result(node, item);
- });
- });
- }
- else
- {
- group.children("li")
- // Only show children that are not selected
- .each(function(index, item) {
- var j = jQuery(item);
- if(jQuery('[data-id="'+j.attr("data-id")+'"]',selected).length == 0)
- {
- j.show();
- }
- });
- }
- }
- else
- {
- jQuery(this).addClass("ui-icon-circlesmall-plus")
- .removeClass("ui-icon-circlesmall-minus");
-
- var group = jQuery(this).parent().children("li").hide();
- }
- });
- }
-
- }
- // User
- else if (item.value)
- {
- node = jQuery(document.createElement('li'));
- }
- node.attr("data-id", item.value);
-
- jQuery('')
- .css("float", "right")
- .appendTo(node)
- .click(function() {
- var button = jQuery(this);
- self._add_selected(selected, button.parent().attr("data-id"));
- // Hide user, but only hide button for group
- if(button.parent().is('li'))
- {
- button.parent().hide();
- }
- else
- {
- button.hide();
- }
- });
-
- // If already in list, hide it
- if(jQuery('[data-id="'+item.value+'"]',selected).length != 0)
- {
- node.hide();
- }
-
- var label = jQuery(document.createElement('label'))
- .addClass("loading")
- .appendTo(node);
-
- this.egw().link_title('api-accounts', item.value, function(name) {
- label.text(name).removeClass("loading");
- }, label);
-
- node.appendTo(list);
- },
-
- _create_selected: function() {
- var node = jQuery(document.createElement("div"))
- .addClass("et2_selectbox");
-
- var header = jQuery(document.createElement("div"))
- .addClass("ui-widget-header ui-helper-clearfix")
- .appendTo(node);
-
- var selected = jQuery(document.createElement("ul"))
- .addClass("ui-multiselect-checkboxes ui-helper-reset")
- .attr("id", this.getInstanceManager().uniqueId + "_selected")
- .css("height", "230px")
- .appendTo(node);
-
- jQuery(document.createElement("span"))
- .text(this.egw().lang("Selection"))
- .addClass("ui-multiselect-header")
- .appendTo(header);
-
- var controls = jQuery(document.createElement("ul"))
- .addClass('ui-helper-reset')
- .appendTo(header);
-
- jQuery(document.createElement("li"))
- .addClass("et2_clickable")
- .click(selected, function(e) {jQuery("li",e.data).remove();})
- .append('')
- .appendTo(controls);
-
- // Add in currently selected
- if(this.getValue())
- {
- var value = this.getValue();
- for(var i = 0; i < value.length; i++) {
- this._add_selected(selected, value[i]);
- }
- }
- return node;
- },
-
- /**
- * Add an option to the list of selected accounts
- * value is the account / group ID
- *
- * @param list
- * @param value
- */
- _add_selected: function(list, value) {
-
- // Each option only once
- var there = jQuery('[data-id="' + value + '"]',list);
- if(there.length)
- {
- there.show();
- return;
- }
-
- var option = jQuery(document.createElement('li'))
- .attr("data-id",value)
- .appendTo(list);
- jQuery('')
- .css("float", "right")
- .appendTo(option)
- .click(function() {
- var id = jQuery(this).parent().attr("data-id");
- jQuery(this).parent().remove();
- // Add 'add' button back, if in results list
- list.parents("tr").find("[data-id='"+id+"']").show()
- // Show button(s) for group
- .children('span').show();
- });
-
- var label = jQuery(document.createElement('label'))
- .addClass("loading")
- .appendTo(option);
- this.egw().link_title('api-accounts', value, function(name) {this.text(name).removeClass("loading");}, label);
- },
-
- /**
- * Overwritten attachToDOM metod to modify attachToDOM
- */
- attachToDOM: function ()
- {
- this._super.apply(this, arguments);
- //Chosen needs to be set after widget dettached from DOM (eg. validation_error), because chosen is not part of the widget node
- if (this.egw().preference('account_selection', 'common') == 'primary_group')
- {
- jQuery(this.node).removeClass('chzn-done');
- this.set_tags(this.options.tags, this.options.width);
- }
- }
-});}).call(this);
-et2_register_widget(et2_selectAccount, ["select-account"]);
-
+var et2_selectAccount = /** @class */ (function (_super) {
+ __extends(et2_selectAccount, _super);
+ /**
+ * Constructor
+ *
+ */
+ function et2_selectAccount(_parent, _attrs, _child) {
+ var _this = _super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_selectAccount._attributes, _child || {})) || this;
+ _this.legacyOptions = ['empty_label', 'account_type'];
+ // Type in rows or somewhere else?
+ if (jQuery.inArray(_attrs['empty_label'], et2_selectAccount.account_types) > 0 && (jQuery.inArray(_attrs['account_type'], et2_selectAccount.account_types) < 0 ||
+ _attrs['account_type'] == et2_selectAccount._attributes.account_type['default'])) {
+ _attrs['account_type'] = _attrs['empty_label'];
+ _attrs['empty_label'] = '';
+ }
+ if (jQuery.inArray(_attrs['account_type'], et2_selectAccount.account_types) < 0) {
+ _this.egw().debug("warn", "Invalid account_type: %s Valid options:", _attrs['account_type'], et2_selectAccount.account_types);
+ }
+ // Holder for search jQuery nodes
+ _this.search = null;
+ // Reference to dialog
+ _this.dialog = null;
+ // Reference to widget within dialog
+ _this.widgets = null;
+ if (!_attrs.empty_label && !_attrs.readonly && _attrs.multiple) {
+ _attrs.empty_label = _this.egw().lang('Select user or group');
+ }
+ // Allow certain widgets inside this one
+ _this.supportedWidgetClasses = [et2_widget_link_1.et2_link_entry];
+ return _this;
+ }
+ et2_selectAccount.prototype.destroy = function () {
+ _super.prototype.destroy.apply(this, arguments);
+ };
+ /**
+ * Single selection - override to add search button
+ */
+ et2_selectAccount.prototype.createInputWidget = function () {
+ var type = this.egw().preference('account_selection', 'common');
+ switch (type) {
+ case 'none':
+ if (typeof egw.user('apps').admin == 'undefined') {
+ this.options.select_options = {};
+ break;
+ }
+ case 'selectbox':
+ case 'groupmembers':
+ default:
+ this.options.select_options = this._get_accounts();
+ break;
+ }
+ _super.prototype.createInputWidget.call(this);
+ // Add search button
+ if (type == 'primary_group') {
+ var button = jQuery(document.createElement("span"))
+ .addClass("et2_clickable")
+ .click(this, jQuery.proxy(function (e) {
+ // Auto-expand
+ if (this.options.expand_multiple_rows && !this.options.multiple) {
+ this.set_multiple(true, this.options.expand_multiple_rows);
+ }
+ if (this.options.multiple) {
+ this._open_multi_search(e);
+ }
+ else {
+ this._open_search(e);
+ }
+ }, this))
+ .attr("title", egw.lang("popup with search"))
+ .append('');
+ this.getSurroundings().insertDOMNode(button[0]);
+ }
+ };
+ /**
+ * Multiple selection - override to add search button
+ */
+ et2_selectAccount.prototype.createMultiSelect = function () {
+ var type = this.egw().preference('account_selection', 'common');
+ if (type == 'none' && typeof egw.user('apps').admin == 'undefined')
+ return;
+ _super.prototype.createMultiSelect.call(this);
+ this.options.select_options = this._get_accounts();
+ if (type == 'primary_group') {
+ // Allow search 'inside' this widget
+ this.supportedWidgetClasses = [et2_widget_link_1.et2_link_entry];
+ // Add quick search - turn off multiple to get normal result list
+ this.options.multiple = false;
+ this._create_search();
+ // Clear search box after select
+ var old_select = this.search_widget.select;
+ var self = this;
+ // @ts-ignore
+ this.search_widget.select = function (e, selected) {
+ var current = self.getValue();
+ // Fix ID as sent from server - must be numeric
+ selected.item.value = parseInt(selected.item.value);
+ // This one is important, it makes sure the option is there
+ old_select.apply(this, arguments);
+ // Add quick search selection into current selection
+ current.push(selected.item.value);
+ // Clear search
+ this.search.val('');
+ self.set_value(current);
+ };
+ // Put search results as a DOM sibling of the options, for proper display
+ this.search_widget.search.on("autocompleteopen", jQuery.proxy(function () {
+ this.search_widget.search.data("ui-autocomplete").menu.element
+ .appendTo(this.node)
+ .position({ my: 'left top', at: 'left bottom', of: this.multiOptions.prev() });
+ }, this));
+ this.search = jQuery(document.createElement("li"))
+ .appendTo(this.multiOptions.prev().find('ul'));
+ this.options.multiple = true;
+ // Add search button
+ var button = jQuery(document.createElement("li"))
+ .addClass("et2_clickable")
+ .click(this, this._open_multi_search)
+ .attr("title", egw.lang("popup with search"))
+ .append('');
+ var type = this.egw().preference('account_selection', 'common');
+ // Put it last so check/uncheck doesn't move around
+ this.multiOptions.prev().find('ul')
+ .append(button);
+ }
+ };
+ /**
+ * Override parent to make sure accounts are there as options.
+ *
+ * Depending on the widget's attributes and the user's preferences, not all selected
+ * accounts may be in the cache as options, so we fetch the extras to make sure
+ * we don't lose any.
+ *
+ * As fetching them might only work asynchron (if they are not yet loaded),
+ * we have to call set_value again, once all labels have arrived from server.
+ *
+ * @param {string|array} _value
+ */
+ et2_selectAccount.prototype.set_value = function (_value) {
+ if (typeof _value == "string" && this.options.multiple && _value.match(this._is_multiple_regexp) !== null) {
+ _value = _value.split(',');
+ }
+ if (_value) {
+ var search = _value;
+ if (!jQuery.isArray(search)) {
+ search = [_value];
+ }
+ var update_options = false;
+ var num_calls = 0;
+ var current_call = 0;
+ for (var j = 0; j < search.length; j++) {
+ var found = false;
+ // Not having a value to look up causes an infinite loop
+ if (!search[j] || search[j] === "0")
+ continue;
+ // Options are not indexed, so we must look
+ for (var i = 0; !found && i < this.options.select_options.length; i++) {
+ if (typeof this.options.select_options[i] != 'object') {
+ egw.debug('warn', this.id + ' wrong option ' + i + ' this.options.select_options=', this.options.select_options);
+ continue;
+ }
+ if (this.options.select_options[i].value == search[j])
+ found = true;
+ }
+ // We only look for numeric IDs, non-numeric IDs cause an exception
+ if (!found && !isNaN(search[j])) {
+ // Add it in
+ var name = this.egw().link_title('api-accounts', search[j]);
+ if (name) // was already cached on client-side
+ {
+ update_options = true;
+ this.options.select_options.push({ value: search[j], label: name });
+ }
+ else // not available: need to call set_value again, after all arrived from server
+ {
+ ++num_calls;
+ // Add immediately with value as label, we'll replace later
+ this._appendOptionElement(search[j], search[j]);
+ this.egw().link_title('api-accounts', search[j], function (name) {
+ if (++current_call >= num_calls) // only run last callback
+ {
+ // Update the label
+ // Options are not indexed, so we must look
+ for (var i = 0; i < this.widget.options.select_options.length; i++) {
+ var opt = this.widget.options.select_options[i];
+ if (opt && opt.value && opt.value == this.unknown && opt.label == this.unknown) {
+ opt.label = name;
+ this.widget.set_select_options(this.widget.options.select_options);
+ break;
+ }
+ }
+ this.widget.set_value(_value);
+ }
+ }, { widget: this, unknown: search[j] });
+ }
+ }
+ }
+ if (update_options) {
+ this.set_select_options(this.options.select_options);
+ }
+ }
+ _super.prototype.set_value.call(this, _value);
+ };
+ /**
+ * Get account info for select options from common client-side account cache
+ *
+ * @return {Array} select options
+ */
+ et2_selectAccount.prototype._get_accounts = function () {
+ if (!jQuery.isArray(this.options.select_options)) {
+ var options = jQuery.extend({}, this.options.select_options);
+ this.options.select_options = [];
+ for (var key in options) {
+ if (typeof options[key] == 'object') {
+ if (typeof (options[key].key) == 'undefined') {
+ options[key].value = key;
+ }
+ this.options.select_options.push(options[key]);
+ }
+ else {
+ this.options.select_options.push({ value: key, label: options[key] });
+ }
+ }
+ }
+ var type = this.egw().preference('account_selection', 'common');
+ var accounts = [];
+ // for primary_group we only display owngroups == own memberships, not other groups
+ if (type == 'primary_group' && this.options.account_type != 'accounts') {
+ if (this.options.account_type == 'both') {
+ accounts = this.egw().accounts('accounts');
+ }
+ accounts = accounts.concat(this.egw().accounts('owngroups'));
+ }
+ else {
+ accounts = this.egw().accounts(this.options.account_type);
+ }
+ return this.options.select_options.concat(accounts);
+ };
+ /**
+ * Create & display a way to search & select a single account / group
+ * Single selection is just link widget
+ *
+ * @param e event
+ */
+ et2_selectAccount.prototype._open_search = function (e) {
+ var widget = e.data;
+ var search = widget._create_search();
+ // Selecting a single user closes the dialog, this only used if user cleared
+ var ok_click = function () {
+ widget.set_value([]);
+ // Fire change event
+ if (widget.input)
+ widget.input.trigger("change");
+ jQuery(this).dialog("close");
+ };
+ widget._create_dialog(search, ok_click);
+ };
+ /**
+ * Create & display a way to search & select multiple accounts / groups
+ *
+ * @param e event
+ */
+ et2_selectAccount.prototype._open_multi_search = function (e) {
+ var widget = e && e.data ? e.data : this;
+ var table = widget.search = jQuery('');
+ table.css("width", "100%").css("height", "100%");
+ var search_col = jQuery('#search_col', table);
+ var select_col = jQuery('#selection_col', table);
+ // Search / Selection
+ search_col.append(widget._create_search());
+ // Currently selected
+ select_col.append(widget._create_selected());
+ var ok_click = function () {
+ jQuery(this).dialog("close");
+ // Update widget with selected
+ var ids = [];
+ var data = {};
+ jQuery('#' + widget.getInstanceManager().uniqueId + '_selected li', select_col).each(function () {
+ var id = jQuery(this).attr("data-id");
+ // Add to list
+ ids.push(id);
+ // Make sure option is there
+ if (!widget.options.multiple && jQuery('input[id$="_opt_' + id + '"]', widget.multiOptions).length == 0) {
+ widget._appendMultiOption(id, jQuery('label', this).text());
+ }
+ else if (widget.options.multiple && jQuery('option[value="' + id + '"]', widget.node).length == 0) {
+ widget._appendOptionElement(id, jQuery('label', this).text());
+ }
+ });
+ widget.set_value(ids);
+ // Fire change event
+ if (widget.input)
+ widget.input.trigger("change");
+ };
+ var container = jQuery(document.createElement("div")).append(table);
+ return widget._create_dialog(container, ok_click);
+ };
+ /**
+ * Create / display popup with search / selection widgets
+ *
+ * @param {et2_dialog} widgets
+ * @param {function} update_function
+ */
+ et2_selectAccount.prototype._create_dialog = function (widgets, update_function) {
+ this.widgets = widgets;
+ this.dialog = et2_widget_dialog_1.et2_dialog.show_dialog(false, '', this.options.label ? this.options.label : this.egw().lang('Select'), {}, [{
+ text: this.egw().lang("ok"),
+ image: 'check',
+ click: update_function
+ }, {
+ text: this.egw().lang("cancel"),
+ image: 'cancel'
+ }]);
+ this.dialog.set_dialog_type('');
+ // Static size for easier layout
+ this.dialog.div.dialog({ width: "500", height: "370" });
+ this.dialog.div.append(widgets.width('100%'));
+ return widgets;
+ };
+ /**
+ * Search is a link-entry widget, with some special display for multi-select
+ */
+ et2_selectAccount.prototype._create_search = function () {
+ var self = this;
+ var search = this.search = jQuery(document.createElement("div"));
+ var search_widget = this.search_widget = et2_createWidget('link-entry', {
+ 'only_app': 'api-accounts',
+ 'query': function (request, response) {
+ // Clear previous search results for multi-select
+ if (!request.options) {
+ search.find('#search_results').empty();
+ }
+ // Restrict to specified account type
+ if (!request.options || !request.options.filter) {
+ request.options = { account_type: self.options.account_type };
+ }
+ return true;
+ },
+ 'select': function (e, selected) {
+ // Make sure option is there
+ var already_there = false;
+ var last_key = null;
+ for (last_key in self.options.select_options) {
+ var option = self.options.select_options[last_key];
+ already_there = already_there || (typeof option.value != 'undefined' && option.value == selected.item.value);
+ }
+ if (!already_there) {
+ self.options.select_options[parseInt(last_key) + 1] = selected.item;
+ self._appendOptionElement(selected.item.value, selected.item.label);
+ }
+ self.set_value(selected.item.value);
+ if (self.dialog) {
+ self.dialog.div.dialog("close");
+ }
+ // Fire change event
+ if (self.input)
+ self.input.trigger("change");
+ return true;
+ }
+ }, this);
+ // add it where we want it
+ search.append(search_widget.getDOMNode());
+ if (!this.options.multiple)
+ return search;
+ // Multiple is more complicated. It uses a custom display for results to
+ // allow choosing multiples from a match
+ var results = jQuery(document.createElement("ul"))
+ .attr("id", "search_results")
+ .css("height", "230px")
+ .addClass("ui-multiselect-checkboxes ui-helper-reset");
+ jQuery(document.createElement("div"))
+ .addClass("et2_selectbox")
+ .css("height", "100%")
+ .append(results)
+ .appendTo(search);
+ // Override link-entry auto-complete for custom display
+ // Don't show normal drop-down
+ search_widget.search.data("ui-autocomplete")._suggest = function (items) {
+ jQuery.each(items, function (index, item) {
+ // Make sure value is numeric
+ item.value = parseInt(item.value);
+ self._add_search_result(results, item);
+ });
+ };
+ return search;
+ };
+ /**
+ * Add the selected result to the list of search results
+ *
+ * @param list
+ * @param item
+ */
+ et2_selectAccount.prototype._add_search_result = function (list, item) {
+ var node = null;
+ var self = this;
+ // Make sure value is numeric
+ if (item.value)
+ item.value = parseInt(item.value);
+ // (containter of) Currently selected users / groups
+ var selected = jQuery('#' + this.getInstanceManager().uniqueId + "_selected", this.widgets);
+ // Group
+ if (item.value && item.value < 0) {
+ node = jQuery(document.createElement('ul'));
+ // Add button to show users
+ if (this.options.account_type != 'groups') {
+ jQuery('')
+ .css("float", "left")
+ .appendTo(node)
+ .click(function () {
+ if (jQuery(this).hasClass("ui-icon-circlesmall-plus")) {
+ jQuery(this).removeClass("ui-icon-circlesmall-plus")
+ .addClass("ui-icon-circlesmall-minus");
+ var group = jQuery(this).parent()
+ .addClass("expanded");
+ if (group.children("li").length == 0) {
+ // Fetch group members
+ self.search_widget.query({
+ term: "",
+ options: { filter: { group: item.value } },
+ no_cache: true
+ }, function (items) {
+ jQuery(items).each(function (index, item) {
+ self._add_search_result(node, item);
+ });
+ });
+ }
+ else {
+ group.children("li")
+ // Only show children that are not selected
+ .each(function (index, item) {
+ var j = jQuery(item);
+ if (jQuery('[data-id="' + j.attr("data-id") + '"]', selected).length == 0) {
+ j.show();
+ }
+ });
+ }
+ }
+ else {
+ jQuery(this).addClass("ui-icon-circlesmall-plus")
+ .removeClass("ui-icon-circlesmall-minus");
+ var group = jQuery(this).parent().children("li").hide();
+ }
+ });
+ }
+ }
+ // User
+ else if (item.value) {
+ node = jQuery(document.createElement('li'));
+ }
+ node.attr("data-id", item.value);
+ jQuery('')
+ .css("float", "right")
+ .appendTo(node)
+ .click(function () {
+ var button = jQuery(this);
+ self._add_selected(selected, button.parent().attr("data-id"));
+ // Hide user, but only hide button for group
+ if (button.parent().is('li')) {
+ button.parent().hide();
+ }
+ else {
+ button.hide();
+ }
+ });
+ // If already in list, hide it
+ if (jQuery('[data-id="' + item.value + '"]', selected).length != 0) {
+ node.hide();
+ }
+ var label = jQuery(document.createElement('label'))
+ .addClass("loading")
+ .appendTo(node);
+ this.egw().link_title('api-accounts', item.value, function (name) {
+ label.text(name).removeClass("loading");
+ }, label);
+ node.appendTo(list);
+ };
+ et2_selectAccount.prototype._create_selected = function () {
+ var node = jQuery(document.createElement("div"))
+ .addClass("et2_selectbox");
+ var header = jQuery(document.createElement("div"))
+ .addClass("ui-widget-header ui-helper-clearfix")
+ .appendTo(node);
+ var selected = jQuery(document.createElement("ul"))
+ .addClass("ui-multiselect-checkboxes ui-helper-reset")
+ .attr("id", this.getInstanceManager().uniqueId + "_selected")
+ .css("height", "230px")
+ .appendTo(node);
+ jQuery(document.createElement("span"))
+ .text(this.egw().lang("Selection"))
+ .addClass("ui-multiselect-header")
+ .appendTo(header);
+ var controls = jQuery(document.createElement("ul"))
+ .addClass('ui-helper-reset')
+ .appendTo(header);
+ jQuery(document.createElement("li"))
+ .addClass("et2_clickable")
+ .click(selected, function (e) { jQuery("li", e.data).remove(); })
+ .append('')
+ .appendTo(controls);
+ // Add in currently selected
+ if (this.getValue()) {
+ var value = this.getValue();
+ for (var i = 0; i < value.length; i++) {
+ this._add_selected(selected, value[i]);
+ }
+ }
+ return node;
+ };
+ /**
+ * Add an option to the list of selected accounts
+ * value is the account / group ID
+ *
+ * @param list
+ * @param value
+ */
+ et2_selectAccount.prototype._add_selected = function (list, value) {
+ // Each option only once
+ var there = jQuery('[data-id="' + value + '"]', list);
+ if (there.length) {
+ there.show();
+ return;
+ }
+ var option = jQuery(document.createElement('li'))
+ .attr("data-id", value)
+ .appendTo(list);
+ jQuery('')
+ .css("float", "right")
+ .appendTo(option)
+ .click(function () {
+ var id = jQuery(this).parent().attr("data-id");
+ jQuery(this).parent().remove();
+ // Add 'add' button back, if in results list
+ list.parents("tr").find("[data-id='" + id + "']").show()
+ // Show button(s) for group
+ .children('span').show();
+ });
+ var label = jQuery(document.createElement('label'))
+ .addClass("loading")
+ .appendTo(option);
+ this.egw().link_title('api-accounts', value, function (name) { this.text(name).removeClass("loading"); }, label);
+ };
+ /**
+ * Overwritten attachToDOM method to modify attachToDOM
+ */
+ et2_selectAccount.prototype.attachToDOM = function () {
+ var result = _super.prototype.attachToDOM.call(this);
+ //Chosen needs to be set after widget dettached from DOM (eg. validation_error), because chosen is not part of the widget node
+ if (this.egw().preference('account_selection', 'common') == 'primary_group') {
+ jQuery(this.node).removeClass('chzn-done');
+ this.set_tags(this.options.tags, this.options.width);
+ }
+ return result;
+ };
+ et2_selectAccount._attributes = {
+ 'account_type': {
+ 'name': 'Account type',
+ 'default': 'accounts',
+ 'type': 'string',
+ 'description': 'Limit type of accounts. One of {accounts,groups,both,owngroups}.'
+ }
+ };
+ et2_selectAccount.account_types = ['accounts', 'groups', 'both', 'owngroups'];
+ return et2_selectAccount;
+}(et2_widget_selectbox_1.et2_selectbox));
+exports.et2_selectAccount = et2_selectAccount;
+et2_core_widget_1.et2_register_widget(et2_selectAccount, ["select-account"]);
/**
* et2_selectAccount_ro is the readonly implementation of select account
* It extends et2_link to avoid needing the whole user list on the client.
@@ -750,139 +618,108 @@ et2_register_widget(et2_selectAccount, ["select-account"]);
*
* @augments et2_link_string
*/
-var et2_selectAccount_ro = (function(){ "use strict"; return et2_link_string.extend([et2_IDetachedDOM],
-{
- attributes: {
- "empty_label": {
- "name": "Empty label",
- "type": "string",
- "default": "",
- "description": "Textual label for first row, eg: 'All' or 'None'. ID will be ''",
- translate:true
- }
- },
-
- legacyOptions: ["empty_label"],
-
- /**
- * Constructor
- *
- * @param _parent
- * @param options
- * @memberOf et2_selectAccount_ro
- */
- init: function(_parent, options) {
- /**
- Resolve some circular dependency problems here
- selectAccount extends link, link is in a file that needs select,
- select has menulist wrapper, which needs to know about selectAccount before it allows it
- */
- if(_parent.supportedWidgetClasses.indexOf(et2_selectAccount_ro) < 0)
- {
- _parent.supportedWidgetClasses.push(et2_selectAccount_ro);
- }
-
- this._super.apply(this, arguments);
-
- // Legacy options could have row count or empty label in first slot
- if(typeof this.options.empty_label == "string")
- {
- if(isNaN(this.options.empty_label))
- {
- this.options.empty_label = this.egw().lang(this.options.empty_label);
- }
- }
-
- this.options.application = 'api-accounts';
-
- // Editable version allows app to set options that aren't accounts, so allow for them
- var options = et2_selectbox.find_select_options(this,options['select_options']);
- if(!jQuery.isEmptyObject(options))
- {
- this.options.select_options = options;
- }
-
- // Don't make it look like a link though
- this.list.removeClass("et2_link_string").addClass("et2_selectbox");
- },
-
- transformAttributes: function(_attrs) {
- et2_selectbox.prototype.transformAttributes.apply(this, arguments);
- },
-
- set_value: function(_value) {
- // Explode csv
- if(typeof _value == 'string' && _value.indexOf(',') > 0)
- {
- _value = _value.split(',');
- }
-
- // Don't bother to lookup if it's not an array, or a number
- if(typeof _value == 'object' || !isNaN(_value) && _value != "")
- {
- this._super.apply(this, arguments);
- // Don't make it look like a link though
- jQuery('li',this.list).removeClass("et2_link et2_link_string")
- // No clicks either
- .off();
- return;
- }
-
- // Don't make it look like a link
- jQuery('li',this.list).removeClass("et2_link et2_link_string")
- // No clicks either
- .off();
-
- if(this.options.select_options && !jQuery.isEmptyObject(this.options.select_options) || this.options.empty_label)
- {
- if(!_value)
- {
- // Empty label from selectbox
- this.list.append(""+this.options.empty_label+"");
- }
- else if (typeof _value == 'object')
- {
- // An array with 0 / empty in it?
- for(var i = 0; i < _value.length; i++)
- {
- if(!_value[i] || !parseInt(_value[i]))
- {
- this.list.append(""+this.options.empty_label+"");
- return;
- }
- else if (this.options.select_options[_value])
- {
- this.list.append(""+this.options.select_options[_value]+"");
- }
- }
- }
- else
- {
- // Options are not indexed, so we must look
- var search = _value;
- if (!jQuery.isArray(search))
- {
- search = [_value];
- }
- for(var j = 0; j < search.length; j++)
- {
- var found = false;
-
- // Not having a value to look up causes an infinite loop
- if(!search[j]) continue;
-
- for(var i in this.options.select_options)
- {
- if(this.options.select_options[i].value == search[j])
- {
- this.list.append(""+this.options.select_options[i].label+"");
- break;
- }
- }
-
- }
- }
- }
- }
-});}).call(this);
-et2_register_widget(et2_selectAccount_ro, ["select-account_ro"]);
+var et2_selectAccount_ro = /** @class */ (function (_super) {
+ __extends(et2_selectAccount_ro, _super);
+ /**
+ * Constructor
+ */
+ function et2_selectAccount_ro(_parent, _attrs, _child) {
+ var _this = _super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_selectAccount_ro._attributes, _child || {})) || this;
+ _this.legacyOptions = ["empty_label"];
+ /**
+ Resolve some circular dependency problems here
+ selectAccount extends link, link is in a file that needs select,
+ select has menulist wrapper, which needs to know about selectAccount before it allows it
+ */
+ if (_parent.supportedWidgetClasses.indexOf(et2_selectAccount_ro) < 0) {
+ _parent.supportedWidgetClasses.push(et2_selectAccount_ro);
+ }
+ // Legacy options could have row count or empty label in first slot
+ if (typeof _this.options.empty_label == "string") {
+ if (isNaN(_this.options.empty_label)) {
+ _this.options.empty_label = _this.egw().lang(_this.options.empty_label);
+ }
+ }
+ _this.options.application = 'api-accounts';
+ // Editable version allows app to set options that aren't accounts, so allow for them
+ var options = et2_widget_selectbox_1.et2_selectbox.find_select_options(_this, _attrs['select_options'], _this.options);
+ if (!jQuery.isEmptyObject(options)) {
+ _this.options.select_options = options;
+ }
+ // Don't make it look like a link though
+ _this.list.removeClass("et2_link_string").addClass("et2_selectbox");
+ return _this;
+ }
+ et2_selectAccount_ro.prototype.transformAttributes = function (_attrs) {
+ et2_widget_selectbox_1.et2_selectbox.prototype.transformAttributes.apply(this, arguments);
+ };
+ et2_selectAccount_ro.prototype.set_value = function (_value) {
+ // Explode csv
+ if (typeof _value == 'string' && _value.indexOf(',') > 0) {
+ _value = _value.split(',');
+ }
+ // Don't bother to lookup if it's not an array, or a number
+ if (typeof _value == 'object' || !isNaN(_value) && _value != "") {
+ _super.prototype.set_value.call(this, _value);
+ // Don't make it look like a link though
+ jQuery('li', this.list).removeClass("et2_link et2_link_string")
+ // No clicks either
+ .off();
+ return;
+ }
+ // Don't make it look like a link
+ jQuery('li', this.list).removeClass("et2_link et2_link_string")
+ // No clicks either
+ .off();
+ if (this.options.select_options && !jQuery.isEmptyObject(this.options.select_options) || this.options.empty_label) {
+ if (!_value) {
+ // Empty label from selectbox
+ this.list.append("" + this.options.empty_label + "");
+ }
+ else if (typeof _value == 'object') {
+ // An array with 0 / empty in it?
+ for (var i = 0; i < _value.length; i++) {
+ if (!_value[i] || !parseInt(_value[i])) {
+ this.list.append("" + this.options.empty_label + "");
+ return;
+ }
+ else if (this.options.select_options[_value]) {
+ this.list.append("" + this.options.select_options[_value] + "");
+ }
+ }
+ }
+ else {
+ // Options are not indexed, so we must look
+ var search = _value;
+ if (!jQuery.isArray(search)) {
+ search = [_value];
+ }
+ for (var j = 0; j < search.length; j++) {
+ var found = false;
+ // Not having a value to look up causes an infinite loop
+ if (!search[j])
+ continue;
+ for (var i in this.options.select_options) {
+ if (this.options.select_options[i].value == search[j]) {
+ this.list.append("" + this.options.select_options[i].label + "");
+ break;
+ }
+ }
+ }
+ }
+ }
+ };
+ et2_selectAccount_ro._attributes = {
+ "empty_label": {
+ "name": "Empty label",
+ "type": "string",
+ "default": "",
+ "description": "Textual label for first row, eg: 'All' or 'None'. ID will be ''",
+ translate: true
+ }
+ };
+ return et2_selectAccount_ro;
+}(et2_link_string));
+exports.et2_selectAccount_ro = et2_selectAccount_ro;
+et2_core_widget_1.et2_register_widget(et2_selectAccount_ro, ["select-account_ro"]);
+//# sourceMappingURL=et2_widget_selectAccount.js.map
\ No newline at end of file
diff --git a/api/js/etemplate/et2_widget_selectAccount.ts b/api/js/etemplate/et2_widget_selectAccount.ts
new file mode 100644
index 0000000000..8cf6ae109b
--- /dev/null
+++ b/api/js/etemplate/et2_widget_selectAccount.ts
@@ -0,0 +1,891 @@
+/**
+ * EGroupware eTemplate2 - JS Select account widget
+ *
+ * Selecting accounts needs special UI, and displaying needs special consideration
+ * to avoid sending the entire user list to the client.
+ *
+ * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
+ * @package etemplate
+ * @subpackage api
+ * @link http://www.egroupware.org
+ * @author Nathan Gray
+ * @copyright Nathan Gray 2012
+ * @version $Id$
+ */
+
+/*egw:uses
+ et2_widget_link;
+*/
+
+import {et2_selectbox} from "./et2_widget_selectbox";
+import {et2_register_widget, WidgetConfig} from "./et2_core_widget";
+import {ClassWithAttributes} from "./et2_core_inheritance";
+import {et2_link_entry} from "./et2_widget_link";
+import {et2_dialog} from "./et2_widget_dialog";
+
+/**
+ * Account selection widget
+ * Changes according to the user's account_selection preference
+ * - 'none' => Server-side: the read-only widget is used, and no values are sent or displayed
+ * - 'groupmembers' => Non admins can only select groupmembers (Server side - normal selectbox)
+ * - 'selectbox' => Selectbox with all accounts and groups (Server side - normal selectbox)
+ * - 'primary_group' => Selectbox with primary group and search
+ *
+ * Only primary_group and popup need anything different from a normal selectbox
+ *
+ */
+export class et2_selectAccount extends et2_selectbox
+{
+ static readonly _attributes : any = {
+ 'account_type': {
+ 'name': 'Account type',
+ 'default': 'accounts',
+ 'type': 'string',
+ 'description': 'Limit type of accounts. One of {accounts,groups,both,owngroups}.'
+ }
+ };
+
+ public readonly legacyOptions = ['empty_label','account_type'];
+
+ public static readonly account_types = ['accounts','groups','both','owngroups'];
+ private search: JQuery;
+ private dialog: et2_dialog;
+ private widgets: any;
+ private search_widget: et2_link_entry;
+
+ /**
+ * Constructor
+ *
+ */
+ constructor(_parent : et2_widget, _attrs? : WidgetConfig, _child? : object)
+ {
+ super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_selectAccount._attributes, _child || {}));
+
+ // Type in rows or somewhere else?
+ if(jQuery.inArray(_attrs['empty_label'], et2_selectAccount.account_types) > 0 && (
+ jQuery.inArray(_attrs['account_type'], et2_selectAccount.account_types) < 0 ||
+ _attrs['account_type'] == et2_selectAccount._attributes.account_type['default'])
+ )
+ {
+ _attrs['account_type'] = _attrs['empty_label'];
+ _attrs['empty_label'] = '';
+ }
+ if(jQuery.inArray(_attrs['account_type'], et2_selectAccount.account_types) < 0)
+ {
+ this.egw().debug("warn", "Invalid account_type: %s Valid options:",_attrs['account_type'], et2_selectAccount.account_types);
+ }
+
+ // Holder for search jQuery nodes
+ this.search = null;
+
+ // Reference to dialog
+ this.dialog = null;
+
+ // Reference to widget within dialog
+ this.widgets = null;
+
+ if(!_attrs.empty_label && !_attrs.readonly && _attrs.multiple)
+ {
+ _attrs.empty_label = this.egw().lang('Select user or group');
+ }
+
+ // Allow certain widgets inside this one
+ this.supportedWidgetClasses = [et2_link_entry];
+ }
+
+ destroy( )
+ {
+ super.destroy.apply(this, arguments);
+ }
+
+ /**
+ * Single selection - override to add search button
+ */
+ createInputWidget()
+ {
+ var type = this.egw().preference('account_selection', 'common');
+
+ switch(type)
+ {
+ case 'none':
+ if(typeof egw.user('apps').admin == 'undefined')
+ {
+ this.options.select_options = {};
+ break;
+ }
+ case 'selectbox':
+ case 'groupmembers':
+ default:
+ this.options.select_options = this._get_accounts();
+ break;
+ }
+
+ super.createInputWidget();
+
+ // Add search button
+ if(type == 'primary_group')
+ {
+ var button = jQuery(document.createElement("span"))
+ .addClass("et2_clickable")
+ .click(this, jQuery.proxy(function(e) {
+ // Auto-expand
+ if(this.options.expand_multiple_rows && !this.options.multiple)
+ {
+ this.set_multiple(true, this.options.expand_multiple_rows);
+ }
+
+ if(this.options.multiple)
+ {
+ this._open_multi_search(e);
+ }
+ else
+ {
+ this._open_search(e);
+ }
+ },this))
+ .attr("title", egw.lang("popup with search"))
+ .append('');
+
+ this.getSurroundings().insertDOMNode(button[0]);
+ }
+ }
+
+ /**
+ * Multiple selection - override to add search button
+ */
+ createMultiSelect( )
+ {
+
+ var type = this.egw().preference('account_selection', 'common');
+ if(type == 'none' && typeof egw.user('apps').admin == 'undefined') return;
+
+ super.createMultiSelect();
+
+ this.options.select_options = this._get_accounts();
+
+ if(type == 'primary_group')
+ {
+ // Allow search 'inside' this widget
+ this.supportedWidgetClasses = [et2_link_entry];
+
+ // Add quick search - turn off multiple to get normal result list
+ this.options.multiple = false;
+ this._create_search();
+
+ // Clear search box after select
+ var old_select = this.search_widget.select;
+ var self = this;
+ // @ts-ignore
+ this.search_widget.select = function(e, selected) {
+ var current = self.getValue();
+
+ // Fix ID as sent from server - must be numeric
+ selected.item.value = parseInt(selected.item.value);
+
+ // This one is important, it makes sure the option is there
+ old_select.apply(this, arguments);
+
+ // Add quick search selection into current selection
+ current.push(selected.item.value);
+
+ // Clear search
+ this.search.val('');
+
+ self.set_value(current);
+ };
+
+ // Put search results as a DOM sibling of the options, for proper display
+ this.search_widget.search.on("autocompleteopen", jQuery.proxy(function() {
+ this.search_widget.search.data("ui-autocomplete").menu.element
+ .appendTo(this.node)
+ .position({my: 'left top', at: 'left bottom', of: this.multiOptions.prev()});
+ },this));
+ this.search = jQuery(document.createElement("li"))
+ .appendTo(this.multiOptions.prev().find('ul'));
+ this.options.multiple = true;
+
+ // Add search button
+ var button = jQuery(document.createElement("li"))
+ .addClass("et2_clickable")
+ .click(this, this._open_multi_search)
+ .attr("title", egw.lang("popup with search"))
+ .append('');
+ var type = this.egw().preference('account_selection', 'common');
+
+ // Put it last so check/uncheck doesn't move around
+ this.multiOptions.prev().find('ul')
+ .append(button);
+ }
+ }
+
+ /**
+ * Override parent to make sure accounts are there as options.
+ *
+ * Depending on the widget's attributes and the user's preferences, not all selected
+ * accounts may be in the cache as options, so we fetch the extras to make sure
+ * we don't lose any.
+ *
+ * As fetching them might only work asynchron (if they are not yet loaded),
+ * we have to call set_value again, once all labels have arrived from server.
+ *
+ * @param {string|array} _value
+ */
+ set_value(_value)
+ {
+ if(typeof _value == "string" && this.options.multiple && _value.match(this._is_multiple_regexp) !== null)
+ {
+ _value = _value.split(',');
+ }
+
+ if(_value)
+ {
+ var search = _value;
+ if (!jQuery.isArray(search))
+ {
+ search = [_value];
+ }
+ var update_options = false;
+ var num_calls = 0;
+ var current_call = 0;
+ for(var j = 0; j < search.length; j++)
+ {
+ var found = false;
+
+ // Not having a value to look up causes an infinite loop
+ if(!search[j] || search[j] === "0") continue;
+
+ // Options are not indexed, so we must look
+ for(var i = 0; !found && i < this.options.select_options.length; i++)
+ {
+ if (typeof this.options.select_options[i] != 'object')
+ {
+ egw.debug('warn',this.id + ' wrong option '+i+' this.options.select_options=', this.options.select_options);
+ continue;
+ }
+ if(this.options.select_options[i].value == search[j]) found = true;
+ }
+ // We only look for numeric IDs, non-numeric IDs cause an exception
+ if(!found && !isNaN(search[j]))
+ {
+ // Add it in
+ var name = this.egw().link_title('api-accounts', search[j]);
+ if (name) // was already cached on client-side
+ {
+ update_options = true;
+ this.options.select_options.push({value: search[j], label:name});
+ }
+ else // not available: need to call set_value again, after all arrived from server
+ {
+ ++num_calls;
+ // Add immediately with value as label, we'll replace later
+ this._appendOptionElement(search[j],search[j]);
+ this.egw().link_title('api-accounts', search[j], function(name)
+ {
+ if (++current_call >= num_calls) // only run last callback
+ {
+ // Update the label
+ // Options are not indexed, so we must look
+ for(var i = 0; i < this.widget.options.select_options.length; i++)
+ {
+ var opt = this.widget.options.select_options[i];
+ if(opt && opt.value && opt.value == this.unknown && opt.label == this.unknown)
+ {
+ opt.label = name;
+ this.widget.set_select_options(this.widget.options.select_options);
+ break;
+ }
+ }
+ this.widget.set_value(_value);
+ }
+ }, {widget: this, unknown: search[j]});
+ }
+ }
+ }
+ if(update_options)
+ {
+ this.set_select_options(this.options.select_options);
+ }
+ }
+ super.set_value(_value);
+ }
+
+ /**
+ * Get account info for select options from common client-side account cache
+ *
+ * @return {Array} select options
+ */
+ _get_accounts()
+ {
+ if (!jQuery.isArray(this.options.select_options))
+ {
+ var options = jQuery.extend({}, this.options.select_options);
+ this.options.select_options = [];
+ for(var key in options)
+ {
+ if (typeof options[key] == 'object')
+ {
+ if (typeof(options[key].key) == 'undefined')
+ {
+ options[key].value = key;
+ }
+ this.options.select_options.push(options[key]);
+ }
+ else
+ {
+ this.options.select_options.push({value: key, label: options[key]});
+ }
+ }
+ }
+ var type = this.egw().preference('account_selection', 'common');
+ var accounts = [];
+ // for primary_group we only display owngroups == own memberships, not other groups
+ if (type == 'primary_group' && this.options.account_type != 'accounts')
+ {
+ if (this.options.account_type == 'both')
+ {
+ accounts = this.egw().accounts('accounts');
+ }
+ accounts = accounts.concat(this.egw().accounts('owngroups'));
+ }
+ else
+ {
+ accounts = this.egw().accounts(this.options.account_type);
+ }
+ return this.options.select_options.concat(accounts);
+ }
+
+ /**
+ * Create & display a way to search & select a single account / group
+ * Single selection is just link widget
+ *
+ * @param e event
+ */
+ _open_search( e)
+ {
+ var widget = e.data;
+ var search = widget._create_search();
+
+ // Selecting a single user closes the dialog, this only used if user cleared
+ var ok_click = function() {
+ widget.set_value([]);
+ // Fire change event
+ if(widget.input) widget.input.trigger("change");
+ jQuery(this).dialog("close");
+ };
+ widget._create_dialog(search, ok_click);
+ }
+
+ /**
+ * Create & display a way to search & select multiple accounts / groups
+ *
+ * @param e event
+ */
+ _open_multi_search( e)
+ {
+ var widget = e && e.data ? e.data : this;
+ var table = widget.search = jQuery('');
+ table.css("width", "100%").css("height", "100%");
+ var search_col = jQuery('#search_col',table);
+ var select_col = jQuery('#selection_col',table);
+
+ // Search / Selection
+ search_col.append(widget._create_search());
+
+ // Currently selected
+ select_col.append(widget._create_selected());
+
+ var ok_click = function() {
+ jQuery(this).dialog("close");
+ // Update widget with selected
+ var ids = [];
+ var data = {};
+ jQuery('#'+widget.getInstanceManager().uniqueId + '_selected li',select_col).each(function() {
+ var id = jQuery(this).attr("data-id");
+ // Add to list
+ ids.push(id);
+
+ // Make sure option is there
+ if(!widget.options.multiple && jQuery('input[id$="_opt_'+id+'"]',widget.multiOptions).length == 0)
+ {
+ widget._appendMultiOption(id,jQuery('label',this).text());
+ }
+ else if (widget.options.multiple && jQuery('option[value="'+id+'"]',widget.node).length == 0)
+ {
+ widget._appendOptionElement(id,jQuery('label',this).text());
+ }
+ });
+
+ widget.set_value(ids);
+
+ // Fire change event
+ if(widget.input) widget.input.trigger("change");
+ };
+
+ var container = jQuery(document.createElement("div")).append(table);
+ return widget._create_dialog(container, ok_click);
+ }
+
+ /**
+ * Create / display popup with search / selection widgets
+ *
+ * @param {et2_dialog} widgets
+ * @param {function} update_function
+ */
+ _create_dialog( widgets, update_function)
+ {
+ this.widgets = widgets;
+ this.dialog = et2_dialog.show_dialog(false,
+ '',
+ this.options.label ? this.options.label : this.egw().lang('Select'),
+ {},
+ [{
+ text: this.egw().lang("ok"),
+ image: 'check',
+ click: update_function
+ },{
+ text: this.egw().lang("cancel"),
+ image: 'cancel'
+ }]
+ );
+ this.dialog.set_dialog_type('');
+ // Static size for easier layout
+ this.dialog.div.dialog({width: "500", height: "370"});
+
+ this.dialog.div.append(widgets.width('100%'));
+ return widgets;
+ }
+
+ /**
+ * Search is a link-entry widget, with some special display for multi-select
+ */
+ _create_search( )
+ {
+ var self = this;
+ var search = this.search = jQuery(document.createElement("div"));
+
+ var search_widget = this.search_widget = et2_createWidget('link-entry', {
+ 'only_app': 'api-accounts',
+ 'query'( request, response)
+ {
+ // Clear previous search results for multi-select
+ if(!request.options)
+ {
+ search.find('#search_results').empty();
+ }
+ // Restrict to specified account type
+ if(!request.options || !request.options.filter)
+ {
+ request.options = {account_type: self.options.account_type};
+ }
+ return true;
+ },
+ 'select'( e, selected)
+ {
+ // Make sure option is there
+ var already_there = false;
+ var last_key = null;
+ for(last_key in self.options.select_options)
+ {
+ var option = self.options.select_options[last_key];
+ already_there = already_there || (typeof option.value != 'undefined' && option.value == selected.item.value);
+ }
+ if(!already_there)
+ {
+ self.options.select_options[parseInt(last_key)+1] = selected.item;
+ self._appendOptionElement(selected.item.value, selected.item.label);
+ }
+ self.set_value(selected.item.value);
+ if(self.dialog)
+ {
+ self.dialog.div.dialog("close");
+ }
+ // Fire change event
+ if(self.input) self.input.trigger("change");
+ return true;
+ }
+ }, this);
+ // add it where we want it
+ search.append(search_widget.getDOMNode());
+
+ if(!this.options.multiple) return search;
+
+ // Multiple is more complicated. It uses a custom display for results to
+ // allow choosing multiples from a match
+ var results = jQuery(document.createElement("ul"))
+ .attr("id", "search_results")
+ .css("height", "230px")
+ .addClass("ui-multiselect-checkboxes ui-helper-reset");
+ jQuery(document.createElement("div"))
+ .addClass("et2_selectbox")
+ .css("height", "100%")
+ .append(results)
+ .appendTo(search);
+
+ // Override link-entry auto-complete for custom display
+ // Don't show normal drop-down
+ search_widget.search.data("ui-autocomplete")._suggest = function(items) {
+ jQuery.each(items, function (index, item) {
+ // Make sure value is numeric
+ item.value = parseInt(item.value);
+ self._add_search_result(results, item);
+ });
+ };
+
+ return search;
+ }
+
+ /**
+ * Add the selected result to the list of search results
+ *
+ * @param list
+ * @param item
+ */
+ _add_search_result( list, item)
+ {
+
+ var node = null;
+ var self = this;
+
+ // Make sure value is numeric
+ if(item.value) item.value = parseInt(item.value);
+
+ // (containter of) Currently selected users / groups
+ var selected = jQuery('#'+this.getInstanceManager().uniqueId + "_selected", this.widgets);
+
+ // Group
+ if(item.value && item.value < 0)
+ {
+ node = jQuery(document.createElement('ul'));
+ // Add button to show users
+ if(this.options.account_type != 'groups')
+ {
+ jQuery('')
+ .css("float", "left")
+ .appendTo(node)
+ .click(function() {
+ if(jQuery(this).hasClass("ui-icon-circlesmall-plus"))
+ {
+ jQuery(this).removeClass("ui-icon-circlesmall-plus")
+ .addClass("ui-icon-circlesmall-minus");
+
+ var group = jQuery(this).parent()
+ .addClass("expanded");
+
+ if(group.children("li").length == 0)
+ {
+ // Fetch group members
+ self.search_widget.query({
+ term:"",
+ options: {filter:{group: item.value}},
+ no_cache:true
+ }, function(items) {
+ jQuery(items).each(function(index,item) {
+ self._add_search_result(node, item);
+ });
+ });
+ }
+ else
+ {
+ group.children("li")
+ // Only show children that are not selected
+ .each(function(index, item) {
+ var j = jQuery(item);
+ if(jQuery('[data-id="'+j.attr("data-id")+'"]',selected).length == 0)
+ {
+ j.show();
+ }
+ });
+ }
+ }
+ else
+ {
+ jQuery(this).addClass("ui-icon-circlesmall-plus")
+ .removeClass("ui-icon-circlesmall-minus");
+
+ var group = jQuery(this).parent().children("li").hide();
+ }
+ });
+ }
+
+ }
+ // User
+ else if (item.value)
+ {
+ node = jQuery(document.createElement('li'));
+ }
+ node.attr("data-id", item.value);
+
+ jQuery('')
+ .css("float", "right")
+ .appendTo(node)
+ .click(function() {
+ var button = jQuery(this);
+ self._add_selected(selected, button.parent().attr("data-id"));
+ // Hide user, but only hide button for group
+ if(button.parent().is('li'))
+ {
+ button.parent().hide();
+ }
+ else
+ {
+ button.hide();
+ }
+ });
+
+ // If already in list, hide it
+ if(jQuery('[data-id="'+item.value+'"]',selected).length != 0)
+ {
+ node.hide();
+ }
+
+ var label = jQuery(document.createElement('label'))
+ .addClass("loading")
+ .appendTo(node);
+
+ this.egw().link_title('api-accounts', item.value, function(name) {
+ label.text(name).removeClass("loading");
+ }, label);
+
+ node.appendTo(list);
+ }
+
+ _create_selected( )
+ {
+ var node = jQuery(document.createElement("div"))
+ .addClass("et2_selectbox");
+
+ var header = jQuery(document.createElement("div"))
+ .addClass("ui-widget-header ui-helper-clearfix")
+ .appendTo(node);
+
+ var selected = jQuery(document.createElement("ul"))
+ .addClass("ui-multiselect-checkboxes ui-helper-reset")
+ .attr("id", this.getInstanceManager().uniqueId + "_selected")
+ .css("height", "230px")
+ .appendTo(node);
+
+ jQuery(document.createElement("span"))
+ .text(this.egw().lang("Selection"))
+ .addClass("ui-multiselect-header")
+ .appendTo(header);
+
+ var controls = jQuery(document.createElement("ul"))
+ .addClass('ui-helper-reset')
+ .appendTo(header);
+
+ jQuery(document.createElement("li"))
+ .addClass("et2_clickable")
+ .click(selected, function(e) {jQuery("li",e.data).remove();})
+ .append('')
+ .appendTo(controls);
+
+ // Add in currently selected
+ if(this.getValue())
+ {
+ var value = this.getValue();
+ for(var i = 0; i < value.length; i++) {
+ this._add_selected(selected, value[i]);
+ }
+ }
+ return node;
+ }
+
+ /**
+ * Add an option to the list of selected accounts
+ * value is the account / group ID
+ *
+ * @param list
+ * @param value
+ */
+ _add_selected( list, value)
+ {
+
+ // Each option only once
+ var there = jQuery('[data-id="' + value + '"]',list);
+ if(there.length)
+ {
+ there.show();
+ return;
+ }
+
+ var option = jQuery(document.createElement('li'))
+ .attr("data-id",value)
+ .appendTo(list);
+ jQuery('')
+ .css("float", "right")
+ .appendTo(option)
+ .click(function() {
+ var id = jQuery(this).parent().attr("data-id");
+ jQuery(this).parent().remove();
+ // Add 'add' button back, if in results list
+ list.parents("tr").find("[data-id='"+id+"']").show()
+ // Show button(s) for group
+ .children('span').show();
+ });
+
+ var label = jQuery(document.createElement('label'))
+ .addClass("loading")
+ .appendTo(option);
+ this.egw().link_title('api-accounts', value, function(name) {this.text(name).removeClass("loading");}, label);
+ }
+
+ /**
+ * Overwritten attachToDOM method to modify attachToDOM
+ */
+ attachToDOM()
+ {
+ let result = super.attachToDOM();
+ //Chosen needs to be set after widget dettached from DOM (eg. validation_error), because chosen is not part of the widget node
+ if (this.egw().preference('account_selection', 'common') == 'primary_group')
+ {
+ jQuery(this.node).removeClass('chzn-done');
+ this.set_tags(this.options.tags, this.options.width);
+ }
+
+ return result;
+ }
+}
+et2_register_widget(et2_selectAccount, ["select-account"]);
+
+/**
+ * et2_selectAccount_ro is the readonly implementation of select account
+ * It extends et2_link to avoid needing the whole user list on the client.
+ * Instead, it just asks for the names of the ones needed, as needed.
+ *
+ * @augments et2_link_string
+ */
+export class et2_selectAccount_ro extends et2_link_string
+{
+ static readonly _attributes : any = {
+ "empty_label": {
+ "name": "Empty label",
+ "type": "string",
+ "default": "",
+ "description": "Textual label for first row, eg: 'All' or 'None'. ID will be ''",
+ translate:true
+ }
+ };
+
+ legacyOptions : string[] = ["empty_label"];
+
+ /**
+ * Constructor
+ */
+ constructor(_parent : et2_widget, _attrs? : WidgetConfig, _child? : object)
+ {
+ super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_selectAccount_ro._attributes, _child || {}));
+
+ /**
+ Resolve some circular dependency problems here
+ selectAccount extends link, link is in a file that needs select,
+ select has menulist wrapper, which needs to know about selectAccount before it allows it
+ */
+ if(_parent.supportedWidgetClasses.indexOf(et2_selectAccount_ro) < 0)
+ {
+ _parent.supportedWidgetClasses.push(et2_selectAccount_ro);
+ }
+
+ // Legacy options could have row count or empty label in first slot
+ if(typeof this.options.empty_label == "string")
+ {
+ if(isNaN(this.options.empty_label))
+ {
+ this.options.empty_label = this.egw().lang(this.options.empty_label);
+ }
+ }
+
+ this.options.application = 'api-accounts';
+
+ // Editable version allows app to set options that aren't accounts, so allow for them
+ let options = et2_selectbox.find_select_options(this,_attrs['select_options'], this.options);
+ if(!jQuery.isEmptyObject(options))
+ {
+ this.options.select_options = options;
+ }
+
+ // Don't make it look like a link though
+ this.list.removeClass("et2_link_string").addClass("et2_selectbox");
+ }
+
+ transformAttributes( _attrs)
+ {
+ et2_selectbox.prototype.transformAttributes.apply(this, arguments);
+ }
+
+ set_value( _value)
+ {
+ // Explode csv
+ if(typeof _value == 'string' && _value.indexOf(',') > 0)
+ {
+ _value = _value.split(',');
+ }
+
+ // Don't bother to lookup if it's not an array, or a number
+ if(typeof _value == 'object' || !isNaN(_value) && _value != "")
+ {
+ super.set_value(_value);
+ // Don't make it look like a link though
+ jQuery('li',this.list).removeClass("et2_link et2_link_string")
+ // No clicks either
+ .off();
+ return;
+ }
+
+ // Don't make it look like a link
+ jQuery('li',this.list).removeClass("et2_link et2_link_string")
+ // No clicks either
+ .off();
+
+ if(this.options.select_options && !jQuery.isEmptyObject(this.options.select_options) || this.options.empty_label)
+ {
+ if(!_value)
+ {
+ // Empty label from selectbox
+ this.list.append(""+this.options.empty_label+"");
+ }
+ else if (typeof _value == 'object')
+ {
+ // An array with 0 / empty in it?
+ for(let i = 0; i < _value.length; i++)
+ {
+ if(!_value[i] || !parseInt(_value[i]))
+ {
+ this.list.append(""+this.options.empty_label+"");
+ return;
+ }
+ else if (this.options.select_options[_value])
+ {
+ this.list.append(""+this.options.select_options[_value]+"");
+ }
+ }
+ }
+ else
+ {
+ // Options are not indexed, so we must look
+ var search = _value;
+ if (!jQuery.isArray(search))
+ {
+ search = [_value];
+ }
+ for(let j = 0; j < search.length; j++)
+ {
+ var found = false;
+
+ // Not having a value to look up causes an infinite loop
+ if(!search[j]) continue;
+
+ for(let i in this.options.select_options)
+ {
+ if(this.options.select_options[i].value == search[j])
+ {
+ this.list.append(""+this.options.select_options[i].label+"");
+ break;
+ }
+ }
+
+ }
+ }
+ }
+ }
+}
+et2_register_widget(et2_selectAccount_ro, ["select-account_ro"]);
diff --git a/api/js/etemplate/et2_widget_selectbox.js b/api/js/etemplate/et2_widget_selectbox.js
index c63e1c4a8d..f35684d00b 100644
--- a/api/js/etemplate/et2_widget_selectbox.js
+++ b/api/js/etemplate/et2_widget_selectbox.js
@@ -117,7 +117,7 @@ var et2_selectbox = /** @class */ (function (_super) {
typeof _attrs.select_options == 'undefined' || _attrs.select_options === null) {
// do not return inside nextmatch, as get_rows data might have changed select_options
// for performance reasons we only do it for first row, which should have id "0[...]"
- if (this.getParent().getType() != 'rowWidget' || !_attrs.id || _attrs.id[0] != '0')
+ if (this.getParent() && this.getParent().getType() != 'rowWidget' || !_attrs.id || _attrs.id[0] != '0')
return;
}
var sel_options = et2_selectbox.find_select_options(this, _attrs['select_options'], _attrs);
diff --git a/api/js/etemplate/et2_widget_selectbox.ts b/api/js/etemplate/et2_widget_selectbox.ts
index 4a9eecfcf8..e11a7a016f 100644
--- a/api/js/etemplate/et2_widget_selectbox.ts
+++ b/api/js/etemplate/et2_widget_selectbox.ts
@@ -201,7 +201,7 @@ export class et2_selectbox extends et2_inputWidget
{
// do not return inside nextmatch, as get_rows data might have changed select_options
// for performance reasons we only do it for first row, which should have id "0[...]"
- if (this.getParent().getType() != 'rowWidget' || !_attrs.id || _attrs.id[0] != '0') return;
+ if (this.getParent() && this.getParent().getType() != 'rowWidget' || !_attrs.id || _attrs.id[0] != '0') return;
}
var sel_options = et2_selectbox.find_select_options(this, _attrs['select_options'], _attrs);