diff --git a/addressbook/inc/class.addressbook_ui.inc.php b/addressbook/inc/class.addressbook_ui.inc.php index db2cf51129..564b2cb557 100644 --- a/addressbook/inc/class.addressbook_ui.inc.php +++ b/addressbook/inc/class.addressbook_ui.inc.php @@ -198,7 +198,7 @@ class addressbook_ui extends addressbook_bo } $typeselection = $_content['nm']['col_filter']['tid']; } - elseif($_GET['add_list']) + elseif(!empty($_GET['add_list'])) { $list = $this->add_list($_GET['add_list'],$_GET['owner']?$_GET['owner']:$this->user); if ($list === true) @@ -214,6 +214,10 @@ class addressbook_ui extends addressbook_bo $msg = lang('List creation failed, no rights!'); } } + elseif (!empty($_GET['template']) && $this->tmpl->read($_GET['template'])) + { + $template = $_GET['template']; + } $preserv = array(); $content = array(); if($msg || $_GET['msg']) @@ -221,7 +225,7 @@ class addressbook_ui extends addressbook_bo Framework::message($msg ? $msg : $_GET['msg']); } - $content['nm'] = Api\Cache::getSession('addressbook', 'index'); + $content['nm'] = Api\Cache::getSession('addressbook', str_replace('addressbook.', '', $template ?? 'index')); if (!is_array($content['nm'])) { $content['nm'] = array( @@ -230,7 +234,8 @@ class addressbook_ui extends addressbook_bo 'never_hide' => True, // I never hide the nextmatch-line if less then maxmatch entrie 'start' => 0, // IO position in list 'cat_id' => '', // IO category, if not 'no_cat' => True - 'search' => '', // IO search pattern + 'search' => ($template ?? 'addressbook.index') === 'addressbook.select' ? '@' : '', // IO search pattern + 'main-template' => $template ?? 'addressbook.index', // do NOT use "template"! 'order' => 'n_family', // IO name of the column to sort after (optional for the sortheaders) 'sort' => 'ASC', // IO direction of the sort: 'ASC' or 'DESC' 'col_filter' => array(), // IO array of column-name value pairs (optional for the filterheaders) @@ -244,11 +249,15 @@ class addressbook_ui extends addressbook_bo 'filter2_no_lang'=> True, // I set no_lang for filter2 (=dont translate the options) 'lettersearch' => true, // using a positiv list now, as we constantly adding new columns in addressbook, but not removing them from default - 'default_cols' => 'type,n_fileas_n_given_n_family_n_family_n_given_org_name_n_family_n_given_n_fileas,'. + 'default_cols' => !isset($template) ? 'type,n_fileas_n_given_n_family_n_family_n_given_org_name_n_family_n_given_n_fileas,'. 'number,org_name,org_unit,'. - 'business_adr_one_countrycode_adr_one_postalcode,tel_work_tel_cell_tel_home,url_email_email_home', + 'business_adr_one_countrycode_adr_one_postalcode,tel_work_tel_cell_tel_home,url_email_email_home' : + '!photo,home_adr_two_countrycode_adr_two_postalcode', /* old negative list 'default_cols' => '!cat_id,contact_created_contact_modified,distribution_list,contact_id,owner,room',*/ + //'no_columnselection' => false, // I turns off the columnselection completly, turned on by default + // I name of the preference (plus 'nextmatch-' prefix), default = template-name + 'columnselection_pref' => isset($template) ? 'nextmatch-'.$template : null, 'filter2_onchange' => "return app.addressbook.filter2_onchange();", 'filter2_tags' => true, //'actions' => $this->get_actions(), // set on each request, as it depends on some filters @@ -261,7 +270,7 @@ class addressbook_ui extends addressbook_bo ); // use the state of the last session stored in the user prefs - if (($state = @unserialize($this->prefs['index_state']))) + if (($state = @unserialize($this->prefs[str_replace('addressbook.', '', $template ?? 'index').'_state'], ['allowed_classes' => false]))) { $content['nm'] = array_merge($content['nm'],$state); } @@ -300,8 +309,8 @@ class addressbook_ui extends addressbook_bo $sel_options['filter2']['add'] = lang('Add a new list').'...'; // put it at the end } - // Organisation stuff is not (yet) availible with ldap - if($GLOBALS['egw_info']['server']['contact_repository'] != 'ldap' && Api\Header\UserAgent::mobile() == '') + // Organisation stuff is not (yet) available with ldap + if($GLOBALS['egw_info']['server']['contact_repository'] != 'ldap' && Api\Header\UserAgent::mobile() == '' && empty($_GET['template'])) { $content['nm']['header_left'] = 'addressbook.index.left'; } @@ -347,6 +356,14 @@ class addressbook_ui extends addressbook_bo } $content['nm']['actions'] = $this->get_actions($content['nm']['col_filter']['tid']); + // only use a small subset of the full actions + if (($template ?? null) === 'addressbook.select') + { + $content['nm']['actions'] = array_filter($content['nm']['actions'], static function($action) + { + return in_array($action, ['open', 'email', 'delete']); + }, ARRAY_FILTER_USE_KEY); + } if (!isset($sel_options['grouped_view'][(string) $content['nm']['grouped_view']])) { @@ -365,9 +382,12 @@ class addressbook_ui extends addressbook_bo 'shared' => lang('shared'), ]; - $this->tmpl->read('addressbook.index'); + if (!isset($template)) + { + $this->tmpl->read('addressbook.index'); + } return $this->tmpl->exec('addressbook.addressbook_ui.index', - $content,$sel_options,array(),$preserv); + $content, $sel_options, array(), $preserv, empty($_GET['template']) ? 0 : 2); } /** @@ -1711,7 +1731,7 @@ class addressbook_ui extends addressbook_bo */ function get_rows(&$query,&$rows,&$readonlys,$id_only=false) { - $what = $query['sitemgr_display'] ? $query['sitemgr_display'] : 'index'; + $what = str_replace('addressbook.', '', $_GET['template'] ?? $query['main-template'] ?? 'index'); if (!$id_only && !$query['csv_export']) // do NOT store state for csv_export or querying id's (no regular view) { @@ -1881,7 +1901,7 @@ class addressbook_ui extends addressbook_bo } } } - else if($query['actions'] && !$query['actions']['edit']) + else if($query['actions'] && empty($query['actions']['open'])) { // Just switched from grouped view, update actions $query['actions'] = $this->get_actions($query['col_filter']['tid']); @@ -2172,24 +2192,31 @@ class addressbook_ui extends addressbook_bo $rows['sel_options']['grouped_view'] = $this->grouped_views; } } - if($query['advanced_search']) + if ($what === 'select') { - $header[] = lang('Advanced search'); + $GLOBALS['egw_info']['flags']['app_header'] = lang('Select contacts to add to mail'); } - if ($query['cat_id']) + else { - $header[] = lang('Category').' '.$GLOBALS['egw']->categories->id2name($query['cat_id']); + if($query['advanced_search']) + { + $header[] = lang('Advanced search'); + } + if ($query['cat_id']) + { + $header[] = lang('Category').' '.$GLOBALS['egw']->categories->id2name($query['cat_id']); + } + if ($query['searchletter']) + { + $order = $order == 'n_given' ? lang('first name') : ($order == 'n_family' ? lang('last name') : lang('Organisation')); + $header[] = lang("%1 starts with '%2'",$order,$query['searchletter']); + } + if ($query['search'] && !$query['advanced_search']) // do not add that, if we have advanced search active + { + $header[] = lang("Search for '%1'",$query['search']); + } + $GLOBALS['egw_info']['flags']['app_header'] = implode(': ', $header); } - if ($query['searchletter']) - { - $order = $order == 'n_given' ? lang('first name') : ($order == 'n_family' ? lang('last name') : lang('Organisation')); - $header[] = lang("%1 starts with '%2'",$order,$query['searchletter']); - } - if ($query['search'] && !$query['advanced_search']) // do not add that, if we have advanced search active - { - $header[] = lang("Search for '%1'",$query['search']); - } - $GLOBALS['egw_info']['flags']['app_header'] = implode(': ', $header); if ($query['grouped_view'] === '' && $query['col_filter']['shared_by'] == $this->user) { diff --git a/addressbook/js/app.ts b/addressbook/js/app.ts index ca6e130601..386d86fc42 100644 --- a/addressbook/js/app.ts +++ b/addressbook/js/app.ts @@ -28,6 +28,7 @@ import type {EgwAction} from "../../api/js/egw_action/EgwAction"; import {EgwActionObject} from "../../api/js/egw_action/EgwActionObject"; import {Et2MergeDialog} from "../../api/js/etemplate/Et2Dialog/Et2MergeDialog"; import {et2_createWidget} from "../../api/js/etemplate/et2_core_widget"; +import type {et2_nextmatch} from "../../api/js/etemplate/et2_extension_nextmatch"; /** * Object to call app.addressbook.openCRMview with @@ -1061,6 +1062,7 @@ class AddressbookApp extends EgwApp */ addEmail(action, selected) { + console.log('addEmail', action, selected); // Check for all selected. var nm = this.et2.getWidgetById('nm'); if(fetchAll(selected, nm, jQuery.proxy(function(ids) { @@ -1114,6 +1116,22 @@ class AddressbookApp extends EgwApp return false; } + /** + * Onclick of "addessbook.select" template for [To], [Cc] or [Bcc] button + * + * @param _event + * @param _widget + */ + addEmailToCompose(_event, _widget) + { + const et2 = etemplate2.getByTemplate('addressbook.select'); + const nm = et2[0]?.widgetContainer.getWidgetById('nm'); + const selected = nm.getSelection().ids.map((uid) => {id: uid}); + console.log('addEmailToCompose', _widget.id, selected); + debugger; + this.addEmail({ id: _widget.id}, selected); + } + /** * Get email addresses from selected contacts * diff --git a/addressbook/lang/egw_de.lang b/addressbook/lang/egw_de.lang index 9749fec08b..6f3e0de2b0 100644 --- a/addressbook/lang/egw_de.lang +++ b/addressbook/lang/egw_de.lang @@ -440,6 +440,7 @@ select a view addressbook de Eine Ansicht auswählen select addressbook type addressbook de Typ des Adressbuchs auswählen select all addressbook de Alles auswählen select an opened dialog addressbook de Wählen Sie einen offenen Dialog +select contacts to add to mail addressbook de Kontakte zum Hinzufügen zur Mail auswählen select migration type admin de Migrationstyp auswählen select multiple contacts for a further action addressbook de Mehrere Adressen für weiteren Befehl auswählen select phone number as prefered way of contact addressbook de Telefonnummer als präferierten Kontaktweg auswählen diff --git a/addressbook/lang/egw_en.lang b/addressbook/lang/egw_en.lang index 001d3cdff6..433c5c642f 100644 --- a/addressbook/lang/egw_en.lang +++ b/addressbook/lang/egw_en.lang @@ -440,6 +440,7 @@ select a view addressbook en Select a view select addressbook type addressbook en Select address book type select all addressbook en Select all select an opened dialog addressbook en Select an opened dialog +select contacts to add to mail addressbook en Select contacts to add to mail select migration type admin en Select migration type select multiple contacts for a further action addressbook en Select multiple contacts for a further action select phone number as prefered way of contact addressbook en Select phone number as preferred way of contact diff --git a/addressbook/templates/default/select.xet b/addressbook/templates/default/select.xet new file mode 100644 index 0000000000..0816dec264 --- /dev/null +++ b/addressbook/templates/default/select.xet @@ -0,0 +1,118 @@ + + + + + + \ No newline at end of file diff --git a/api/js/jsapi/app_base.js b/api/js/jsapi/app_base.js index ebcaac8a9b..f7fa983cc2 100644 --- a/api/js/jsapi/app_base.js +++ b/api/js/jsapi/app_base.js @@ -1700,6 +1700,47 @@ export const AppJS = (function(){ "use strict"; return Class.extend( width: 450, value: {content:{ "share_link": _data.share_link }} }); - } + }, + /** + * Opens _menuaction in an Et2Dialog + * + * Equivalent to egw.openDialog, though this one works in popups too. + * + * @param _menuaction + * @return Promise + */ + openDialog: function (_menuaction) + { + let resolver; + let rejector; + const dialog_promise = new Promise((resolve, reject) => + { + resolver = value => resolve(value); + rejector = reason => reject(reason); + }); + let request = this.egw.json(_menuaction.match(/^([^.:]+)/)[0] + '.jdots_framework.ajax_exec.template.' + _menuaction, + ['index.php?menuaction=' + _menuaction, true], _response => + { + if(Array.isArray(_response) && typeof _response[0] === 'string') + { + let dialog = jQuery(_response[0]).appendTo(document.body); + if(dialog.length > 0 && dialog.get(0)) + { + resolver(dialog.get(0)); + } + else + { + console.log("Unable to add dialog with dialogExec('" + _menuaction + "')", _response); + rejector(new Error("Unable to add dialog")); + } + } + else + { + console.log("Invalid response to dialogExec('" + _menuaction + "')", _response); + rejector(new Error("Invalid response to dialogExec('" + _menuaction + "')")); + } + }).sendRequest(); + return dialog_promise; + } });}).call(window); \ No newline at end of file diff --git a/api/src/Etemplate.php b/api/src/Etemplate.php index 5afd85eec6..88771ce540 100644 --- a/api/src/Etemplate.php +++ b/api/src/Etemplate.php @@ -267,7 +267,7 @@ class Etemplate extends Etemplate\Widget\Template { if ($output_mode == 2) { - $content = '
'."\n"; + $content = '
'."\n"; } else { diff --git a/mail/templates/default/compose.xet b/mail/templates/default/compose.xet index 342b794cdb..99ca520d3d 100644 --- a/mail/templates/default/compose.xet +++ b/mail/templates/default/compose.xet @@ -69,7 +69,11 @@ + placeholder="select or insert email address" includeLists="true" allowPlaceholder="true"> + +