Import Ruty
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Copy a contact record from one directory to another |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_copy extends rcmail_action_contacts_index
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
$cids = self::get_cids();
|
||||
$target = rcube_utils::get_input_string('_to', rcube_utils::INPUT_POST);
|
||||
$target_group = rcube_utils::get_input_string('_togid', rcube_utils::INPUT_POST);
|
||||
|
||||
$success = 0;
|
||||
$errormsg = 'copyerror';
|
||||
$maxnum = $rcmail->config->get('max_group_members', 0);
|
||||
|
||||
foreach ($cids as $source => $cid) {
|
||||
// Something wrong, target not specified
|
||||
if (!strlen($target)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// It might happen when copying records from search result
|
||||
// Do nothing, go to next source
|
||||
if ((string) $target == (string) $source) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$CONTACTS = $rcmail->get_address_book($source);
|
||||
$TARGET = $rcmail->get_address_book($target);
|
||||
|
||||
if (!$TARGET || !$TARGET->ready || $TARGET->readonly) {
|
||||
break;
|
||||
}
|
||||
|
||||
$ids = [];
|
||||
|
||||
foreach ($cid as $cid) {
|
||||
$a_record = $CONTACTS->get_record($cid, true);
|
||||
|
||||
// avoid copying groups
|
||||
if (isset($a_record['_type']) && $a_record['_type'] == 'group') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if contact exists, if so, we'll need it's ID
|
||||
// Note: Some addressbooks allows empty email address field
|
||||
// @TODO: should we check all email addresses?
|
||||
$email = $CONTACTS->get_col_values('email', $a_record, true);
|
||||
if (!empty($email)) {
|
||||
$result = $TARGET->search('email', $email[0], 1, true, true);
|
||||
}
|
||||
else if (!empty($a_record['name'])) {
|
||||
$result = $TARGET->search('name', $a_record['name'], 1, true, true);
|
||||
}
|
||||
else {
|
||||
$result = new rcube_result_set();
|
||||
}
|
||||
|
||||
// insert contact record
|
||||
if (!$result->count) {
|
||||
$plugin = $rcmail->plugins->exec_hook('contact_create', [
|
||||
'record' => $a_record,
|
||||
'source' => $target,
|
||||
'group' => $target_group
|
||||
]);
|
||||
|
||||
if (!$plugin['abort']) {
|
||||
if ($insert_id = $TARGET->insert($plugin['record'], false)) {
|
||||
$ids[] = $insert_id;
|
||||
$success++;
|
||||
}
|
||||
}
|
||||
else if ($plugin['result']) {
|
||||
$ids = array_merge($ids, $plugin['result']);
|
||||
$success++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$record = $result->first();
|
||||
$ids[] = $record['ID'];
|
||||
$errormsg = empty($email) ? 'contactnameexists' : 'contactexists';
|
||||
}
|
||||
}
|
||||
|
||||
// assign to group
|
||||
if ($target_group && $TARGET->groups && !empty($ids)) {
|
||||
$plugin = $rcmail->plugins->exec_hook('group_addmembers', [
|
||||
'group_id' => $target_group,
|
||||
'ids' => $ids,
|
||||
'source' => $target
|
||||
]);
|
||||
|
||||
if (!$plugin['abort']) {
|
||||
$TARGET->reset();
|
||||
$TARGET->set_group($target_group);
|
||||
|
||||
if ($maxnum && ($TARGET->count()->count + count($plugin['ids']) > $maxnum)) {
|
||||
$rcmail->output->show_message('maxgroupmembersreached', 'warning', ['max' => $maxnum]);
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
if (($cnt = $TARGET->add_to_group($target_group, $plugin['ids'])) && $cnt > $success) {
|
||||
$success = $cnt;
|
||||
}
|
||||
}
|
||||
else if (!empty($plugin['result'])) {
|
||||
$success = $plugin['result'];
|
||||
}
|
||||
|
||||
$errormsg = !empty($plugin['message']) ? $plugin['message'] : 'copyerror';
|
||||
}
|
||||
}
|
||||
|
||||
if (!$success) {
|
||||
$rcmail->output->show_message($errormsg, 'error');
|
||||
}
|
||||
else {
|
||||
$rcmail->output->show_message('copysuccess', 'confirmation', ['nr' => $success]);
|
||||
}
|
||||
|
||||
// send response
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Delete the submitted contacts (CIDs) from the users address book |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_delete extends rcmail_action_contacts_index
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$cids = self::get_cids(null, rcube_utils::INPUT_POST);
|
||||
$delcnt = 0;
|
||||
|
||||
// remove previous deletes
|
||||
$undo_time = $rcmail->config->get('undo_timeout', 0);
|
||||
$rcmail->session->remove('contact_undo');
|
||||
|
||||
foreach ($cids as $source => $cid) {
|
||||
$CONTACTS = self::contact_source($source);
|
||||
|
||||
if ($CONTACTS->readonly && empty($CONTACTS->deletable)) {
|
||||
// more sources? do nothing, probably we have search results from
|
||||
// more than one source, some of these sources can be readonly
|
||||
if (count($cids) == 1) {
|
||||
$rcmail->output->show_message('contactdelerror', 'error');
|
||||
$rcmail->output->command('list_contacts');
|
||||
$rcmail->output->send();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$plugin = $rcmail->plugins->exec_hook('contact_delete', [
|
||||
'id' => $cid,
|
||||
'source' => $source
|
||||
]);
|
||||
|
||||
$deleted = !$plugin['abort'] ? $CONTACTS->delete($cid, $undo_time < 1) : $plugin['result'];
|
||||
|
||||
if (!$deleted) {
|
||||
if (!empty($plugin['message'])) {
|
||||
$error = $plugin['message'];
|
||||
}
|
||||
else if (($error = $CONTACTS->get_error()) && !empty($error['message'])) {
|
||||
$error = $error['message'];
|
||||
}
|
||||
else {
|
||||
$error = 'contactdelerror';
|
||||
}
|
||||
|
||||
$source = rcube_utils::get_input_string('_source', rcube_utils::INPUT_GP);
|
||||
$group = rcube_utils::get_input_string('_gid', rcube_utils::INPUT_GP);
|
||||
|
||||
$rcmail->output->show_message($error, 'error');
|
||||
$rcmail->output->command('list_contacts', $source, $group);
|
||||
$rcmail->output->send();
|
||||
}
|
||||
else {
|
||||
$delcnt += $deleted;
|
||||
|
||||
// store deleted contacts IDs in session for undo action
|
||||
if ($undo_time > 0 && $CONTACTS->undelete) {
|
||||
$_SESSION['contact_undo']['data'][$source] = $cid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($_SESSION['contact_undo'])) {
|
||||
$_SESSION['contact_undo']['ts'] = time();
|
||||
$msg = html::span(null, $rcmail->gettext('contactdeleted'))
|
||||
. ' ' . html::a(
|
||||
['onclick' => rcmail_output::JS_OBJECT_NAME.".command('undo', '', this)"],
|
||||
$rcmail->gettext('undo')
|
||||
);
|
||||
|
||||
$rcmail->output->show_message($msg, 'confirmation', null, true, $undo_time);
|
||||
}
|
||||
else {
|
||||
$rcmail->output->show_message('contactdeleted', 'confirmation');
|
||||
}
|
||||
|
||||
$page_size = $rcmail->config->get('addressbook_pagesize', $rcmail->config->get('pagesize', 50));
|
||||
$page = $_SESSION['page'] ?? 1;
|
||||
|
||||
// update saved search after data changed
|
||||
if (($records = self::search_update(true)) !== false) {
|
||||
// create resultset object
|
||||
$count = count($records);
|
||||
$first = ($page-1) * $page_size;
|
||||
$result = new rcube_result_set($count, $first);
|
||||
$pages = ceil((count($records) + $delcnt) / $page_size);
|
||||
|
||||
// last page and it's empty, display previous one
|
||||
if ($result->count && $result->count <= ($page_size * ($page - 1))) {
|
||||
$rcmail->output->command('list_page', 'prev');
|
||||
$rowcount = $rcmail->gettext('loading');
|
||||
}
|
||||
// get records from the next page to add to the list
|
||||
else if ($pages > 1 && $page < $pages) {
|
||||
// sort the records
|
||||
ksort($records, SORT_LOCALE_STRING);
|
||||
|
||||
$first += $page_size;
|
||||
// create resultset object
|
||||
$res = new rcube_result_set($count, $first - $delcnt);
|
||||
|
||||
if ($page_size < $count) {
|
||||
$records = array_slice($records, $first - $delcnt, $delcnt);
|
||||
}
|
||||
|
||||
$res->records = array_values($records);
|
||||
$records = $res;
|
||||
}
|
||||
else {
|
||||
unset($records);
|
||||
}
|
||||
}
|
||||
else if (isset($CONTACTS)) {
|
||||
// count contacts for this user
|
||||
$result = $CONTACTS->count();
|
||||
$pages = ceil(($result->count + $delcnt) / $page_size);
|
||||
|
||||
// last page and it's empty, display previous one
|
||||
if ($result->count && $result->count <= ($page_size * ($page - 1))) {
|
||||
$rcmail->output->command('list_page', 'prev');
|
||||
$rowcount = $rcmail->gettext('loading');
|
||||
}
|
||||
// get records from the next page to add to the list
|
||||
else if ($pages > 1 && $page < $pages) {
|
||||
$CONTACTS->set_page($page);
|
||||
$records = $CONTACTS->list_records(null, -$delcnt);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($rowcount)) {
|
||||
$rowcount = isset($result) ? self::get_rowcount_text($result) : '';
|
||||
}
|
||||
|
||||
// update message count display
|
||||
$rcmail->output->set_env('pagecount', isset($result) ? ceil($result->count / $page_size) : 0);
|
||||
$rcmail->output->command('set_rowcount', $rowcount);
|
||||
|
||||
// add new rows from next page (if any)
|
||||
if (!empty($records)) {
|
||||
self::js_contacts_list($records);
|
||||
}
|
||||
|
||||
// send response
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Show edit form for a contact entry |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_edit extends rcmail_action_contacts_index
|
||||
{
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
if ($rcmail->action == 'edit') {
|
||||
// Get contact ID and source ID from request
|
||||
$cids = self::get_cids();
|
||||
$source = key($cids);
|
||||
$cid = array_first($cids[$source]);
|
||||
|
||||
// Initialize addressbook
|
||||
$CONTACTS = self::contact_source($source, true);
|
||||
|
||||
// Contact edit
|
||||
if ($cid && (self::$contact = $CONTACTS->get_record($cid, true))) {
|
||||
$rcmail->output->set_env('cid', self::$contact['ID']);
|
||||
}
|
||||
|
||||
// editing not allowed here
|
||||
if ($CONTACTS->readonly || !empty(self::$contact['readonly'])) {
|
||||
$rcmail->output->show_message('sourceisreadonly');
|
||||
$rcmail->overwrite_action('show');
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty(self::$contact)) {
|
||||
$rcmail->output->show_message('contactnotfound', 'error');
|
||||
}
|
||||
}
|
||||
else {
|
||||
$source = rcube_utils::get_input_string('_source', rcube_utils::INPUT_GPC);
|
||||
|
||||
if (strlen($source)) {
|
||||
$CONTACTS = $rcmail->get_address_book($source, true);
|
||||
}
|
||||
|
||||
if (empty($CONTACTS) || $CONTACTS->readonly) {
|
||||
$CONTACTS = $rcmail->get_address_book(rcube_addressbook::TYPE_DEFAULT, true);
|
||||
$source = $rcmail->get_address_book_id($CONTACTS);
|
||||
}
|
||||
|
||||
// Initialize addressbook
|
||||
$CONTACTS = self::contact_source($source, true);
|
||||
}
|
||||
|
||||
self::$SOURCE_ID = $source;
|
||||
self::$CONTACTS = $CONTACTS;
|
||||
self::set_sourcename($CONTACTS);
|
||||
|
||||
// check if we have a valid result
|
||||
if (!empty($args['contact'])) {
|
||||
self::$contact = $args['contact'];
|
||||
}
|
||||
|
||||
$rcmail->output->add_handlers([
|
||||
'contactedithead' => [$this, 'contact_edithead'],
|
||||
'contacteditform' => [$this, 'contact_editform'],
|
||||
'contactphoto' => [$this, 'contact_photo'],
|
||||
'photouploadform' => [$this, 'upload_photo_form'],
|
||||
'sourceselector' => [$this, 'source_selector'],
|
||||
'filedroparea' => [$this, 'photo_drop_area'],
|
||||
]);
|
||||
|
||||
$rcmail->output->set_pagetitle($rcmail->gettext(($rcmail->action == 'add' ? 'addcontact' : 'editcontact')));
|
||||
|
||||
if ($rcmail->action == 'add' && $rcmail->output->template_exists('contactadd')) {
|
||||
$rcmail->output->send('contactadd');
|
||||
}
|
||||
|
||||
// this will be executed if no template for addcontact exists
|
||||
$rcmail->output->send('contactedit');
|
||||
}
|
||||
|
||||
public static function contact_edithead($attrib)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$business_mode = $rcmail->config->get('contact_form_mode') === 'business';
|
||||
|
||||
// check if we have a valid result
|
||||
$i_size = !empty($attrib['size']) ? $attrib['size'] : 20;
|
||||
|
||||
$form = [
|
||||
'head' => [
|
||||
'name' => $rcmail->gettext('contactnameandorg'),
|
||||
'content' => [
|
||||
'source' => ['id' => '_source', 'label' => $rcmail->gettext('addressbook')],
|
||||
'prefix' => ['size' => $i_size],
|
||||
'firstname' => ['size' => $i_size, 'visible' => true],
|
||||
'middlename' => ['size' => $i_size],
|
||||
'surname' => ['size' => $i_size, 'visible' => true],
|
||||
'suffix' => ['size' => $i_size],
|
||||
'name' => ['size' => $i_size * 2],
|
||||
'nickname' => ['size' => $i_size * 2],
|
||||
'organization' => ['size' => $i_size * 2, 'visible' => $business_mode],
|
||||
'department' => ['size' => $i_size * 2, 'visible' => $business_mode],
|
||||
'jobtitle' => ['size' => $i_size * 2, 'visible' => $business_mode],
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
list($form_start, $form_end) = self::get_form_tags($attrib);
|
||||
unset($attrib['form'], $attrib['name'], $attrib['size']);
|
||||
|
||||
// return the address edit form
|
||||
$out = self::contact_form($form, self::$contact, $attrib);
|
||||
|
||||
return $form_start . $out . $form_end;
|
||||
}
|
||||
|
||||
public static function contact_editform($attrib)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$addr_tpl = $rcmail->config->get('address_template', '');
|
||||
|
||||
// copy (parsed) address template to client
|
||||
if (preg_match_all('/\{([a-z0-9]+)\}([^{]*)/i', $addr_tpl, $templ, PREG_SET_ORDER)) {
|
||||
$rcmail->output->set_env('address_template', $templ);
|
||||
}
|
||||
|
||||
$i_size = !empty($attrib['size']) ? $attrib['size'] : 40;
|
||||
$t_rows = !empty($attrib['textarearows']) ? $attrib['textarearows'] : 10;
|
||||
$t_cols = !empty($attrib['textareacols']) ? $attrib['textareacols'] : 40;
|
||||
$short_labels = self::get_bool_attr($attrib, 'short-legend-labels');
|
||||
|
||||
$form = [
|
||||
'contact' => [
|
||||
'name' => $rcmail->gettext('properties'),
|
||||
'content' => [
|
||||
'email' => ['size' => $i_size, 'maxlength' => 254, 'visible' => true],
|
||||
'phone' => ['size' => $i_size, 'visible' => true],
|
||||
'address' => ['visible' => true],
|
||||
'website' => ['size' => $i_size],
|
||||
'im' => ['size' => $i_size],
|
||||
],
|
||||
],
|
||||
'personal' => [
|
||||
'name' => $rcmail->gettext($short_labels ? 'personal' : 'personalinfo'),
|
||||
'content' => [
|
||||
'gender' => ['visible' => true],
|
||||
'maidenname' => ['size' => $i_size],
|
||||
'birthday' => ['visible' => true],
|
||||
'anniversary' => [],
|
||||
'manager' => ['size' => $i_size],
|
||||
'assistant' => ['size' => $i_size],
|
||||
'spouse' => ['size' => $i_size],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
if (isset(self::$CONTACT_COLTYPES['notes'])) {
|
||||
$form['notes'] = [
|
||||
'name' => $rcmail->gettext('notes'),
|
||||
'single' => true,
|
||||
'content' => [
|
||||
'notes' => ['size' => $t_cols, 'rows' => $t_rows, 'label' => false, 'visible' => true, 'limit' => 1],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
list($form_start, $form_end) = self::get_form_tags($attrib);
|
||||
unset($attrib['form']);
|
||||
|
||||
// return the complete address edit form as table
|
||||
$out = self::contact_form($form, self::$contact, $attrib);
|
||||
|
||||
return $form_start . $out . $form_end;
|
||||
}
|
||||
|
||||
public static function upload_photo_form($attrib)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$hidden = new html_hiddenfield(['name' => '_cid', 'value' => $rcmail->output->get_env('cid')]);
|
||||
|
||||
$attrib['prefix'] = $hidden->show();
|
||||
$input_attr = ['name' => '_photo', 'accept' => 'image/*'];
|
||||
|
||||
$rcmail->output->add_label('addphoto','replacephoto');
|
||||
|
||||
return self::upload_form($attrib, 'uploadform', 'upload-photo', $input_attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* similar function as in /steps/settings/edit_identity.inc
|
||||
* @todo: Use rcmail_action::get_form_tags()
|
||||
*/
|
||||
public static function get_form_tags($attrib, $action = null, $id = null, $hidden = null)
|
||||
{
|
||||
static $edit_form;
|
||||
|
||||
$rcmail = rcmail::get_instance();
|
||||
$form_start = $form_end = '';
|
||||
|
||||
if (empty($edit_form)) {
|
||||
$hiddenfields = new html_hiddenfield();
|
||||
|
||||
if ($rcmail->action == 'edit') {
|
||||
$hiddenfields->add(['name' => '_source', 'value' => self::$SOURCE_ID]);
|
||||
}
|
||||
|
||||
$hiddenfields->add(['name' => '_gid', 'value' => self::$CONTACTS->group_id]);
|
||||
$hiddenfields->add(['name' => '_search', 'value' => rcube_utils::get_input_string('_search', rcube_utils::INPUT_GPC)]);
|
||||
|
||||
if ($cid = $rcmail->output->get_env('cid')) {
|
||||
$hiddenfields->add(['name' => '_cid', 'value' => $cid]);
|
||||
}
|
||||
|
||||
$form_attrib = [
|
||||
'name' => 'form',
|
||||
'method' => 'post',
|
||||
'task' => $rcmail->task,
|
||||
'action' => 'save',
|
||||
'request' => 'save.' . intval($cid),
|
||||
'noclose' => true,
|
||||
];
|
||||
|
||||
$form_start = $rcmail->output->request_form($form_attrib + $attrib, $hiddenfields->show());
|
||||
$form_end = empty($attrib['form']) ? '</form>' : '';
|
||||
$edit_form = !empty($attrib['form']) ? $attrib['form'] : 'form';
|
||||
|
||||
$rcmail->output->add_gui_object('editform', $edit_form);
|
||||
}
|
||||
|
||||
return [$form_start, $form_end];
|
||||
}
|
||||
|
||||
/**
|
||||
* Register container as active area to drop photos onto
|
||||
*/
|
||||
public static function photo_drop_area($attrib)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
if (!empty($attrib['id'])) {
|
||||
$rcmail->output->add_gui_object('filedrop', $attrib['id']);
|
||||
$rcmail->output->set_env('filedrop', [
|
||||
'action' => 'upload-photo',
|
||||
'fieldname' => '_photo',
|
||||
'single' => 1,
|
||||
'filter' => '^image/.+'
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| Copyright (C) Kolab Systems AG |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Export the selected address book as vCard file |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
| Author: Aleksander Machniak <machniak@kolabsys.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_export extends rcmail_action_contacts_index
|
||||
{
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
$rcmail->request_security_check(rcube_utils::INPUT_GET);
|
||||
|
||||
$sort_col = $rcmail->config->get('addressbook_sort_col', 'name');
|
||||
|
||||
// Use search result
|
||||
if (!empty($_REQUEST['_search']) && isset($_SESSION['contact_search'][$_REQUEST['_search']])) {
|
||||
$search = (array) $_SESSION['contact_search'][$_REQUEST['_search']];
|
||||
$records = [];
|
||||
|
||||
// Get records from all sources
|
||||
foreach ($search as $s => $set) {
|
||||
$source = $rcmail->get_address_book($s);
|
||||
|
||||
// reset page
|
||||
$source->set_page(1);
|
||||
$source->set_pagesize(99999);
|
||||
$source->set_search_set($set);
|
||||
|
||||
// get records
|
||||
$result = $source->list_records();
|
||||
|
||||
while ($record = $result->next()) {
|
||||
// because vcard_map is per-source we need to create vcard here
|
||||
self::prepare_for_export($record, $source);
|
||||
|
||||
$record['sourceid'] = $s;
|
||||
$key = rcube_addressbook::compose_contact_key($record, $sort_col);
|
||||
$records[$key] = $record;
|
||||
}
|
||||
|
||||
unset($result);
|
||||
}
|
||||
|
||||
// sort the records
|
||||
ksort($records, SORT_LOCALE_STRING);
|
||||
|
||||
// create resultset object
|
||||
$count = count($records);
|
||||
$result = new rcube_result_set($count);
|
||||
$result->records = array_values($records);
|
||||
}
|
||||
// selected contacts
|
||||
else if (!empty($_REQUEST['_cid'])) {
|
||||
$records = [];
|
||||
|
||||
// Selected contact IDs (with multi-source support)
|
||||
$cids = self::get_cids();
|
||||
|
||||
foreach ($cids as $s => $ids) {
|
||||
$source = $rcmail->get_address_book($s);
|
||||
|
||||
// reset page and page size (#6103)
|
||||
$source->set_page(1);
|
||||
$source->set_pagesize(count($ids));
|
||||
|
||||
$result = $source->search('ID', $ids, 1, true, true);
|
||||
|
||||
while ($record = $result->next()) {
|
||||
// because vcard_map is per-source we need to create vcard here
|
||||
self::prepare_for_export($record, $source);
|
||||
|
||||
$record['sourceid'] = $s;
|
||||
$key = rcube_addressbook::compose_contact_key($record, $sort_col);
|
||||
$records[$key] = $record;
|
||||
}
|
||||
}
|
||||
|
||||
ksort($records, SORT_LOCALE_STRING);
|
||||
|
||||
// create resultset object
|
||||
$count = count($records);
|
||||
$result = new rcube_result_set($count);
|
||||
$result->records = array_values($records);
|
||||
}
|
||||
// selected directory/group
|
||||
else {
|
||||
$CONTACTS = self::contact_source(null, true);
|
||||
|
||||
// get contacts for this user
|
||||
$CONTACTS->set_page(1);
|
||||
$CONTACTS->set_pagesize(99999);
|
||||
$result = $CONTACTS->list_records(null, 0, true);
|
||||
}
|
||||
|
||||
// Give plugins a possibility to implement other output formats or modify the result
|
||||
$plugin = $rcmail->plugins->exec_hook('addressbook_export', ['result' => $result]);
|
||||
$result = $plugin['result'];
|
||||
|
||||
if ($plugin['abort']) {
|
||||
$rcmail->output->sendExit();
|
||||
}
|
||||
|
||||
// send download headers
|
||||
$rcmail->output->header('Content-Type: text/vcard; charset=' . RCUBE_CHARSET);
|
||||
$rcmail->output->header('Content-Disposition: attachment; filename="contacts.vcf"');
|
||||
|
||||
while ($result && ($row = $result->next())) {
|
||||
if (!empty($CONTACTS)) {
|
||||
self::prepare_for_export($row, $CONTACTS);
|
||||
}
|
||||
|
||||
// fix folding and end-of-line chars
|
||||
$row['vcard'] = preg_replace('/\r|\n\s+/', '', $row['vcard']);
|
||||
$row['vcard'] = preg_replace('/\n/', rcube_vcard::$eol, $row['vcard']);
|
||||
|
||||
echo rcube_vcard::rfc2425_fold($row['vcard']) . rcube_vcard::$eol;
|
||||
}
|
||||
|
||||
$rcmail->output->sendExit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy contact record properties into a vcard object
|
||||
*/
|
||||
public static function prepare_for_export(&$record, $source = null)
|
||||
{
|
||||
$groups = $source && $source->groups && $source->export_groups ? $source->get_record_groups($record['ID']) : null;
|
||||
$fieldmap = $source ? $source->vcard_map : null;
|
||||
|
||||
if (empty($record['vcard'])) {
|
||||
$vcard = new rcube_vcard(null, RCUBE_CHARSET, false, $fieldmap);
|
||||
$vcard->reset();
|
||||
|
||||
foreach ($record as $key => $values) {
|
||||
list($field, $section) = rcube_utils::explode(':', $key);
|
||||
// avoid unwanted casting of DateTime objects to an array
|
||||
// (same as in rcube_contacts::convert_save_data())
|
||||
if (is_object($values) && is_a($values, 'DateTime')) {
|
||||
$values = [$values];
|
||||
}
|
||||
|
||||
foreach ((array) $values as $value) {
|
||||
if (is_array($value) || is_a($value, 'DateTime') || @strlen($value)) {
|
||||
$vcard->set($field, $value, $section ? strtoupper($section) : '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// append group names
|
||||
if ($groups) {
|
||||
$vcard->set('groups', join(',', $groups), null);
|
||||
}
|
||||
|
||||
$record['vcard'] = $vcard->export();
|
||||
}
|
||||
// patch categories to already existing vcard block
|
||||
else {
|
||||
$vcard = new rcube_vcard($record['vcard'], RCUBE_CHARSET, false, $fieldmap);
|
||||
|
||||
// unset CATEGORIES entry, it might be not up-to-date (#1490277)
|
||||
$vcard->set('groups', null);
|
||||
$record['vcard'] = $vcard->export();
|
||||
|
||||
if (!empty($groups)) {
|
||||
$vgroups = 'CATEGORIES:' . rcube_vcard::vcard_quote($groups, ',') . rcube_vcard::$eol;
|
||||
$record['vcard'] = str_replace('END:VCARD', $vgroups . 'END:VCARD', $record['vcard']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Handle adding members to a contact group |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_group_addmembers extends rcmail_action_contacts_index
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$source = rcube_utils::get_input_string('_source', rcube_utils::INPUT_GPC);
|
||||
$contacts = self::contact_source($source);
|
||||
|
||||
if ($contacts->readonly || !$contacts->groups) {
|
||||
$rcmail->output->show_message('sourceisreadonly', 'warning');
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
$gid = rcube_utils::get_input_string('_gid', rcube_utils::INPUT_POST);
|
||||
$ids = self::get_cids($source);
|
||||
$result = false;
|
||||
|
||||
if ($gid && $ids) {
|
||||
$plugin = $rcmail->plugins->exec_hook('group_addmembers', [
|
||||
'group_id' => $gid,
|
||||
'ids' => $ids,
|
||||
'source' => $source,
|
||||
]);
|
||||
|
||||
$contacts->set_group($gid);
|
||||
$num2add = count($plugin['ids']);
|
||||
|
||||
if (empty($plugin['abort'])) {
|
||||
if (
|
||||
($maxnum = $rcmail->config->get('max_group_members'))
|
||||
&& ($contacts->count()->count + $num2add > $maxnum)
|
||||
) {
|
||||
$rcmail->output->show_message('maxgroupmembersreached', 'warning', ['max' => $maxnum]);
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
$result = $contacts->add_to_group($gid, $plugin['ids']);
|
||||
}
|
||||
else {
|
||||
$result = $plugin['result'];
|
||||
}
|
||||
}
|
||||
|
||||
if ($result) {
|
||||
$rcmail->output->show_message('contactaddedtogroup', 'confirmation');
|
||||
}
|
||||
else if (!empty($plugin['abort']) || $contacts->get_error()) {
|
||||
$error = !empty($plugin['message']) ? $plugin['message'] : 'errorsaving';
|
||||
$rcmail->output->show_message($error, 'error');
|
||||
}
|
||||
else {
|
||||
$message = !empty($plugin['message']) ? $plugin['message'] : 'nogroupassignmentschanged';
|
||||
$rcmail->output->show_message($message);
|
||||
}
|
||||
|
||||
// send response
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| A handler for contact groups creation |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_group_create extends rcmail_action_contacts_index
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$source = rcube_utils::get_input_string('_source', rcube_utils::INPUT_GPC);
|
||||
$contacts = self::contact_source($source);
|
||||
|
||||
if ($contacts->readonly || !$contacts->groups) {
|
||||
$rcmail->output->show_message('sourceisreadonly', 'warning');
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
if ($name = trim(rcube_utils::get_input_string('_name', rcube_utils::INPUT_POST, true))) {
|
||||
$plugin = $rcmail->plugins->exec_hook('group_create', [
|
||||
'name' => $name,
|
||||
'source' => $source,
|
||||
]);
|
||||
|
||||
if (empty($plugin['abort'])) {
|
||||
$created = $contacts->create_group($plugin['name']);
|
||||
}
|
||||
else {
|
||||
$created = $plugin['result'];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($created)) {
|
||||
$rcmail->output->show_message('groupcreated', 'confirmation');
|
||||
$rcmail->output->command('insert_contact_group', ['source' => $source] + $created);
|
||||
}
|
||||
else if (empty($created)) {
|
||||
$error = !empty($plugin['message']) ? $plugin['message'] : 'errorsaving';
|
||||
$rcmail->output->show_message($error, 'error');
|
||||
}
|
||||
|
||||
// send response
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| A handler for contact group delete action |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_group_delete extends rcmail_action_contacts_index
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$source = rcube_utils::get_input_string('_source', rcube_utils::INPUT_GPC);
|
||||
$contacts = self::contact_source($source);
|
||||
|
||||
if ($contacts->readonly || !$contacts->groups) {
|
||||
$rcmail->output->show_message('sourceisreadonly', 'warning');
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
if ($gid = rcube_utils::get_input_string('_gid', rcube_utils::INPUT_POST)) {
|
||||
$plugin = $rcmail->plugins->exec_hook('group_delete', [
|
||||
'group_id' => $gid,
|
||||
'source' => $source,
|
||||
]);
|
||||
|
||||
if (empty($plugin['abort'])) {
|
||||
$deleted = $contacts->delete_group($gid);
|
||||
}
|
||||
else {
|
||||
$deleted = $plugin['result'];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($deleted)) {
|
||||
$rcmail->output->show_message('groupdeleted', 'confirmation');
|
||||
$rcmail->output->command('remove_group_item', ['source' => $source, 'id' => $gid]);
|
||||
}
|
||||
else {
|
||||
$error = !empty($plugin['message']) ? $plugin['message'] : 'errorsaving';
|
||||
$rcmail->output->show_message($error, 'error');
|
||||
}
|
||||
|
||||
// send response
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Removing members from a contact group |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_group_delmembers extends rcmail_action_contacts_index
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$source = rcube_utils::get_input_string('_source', rcube_utils::INPUT_GPC);
|
||||
$contacts = self::contact_source($source);
|
||||
|
||||
if ($contacts->readonly || !$contacts->groups) {
|
||||
$rcmail->output->show_message('sourceisreadonly', 'warning');
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
$gid = rcube_utils::get_input_string('_gid', rcube_utils::INPUT_POST);
|
||||
$ids = self::get_cids($source);
|
||||
|
||||
if ($gid && $ids) {
|
||||
$plugin = $rcmail->plugins->exec_hook('group_delmembers', [
|
||||
'group_id' => $gid,
|
||||
'ids' => $ids,
|
||||
'source' => $source,
|
||||
]);
|
||||
|
||||
if (empty($plugin['abort'])) {
|
||||
$result = $contacts->remove_from_group($gid, $plugin['ids']);
|
||||
}
|
||||
else {
|
||||
$result = $plugin['result'];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($result)) {
|
||||
$rcmail->output->show_message('contactremovedfromgroup', 'confirmation');
|
||||
$rcmail->output->command('remove_group_contacts', ['source' => $source, 'gid' => $gid]);
|
||||
}
|
||||
else {
|
||||
$error = !empty($plugin['message']) ? $plugin['message'] : 'errorsaving';
|
||||
$rcmail->output->show_message($error, 'error');
|
||||
}
|
||||
|
||||
// send response
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| A handler for contact groups rename action |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_group_rename extends rcmail_action_contacts_index
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$source = rcube_utils::get_input_string('_source', rcube_utils::INPUT_GPC);
|
||||
$contacts = self::contact_source($source);
|
||||
|
||||
if ($contacts->readonly || !$contacts->groups) {
|
||||
$rcmail->output->show_message('sourceisreadonly', 'warning');
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
if (
|
||||
($gid = rcube_utils::get_input_string('_gid', rcube_utils::INPUT_POST))
|
||||
&& ($name = trim(rcube_utils::get_input_string('_name', rcube_utils::INPUT_POST, true)))
|
||||
) {
|
||||
$newgid = null;
|
||||
$plugin = $rcmail->plugins->exec_hook('group_rename', [
|
||||
'group_id' => $gid,
|
||||
'name' => $name,
|
||||
'source' => $source,
|
||||
]);
|
||||
|
||||
if (empty($plugin['abort'])) {
|
||||
$newname = $contacts->rename_group($gid, $plugin['name'], $newgid);
|
||||
}
|
||||
else {
|
||||
$newname = $plugin['result'];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($newname)) {
|
||||
$rcmail->output->show_message('grouprenamed', 'confirmation');
|
||||
$rcmail->output->command('update_contact_group', [
|
||||
'source' => $source,
|
||||
'id' => $gid,
|
||||
'name' => $newname,
|
||||
'newid' => $newgid ?? null
|
||||
]);
|
||||
}
|
||||
else {
|
||||
$error = !empty($plugin['message']) ? $plugin['message'] : 'errorsaving';
|
||||
$rcmail->output->show_message($error, 'error');
|
||||
}
|
||||
|
||||
// send response
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,486 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Import contacts from a vCard or CSV file |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
| Author: Aleksander Machniak <machniak@kolabsys.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_import extends rcmail_action_contacts_index
|
||||
{
|
||||
const UPLOAD_ERR_CSV_FIELDS = 101;
|
||||
|
||||
protected static $stats;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$importstep = 'import_form';
|
||||
$has_map = isset($_POST['_map']) && is_array($_POST['_map']);
|
||||
|
||||
if ($has_map || (isset($_FILES['_file']) && is_array($_FILES['_file']))) {
|
||||
$replace = (bool) rcube_utils::get_input_string('_replace', rcube_utils::INPUT_GPC);
|
||||
$target = rcube_utils::get_input_string('_target', rcube_utils::INPUT_GPC);
|
||||
$with_groups = (int) rcube_utils::get_input_string('_groups', rcube_utils::INPUT_GPC);
|
||||
|
||||
// reload params for CSV field mapping screen
|
||||
if ($has_map && !empty($_SESSION['contactcsvimport']['params'])) {
|
||||
$params = $_SESSION['contactcsvimport']['params'];
|
||||
|
||||
$replace = $params['replace'];
|
||||
$target = $params['target'];
|
||||
$with_groups = $params['with_groups'];
|
||||
}
|
||||
|
||||
$vcards = [];
|
||||
$csvs = [];
|
||||
$map = [];
|
||||
$upload_error = null;
|
||||
|
||||
$CONTACTS = $rcmail->get_address_book($target, true);
|
||||
|
||||
if (!$CONTACTS->groups) {
|
||||
$with_groups = false;
|
||||
}
|
||||
|
||||
if ($CONTACTS->readonly) {
|
||||
$rcmail->output->show_message('addresswriterror', 'error');
|
||||
}
|
||||
else {
|
||||
$filepaths = [];
|
||||
if ($has_map) {
|
||||
$filepaths = $_SESSION['contactcsvimport']['files'];
|
||||
}
|
||||
else {
|
||||
foreach ((array) $_FILES['_file']['tmp_name'] as $i => $filepath) {
|
||||
// Process uploaded file if there is no error
|
||||
$err = $_FILES['_file']['error'][$i];
|
||||
|
||||
if ($err) {
|
||||
$upload_error = $err;
|
||||
}
|
||||
else {
|
||||
$filepaths[] = $filepath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($filepaths as $filepath) {
|
||||
$file_content = file_get_contents($filepath);
|
||||
|
||||
// let rcube_vcard do the hard work :-)
|
||||
$vcard_o = new rcube_vcard();
|
||||
$vcard_o->extend_fieldmap($CONTACTS->vcard_map);
|
||||
$v_list = $vcard_o->import($file_content);
|
||||
|
||||
if (!empty($v_list)) {
|
||||
$vcards = array_merge($vcards, $v_list);
|
||||
continue;
|
||||
}
|
||||
|
||||
// no vCards found, try CSV
|
||||
$csv = new rcube_csv2vcard($_SESSION['language']);
|
||||
|
||||
if ($has_map) {
|
||||
$skip_head = isset($_POST['_skip_header']);
|
||||
$map = rcube_utils::get_input_value('_map', rcube_utils::INPUT_GPC);
|
||||
$map = array_filter($map);
|
||||
|
||||
$csv->set_map($map);
|
||||
$csv->import($file_content, false, $skip_head);
|
||||
|
||||
unlink($filepath);
|
||||
}
|
||||
else {
|
||||
// save uploaded file for the real import in the next step
|
||||
$temp_csv = rcube_utils::temp_filename('csvimpt');
|
||||
if (move_uploaded_file($filepath, $temp_csv) && file_exists($temp_csv)) {
|
||||
$fields = $csv->get_fields();
|
||||
$last_map = $map;
|
||||
$map = $csv->import($file_content, true);
|
||||
|
||||
// when multiple CSV files are uploaded check they all have the same structure
|
||||
if ($last_map && $last_map !== $map) {
|
||||
$csvs = [];
|
||||
$upload_error = self::UPLOAD_ERR_CSV_FIELDS;
|
||||
break;
|
||||
}
|
||||
|
||||
$csvs[] = $temp_csv;
|
||||
}
|
||||
else {
|
||||
$upload_error = UPLOAD_ERR_CANT_WRITE;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$v_list = $csv->export();
|
||||
|
||||
if (!empty($v_list)) {
|
||||
$vcards = array_merge($vcards, $v_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count($csvs) > 0) {
|
||||
// csv import, show field mapping options
|
||||
$importstep = 'import_map';
|
||||
|
||||
$_SESSION['contactcsvimport']['files'] = $csvs;
|
||||
$_SESSION['contactcsvimport']['params'] = [
|
||||
'replace' => $replace,
|
||||
'target' => $target,
|
||||
'with_groups' => $with_groups,
|
||||
'fields' => !empty($fields) ? $fields : [],
|
||||
];
|
||||
|
||||
// Stored separately due to nested array limitations in session
|
||||
$_SESSION['contactcsvimport']['map'] = $map;
|
||||
|
||||
// Re-enable the import button
|
||||
$rcmail->output->command('parent.import_state_set', 'error');
|
||||
}
|
||||
elseif (count($vcards) > 0) {
|
||||
// import vcards
|
||||
self::$stats = new stdClass;
|
||||
self::$stats->names = [];
|
||||
self::$stats->skipped_names = [];
|
||||
self::$stats->count = count($vcards);
|
||||
self::$stats->inserted = 0;
|
||||
self::$stats->skipped = 0;
|
||||
self::$stats->invalid = 0;
|
||||
self::$stats->errors = 0;
|
||||
|
||||
if ($replace) {
|
||||
$CONTACTS->delete_all($CONTACTS->groups && $with_groups < 2);
|
||||
}
|
||||
|
||||
if ($with_groups) {
|
||||
$import_groups = $CONTACTS->list_groups();
|
||||
}
|
||||
|
||||
foreach ($vcards as $vcard) {
|
||||
$a_record = $vcard->get_assoc();
|
||||
|
||||
// Generate contact's display name (must be before validation), the same we do in save.inc
|
||||
if (empty($a_record['name'])) {
|
||||
$a_record['name'] = rcube_addressbook::compose_display_name($a_record, true);
|
||||
// Reset it if equals to email address (from compose_display_name())
|
||||
if ($a_record['name'] == ($a_record['email'][0] ?? null)) {
|
||||
$a_record['name'] = '';
|
||||
}
|
||||
}
|
||||
|
||||
// skip invalid (incomplete) entries
|
||||
if (!$CONTACTS->validate($a_record, true)) {
|
||||
self::$stats->invalid++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// We're using UTF8 internally
|
||||
$email = null;
|
||||
if (isset($vcard->email[0])) {
|
||||
$email = $vcard->email[0];
|
||||
$email = rcube_utils::idn_to_utf8($email);
|
||||
}
|
||||
|
||||
if (!$replace) {
|
||||
$existing = null;
|
||||
// compare e-mail address
|
||||
if ($email) {
|
||||
$existing = $CONTACTS->search('email', $email, 1, false);
|
||||
}
|
||||
// compare display name if email not found
|
||||
if ((!$existing || !$existing->count) && $vcard->displayname) {
|
||||
$existing = $CONTACTS->search('name', $vcard->displayname, 1, false);
|
||||
}
|
||||
if ($existing && $existing->count) {
|
||||
self::$stats->skipped++;
|
||||
self::$stats->skipped_names[] = $vcard->displayname ?: $email;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$a_record['vcard'] = $vcard->export();
|
||||
|
||||
$plugin = $rcmail->plugins->exec_hook('contact_create', ['record' => $a_record, 'source' => null]);
|
||||
$a_record = $plugin['record'];
|
||||
|
||||
// insert record and send response
|
||||
if (empty($plugin['abort'])) {
|
||||
$success = $CONTACTS->insert($a_record);
|
||||
}
|
||||
else {
|
||||
$success = $plugin['result'];
|
||||
}
|
||||
|
||||
if ($success) {
|
||||
// assign groups for this contact (if enabled)
|
||||
if ($with_groups && !empty($a_record['groups'])) {
|
||||
foreach (explode(',', $a_record['groups'][0]) as $group_name) {
|
||||
if ($group_id = self::import_group_id($group_name, $CONTACTS, $with_groups == 1, $import_groups)) {
|
||||
$CONTACTS->add_to_group($group_id, $success);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self::$stats->inserted++;
|
||||
self::$stats->names[] = $a_record['name'] ?: $email;
|
||||
}
|
||||
else {
|
||||
self::$stats->errors++;
|
||||
}
|
||||
}
|
||||
|
||||
$importstep = 'import_confirm';
|
||||
$_SESSION['contactcsvimport'] = null;
|
||||
|
||||
$rcmail->output->command('parent.import_state_set', self::$stats->inserted ? 'reload' : 'ok');
|
||||
}
|
||||
else {
|
||||
if ($upload_error == self::UPLOAD_ERR_CSV_FIELDS) {
|
||||
$rcmail->output->show_message('csvfilemismatch', 'error');
|
||||
}
|
||||
else {
|
||||
self::upload_error($upload_error);
|
||||
}
|
||||
|
||||
$rcmail->output->command('parent.import_state_set', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
$rcmail->output->set_pagetitle($rcmail->gettext('importcontacts'));
|
||||
|
||||
$rcmail->output->add_handlers([
|
||||
'importstep' => [$this, $importstep],
|
||||
]);
|
||||
|
||||
// render page
|
||||
if ($rcmail->output->template_exists('contactimport')) {
|
||||
$rcmail->output->send('contactimport');
|
||||
}
|
||||
else {
|
||||
$rcmail->output->send('importcontacts'); // deprecated
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler function to display the import/upload form
|
||||
*/
|
||||
public static function import_form($attrib)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$target = rcube_utils::get_input_string('_target', rcube_utils::INPUT_GPC);
|
||||
|
||||
$attrib += ['id' => 'rcmImportForm'];
|
||||
|
||||
$writable_books = $rcmail->get_address_sources(true, true);
|
||||
$max_filesize = self::upload_init();
|
||||
|
||||
$form = '';
|
||||
$hint = $rcmail->gettext(['id' => 'importfile', 'name' => 'maxuploadsize', 'vars' => ['size' => $max_filesize]]);
|
||||
$table = new html_table(['cols' => 2]);
|
||||
$upload = new html_inputfield([
|
||||
'type' => 'file',
|
||||
'name' => '_file[]',
|
||||
'id' => 'rcmimportfile',
|
||||
'size' => 40,
|
||||
'multiple' => 'multiple',
|
||||
'class' => 'form-control-file',
|
||||
]);
|
||||
|
||||
$table->add('title', html::label('rcmimportfile', $rcmail->gettext('importfromfile')));
|
||||
$table->add(null, $upload->show() . html::div('hint', $hint));
|
||||
|
||||
// addressbook selector
|
||||
if (count($writable_books) > 1) {
|
||||
$select = new html_select([
|
||||
'name' => '_target',
|
||||
'id' => 'rcmimporttarget',
|
||||
'is_escaped' => true,
|
||||
'class' => 'custom-select'
|
||||
]);
|
||||
|
||||
foreach ($writable_books as $book) {
|
||||
$select->add($book['name'], $book['id']);
|
||||
}
|
||||
|
||||
$table->add('title', html::label('rcmimporttarget', $rcmail->gettext('importtarget')));
|
||||
$table->add(null, $select->show($target));
|
||||
}
|
||||
else {
|
||||
$abook = new html_hiddenfield(['name' => '_target', 'value' => key($writable_books)]);
|
||||
$form .= $abook->show();
|
||||
}
|
||||
|
||||
$form .= html::tag('input', ['type' => 'hidden', 'name' => '_unlock', 'value' => '']);
|
||||
|
||||
// selector for group import options
|
||||
if (count($writable_books) >= 1 || $writable_books[0]->groups) {
|
||||
$select = new html_select([
|
||||
'name' => '_groups',
|
||||
'id' => 'rcmimportgroups',
|
||||
'is_escaped' => true,
|
||||
'class' => 'custom-select'
|
||||
]);
|
||||
$select->add($rcmail->gettext('none'), '0');
|
||||
$select->add($rcmail->gettext('importgroupsall'), '1');
|
||||
$select->add($rcmail->gettext('importgroupsexisting'), '2');
|
||||
|
||||
$table->add('title', html::label('rcmimportgroups', $rcmail->gettext('importgroups')));
|
||||
$table->add(null, $select->show(rcube_utils::get_input_value('_groups', rcube_utils::INPUT_GPC)));
|
||||
}
|
||||
|
||||
// checkbox to replace the entire address book
|
||||
$check_replace = new html_checkbox(['name' => '_replace', 'value' => 1, 'id' => 'rcmimportreplace']);
|
||||
$table->add('title', html::label('rcmimportreplace', $rcmail->gettext('importreplace')));
|
||||
$table->add(null, $check_replace->show(rcube_utils::get_input_string('_replace', rcube_utils::INPUT_GPC)));
|
||||
|
||||
$form .= $table->show(['id' => null] + $attrib);
|
||||
|
||||
// remove any info left over info from previous import attempts
|
||||
$_SESSION['contactcsvimport'] = null;
|
||||
|
||||
$rcmail->output->set_env('writable_source', !empty($writable_books));
|
||||
$rcmail->output->add_label('selectimportfile','importwait');
|
||||
$rcmail->output->add_gui_object('importform', $attrib['id']);
|
||||
|
||||
$attrib = [
|
||||
'action' => $rcmail->url('import'),
|
||||
'method' => 'post',
|
||||
'enctype' => 'multipart/form-data'
|
||||
] + $attrib;
|
||||
|
||||
return html::p(null, rcube::Q($rcmail->gettext('importdesc'), 'show'))
|
||||
. $rcmail->output->form_tag($attrib, $form);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the field mapping page for the CSV import process
|
||||
*/
|
||||
public static function import_map($attrib)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$params = $_SESSION['contactcsvimport']['params'];
|
||||
|
||||
// hide groups field from list when group import disabled
|
||||
if (empty($params['with_groups'])) {
|
||||
unset($params['fields']['groups']);
|
||||
}
|
||||
|
||||
$fieldlist = new html_select(['name' => '_map[]']);
|
||||
$fieldlist->add($rcmail->gettext('fieldnotmapped'), '');
|
||||
foreach ($params['fields'] as $id => $name) {
|
||||
$fieldlist->add($name, $id);
|
||||
}
|
||||
|
||||
$field_table = new html_table(['cols' => 2] + $attrib);
|
||||
|
||||
if ($classes = $attrib['table-header-class']) {
|
||||
$field_table->set_header_attribs($classes);
|
||||
}
|
||||
|
||||
$field_table->add_header($attrib['table-col-source-class'] ?: null, $rcmail->gettext('source'));
|
||||
$field_table->add_header($attrib['table-col-destination-class'] ?: null, $rcmail->gettext('destination'));
|
||||
|
||||
$map = $_SESSION['contactcsvimport']['map'];
|
||||
foreach ($map['source'] as $i => $name) {
|
||||
$field_table->add('title', html::label('rcmimportmap' . $i, rcube::Q($name)));
|
||||
$field_table->add(null, $fieldlist->show(array_key_exists($i, $map['destination']) ? $map['destination'][$i] : '', ['id' => 'rcmimportmap' . $i]));
|
||||
}
|
||||
|
||||
$form = '';
|
||||
$form .= html::tag('input', ['type' => 'hidden', 'name' => '_unlock', 'value' => '']);
|
||||
|
||||
// show option to import data from first line of the file
|
||||
$check_header = new html_checkbox(['name' => '_skip_header', 'value' => 1, 'id' => 'rcmskipheader']);
|
||||
$form .= html::p(null, html::label('rcmskipheader', $check_header->show(1) . $rcmail->gettext('skipheader')));
|
||||
|
||||
$form .= $field_table->show();
|
||||
|
||||
$attrib = ['action' => $rcmail->url('import'), 'method' => 'post'] + $attrib + ['id' => 'rcmImportFormMap'];
|
||||
|
||||
$rcmail->output->add_gui_object('importformmap', $attrib['id']);
|
||||
|
||||
return html::p(null, rcube::Q($rcmail->gettext('importmapdesc'), 'show'))
|
||||
. $rcmail->output->form_tag($attrib, $form);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the confirmation page for the import process
|
||||
*/
|
||||
public static function import_confirm($attrib)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$vars = get_object_vars(self::$stats);
|
||||
$vars['names'] = $vars['skipped_names'] = '';
|
||||
|
||||
$content = html::p(null, $rcmail->gettext([
|
||||
'name' => 'importconfirm',
|
||||
'nr' => self::$stats->inserted,
|
||||
'vars' => $vars,
|
||||
]) . (self::$stats->names ? ':' : '.')
|
||||
);
|
||||
|
||||
if (self::$stats->names) {
|
||||
$content .= html::p('em', join(', ', array_map(['rcube', 'Q'], self::$stats->names)));
|
||||
}
|
||||
|
||||
if (self::$stats->skipped) {
|
||||
$content .= html::p(null, $rcmail->gettext([
|
||||
'name' => 'importconfirmskipped',
|
||||
'nr' => self::$stats->skipped,
|
||||
'vars' => $vars,
|
||||
]) . ':')
|
||||
. html::p('em', join(', ', array_map(['rcube', 'Q'], self::$stats->skipped_names)));
|
||||
}
|
||||
|
||||
return html::div($attrib, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the matching group id. If group doesn't exist, it'll be created if allowed.
|
||||
*/
|
||||
public static function import_group_id($group_name, $contacts, $create, &$import_groups)
|
||||
{
|
||||
$group_id = 0;
|
||||
foreach ($import_groups as $group) {
|
||||
if (strtolower($group['name']) === strtolower($group_name)) {
|
||||
$group_id = $group['ID'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// create a new group
|
||||
if (!$group_id && $create) {
|
||||
$new_group = $contacts->create_group($group_name);
|
||||
|
||||
if (empty($new_group['ID'])) {
|
||||
$new_group['ID'] = $new_group['id'];
|
||||
}
|
||||
|
||||
$import_groups[] = $new_group;
|
||||
$group_id = $new_group['ID'];
|
||||
}
|
||||
|
||||
return $group_id;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Send contacts list to client (as remote response) |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_list extends rcmail_action_contacts_index
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
if (!empty($_GET['_page'])) {
|
||||
$page = intval($_GET['_page']);
|
||||
}
|
||||
else {
|
||||
$page = !empty($_SESSION['page']) ? $_SESSION['page'] : 1;
|
||||
}
|
||||
|
||||
$_SESSION['page'] = $page;
|
||||
|
||||
$page_size = $rcmail->config->get('addressbook_pagesize', $rcmail->config->get('pagesize', 50));
|
||||
$group_data = null;
|
||||
|
||||
// Use search result
|
||||
if (($records = self::search_update(true)) !== false) {
|
||||
// sort the records
|
||||
ksort($records, SORT_LOCALE_STRING);
|
||||
|
||||
// create resultset object
|
||||
$count = count($records);
|
||||
$first = ($page-1) * $page_size;
|
||||
$result = new rcube_result_set($count, $first);
|
||||
|
||||
// we need only records for current page
|
||||
if ($page_size < $count) {
|
||||
$records = array_slice($records, $first, $page_size);
|
||||
}
|
||||
|
||||
$result->records = array_values($records);
|
||||
}
|
||||
// List selected directory
|
||||
else {
|
||||
$afields = $rcmail->config->get('contactlist_fields');
|
||||
$contacts = self::contact_source(null, true);
|
||||
|
||||
// get contacts for this user
|
||||
$result = $contacts->list_records($afields);
|
||||
|
||||
if (!$result->count && $result->searchonly) {
|
||||
$rcmail->output->show_message('contactsearchonly', 'notice');
|
||||
// Don't invoke advanced search dialog automatically from here (#6679)
|
||||
}
|
||||
|
||||
if (!empty($contacts->group_id)) {
|
||||
$group_data = ['ID' => $contacts->group_id]
|
||||
+ array_intersect_key((array) $contacts->get_group($contacts->group_id), ['name' => 1,'email' => 1]);
|
||||
}
|
||||
}
|
||||
|
||||
$rcmail->output->command('set_group_prop', $group_data);
|
||||
|
||||
// update message count display
|
||||
$rcmail->output->set_env('pagecount', ceil($result->count / $page_size));
|
||||
$rcmail->output->command('set_rowcount', self::get_rowcount_text($result));
|
||||
|
||||
// create javascript list
|
||||
self::js_contacts_list($result);
|
||||
|
||||
// send response
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Compose a recipient list with all selected contacts |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_mailto extends rcmail_action_contacts_index
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$cids = self::get_cids();
|
||||
$mailto = [];
|
||||
$sources = [];
|
||||
|
||||
foreach ($cids as $source => $cid) {
|
||||
$contacts = $rcmail->get_address_book($source);
|
||||
|
||||
if ($contacts->ready) {
|
||||
$contacts->set_page(1);
|
||||
$contacts->set_pagesize(count($cid) + 2); // +2 to skip counting query
|
||||
$sources[] = $contacts->search($contacts->primary_key, $cid, 0, true, true, 'email');
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($_REQUEST['_gid']) && isset($_REQUEST['_source'])) {
|
||||
$source = rcube_utils::get_input_string('_source', rcube_utils::INPUT_GP);
|
||||
$group_id = rcube_utils::get_input_string('_gid', rcube_utils::INPUT_GP);
|
||||
|
||||
$contacts = $rcmail->get_address_book($source);
|
||||
$group_data = $contacts->get_group($group_id);
|
||||
|
||||
// group has an email address assigned: use that
|
||||
if (!empty($group_data['email'])) {
|
||||
$mailto[] = format_email_recipient($group_data['email'][0], $group_data['name']);
|
||||
}
|
||||
else if ($contacts->ready) {
|
||||
$maxnum = (int) $rcmail->config->get('max_group_members');
|
||||
|
||||
$contacts->set_group($group_id);
|
||||
$contacts->set_page(1);
|
||||
$contacts->set_pagesize($maxnum ?: 999);
|
||||
$sources[] = $contacts->list_records();
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($sources as $source) {
|
||||
while (is_object($source) && ($rec = $source->iterate())) {
|
||||
$emails = rcube_addressbook::get_col_values('email', $rec, true);
|
||||
|
||||
if (!empty($emails)) {
|
||||
$mailto[] = format_email_recipient($emails[0], $rec['name']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($mailto)) {
|
||||
$mailto_str = join(', ', $mailto);
|
||||
$mailto_id = substr(md5($mailto_str), 0, 16);
|
||||
$_SESSION['mailto'][$mailto_id] = urlencode($mailto_str);
|
||||
$rcmail->output->command('open_compose_step', ['_mailto' => $mailto_id]);
|
||||
}
|
||||
else {
|
||||
$rcmail->output->show_message('nocontactsfound', 'warning');
|
||||
}
|
||||
|
||||
// send response
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Move a contact record from one directory to another |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_move extends rcmail_action_contacts_index
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$cids = self::get_cids();
|
||||
$target = rcube_utils::get_input_string('_to', rcube_utils::INPUT_POST);
|
||||
$target_group = rcube_utils::get_input_string('_togid', rcube_utils::INPUT_POST);
|
||||
|
||||
$rcmail = rcmail::get_instance();
|
||||
$all = 0;
|
||||
$deleted = 0;
|
||||
$success = 0;
|
||||
$errormsg = 'moveerror';
|
||||
$maxnum = $rcmail->config->get('max_group_members', 0);
|
||||
$page_size = $rcmail->config->get('addressbook_pagesize', $rcmail->config->get('pagesize', 50));
|
||||
$page = !empty($_SESSION['page']) ? $_SESSION['page'] : 1;
|
||||
|
||||
foreach ($cids as $source => $source_cids) {
|
||||
// Something wrong, target not specified
|
||||
if (!strlen($target)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// It might happen when moving records from search result
|
||||
// Do nothing, go to the next source
|
||||
if ((string) $target === (string) $source) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$CONTACTS = $rcmail->get_address_book($source);
|
||||
$TARGET = $rcmail->get_address_book($target);
|
||||
|
||||
if (!$TARGET || !$TARGET->ready || $TARGET->readonly) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$CONTACTS || !$CONTACTS->ready || ($CONTACTS->readonly && empty($CONTACTS->deletable))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$ids = [];
|
||||
|
||||
foreach ($source_cids as $idx => $cid) {
|
||||
$record = $CONTACTS->get_record($cid, true);
|
||||
|
||||
// avoid moving groups
|
||||
if (isset($record['_type']) && $record['_type'] == 'group') {
|
||||
unset($source_cids[$idx]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if contact exists, if so, we'll need it's ID
|
||||
// Note: Some addressbooks allows empty email address field
|
||||
// @TODO: should we check all email addresses?
|
||||
$email = $CONTACTS->get_col_values('email', $record, true);
|
||||
|
||||
if (!empty($email)) {
|
||||
$result = $TARGET->search('email', $email[0], 1, true, true);
|
||||
}
|
||||
else if (!empty($record['name'])) {
|
||||
$result = $TARGET->search('name', $record['name'], 1, true, true);
|
||||
}
|
||||
else {
|
||||
$result = new rcube_result_set();
|
||||
}
|
||||
|
||||
// insert contact record
|
||||
if (!$result->count) {
|
||||
$plugin = $rcmail->plugins->exec_hook('contact_create', [
|
||||
'record' => $record,
|
||||
'source' => $target,
|
||||
'group' => $target_group
|
||||
]);
|
||||
|
||||
if (empty($plugin['abort'])) {
|
||||
if ($insert_id = $TARGET->insert($plugin['record'], false)) {
|
||||
$ids[] = $insert_id;
|
||||
$success++;
|
||||
}
|
||||
}
|
||||
else if (!empty($plugin['result'])) {
|
||||
$ids = array_merge($ids, $plugin['result']);
|
||||
$success++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$record = $result->first();
|
||||
$ids[] = $record['ID'];
|
||||
$errormsg = empty($email) ? 'contactnameexists' : 'contactexists';
|
||||
}
|
||||
}
|
||||
|
||||
// remove source contacts
|
||||
if ($success && !empty($source_cids)) {
|
||||
$all += count($source_cids);
|
||||
$plugin = $rcmail->plugins->exec_hook('contact_delete', [
|
||||
'id' => $source_cids,
|
||||
'source' => $source
|
||||
]);
|
||||
|
||||
$del_status = !$plugin['abort'] ? $CONTACTS->delete($source_cids) : $plugin['result'];
|
||||
|
||||
if ($del_status) {
|
||||
$deleted += $del_status;
|
||||
}
|
||||
}
|
||||
|
||||
// assign to group
|
||||
if ($target_group && $TARGET->groups && !empty($ids)) {
|
||||
$plugin = $rcmail->plugins->exec_hook('group_addmembers', [
|
||||
'group_id' => $target_group,
|
||||
'ids' => $ids,
|
||||
'source' => $target
|
||||
]);
|
||||
|
||||
if (empty($plugin['abort'])) {
|
||||
$TARGET->reset();
|
||||
$TARGET->set_group($target_group);
|
||||
|
||||
if ($maxnum && ($TARGET->count()->count + count($plugin['ids']) > $maxnum)) {
|
||||
$rcmail->output->show_message('maxgroupmembersreached', 'warning', ['max' => $maxnum]);
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
if (($cnt = $TARGET->add_to_group($target_group, $plugin['ids'])) && $cnt > $success) {
|
||||
$success = $cnt;
|
||||
}
|
||||
}
|
||||
else if ($plugin['result']) {
|
||||
$success = $plugin['result'];
|
||||
}
|
||||
|
||||
$errormsg = !empty($plugin['message']) ? $plugin['message'] : 'moveerror';
|
||||
}
|
||||
}
|
||||
|
||||
if (!$deleted || $deleted != $all) {
|
||||
$rcmail->output->command('list_contacts');
|
||||
}
|
||||
else {
|
||||
// update saved search after data changed
|
||||
if (($records = self::search_update(true)) !== false) {
|
||||
// create resultset object
|
||||
$count = count($records);
|
||||
$first = ($page-1) * $page_size;
|
||||
$result = new rcube_result_set($count, $first);
|
||||
$pages = ceil((count($records) + $deleted) / $page_size);
|
||||
|
||||
// last page and it's empty, display previous one
|
||||
if ($result->count && $result->count <= ($page_size * ($page - 1))) {
|
||||
$rcmail->output->command('list_page', 'prev');
|
||||
$rowcount = $rcmail->gettext('loading');
|
||||
}
|
||||
// get records from the next page to add to the list
|
||||
else if ($pages > 1 && $page < $pages) {
|
||||
// sort the records
|
||||
ksort($records, SORT_LOCALE_STRING);
|
||||
|
||||
$first += $page_size;
|
||||
// create resultset object
|
||||
$res = new rcube_result_set($count, $first - $deleted);
|
||||
|
||||
if ($page_size < $count) {
|
||||
$records = array_slice($records, $first - $deleted, $deleted);
|
||||
}
|
||||
|
||||
$res->records = array_values($records);
|
||||
$records = $res;
|
||||
}
|
||||
else {
|
||||
unset($records);
|
||||
}
|
||||
}
|
||||
else if (isset($CONTACTS)) {
|
||||
// count contacts for this user
|
||||
$result = $CONTACTS->count();
|
||||
$pages = ceil(($result->count + $deleted) / $page_size);
|
||||
|
||||
// last page and it's empty, display previous one
|
||||
if ($result->count && $result->count <= ($page_size * ($page - 1))) {
|
||||
$rcmail->output->command('list_page', 'prev');
|
||||
$rowcount = $rcmail->gettext('loading');
|
||||
}
|
||||
// get records from the next page to add to the list
|
||||
else if ($pages > 1 && $page < $pages) {
|
||||
$CONTACTS->set_page($page);
|
||||
$records = $CONTACTS->list_records(null, -$deleted);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($rowcount)) {
|
||||
$rowcount = isset($result) ? self::get_rowcount_text($result) : 0;
|
||||
}
|
||||
|
||||
// update message count display
|
||||
$rcmail->output->set_env('pagecount', isset($result) ? ceil($result->count / $page_size) : 0);
|
||||
$rcmail->output->command('set_rowcount', $rowcount);
|
||||
|
||||
// add new rows from next page (if any)
|
||||
if (!empty($records)) {
|
||||
self::js_contacts_list($records);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$success) {
|
||||
$rcmail->output->show_message($errormsg, 'error');
|
||||
}
|
||||
else {
|
||||
$rcmail->output->show_message('movesuccess', 'confirmation', ['nr' => $success]);
|
||||
}
|
||||
|
||||
// send response
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Show contact photo |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_photo extends rcmail_action_contacts_index
|
||||
{
|
||||
protected static $mode = self::MODE_HTTP;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
// Get contact ID and source ID from request
|
||||
$cids = self::get_cids();
|
||||
$source = key($cids);
|
||||
$cid = $cids ? array_first($cids[$source]) : null;
|
||||
$file_id = rcube_utils::get_input_string('_photo', rcube_utils::INPUT_GPC);
|
||||
|
||||
// read the referenced file
|
||||
if ($file_id && !empty($_SESSION['contacts']['files'][$file_id])) {
|
||||
$tempfile = $_SESSION['contacts']['files'][$file_id];
|
||||
$tempfile = $rcmail->plugins->exec_hook('attachment_display', $tempfile);
|
||||
|
||||
if (!empty($tempfile['status'])) {
|
||||
if (!empty($tempfile['data'])) {
|
||||
$data = $tempfile['data'];
|
||||
}
|
||||
else if ($tempfile['path']) {
|
||||
$data = file_get_contents($tempfile['path']);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// by email, search for contact first
|
||||
if ($email = rcube_utils::get_input_string('_email', rcube_utils::INPUT_GPC)) {
|
||||
foreach ($rcmail->get_address_sources() as $s) {
|
||||
$abook = $rcmail->get_address_book($s['id']);
|
||||
$result = $abook->search(['email'], $email, 1, true, true, 'photo');
|
||||
while ($result && ($record = $result->iterate())) {
|
||||
if (!empty($record['photo'])) {
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// by contact id
|
||||
if (empty($record) && $cid) {
|
||||
// Initialize addressbook source
|
||||
$CONTACTS = self::contact_source($source, true);
|
||||
// read contact record
|
||||
$record = $CONTACTS->get_record($cid, true);
|
||||
}
|
||||
|
||||
if (!empty($record['photo'])) {
|
||||
$data = is_array($record['photo']) ? $record['photo'][0] : $record['photo'];
|
||||
if (!preg_match('![^a-z0-9/=+-]!i', $data)) {
|
||||
$data = base64_decode($data, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// let plugins do fancy things with contact photos
|
||||
$plugin = $rcmail->plugins->exec_hook('contact_photo', [
|
||||
'record' => $record ?? null,
|
||||
'email' => $email ?? null,
|
||||
'data' => $data ?? null,
|
||||
]);
|
||||
|
||||
// redirect to url provided by a plugin
|
||||
if (!empty($plugin['url'])) {
|
||||
$rcmail->output->redirect($plugin['url']);
|
||||
}
|
||||
|
||||
$data = $plugin['data'];
|
||||
|
||||
// detect if photo data is a URL
|
||||
if ($data && strlen($data) < 1024 && filter_var($data, FILTER_VALIDATE_URL)) {
|
||||
$rcmail->output->redirect($data);
|
||||
}
|
||||
|
||||
// cache for one day if requested by email
|
||||
if (!$cid && !empty($email)) {
|
||||
$rcmail->output->future_expire_header(86400);
|
||||
}
|
||||
|
||||
if ($data) {
|
||||
$rcmail->output->sendExit($data, ['Content-Type: ' . rcube_mime::image_content_type($data)]);
|
||||
}
|
||||
|
||||
if (!empty($_GET['_error'])) {
|
||||
$rcmail->output->sendExit('', ['HTTP/1.0 204 Photo not found']);
|
||||
}
|
||||
|
||||
$rcmail->output->sendExit(base64_decode(rcmail_output::BLANK_GIF), ['Content-Type: image/gif']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| Copyright (C) Kolab Systems AG |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Print contact details |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
| Author: Aleksander Machniak <machniak@kolabsys.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_print extends rcmail_action_contacts_index
|
||||
{
|
||||
protected static $mode = self::MODE_HTTP;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
// Get contact ID and source ID from request
|
||||
$cids = self::get_cids();
|
||||
$source = key($cids);
|
||||
$cid = $cids ? array_first($cids[$source]) : null;
|
||||
|
||||
// Initialize addressbook source
|
||||
self::$CONTACTS = self::contact_source($source, true);
|
||||
self::$SOURCE_ID = $source;
|
||||
|
||||
// read contact record
|
||||
if ($cid && self::$CONTACTS) {
|
||||
self::$contact = self::$CONTACTS->get_record($cid, true);
|
||||
}
|
||||
|
||||
$rcmail->output->add_handlers([
|
||||
'contacthead' => [$this, 'contact_head'],
|
||||
'contactdetails' => [$this, 'contact_details'],
|
||||
'contactphoto' => [$this, 'contact_photo'],
|
||||
]);
|
||||
|
||||
$rcmail->output->send('contactprint');
|
||||
}
|
||||
|
||||
public static function contact_head($attrib)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
// check if we have a valid result
|
||||
if (!self::$contact) {
|
||||
$rcmail->output->show_message('contactnotfound', 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
$form = [
|
||||
'head' => [ // section 'head' is magic!
|
||||
'name' => $rcmail->gettext('contactnameandorg'),
|
||||
'content' => [
|
||||
'prefix' => [],
|
||||
'name' => [],
|
||||
'firstname' => [],
|
||||
'middlename' => [],
|
||||
'surname' => [],
|
||||
'suffix' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
unset($attrib['name']);
|
||||
|
||||
return self::contact_form($form, self::$contact, $attrib);
|
||||
}
|
||||
|
||||
public static function contact_details($attrib)
|
||||
{
|
||||
// check if we have a valid result
|
||||
if (!self::$contact) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
$form = [
|
||||
'contact' => [
|
||||
'name' => $rcmail->gettext('properties'),
|
||||
'content' => [
|
||||
'organization' => [],
|
||||
'department' => [],
|
||||
'jobtitle' => [],
|
||||
'email' => [],
|
||||
'phone' => [],
|
||||
'address' => [],
|
||||
'website' => [],
|
||||
'im' => [],
|
||||
'groups' => [],
|
||||
],
|
||||
],
|
||||
'personal' => [
|
||||
'name' => $rcmail->gettext('personalinfo'),
|
||||
'content' => [
|
||||
'nickname' => [],
|
||||
'gender' => [],
|
||||
'maidenname' => [],
|
||||
'birthday' => [],
|
||||
'anniversary' => [],
|
||||
'manager' => [],
|
||||
'assistant' => [],
|
||||
'spouse' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
if (isset(rcmail_action_contacts_index::$CONTACT_COLTYPES['notes'])) {
|
||||
$form['notes'] = [
|
||||
'name' => $rcmail->gettext('notes'),
|
||||
'content' => [
|
||||
'notes' => ['type' => 'textarea', 'label' => false],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
if (self::$CONTACTS->groups) {
|
||||
$groups = self::$CONTACTS->get_record_groups(self::$contact['ID']);
|
||||
if (!empty($groups)) {
|
||||
$form['contact']['content']['groups'] = [
|
||||
'value' => rcube::Q(implode(', ', $groups)),
|
||||
'label' => $rcmail->gettext('groups')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return self::contact_form($form, self::$contact, $attrib);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Show contact data as QR code |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_qrcode extends rcmail_action_contacts_index
|
||||
{
|
||||
protected static $mode = self::MODE_HTTP;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
// Get contact ID and source ID from request
|
||||
$cids = self::get_cids();
|
||||
$source = key($cids);
|
||||
$cid = $cids ? array_first($cids[$source]) : null;
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
// read contact record
|
||||
$abook = self::contact_source($source, true);
|
||||
$contact = $abook->get_record($cid, true);
|
||||
|
||||
// generate QR code image
|
||||
if ($data = self::contact_qrcode($contact)) {
|
||||
$headers = [
|
||||
'Content-Type: ' . self::check_support(),
|
||||
'Content-Length: ' . strlen($data)
|
||||
];
|
||||
|
||||
$rcmail->output->sendExit($data, $headers);
|
||||
}
|
||||
|
||||
$rcmail->output->sendExit('', ['HTTP/1.0 404 Contact not found']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a QR-code image for a contact
|
||||
*
|
||||
* @param array $contact Contact record
|
||||
*
|
||||
* @return string|null Image content, Null on error or missing PHP extensions
|
||||
*/
|
||||
public static function contact_qrcode($contact)
|
||||
{
|
||||
if (empty($contact)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$type = self::check_support();
|
||||
|
||||
if (empty($type)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$vcard = new rcube_vcard();
|
||||
|
||||
// QR code input is limited, use only common fields
|
||||
$fields = ['name', 'firstname', 'surname', 'middlename', 'nickname',
|
||||
'organization', 'phone', 'email', 'jobtitle', 'prefix', 'suffix'];
|
||||
|
||||
foreach ($contact as $field => $value) {
|
||||
if (strpos($field, ':') !== false) {
|
||||
list($field, $section) = explode(':', $field, 2);
|
||||
}
|
||||
else {
|
||||
$section = null;
|
||||
}
|
||||
|
||||
if (in_array($field, $fields)) {
|
||||
foreach ((array) $value as $v) {
|
||||
$vcard->set($field, $v, $section);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$data = $vcard->export();
|
||||
|
||||
if (empty($data)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$renderer_style = new BaconQrCode\Renderer\RendererStyle\RendererStyle(300, 1);
|
||||
$renderer_image = $type == 'image/png'
|
||||
? new BaconQrCode\Renderer\Image\ImagickImageBackEnd()
|
||||
: new BaconQrCode\Renderer\Image\SvgImageBackEnd();
|
||||
|
||||
$renderer = new BaconQrCode\Renderer\ImageRenderer($renderer_style, $renderer_image);
|
||||
$writer = new BaconQrCode\Writer($renderer);
|
||||
|
||||
return $writer->writeString($data, RCUBE_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check required extensions and classes for QR code generation
|
||||
*
|
||||
* @return string|null Content-type of the image result
|
||||
*/
|
||||
public static function check_support()
|
||||
{
|
||||
if (extension_loaded('iconv') && class_exists('BaconQrCode\Renderer\ImageRenderer')) {
|
||||
if (extension_loaded('xmlwriter')) {
|
||||
return 'image/svg+xml';
|
||||
}
|
||||
|
||||
if (extension_loaded('imagick')) {
|
||||
return 'image/png';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Save a contact entry or to add a new one |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_save extends rcmail_action_contacts_index
|
||||
{
|
||||
protected static $mode = self::MODE_HTTP;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$contacts = self::contact_source(null, true);
|
||||
$cid = rcube_utils::get_input_string('_cid', rcube_utils::INPUT_POST);
|
||||
$source = rcube_utils::get_input_string('_source', rcube_utils::INPUT_GPC);
|
||||
$return_action = empty($cid) ? 'add' : 'edit';
|
||||
|
||||
// Source changed, display the form again
|
||||
if (!empty($_GET['_reload'])) {
|
||||
$rcmail->overwrite_action($return_action);
|
||||
return;
|
||||
}
|
||||
|
||||
// cannot edit record
|
||||
if (!$contacts || $contacts->readonly) {
|
||||
$rcmail->output->show_message('contactreadonly', 'error');
|
||||
$rcmail->overwrite_action($return_action);
|
||||
return;
|
||||
}
|
||||
|
||||
// read POST values into hash array
|
||||
$a_record = self::process_input();
|
||||
|
||||
// do input checks (delegated to $contacts instance)
|
||||
if (!$contacts->validate($a_record)) {
|
||||
$err = (array) $contacts->get_error();
|
||||
$rcmail->output->show_message(!empty($err['message']) ? rcube::Q($err['message']) : 'formincomplete', 'warning');
|
||||
$rcmail->overwrite_action($return_action, ['contact' => $a_record]);
|
||||
return;
|
||||
}
|
||||
|
||||
// get raw photo data if changed
|
||||
if (isset($a_record['photo'])) {
|
||||
if ($a_record['photo'] == '-del-') {
|
||||
$a_record['photo'] = '';
|
||||
}
|
||||
else if (!empty($_SESSION['contacts']['files'][$a_record['photo']])) {
|
||||
$tempfile = $_SESSION['contacts']['files'][$a_record['photo']];
|
||||
$tempfile = $rcmail->plugins->exec_hook('attachment_get', $tempfile);
|
||||
if ($tempfile['status']) {
|
||||
$a_record['photo'] = $tempfile['data'] ?: @file_get_contents($tempfile['path']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
unset($a_record['photo']);
|
||||
}
|
||||
|
||||
// cleanup session data
|
||||
$rcmail->plugins->exec_hook('attachments_cleanup', ['group' => 'contact']);
|
||||
$rcmail->session->remove('contacts');
|
||||
}
|
||||
|
||||
// update an existing contact
|
||||
if (!empty($cid)) {
|
||||
$plugin = $rcmail->plugins->exec_hook('contact_update', [
|
||||
'id' => $cid,
|
||||
'record' => $a_record,
|
||||
'source' => $source
|
||||
]);
|
||||
|
||||
$a_record = $plugin['record'];
|
||||
|
||||
if (!$plugin['abort']) {
|
||||
$result = $contacts->update($cid, $a_record);
|
||||
}
|
||||
else {
|
||||
$result = $plugin['result'];
|
||||
}
|
||||
|
||||
if ($result) {
|
||||
// show confirmation
|
||||
$rcmail->output->show_message('successfullysaved', 'confirmation', null, false);
|
||||
|
||||
// in search mode, just reload the list (#1490015)
|
||||
if (!empty($_REQUEST['_search'])) {
|
||||
$rcmail->output->command('parent.command', 'list');
|
||||
$rcmail->output->send('iframe');
|
||||
}
|
||||
|
||||
$newcid = null;
|
||||
|
||||
// LDAP DN change
|
||||
if (is_string($result) && strlen($result) > 1) {
|
||||
$newcid = $result;
|
||||
// change cid in POST for 'show' action
|
||||
$_POST['_cid'] = $newcid;
|
||||
}
|
||||
|
||||
// refresh contact data for list update and 'show' action
|
||||
$contact = $contacts->get_record($newcid ?: $cid, true);
|
||||
|
||||
// Plugins can decide to remove the contact on edit, e.g. automatic_addressbook
|
||||
// Best we can do is to refresh the list (#5522)
|
||||
if (empty($contact)) {
|
||||
$rcmail->output->command('parent.command', 'list');
|
||||
$rcmail->output->send('iframe');
|
||||
}
|
||||
|
||||
// Update contacts list
|
||||
$a_js_cols = [];
|
||||
$record = $contact;
|
||||
$record['email'] = array_first($contacts->get_col_values('email', $record, true));
|
||||
$record['name'] = rcube_addressbook::compose_list_name($record);
|
||||
|
||||
foreach (['name'] as $col) {
|
||||
$a_js_cols[] = rcube::Q((string) $record[$col]);
|
||||
}
|
||||
|
||||
// performance: unset some big data items we don't need here
|
||||
$record = array_intersect_key($record, ['ID' => 1,'email' => 1,'name' => 1]);
|
||||
$record['_type'] = 'person';
|
||||
|
||||
// update the changed col in list
|
||||
$rcmail->output->command('parent.update_contact_row', $cid, $a_js_cols, $newcid, $source, $record);
|
||||
|
||||
$rcmail->overwrite_action('show', ['contact' => $contact]);
|
||||
}
|
||||
else {
|
||||
// show error message
|
||||
$error = self::error_str($contacts, $plugin);
|
||||
|
||||
$rcmail->output->show_message($error, 'error', null, false);
|
||||
$rcmail->overwrite_action('show');
|
||||
}
|
||||
}
|
||||
// insert a new contact
|
||||
else {
|
||||
// Name of the addressbook already selected on the list
|
||||
$orig_source = rcube_utils::get_input_string('_orig_source', rcube_utils::INPUT_GPC);
|
||||
|
||||
if (!strlen($source)) {
|
||||
$source = $orig_source;
|
||||
}
|
||||
|
||||
// show notice if existing contacts with same e-mail are found
|
||||
foreach ($contacts->get_col_values('email', $a_record, true) as $email) {
|
||||
if ($email && ($res = $contacts->search('email', $email, 1, false, true)) && $res->count) {
|
||||
$rcmail->output->show_message('contactexists', 'notice', null, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$plugin = $rcmail->plugins->exec_hook('contact_create', [
|
||||
'record' => $a_record,
|
||||
'source' => $source
|
||||
]);
|
||||
|
||||
$a_record = $plugin['record'];
|
||||
|
||||
// insert record and send response
|
||||
if (!$plugin['abort']) {
|
||||
$insert_id = $contacts->insert($a_record);
|
||||
}
|
||||
else {
|
||||
$insert_id = $plugin['result'];
|
||||
}
|
||||
|
||||
if ($insert_id) {
|
||||
$contacts->reset();
|
||||
|
||||
// add new contact to the specified group
|
||||
if ($contacts->groups && $contacts->group_id) {
|
||||
$plugin = $rcmail->plugins->exec_hook('group_addmembers', [
|
||||
'group_id' => $contacts->group_id,
|
||||
'ids' => $insert_id,
|
||||
'source' => $source
|
||||
]);
|
||||
|
||||
if (!$plugin['abort']) {
|
||||
if (($maxnum = $rcmail->config->get('max_group_members', 0)) && ($contacts->count()->count + 1 > $maxnum)) {
|
||||
// @FIXME: should we remove the contact?
|
||||
$msgtext = $rcmail->gettext(['name' => 'maxgroupmembersreached', 'vars' => ['max' => $maxnum]]);
|
||||
$rcmail->output->command('parent.display_message', $msgtext, 'warning');
|
||||
}
|
||||
else {
|
||||
$contacts->add_to_group($plugin['group_id'], $plugin['ids']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// show confirmation
|
||||
$rcmail->output->show_message('successfullysaved', 'confirmation', null, false);
|
||||
|
||||
$rcmail->output->command('parent.set_rowcount', $rcmail->gettext('loading'));
|
||||
$rcmail->output->command('parent.list_contacts');
|
||||
|
||||
$rcmail->output->send('iframe');
|
||||
}
|
||||
else {
|
||||
// show error message
|
||||
$error = self::error_str($contacts, $plugin);
|
||||
$rcmail->output->show_message($error, 'error', null, false);
|
||||
$rcmail->overwrite_action('add');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function process_input()
|
||||
{
|
||||
$record = [];
|
||||
|
||||
foreach (rcmail_action_contacts_index::$CONTACT_COLTYPES as $col => $colprop) {
|
||||
if (!empty($colprop['composite'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fname = '_' . $col;
|
||||
|
||||
// gather form data of composite fields
|
||||
if (!empty($colprop['childs'])) {
|
||||
$values = [];
|
||||
foreach ($colprop['childs'] as $childcol => $cp) {
|
||||
$vals = rcube_utils::get_input_value('_' . $childcol, rcube_utils::INPUT_POST, true);
|
||||
foreach ((array) $vals as $i => $val) {
|
||||
$values[$i][$childcol] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_REQUEST['_subtype_' . $col])) {
|
||||
$subtypes = (array) rcube_utils::get_input_value('_subtype_' . $col, rcube_utils::INPUT_POST);
|
||||
}
|
||||
else {
|
||||
$subtypes = [''];
|
||||
}
|
||||
|
||||
foreach ($subtypes as $i => $subtype) {
|
||||
$suffix = $subtype ? ":$subtype" : '';
|
||||
if ($values[$i]) {
|
||||
$record[$col . $suffix][] = $values[$i];
|
||||
}
|
||||
}
|
||||
}
|
||||
// assign values and subtypes
|
||||
else if (isset($_POST[$fname]) && is_array($_POST[$fname])) {
|
||||
$values = rcube_utils::get_input_value($fname, rcube_utils::INPUT_POST, true);
|
||||
$subtypes = rcube_utils::get_input_value('_subtype_' . $col, rcube_utils::INPUT_POST);
|
||||
|
||||
foreach ($values as $i => $val) {
|
||||
if ($col == 'email') {
|
||||
// extract email from full address specification, e.g. "Name" <addr@domain.tld>
|
||||
$addr = rcube_mime::decode_address_list($val, 1, false);
|
||||
if (!empty($addr) && ($addr = array_pop($addr)) && $addr['mailto']) {
|
||||
$val = $addr['mailto'];
|
||||
}
|
||||
}
|
||||
|
||||
$subtype = $subtypes[$i] ? ':'.$subtypes[$i] : '';
|
||||
$record[$col.$subtype][] = $val;
|
||||
}
|
||||
}
|
||||
else if (isset($_POST[$fname])) {
|
||||
$record[$col] = rcube_utils::get_input_value($fname, rcube_utils::INPUT_POST, true);
|
||||
|
||||
// normalize the submitted date strings
|
||||
if ($colprop['type'] == 'date') {
|
||||
if ($record[$col] && ($dt = rcube_utils::anytodatetime($record[$col]))) {
|
||||
$record[$col] = $dt->format('Y-m-d');
|
||||
}
|
||||
else {
|
||||
unset($record[$col]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate contact's display name (must be before validation)
|
||||
if (empty($record['name'])) {
|
||||
$record['name'] = rcube_addressbook::compose_display_name($record, true);
|
||||
|
||||
// Reset it if equals to email address (from compose_display_name())
|
||||
$email = rcube_addressbook::get_col_values('email', $record, true);
|
||||
if (isset($email[0]) && $record['name'] == $email[0]) {
|
||||
$record['name'] = '';
|
||||
}
|
||||
}
|
||||
|
||||
return $record;
|
||||
}
|
||||
|
||||
public static function error_str($contacts, $plugin)
|
||||
{
|
||||
if (!empty($plugin['message'])) {
|
||||
return $plugin['message'];
|
||||
}
|
||||
|
||||
$err = $contacts->get_error();
|
||||
|
||||
if (!empty($err['message'])) {
|
||||
return $err['message'];
|
||||
}
|
||||
|
||||
return 'errorsaving';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| Copyright (C) Kolab Systems AG |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Search action (and form) for address book contacts |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
| Author: Aleksander Machniak <machniak@kolabsys.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_search extends rcmail_action_contacts_index
|
||||
{
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
if (empty($_GET['_form'])) {
|
||||
self::contact_search();
|
||||
}
|
||||
|
||||
$rcmail->output->add_handler('searchform', [$this, 'contact_search_form']);
|
||||
$rcmail->output->send('contactsearch');
|
||||
}
|
||||
|
||||
public static function contact_search()
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$adv = isset($_POST['_adv']);
|
||||
$sid = rcube_utils::get_input_string('_sid', rcube_utils::INPUT_GET);
|
||||
$search = null;
|
||||
|
||||
// get search criteria from saved search
|
||||
if ($sid && ($search = $rcmail->user->get_search($sid))) {
|
||||
$fields = $search['data']['fields'];
|
||||
$search = $search['data']['search'];
|
||||
}
|
||||
// get fields/values from advanced search form
|
||||
else if ($adv) {
|
||||
foreach (array_keys($_POST) as $key) {
|
||||
$s = trim(rcube_utils::get_input_string($key, rcube_utils::INPUT_POST, true));
|
||||
if (strlen($s) && preg_match('/^_search_([a-zA-Z0-9_-]+)$/', $key, $m)) {
|
||||
$search[] = $s;
|
||||
$fields[] = $m[1];
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($fields)) {
|
||||
// do nothing, show the form again
|
||||
return;
|
||||
}
|
||||
}
|
||||
// quick-search
|
||||
else {
|
||||
$search = trim(rcube_utils::get_input_string('_q', rcube_utils::INPUT_GET, true));
|
||||
$fields = rcube_utils::get_input_string('_headers', rcube_utils::INPUT_GET);
|
||||
|
||||
if (empty($fields)) {
|
||||
$fields = array_keys(self::$SEARCH_MODS_DEFAULT);
|
||||
}
|
||||
else {
|
||||
$fields = array_filter(explode(',', $fields));
|
||||
}
|
||||
|
||||
// update search_mods setting
|
||||
$old_mods = $rcmail->config->get('addressbook_search_mods');
|
||||
$search_mods = array_fill_keys($fields, 1);
|
||||
|
||||
if ($old_mods != $search_mods) {
|
||||
$rcmail->user->save_prefs(['addressbook_search_mods' => $search_mods]);
|
||||
}
|
||||
|
||||
if (in_array('*', $fields)) {
|
||||
$fields = '*';
|
||||
}
|
||||
}
|
||||
|
||||
// Values matching mode
|
||||
$mode = (int) $rcmail->config->get('addressbook_search_mode');
|
||||
$mode |= rcube_addressbook::SEARCH_GROUPS;
|
||||
|
||||
// get sources list
|
||||
$sources = $rcmail->get_address_sources();
|
||||
$sort_col = $rcmail->config->get('addressbook_sort_col', 'name');
|
||||
$afields = $rcmail->config->get('contactlist_fields');
|
||||
$page_size = $rcmail->config->get('addressbook_pagesize', $rcmail->config->get('pagesize', 50));
|
||||
$search_set = [];
|
||||
$records = [];
|
||||
|
||||
foreach ($sources as $s) {
|
||||
$source = $rcmail->get_address_book($s['id']);
|
||||
|
||||
// check if search fields are supported....
|
||||
if (is_array($fields)) {
|
||||
$cols = !empty($source->coltypes[0]) ? array_flip($source->coltypes) : $source->coltypes;
|
||||
$supported = 0;
|
||||
|
||||
foreach ($fields as $f) {
|
||||
if (array_key_exists($f, $cols)) {
|
||||
$supported ++;
|
||||
}
|
||||
}
|
||||
|
||||
// in advanced search we require all fields (AND operator)
|
||||
// in quick search we require at least one field (OR operator)
|
||||
if (($adv && $supported < count($fields)) || (!$adv && !$supported)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// reset page
|
||||
$source->set_page(1);
|
||||
$source->set_pagesize(9999);
|
||||
|
||||
// get contacts count
|
||||
$result = $source->search($fields, $search, $mode, false);
|
||||
|
||||
if (empty($result) || !$result->count) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// get records
|
||||
$result = $source->list_records($afields);
|
||||
|
||||
while ($row = $result->next()) {
|
||||
$row['sourceid'] = $s['id'];
|
||||
$key = rcube_addressbook::compose_contact_key($row, $sort_col);
|
||||
$records[$key] = $row;
|
||||
}
|
||||
|
||||
unset($result);
|
||||
$search_set[$s['id']] = $source->get_search_set();
|
||||
}
|
||||
|
||||
// sort the records
|
||||
ksort($records, SORT_LOCALE_STRING);
|
||||
|
||||
// create resultset object
|
||||
$count = count($records);
|
||||
$result = new rcube_result_set($count);
|
||||
|
||||
// cut first-page records
|
||||
if ($page_size < $count) {
|
||||
$records = array_slice($records, 0, $page_size);
|
||||
}
|
||||
|
||||
$result->records = array_values($records);
|
||||
|
||||
// search request ID
|
||||
$search_request = md5('addr'
|
||||
. (is_array($fields) ? implode(',', $fields) : $fields)
|
||||
. (is_array($search) ? implode(',', $search) : $search)
|
||||
);
|
||||
|
||||
// save search settings in session
|
||||
$_SESSION['contact_search'][$search_request] = $search_set;
|
||||
$_SESSION['contact_search_params'] = ['id' => $search_request, 'data' => [$fields, $search]];
|
||||
$_SESSION['page'] = 1;
|
||||
|
||||
if ($adv) {
|
||||
$rcmail->output->command('list_contacts_clear');
|
||||
}
|
||||
|
||||
if ($result->count > 0) {
|
||||
// create javascript list
|
||||
self::js_contacts_list($result);
|
||||
$rcmail->output->show_message('contactsearchsuccessful', 'confirmation', ['nr' => $result->count]);
|
||||
}
|
||||
else {
|
||||
$rcmail->output->show_message('nocontactsfound', 'notice');
|
||||
}
|
||||
|
||||
// update message count display
|
||||
$rcmail->output->set_env('search_request', $search_request);
|
||||
$rcmail->output->set_env('pagecount', ceil($result->count / $page_size));
|
||||
$rcmail->output->command('set_rowcount', self::get_rowcount_text($result));
|
||||
// Re-set current source
|
||||
$rcmail->output->set_env('search_id', $sid);
|
||||
$rcmail->output->set_env('source', '');
|
||||
$rcmail->output->set_env('group', '');
|
||||
// Re-set list header
|
||||
$rcmail->output->command('set_group_prop', null);
|
||||
|
||||
if (!$sid) {
|
||||
// unselect currently selected directory/group
|
||||
$rcmail->output->command('unselect_directory');
|
||||
// enable "Save search" command
|
||||
$rcmail->output->command('enable_command', 'search-create', true);
|
||||
}
|
||||
|
||||
$rcmail->output->command('update_group_commands');
|
||||
|
||||
// send response
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
public static function contact_search_form($attrib)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$i_size = !empty($attrib['size']) ? $attrib['size'] : 30;
|
||||
$short_labels = self::get_bool_attr($attrib, 'short-legend-labels');
|
||||
|
||||
$form = [
|
||||
'main' => [
|
||||
'name' => $rcmail->gettext('properties'),
|
||||
'content' => [],
|
||||
],
|
||||
'personal' => [
|
||||
'name' => $rcmail->gettext($short_labels ? 'personal' : 'personalinfo'),
|
||||
'content' => [],
|
||||
],
|
||||
'other' => [
|
||||
'name' => $rcmail->gettext('other'),
|
||||
'content' => [],
|
||||
],
|
||||
];
|
||||
|
||||
// get supported coltypes from all address sources
|
||||
$sources = $rcmail->get_address_sources();
|
||||
$coltypes = [];
|
||||
|
||||
foreach ($sources as $s) {
|
||||
$CONTACTS = $rcmail->get_address_book($s['id']);
|
||||
|
||||
if (!empty($CONTACTS->coltypes)) {
|
||||
$contact_cols = isset($CONTACTS->coltypes[0]) ? array_flip($CONTACTS->coltypes) : $CONTACTS->coltypes;
|
||||
$coltypes = array_merge($coltypes, $contact_cols);
|
||||
}
|
||||
}
|
||||
|
||||
// merge supported coltypes with global coltypes
|
||||
foreach ($coltypes as $col => $colprop) {
|
||||
if (!empty(rcmail_action_contacts_index::$CONTACT_COLTYPES[$col])) {
|
||||
$coltypes[$col] = array_merge(rcmail_action_contacts_index::$CONTACT_COLTYPES[$col], (array) $colprop);
|
||||
}
|
||||
else {
|
||||
$coltypes[$col] = (array) $colprop;
|
||||
}
|
||||
}
|
||||
|
||||
// build form fields list
|
||||
foreach ($coltypes as $col => $colprop) {
|
||||
if (!isset($colprop['type'])) {
|
||||
$colprop['type'] = 'text';
|
||||
}
|
||||
if ($colprop['type'] != 'image' && empty($colprop['nosearch'])) {
|
||||
$ftype = $colprop['type'] == 'select' ? 'select' : 'text';
|
||||
$label = $colprop['label'] ?? $rcmail->gettext($col);
|
||||
$category = !empty($colprop['category']) ? $colprop['category'] : 'other';
|
||||
|
||||
// load jquery UI datepicker for date fields
|
||||
if ($colprop['type'] == 'date') {
|
||||
$colprop['class'] = (!empty($colprop['class']) ? $colprop['class'] . ' ' : '') . 'datepicker';
|
||||
}
|
||||
else if ($ftype == 'text') {
|
||||
$colprop['size'] = $i_size;
|
||||
}
|
||||
|
||||
$colprop['id'] = '_search_' . $col;
|
||||
|
||||
$content = html::div('row',
|
||||
html::label(['class' => 'contactfieldlabel label', 'for' => $colprop['id']], rcube::Q($label))
|
||||
. html::div('contactfieldcontent', rcube_output::get_edit_field('search_' . $col, '', $colprop, $ftype))
|
||||
);
|
||||
|
||||
$form[$category]['content'][] = $content;
|
||||
}
|
||||
}
|
||||
|
||||
$hiddenfields = new html_hiddenfield();
|
||||
$hiddenfields->add(['name' => '_adv', 'value' => 1]);
|
||||
|
||||
$out = $rcmail->output->request_form([
|
||||
'name' => 'form',
|
||||
'method' => 'post',
|
||||
'task' => $rcmail->task,
|
||||
'action' => 'search',
|
||||
'noclose' => true,
|
||||
] + $attrib, $hiddenfields->show()
|
||||
);
|
||||
|
||||
$rcmail->output->add_gui_object('editform', $attrib['id']);
|
||||
|
||||
unset($attrib['name']);
|
||||
unset($attrib['id']);
|
||||
|
||||
foreach ($form as $f) {
|
||||
if (!empty($f['content'])) {
|
||||
$content = html::div('contactfieldgroup', join("\n", $f['content']));
|
||||
$legend = html::tag('legend', null, rcube::Q($f['name']));
|
||||
|
||||
$out .= html::tag('fieldset', $attrib, $legend . $content) . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return $out . '</form>';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| Copyright (C) Kolab Systems AG |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Create saved search |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
| Author: Aleksander Machniak <machniak@kolabsys.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_search_create extends rcmail_action
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$id = rcube_utils::get_input_value('_search', rcube_utils::INPUT_POST);
|
||||
$name = rcube_utils::get_input_value('_name', rcube_utils::INPUT_POST, true);
|
||||
|
||||
if (
|
||||
!empty($_SESSION['contact_search_params'])
|
||||
&& ($params = $_SESSION['contact_search_params'])
|
||||
&& $params['id'] == $id
|
||||
) {
|
||||
$data = [
|
||||
'type' => rcube_user::SEARCH_ADDRESSBOOK,
|
||||
'name' => $name,
|
||||
'data' => [
|
||||
'fields' => $params['data'][0],
|
||||
'search' => $params['data'][1],
|
||||
],
|
||||
];
|
||||
|
||||
$plugin = $rcmail->plugins->exec_hook('saved_search_create', ['data' => $data]);
|
||||
|
||||
if (empty($plugin['abort'])) {
|
||||
$result = $rcmail->user->insert_search($plugin['data']);
|
||||
}
|
||||
else {
|
||||
$result = $plugin['result'];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($result)) {
|
||||
$rcmail->output->show_message('savedsearchcreated', 'confirmation');
|
||||
$rcmail->output->command('insert_saved_search', rcube::Q($name), rcube::Q($result));
|
||||
}
|
||||
else {
|
||||
$error = !empty($plugin['message']) ? $plugin['message'] : 'savedsearchcreateerror';
|
||||
$rcmail->output->show_message($error, 'error');
|
||||
}
|
||||
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| Copyright (C) Kolab Systems AG |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Delete saved search |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
| Author: Aleksander Machniak <machniak@kolabsys.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_search_delete extends rcmail_action
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$id = rcube_utils::get_input_string('_sid', rcube_utils::INPUT_POST);
|
||||
$result = false;
|
||||
|
||||
if (!empty($id)) {
|
||||
$plugin = $rcmail->plugins->exec_hook('saved_search_delete', ['id' => $id]);
|
||||
|
||||
if (empty($plugin['abort'])) {
|
||||
$result = $rcmail->user->delete_search($id);
|
||||
}
|
||||
else {
|
||||
$result = $plugin['result'];
|
||||
}
|
||||
}
|
||||
|
||||
if ($result) {
|
||||
$rcmail->output->show_message('savedsearchdeleted', 'confirmation');
|
||||
$rcmail->output->command('remove_search_item', rcube::Q($id));
|
||||
// contact list will be cleared, clear also page counter
|
||||
$rcmail->output->command('set_rowcount', $rcmail->gettext('nocontactsfound'));
|
||||
$rcmail->output->set_env('pagecount', 0);
|
||||
}
|
||||
else {
|
||||
$error = !empty($plugin['message']) ? $plugin['message'] : 'savedsearchdeleteerror';
|
||||
$rcmail->output->show_message($error, 'error');
|
||||
}
|
||||
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Show contact details |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_show extends rcmail_action_contacts_index
|
||||
{
|
||||
protected static $mode = self::MODE_HTTP;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
// Get contact ID and source ID from request
|
||||
$cids = self::get_cids();
|
||||
$source = key($cids);
|
||||
$cid = $cids ? array_first($cids[$source]) : null;
|
||||
|
||||
// Initialize addressbook source
|
||||
self::$CONTACTS = self::contact_source($source, true);
|
||||
self::$SOURCE_ID = $source;
|
||||
|
||||
// read contact record (or get the one defined in 'save' action)
|
||||
if (!empty($args['contact'])) {
|
||||
self::$contact = $args['contact'];
|
||||
}
|
||||
else if ($cid) {
|
||||
self::$contact = self::$CONTACTS->get_record($cid, true);
|
||||
}
|
||||
|
||||
if ($cid && self::$contact) {
|
||||
$rcmail->output->set_env('readonly', self::$CONTACTS->readonly || !empty(self::$contact['readonly']));
|
||||
$rcmail->output->set_env('cid', self::$contact['ID']);
|
||||
|
||||
// remember current search request ID (if in search mode)
|
||||
if ($search = rcube_utils::get_input_string('_search', rcube_utils::INPUT_GET)) {
|
||||
$rcmail->output->set_env('search_request', $search);
|
||||
}
|
||||
}
|
||||
|
||||
// get address book name (for display)
|
||||
self::set_sourcename(self::$CONTACTS);
|
||||
|
||||
$rcmail->output->add_handlers([
|
||||
'contacthead' => [$this, 'contact_head'],
|
||||
'contactdetails' => [$this, 'contact_details'],
|
||||
'contactphoto' => [$this, 'contact_photo'],
|
||||
]);
|
||||
|
||||
$rcmail->output->send('contact');
|
||||
}
|
||||
|
||||
public static function contact_head($attrib)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
// check if we have a valid result
|
||||
if (!self::$contact) {
|
||||
$rcmail->output->show_message('contactnotfound', 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
$form = [
|
||||
'head' => [ // section 'head' is magic!
|
||||
'name' => $rcmail->gettext('contactnameandorg'),
|
||||
'content' => [
|
||||
'source' => ['type' => 'text'],
|
||||
'prefix' => ['type' => 'text'],
|
||||
'firstname' => ['type' => 'text'],
|
||||
'middlename' => ['type' => 'text'],
|
||||
'surname' => ['type' => 'text'],
|
||||
'suffix' => ['type' => 'text'],
|
||||
'name' => ['type' => 'text'],
|
||||
'nickname' => ['type' => 'text'],
|
||||
'organization' => ['type' => 'text'],
|
||||
'department' => ['type' => 'text'],
|
||||
'jobtitle' => ['type' => 'text'],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
unset($attrib['name']);
|
||||
|
||||
return self::contact_form($form, self::$contact, $attrib);
|
||||
}
|
||||
|
||||
public static function contact_details($attrib)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
// check if we have a valid result
|
||||
if (!self::$contact) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$i_size = !empty($attrib['size']) ? $attrib['size'] : 40;
|
||||
$short_labels = self::get_bool_attr($attrib, 'short-legend-labels');
|
||||
|
||||
$form = [
|
||||
'contact' => [
|
||||
'name' => $rcmail->gettext('properties'),
|
||||
'content' => [
|
||||
'email' => ['size' => $i_size, 'render_func' => 'rcmail_action_contacts_show::render_email_value'],
|
||||
'phone' => ['size' => $i_size, 'render_func' => 'rcmail_action_contacts_show::render_phone_value'],
|
||||
'address' => [],
|
||||
'website' => ['size' => $i_size, 'render_func' => 'rcmail_action_contacts_show::render_url_value'],
|
||||
'im' => ['size' => $i_size],
|
||||
],
|
||||
],
|
||||
'personal' => [
|
||||
'name' => $rcmail->gettext($short_labels ? 'personal' : 'personalinfo'),
|
||||
'content' => [
|
||||
'gender' => ['size' => $i_size],
|
||||
'maidenname' => ['size' => $i_size],
|
||||
'birthday' => ['size' => $i_size],
|
||||
'anniversary' => ['size' => $i_size],
|
||||
'manager' => ['size' => $i_size],
|
||||
'assistant' => ['size' => $i_size],
|
||||
'spouse' => ['size' => $i_size],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
if (isset(rcmail_action_contacts_index::$CONTACT_COLTYPES['notes'])) {
|
||||
$form['notes'] = [
|
||||
'name' => $rcmail->gettext('notes'),
|
||||
'content' => [
|
||||
'notes' => ['type' => 'textarea', 'label' => false],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
if (self::$CONTACTS->groups) {
|
||||
$form['groups'] = [
|
||||
'name' => $rcmail->gettext('groups'),
|
||||
'content' => self::contact_record_groups(self::$contact['ID']),
|
||||
];
|
||||
}
|
||||
|
||||
return self::contact_form($form, self::$contact, $attrib);
|
||||
}
|
||||
|
||||
public static function render_email_value($email)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
return html::a([
|
||||
'href' => 'mailto:' . $email,
|
||||
'onclick' => sprintf(
|
||||
"return %s.command('compose','%s',this)",
|
||||
rcmail_output::JS_OBJECT_NAME,
|
||||
rcube::JQ($email)
|
||||
),
|
||||
'title' => $rcmail->gettext('composeto'),
|
||||
'class' => 'email',
|
||||
],
|
||||
rcube::Q($email)
|
||||
);
|
||||
}
|
||||
|
||||
public static function render_phone_value($phone)
|
||||
{
|
||||
$attrs = [
|
||||
'href' => 'tel:' . preg_replace('/[^0-9+,;-]/', '', $phone),
|
||||
'class' => 'phone',
|
||||
];
|
||||
|
||||
return html::a($attrs, rcube::Q($phone));
|
||||
}
|
||||
|
||||
public static function render_url_value($url)
|
||||
{
|
||||
$prefix = preg_match('!^(http|ftp)s?://!', $url) ? '' : 'http://';
|
||||
|
||||
return html::a([
|
||||
'href' => $prefix . $url,
|
||||
'target' => '_blank',
|
||||
'class' => 'url',
|
||||
],
|
||||
rcube::Q($url)
|
||||
);
|
||||
}
|
||||
|
||||
public static function contact_record_groups($contact_id)
|
||||
{
|
||||
$groups = self::$CONTACTS->list_groups();
|
||||
|
||||
if (empty($groups)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$rcmail = rcmail::get_instance();
|
||||
$source = rcube_utils::get_input_string('_source', rcube_utils::INPUT_GPC);
|
||||
$members = self::$CONTACTS->get_record_groups($contact_id);
|
||||
$table = new html_table(['tagname' => 'ul', 'cols' => 1, 'class' => 'proplist simplelist']);
|
||||
$checkbox = new html_checkbox(['name' => '_gid[]', 'class' => 'groupmember', 'disabled' => self::$CONTACTS->readonly]);
|
||||
|
||||
foreach ($groups as $group) {
|
||||
$gid = $group['ID'];
|
||||
$input = $checkbox->show(!empty($members[$gid]) ? $gid : null, ['value' => $gid]);
|
||||
$table->add(null, html::label(null, $input . rcube::Q($group['name'])));
|
||||
}
|
||||
|
||||
$hiddenfields = new html_hiddenfield(['name' => '_source', 'value' => $source]);
|
||||
$hiddenfields->add(['name' => '_cid', 'value' => $contact_id]);
|
||||
|
||||
$form_attrs = [
|
||||
'name' => 'form',
|
||||
'method' => 'post',
|
||||
'task' => $rcmail->task,
|
||||
'action' => 'save',
|
||||
'request' => 'save.' . intval($contact_id),
|
||||
'noclose' => true,
|
||||
];
|
||||
|
||||
$form_start = $rcmail->output->request_form($form_attrs, $hiddenfields->show());
|
||||
$form_end = '</form>';
|
||||
|
||||
$rcmail->output->add_gui_object('editform', 'form');
|
||||
$rcmail->output->add_label('addingmember', 'removingmember');
|
||||
|
||||
return $form_start . html::tag('fieldset', 'contactfieldgroup contactgroups', $table->show()) . $form_end;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Undelete contacts (CIDs) from last delete action |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Aleksander Machniak <machniak@kolabsys.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_undo extends rcmail_action_contacts_index
|
||||
{
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$delcnt = 0;
|
||||
|
||||
if (!empty($_SESSION['contact_undo']) && !empty($_SESSION['contact_undo']['data'])) {
|
||||
foreach ((array) $_SESSION['contact_undo']['data'] as $source => $cid) {
|
||||
$contacts = self::contact_source($source);
|
||||
|
||||
$plugin = $rcmail->plugins->exec_hook('contact_undelete', [
|
||||
'id' => $cid,
|
||||
'source' => $source
|
||||
]);
|
||||
|
||||
$restored = empty($plugin['abort']) ? $contacts->undelete($cid) : $plugin['result'];
|
||||
|
||||
if (!$restored) {
|
||||
$error = !empty($plugin['message']) ? $plugin['message'] : 'contactrestoreerror';
|
||||
|
||||
$rcmail->output->show_message($error, 'error');
|
||||
$rcmail->output->command('list_contacts');
|
||||
$rcmail->output->send();
|
||||
}
|
||||
else {
|
||||
$delcnt += $restored;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$rcmail->session->remove('contact_undo');
|
||||
|
||||
if ($delcnt) {
|
||||
$rcmail->output->show_message('contactrestored', 'confirmation');
|
||||
$rcmail->output->command('list_contacts');
|
||||
}
|
||||
|
||||
// send response
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Handles contact photo uploads |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_upload_photo extends rcmail_action_contacts_index
|
||||
{
|
||||
/**
|
||||
* Supported image format types
|
||||
* ImageMagick works with other non-image types (e.g. pdf), we don't want that here
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $IMAGE_TYPES = ['jpeg','jpg','jp2','tiff','tif','bmp','eps','gif','png','png8','png24','png32','svg','ico'];
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
// clear all stored output properties (like scripts and env vars)
|
||||
$rcmail->output->reset();
|
||||
|
||||
if (!empty($_FILES['_photo']['tmp_name'])) {
|
||||
$filepath = $_FILES['_photo']['tmp_name'];
|
||||
|
||||
// check file type and resize image
|
||||
$image = new rcube_image($_FILES['_photo']['tmp_name']);
|
||||
$imageprop = $image->props();
|
||||
|
||||
if (
|
||||
in_array(strtolower($imageprop['type']), self::$IMAGE_TYPES)
|
||||
&& $imageprop['width']
|
||||
&& $imageprop['height']
|
||||
) {
|
||||
$maxsize = intval($rcmail->config->get('contact_photo_size', 160));
|
||||
$tmpfname = rcube_utils::temp_filename('imgconvert');
|
||||
$save_hook = 'attachment_upload';
|
||||
|
||||
// scale image to a maximum size
|
||||
if (($imageprop['width'] > $maxsize || $imageprop['height'] > $maxsize) && $image->resize($maxsize, $tmpfname)) {
|
||||
$filepath = $tmpfname;
|
||||
$save_hook = 'attachment_save';
|
||||
}
|
||||
|
||||
// save uploaded file in storage backend
|
||||
$attachment = $rcmail->plugins->exec_hook($save_hook, [
|
||||
'path' => $filepath,
|
||||
'size' => $_FILES['_photo']['size'],
|
||||
'name' => $_FILES['_photo']['name'],
|
||||
'mimetype' => 'image/' . $imageprop['type'],
|
||||
'group' => 'contact',
|
||||
]);
|
||||
}
|
||||
else {
|
||||
$attachment = ['error' => $rcmail->gettext('invalidimageformat')];
|
||||
}
|
||||
|
||||
if (!empty($attachment['status']) && empty($attachment['abort'])) {
|
||||
$file_id = $attachment['id'];
|
||||
$_SESSION['contacts']['files'][$file_id] = $attachment;
|
||||
$rcmail->output->command('replace_contact_photo', $file_id);
|
||||
}
|
||||
else {
|
||||
// upload failed
|
||||
self::upload_error($_FILES['_photo']['error'], $attachment);
|
||||
}
|
||||
}
|
||||
else {
|
||||
self::upload_failure();
|
||||
}
|
||||
|
||||
$rcmail->output->command('photo_upload_end');
|
||||
$rcmail->output->send('iframe');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user