WIP address-selection for mail-compose with all filters from addressbook.index

This commit is contained in:
ralf 2025-01-27 12:46:29 +01:00
parent 054b33121e
commit bd84b18b7e
8 changed files with 239 additions and 29 deletions

View File

@ -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'),
];
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,6 +2192,12 @@ class addressbook_ui extends addressbook_bo
$rows['sel_options']['grouped_view'] = $this->grouped_views;
}
}
if ($what === 'select')
{
$GLOBALS['egw_info']['flags']['app_header'] = lang('Select contacts to add to mail');
}
else
{
if($query['advanced_search'])
{
$header[] = lang('Advanced search');
@ -2190,6 +2216,7 @@ class addressbook_ui extends addressbook_bo
$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)
{

View File

@ -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_nextmatch>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
*

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE overlay PUBLIC "-//EGroupware GmbH//eTemplate 2.0//EN" "https://www.egroupware.org/etemplate2.0.dtd">
<overlay>
<template id="addressbook.select.rows" template="" lang="" group="0" version="1.9.005">
<grid width="100%">
<columns>
<column width="20"/> <!-- type -->
<column width="30%" minWidth="150"/> <!-- file as -->
<column width="80"/> <!-- photo -->
<column width="180"/> <!-- business addr -->
<column width="180"/> <!-- home addr -->
<column width="200"/> <!-- url, email, email-home -->
</columns>
<rows>
<row class="th">
<nextmatch-header label="Type" id="type"/>
<grid spacing="0" padding="0">
<columns>
<column/>
<column/>
</columns>
<rows>
<row disabled="!@order=n_fileas">
<nextmatch-sortheader label="own sorting" id="n_fileas" span="all"/>
</row>
<row disabled="!@order=n_given">
<nextmatch-sortheader label="Firstname" id="n_given"/>
<nextmatch-sortheader label="Name" id="n_family"/>
</row>
<row disabled="!@order=n_family">
<nextmatch-sortheader label="Name" id="n_family"/>
<nextmatch-sortheader label="Firstname" id="n_given"/>
</row>
<row>
<nextmatch-sortheader label="Organisation" id="org_name" span="all"/>
</row>
<row disabled="!@order=/^(org_name|n_fileas|adr_one_postalcode|contact_modified|contact_created|#)/">
<nextmatch-sortheader label="Name" id="n_family"/>
<nextmatch-sortheader label="Firstname" id="n_given" class="leftPad5"/>
</row>
<row disabled="@order=n_fileas">
<nextmatch-sortheader label="own sorting" id="n_fileas" span="all"/>
</row>
</rows>
</grid>
<nextmatch-header label="Photo" id="photo"/>
<et2-vbox>
<nextmatch-header label="Business address" id="business"/>
<et2-nextmatch-header-custom id="adr_one_countrycode" class="countrySelect" widgetType="select-country" emptyLabel="Country"/>
<nextmatch-sortheader label="zip code" id="adr_one_postalcode"/>
</et2-vbox>
<et2-vbox>
<nextmatch-header label="Home address" id="home"/>
<et2-nextmatch-header-custom id="adr_two_countrycode" class="countrySelect" widgetType="select-country" emptyLabel="Country"/>
<nextmatch-sortheader label="zip code" id="adr_two_postalcode"/>
</et2-vbox>
<et2-vbox>
<nextmatch-header label="Business email" id="email"/>
<nextmatch-header label="Home email" id="email_home"/>
</et2-vbox>
</row>
<row class="$row_cont[class] $row_cont[cat_id]" valign="top">
<et2-image align="center" label="$row_cont[type_label]" src="$row_cont[type]" noLang="1" style="font-size: 22px"/>
<et2-vbox id="${row}[id]">
<et2-description id="${row}[line1]" noLang="1"></et2-description>
<et2-description id="${row}[line2]" noLang="1"></et2-description>
<et2-description id="${row}[org_unit]" noLang="1"></et2-description>
<et2-description id="${row}[title]" noLang="1"></et2-description>
<et2-description id="${row}[first_org]" noLang="1"></et2-description>
</et2-vbox>
<et2-lavatar src="$row_cont[photo]" contactId="$row_cont[id]" lname="$row_cont[n_family]" fname="$row_cont[n_given]"></et2-lavatar>
<et2-vbox>
<et2-hbox>
<et2-description id="${row}[adr_one_postalcode]" noLang="1"></et2-description>
<et2-description value=" " id="${row}[adr_one_locality]" class="leftPad5" noLang="1"></et2-description>
<et2-description id="${row}[adr_one_region]" class="leftPad5" noLang="1"></et2-description>
</et2-hbox>
<et2-description id="${row}[adr_one_countryname]" noLang="1"></et2-description>
<et2-select-country id="${row}[adr_one_countrycode]" readonly="true"></et2-select-country>
</et2-vbox>
<et2-vbox>
<et2-hbox>
<et2-description id="${row}[adr_two_postalcode]" noLang="1"></et2-description>
<et2-description value=" " id="${row}[adr_two_locality]" class="leftPad5" noLang="1"></et2-description>
<et2-description id="${row}[adr_two_region]" class="leftPad5" noLang="1"></et2-description>
</et2-hbox>
<et2-description id="${row}[adr_two_countryname]" noLang="1"></et2-description>
<et2-select-country id="${row}[adr_two_countrycode]" readonly="true"></et2-select-country>
</et2-vbox>
<et2-vbox>
<et2-url-email id="${row}[email]" readonly="true" class="fixedHeight" emailDisplay="email"></et2-url-email>
<et2-url-email id="${row}[email_home]" readonly="true" class="fixedHeight" emailDisplay="email"></et2-url-email>
</et2-vbox>
</row>
</rows>
</grid>
</template>
<template id="addressbook.select" template="" lang="" group="0" version="1.9.002">
<nextmatch id="nm" template="addressbook.select.rows" span="all"/>
<et2-hbox>
<et2-select label="Add">
<option value="">Business or home email</option>
<option value="email">Business email</option>
<option value="email_home">Home email</option>
</et2-select>
<et2-button label="To" id="add_to_to" onclick="app.addressbook.addEmailToCompose"></et2-button>
<et2-button label="Cc" id="add_to_cc" onclick="app.addressbook.addEmailToCompose"></et2-button>
<et2-button label="Bcc" id="add_to_bcc" onclick="app.addressbook.addEmailToCompose"></et2-button>
<et2-button label="Close" onclick="alert('ToDo ;)'); return false;"></et2-button>
</et2-hbox>
<styles>
div.dialog_content img.dialog_icon[src=""] { display: none; }
et2-dialog#dialog-addressbook-select et2-button { max-width: 125px !important; }
et2-dialog#dialog-addressbook-select::part(panel) { width: 90vh; height: 75vh; position: absolute; bottom: 1vh; }
</styles>
</template>
</overlay>

View File

@ -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<Et2Dialog>
*/
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);

View File

@ -267,7 +267,7 @@ class Etemplate extends Etemplate\Widget\Template
{
if ($output_mode == 2)
{
$content = '<et2-dialog><form target="egw_iframe_autocomplete_helper" action="'.$form_action.'" id="'.$dom_id.'" class="et2_container dialog_content"></form></et2-dialog>'."\n";
$content = '<et2-dialog id="dialog-'.$dom_id.'"><form target="egw_iframe_autocomplete_helper" action="'.$form_action.'" id="'.$dom_id.'" class="et2_container dialog_content"></form></et2-dialog>'."\n";
}
else
{

View File

@ -69,7 +69,11 @@
</et2-dropdown>
<et2-email id="to" width="100%" onclick="app.mail.address_click" autofocus="true"
onchange="app.mail.recipients_onchange"
placeholder="select or insert email address" includeLists="true" allowPlaceholder="true"></et2-email>
placeholder="select or insert email address" includeLists="true" allowPlaceholder="true">
<et2-button-icon image="addressbook/navbar" statustext="click to select in addressbook"
onclick="app.mail.openDialog('addressbook.addressbook_ui.index&amp;template=addressbook.select')"
slot="suffix"></et2-button-icon>
</et2-email>
</row>
<row class="mailComposeHeaders mailComposeJQueryCc">
<et2-description value="Cc"></et2-description>