diff --git a/etemplate/inc/class.etemplate_widget_menupopup.inc.php b/etemplate/inc/class.etemplate_widget_menupopup.inc.php
index 31812a89cc..4d2545915f 100644
--- a/etemplate/inc/class.etemplate_widget_menupopup.inc.php
+++ b/etemplate/inc/class.etemplate_widget_menupopup.inc.php
@@ -120,6 +120,13 @@ class etemplate_widget_menupopup extends etemplate_widget
if (!is_array(self::$request->sel_options[$form_name])) self::$request->sel_options[$form_name] = array();
if ($this->attrs['type'])
{
+ // Check selection preference, we may be able to skip reading some data
+ $select_pref = $GLOBALS['egw_info']['user']['preferences']['common']['account_selection'];
+ if(!$GLOBALS['egw_info']['apps']['admin'] && $select_pref == 'none')
+ {
+ $this->attrs['readonly'] = true;
+ }
+
// += to keep further options set by app code
self::$request->sel_options[$form_name] += self::typeOptions($this->attrs['type'],
// typeOptions thinks # of rows is the first thing in options
@@ -300,7 +307,10 @@ class etemplate_widget_menupopup extends etemplate_widget
'title' => empty($cat['description']) ? $s : $cat['description'],
);
// Send data too
- $options[$cat['id']] += $cat['data'];
+ if(is_array($cat['data']))
+ {
+ $options[$cat['id']] += $cat['data'];
+ }
}
// preserv unavailible cats (eg. private user-cats)
/* TODO
@@ -316,8 +326,12 @@ class etemplate_widget_menupopup extends etemplate_widget
case 'select-account': // options: #rows,{accounts(default)|both|groups|owngroups},{0(=lid)|1(default=name)|2(=lid+name),expand-multiselect-rows,not-to-show-accounts,...)}
//echo "
select-account widget: name=$cell[name], type='$type', rows=$rows, readonly=".(int)($cell['readonly'] || $readonlys)."
\n";
+
+ // Get preference for selection display
+ $select_pref = $GLOBALS['egw_info']['user']['preferences']['common']['account_selection'];
+
// in case of readonly, we read/create only the needed entries, as reading accounts is expensive
- if ($readonly)
+ if ($readonly || $select_pref == 'popup')
{
$no_lang = True;
if (!is_array($value) && strpos($value,',') !== false) $value = explode(',',$value);
@@ -328,42 +342,21 @@ class etemplate_widget_menupopup extends etemplate_widget
}
break;
}
- if($type == 'owngroups')
+ if($type == 'owngroups' || $select_pref == 'groupmembers')
{
$type = 'groups';
$owngroups = true;
foreach($GLOBALS['egw']->accounts->membership() as $group) $mygroups[] = $group['account_id'];
}
- /* account-selection for hughe number of accounts
- if ($this->ui == 'html' && $type != 'groups') // use eGW's new account-selection (html only)
+ elseif (in_array($type, array('accounts', 'both')) && $select_pref == 'primary_group')
{
- $not = array_slice(explode(',',$cell['size']),4);
- $help = (int)$no_lang < 2 ? lang($cell['help']) : $cell['help'];
- $onFocus = "self.status='".addslashes(htmlspecialchars($help))."'; return true;";
- $onBlur = "self.status=''; return true;";
- if ($cell['noprint'])
- {
- foreach(is_array($value) ? $value : (strpos($value,',') !== false ? explode(',',$value) : array($value)) as $id)
- {
- if ($id) $onlyPrint[] = self::accountInfo($id,$acc,$type2,$type=='both');
- }
- $onlyPrint = $onlyPrint ? implode('
',$onlyPrint) : lang((int)$rows < 0 ? 'all' : $rows);
- $noPrint_class = ' class="noPrint"';
- }
- if (($rows > 0 || $type3) && substr($name,-2) != '[]') $name .= '[]';
- $value = $GLOBALS['egw']->uiaccountsel->selection($name,'eT_accountsel_'.str_replace(array('[','][',']'),array('_','_',''),$name),
- $value,$type,$rows > 0 ? $rows : ($type3 ? -$type3 : 0),$not,' onfocus="'.$onFocus.'" onblur="'.$onBlur.'"'.$noPrint_class,
- $cell['onchange'] == '1' ? 'this.form.submit();' : $cell['onchange'],
- !empty($rows) && 0+$rows <= 0 ? lang($rows < 0 ? 'all' : $rows) : False);
- if ($cell['noprint'])
- {
- $value = ''.$onlyPrint.''.$value;
- }
- $cell['type'] = 'html';
- $cell['size'] = ''; // is interpreted as link otherwise
- etemplate::$request->set_to_process($name,'select');
- break;
- }*/
+ $owngroups = true;
+ $mygroups[] = $GLOBALS['egw_info']['user']['primary_group'];
+ }
+
+ // Popup is the only preference option that doesn't need more select options
+ if($select_pref == 'popup') break;
+
$no_lang = True;
$accs = $GLOBALS['egw']->accounts->get_list(empty($type) ? 'accounts' : $type); // default is accounts
foreach($accs as $acc)
diff --git a/etemplate/js/et2_widget_selectAccount.js b/etemplate/js/et2_widget_selectAccount.js
index 5092b2c8e2..e4d3cafe62 100644
--- a/etemplate/js/et2_widget_selectAccount.js
+++ b/etemplate/js/et2_widget_selectAccount.js
@@ -19,6 +19,418 @@
et2_widget_link;
*/
+/**
+ * Account selection widget
+ * Changes according to the user's account_selection preference
+ * - 'none' => Server-side: the read-only widget is used
+ * - '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
+ * - 'popup' => Empty selectbox with search
+ *
+ * Only primary_group and popup need anything different from a normal selectbox
+ */
+var et2_selectAccount = 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'],
+
+ 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);
+ }
+
+ this._super.apply(this, arguments);
+
+ // Holder for search jQuery nodes
+ this.search = null;
+
+ // Reference to object with dialog
+ this.dialog = null;
+ },
+
+ destroy: function() {
+ this._super.apply(this, arguments);
+ },
+
+ /**
+ * Single selection - override to add search button
+ */
+ createInputWidget: function() {
+
+ this._super.apply(this, arguments);
+
+ // Add search button
+ var type = this.egw().preference('account_selection', 'common');
+ if(type == 'primary_group' || type == 'popup')
+ {
+ var button = jQuery(document.createElement("span"))
+ .addClass("et2_clickable")
+ .click(this, this._open_search)
+ .append('')
+
+ this.getSurroundings().insertDOMNode(button[0]);
+ }
+ },
+
+ /**
+ * Multiple selection - override to add search button
+ */
+ createMultiSelect: function() {
+ this._super.apply(this, arguments);
+
+ // Add search button
+ var button = jQuery(document.createElement("li"))
+ .addClass("et2_clickable")
+ .click(this, this._open_multi_search)
+ .append('')
+ .append(""+this.egw().lang('search')+"");
+ var type = this.egw().preference('account_selection', 'common');
+
+ // Put it first so check/uncheck don't move around
+ this.multiOptions.prev().show().find('ul')
+ .prepend(button);
+ },
+
+ /**
+ * Create & display a way to search & select a single account / group
+ * Single selection is just link widget
+ */
+ _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() {
+ jQuery(this).dialog("close");
+ widget.set_value([]);
+ // Free it up, it will be re-created, if ever needed again
+ jQuery(this).dialog("destroy");
+ };
+ widget._create_dialog(search, ok_click);
+ },
+
+ /**
+ * Create & display a way to search & select multiple accounts / groups
+ */
+ _open_multi_search: function(e) {
+ var widget = e.data;
+ 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('#selected li',select_col).each(function() {
+ // Remove sel_ prefix and add to list
+ ids.push(this.id.substr(4));
+
+ // Make sure option is there
+ if(jQuery('input[id$="_opt_'+this.id.substr(4)+'"]',widget.multiOptions).length == 0)
+ {
+ widget._appendMultiOption(this.id.substr(4),jQuery('label',this).text());
+ }
+ });
+
+ widget.set_value(ids);
+
+ // Free it up, it will be re-created, if ever needed again
+ jQuery(this).dialog("destroy");
+ };
+
+ var container = jQuery(document.createElement("div")).append(table);
+ return widget._create_dialog(container, ok_click);
+ },
+
+ /**
+ * Create / display popup with search / selection widgets
+ */
+ _create_dialog: function(widgets, update_function) {
+ this.dialog = widgets;
+ widgets.dialog({
+ title: this.options.label ? this.options.label : this.egw().lang('Select'),
+ modal: true,
+ // Static size for easier layout
+ width: "500",
+ height: "350",
+ buttons: [{
+ text: this.egw().lang("ok"),
+ click: update_function
+ },{
+ text: this.egw().lang("cancel"),
+ click: function() {
+ jQuery(this).dialog("close");
+ jQuery(this).dialog("destroy");
+ }}
+ ]
+ });
+ },
+
+ /**
+ * 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', {
+ 'application': 'home-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'] = {filter:{group:self.options.account_type}};
+ }
+ return true;
+ },
+ 'select': function(e, selected) {
+ // Make sure option is there
+ if(typeof self.options.select_options[selected.item.value] == 'undefined')
+ {
+ self.options.select_options[selected.item.value] = selected.item;
+ self._appendOptionElement(selected.item.value, selected.item.label);
+ }
+ self.set_value(selected.item.value);
+ if(self.dialog)
+ {
+ self.dialog.dialog("close");
+ self.dialog.dialog("destroy");
+ }
+ }
+ }, this);
+ // select widget doesn't allow children, so add 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("autocomplete")._suggest = function(items) {
+ jQuery.each(items, function (index, item) {
+ self._add_search_result(results, item);
+ });
+ }
+
+ return search;
+ },
+
+ /**
+ * Add the selected result to the list of search results
+ */
+ _add_search_result: function(list, item) {
+
+ var node = null;
+ var self = this;
+
+ // (containter of) Currently selected users / groups
+ var selected = jQuery('#selected', this.dialog);
+
+ // 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)
+ .toggle(function() {
+ 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}}}, 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('#sel_'+j.attr("id").substr(5),selected).length == 0)
+ {
+ j.show();
+ }
+ });
+ }
+ },function() {
+ 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("id", "list_"+item.value);
+
+ jQuery('')
+ .css("float", "right")
+ .appendTo(node)
+ .click(function() {
+ var button = jQuery(this);
+ self._add_selected(selected, button.parent().attr("id").substr(5));
+ // 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('#sel_'+item.value,selected).length != 0)
+ {
+ node.hide();
+ }
+
+ var label = jQuery(document.createElement('label'))
+ .addClass("loading")
+ .appendTo(node);
+ this.egw().link_title('home-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", "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('')
+ .append(""+this.egw().lang('Remove all')+"")
+ .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
+ */
+ _add_selected: function(list, value) {
+
+ var option = jQuery(document.createElement('li'))
+ .attr("id",'sel_'+value)
+ .appendTo(list);
+ jQuery('')
+ .css("float", "right")
+ .appendTo(option)
+ .click(function() {
+ var id = jQuery(this).parent().attr("id").substr(4);
+ jQuery(this).parent().remove();
+ // Add 'add' button back, if in results list
+ list.parents("tr").find("#list_"+id).show()
+ // Show button(s) for group
+ .children('span').show();
+ });
+
+ var label = jQuery(document.createElement('label'))
+ .addClass("loading")
+ .appendTo(option);
+ this.egw().link_title('home-accounts', value, function(name) {this.text(name).removeClass("loading")}, label);
+ }
+});
+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.
diff --git a/etemplate/templates/default/etemplate2.css b/etemplate/templates/default/etemplate2.css
index 1b10ea7d0f..6b36a10438 100644
--- a/etemplate/templates/default/etemplate2.css
+++ b/etemplate/templates/default/etemplate2.css
@@ -211,6 +211,18 @@ button.et2_button_text:focus, input[type=button]:focus {
list-style-type: none;
text-indent: -26px;
}
+.et2_selectbox .ui-multiselect-checkboxes ul {
+ margin: 0px auto;
+ padding-left: 0px;
+ clear:both;
+ text-decoration: none;
+ list-style-image: none;
+ list-style-type: none;
+}
+.et2_selectbox .ui-multiselect-checkboxes ul>label {
+ border-bottom: 1px solid black;
+}
+
.et2_selectbox .ui-multiselect-checkboxes label {
display: block;
border: 1px solid transparent;
@@ -234,6 +246,10 @@ ul.et2_selectbox {
list-style-type: none;
}
+/**
+ * Select account dialog
+ */
+
/**
* Date / Time widgets
*/