diff options
-rw-r--r-- | CHANGELOG | 2 | ||||
-rw-r--r-- | config/main.inc.php.dist | 1 | ||||
-rw-r--r-- | program/js/app.js | 87 | ||||
-rw-r--r-- | program/localization/en_US/labels.inc | 1 | ||||
-rw-r--r-- | program/localization/pl_PL/labels.inc | 1 | ||||
-rw-r--r-- | program/steps/addressbook/copy.inc | 112 | ||||
-rw-r--r-- | program/steps/addressbook/delete.inc | 126 | ||||
-rw-r--r-- | program/steps/addressbook/edit.inc | 61 | ||||
-rw-r--r-- | program/steps/addressbook/export.inc | 85 | ||||
-rw-r--r-- | program/steps/addressbook/func.inc | 209 | ||||
-rw-r--r-- | program/steps/addressbook/groups.inc | 5 | ||||
-rw-r--r-- | program/steps/addressbook/import.inc | 65 | ||||
-rw-r--r-- | program/steps/addressbook/list.inc | 62 | ||||
-rw-r--r-- | program/steps/addressbook/mailto.inc | 41 | ||||
-rw-r--r-- | program/steps/addressbook/save.inc | 18 | ||||
-rw-r--r-- | program/steps/addressbook/search.inc | 119 | ||||
-rw-r--r-- | program/steps/addressbook/show.inc | 22 |
17 files changed, 733 insertions, 284 deletions
@@ -1,6 +1,8 @@ CHANGELOG Roundcube Webmail =========================== +- Added searching in all addressbook sources +- Added addressbook source selection in contacts import - Implement LDAPv3 Virtual List View (VLV) for paged results listing - Use 'address_template' config option when adding a new address block (#1487944) - Added addressbook advanced search diff --git a/config/main.inc.php.dist b/config/main.inc.php.dist index 44e80c023..7eac777e0 100644 --- a/config/main.inc.php.dist +++ b/config/main.inc.php.dist @@ -450,6 +450,7 @@ $rcmail_config['address_book_type'] = 'sql'; // In order to enable public ldap search, configure an array like the Verisign // example further below. if you would like to test, simply uncomment the example. +// Array key must contain only safe characters, ie. a-zA-Z0-9_ $rcmail_config['ldap_public'] = array(); // If you are going to use LDAP for individual address books, you will need to diff --git a/program/js/app.js b/program/js/app.js index 88297e91d..2c8fad6d2 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -336,7 +336,7 @@ function rcube_webmail() if (this.contact_list && this.contact_list.rowcount > 0) this.enable_command('export', true); - this.enable_command('add', 'import', !this.env.readonly); + this.enable_command('add', 'import', this.env.writable_source); this.enable_command('list', 'listgroup', 'advanced-search', true); break; @@ -529,12 +529,12 @@ function rcube_webmail() if (this.env.trash_mailbox) this.set_alttext('delete', this.env.mailbox != this.env.trash_mailbox ? 'movemessagetotrash' : 'deletemessage'); } - else if (this.task=='addressbook') { + else if (this.task == 'addressbook') { if (!this.env.search_request || (props != this.env.source)) this.reset_qsearch(); this.list_contacts(props); - this.enable_command('add', 'import', (this.env.address_sources && !this.env.address_sources[this.env.source].readonly)); + this.enable_command('add', 'import', this.env.writable_source); } break; @@ -988,8 +988,14 @@ function rcube_webmail() if (s && this.env.mailbox) this.list_mailbox(this.env.mailbox); - else if (s && this.task == 'addressbook') + else if (s && this.task == 'addressbook') { + if (this.env.source == '') { + for (var n in this.env.address_sources) break; + this.env.source = n; + this.env.group = ''; + } this.list_contacts(this.env.source, this.env.group); + } break; case 'listgroup': @@ -3651,15 +3657,34 @@ function rcube_webmail() if (this.preview_timer) clearTimeout(this.preview_timer); - var id, frame, ref = this; + var n, id, sid, ref = this, writable = false, + source = this.env.source ? this.env.address_sources[this.env.source] : null; + if (id = list.get_single_selection()) this.preview_timer = window.setTimeout(function(){ ref.load_contact(id, 'show'); }, 200); else if (this.env.contentframe) this.show_contentframe(false); + // no source = search result, we'll need to detect if any of + // selected contacts are in writable addressbook to enable edit/delete + if (list.selection.length) { + if (!source) { + for (n in list.selection) { + sid = String(list.selection[n]).replace(/^[^-]+-/, ''); + if (sid && this.env.address_sources[sid] && !this.env.address_sources[sid].readonly) { + writable = true; + break; + } + } + } + else { + writable = !source.readonly; + } + } + this.enable_command('compose', list.selection.length > 0); - this.enable_command('edit', (id && this.env.address_sources && !this.env.address_sources[this.env.source].readonly) ? true : false); - this.enable_command('delete', list.selection.length && this.env.address_sources && !this.env.address_sources[this.env.source].readonly); + this.enable_command('edit', id && writable); + this.enable_command('delete', list.selection.length && writable); return false; }; @@ -3797,12 +3822,12 @@ function rcube_webmail() if (!(selection.length || this.env.cid) || !confirm(this.get_label('deletecontactconfirm'))) return; - var id, a_cids = [], qs = ''; + var id, n, a_cids = [], qs = ''; if (this.env.cid) a_cids.push(this.env.cid); else { - for (var n=0; n<selection.length; n++) { + for (n=0; n<selection.length; n++) { id = selection[n]; a_cids.push(id); this.contact_list.remove_row(id, (n == selection.length-1)); @@ -3817,7 +3842,7 @@ function rcube_webmail() qs += '&_gid='+urlencode(this.env.group); // also send search request to get the right records from the next page - if (this.env.search_request) + if (this.env.search_request) qs += '&_search='+this.env.search_request; // send request to server @@ -4119,7 +4144,7 @@ function rcube_webmail() for (childcol in colprop.childs) cols.push(childcol); } - + for (var i=0; i < cols.length; i++) { childcol = cols[i]; cp = colprop.childs[childcol]; @@ -4241,16 +4266,22 @@ function rcube_webmail() target = window.frames[this.env.contentframe]; this.contact_list.clear_selection(); } - else if (framed) - return false; - this.location_href(this.env.comm_path+'&_action=search'+add_url - +'&_source='+urlencode(this.env.source) - +(this.env.group ? '&_gid='+urlencode(this.env.group) : ''), target); + this.location_href(this.env.comm_path+'&_action=search'+add_url, target); return true; }; + // unselect directory/group + this.unselect_directory = function() + { + if (this.env.address_sources.length > 1 || this.env.group != '') { + this.select_folder('', (this.env.group ? 'G'+this.env.source+this.env.group : this.env.source)); + this.env.group = ''; + this.env.source = ''; + } + }; + /*********************************************************/ /********* user settings methods *********/ @@ -5468,9 +5499,20 @@ function rcube_webmail() switch (response.action) { case 'delete': if (this.task == 'addressbook') { - var uid = this.contact_list.get_selection(); + var sid, uid = this.contact_list.get_selection(), writable = false; + + if (uid && this.contact_list.rows[uid]) { + // search results, get source ID from record ID + if (this.env.source == '') { + sid = String(uid).replace(/^[^-]+-/, ''); + writable = sid && this.env.address_sources[sid] && !this.env.address_sources[sid].readonly; + } + else { + writable = !this.env.address_sources[this.env.source].readonly; + } + } this.enable_command('compose', (uid && this.contact_list.rows[uid])); - this.enable_command('delete', 'edit', (uid && this.contact_list.rows[uid] && this.env.address_sources && !this.env.address_sources[this.env.source].readonly)); + this.enable_command('delete', 'edit', writable); this.enable_command('export', (this.contact_list && this.contact_list.rowcount > 0)); } @@ -5519,10 +5561,9 @@ function rcube_webmail() this.enable_command('export', (this.contact_list && this.contact_list.rowcount > 0)); if (response.action == 'list' || response.action == 'search') { - this.enable_command('group-create', - (this.env.address_sources[this.env.source].groups && !this.env.address_sources[this.env.source].readonly)); - this.enable_command('group-rename', 'group-delete', - (this.env.address_sources[this.env.source].groups && this.env.group && !this.env.address_sources[this.env.source].readonly)); + var source = this.env.source != '' ? this.env.address_sources[this.env.source] : null; + this.enable_command('group-create', (source && source.groups && !source.readonly)); + this.enable_command('group-rename', 'group-delete', (source && source.groups && this.env.group && !source.readonly)); this.triggerEvent('listupdate', { folder:this.env.source, rowcount:this.contact_list.rowcount }); } } @@ -5580,7 +5621,7 @@ function rcube_webmail() return frame_name; }; - + // starts interval for keep-alive/check-recent signal this.start_keepalive = function() { diff --git a/program/localization/en_US/labels.inc b/program/localization/en_US/labels.inc index fa0fab581..e6927e89b 100644 --- a/program/localization/en_US/labels.inc +++ b/program/localization/en_US/labels.inc @@ -326,6 +326,7 @@ $labels['personaladrbook'] = 'Personal Addresses'; $labels['import'] = 'Import'; $labels['importcontacts'] = 'Import contacts'; $labels['importfromfile'] = 'Import from file:'; +$labels['importtarget'] = 'Add new contacts to address book:'; $labels['importreplace'] = 'Replace the entire address book'; $labels['importtext'] = 'You can upload contacts from an existing address book.<br/>We currently support importing addresses from the <a href="http://en.wikipedia.org/wiki/VCard">vCard</a> data format.'; $labels['done'] = 'Done'; diff --git a/program/localization/pl_PL/labels.inc b/program/localization/pl_PL/labels.inc index e425f1202..f51e2e0d2 100644 --- a/program/localization/pl_PL/labels.inc +++ b/program/localization/pl_PL/labels.inc @@ -413,5 +413,6 @@ $labels['allfields'] = 'Wszystkie pola'; $labels['search'] = 'Szukaj'; $labels['advsearch'] = 'Wyszukiwanie zaawansowane'; $labels['other'] = 'Inne'; +$labels['importtarget'] = 'Dodaj nowe kontakty do książki adresowej:'; ?> diff --git a/program/steps/addressbook/copy.inc b/program/steps/addressbook/copy.inc index b891e012c..4ee885b3f 100644 --- a/program/steps/addressbook/copy.inc +++ b/program/steps/addressbook/copy.inc @@ -23,75 +23,91 @@ if (!$OUTPUT->ajax_call) return; -$cid = get_input_value('_cid', RCUBE_INPUT_POST); -$target = get_input_value('_to', RCUBE_INPUT_POST); + +$cids = rcmail_get_cids(); +$target = get_input_value('_to', RCUBE_INPUT_POST); $target_group = get_input_value('_togid', RCUBE_INPUT_POST); -if ($cid && preg_match('/^[a-zA-Z0-9\+\/=_-]+(,[a-zA-Z0-9\+\/=_-]+)*$/', $cid) && strlen($target) && $target !== $source) +$success = 0; +$maxnum = $RCMAIL->config->get('max_group_members', 0); + +foreach ($cids as $source => $cid) { - $success = 0; - $TARGET = $RCMAIL->get_address_book($target); + // Something wrong, target not specified + if (!strlen($target)) { + break; + } - if ($TARGET && $TARGET->ready && !$TARGET->readonly) { - $arr_cids = explode(',', $cid); - $ids = array(); + // It maight happen when copying records from search result + // Do nothing, go to next source + if ($target == $source) { + continue; + } - foreach ($arr_cids as $cid) { - $a_record = $CONTACTS->get_record($cid, true); + $CONTACTS = $RCMAIL->get_address_book($source); + $TARGET = $RCMAIL->get_address_book($target); - // check if contact exists, if so, we'll need it's ID - $result = $TARGET->search('email', $a_record['email'], true, true); + if (!$TARGET || !$TARGET->ready || $TARGET->readonly) { + break; + } - // insert contact record - if (!$result->count) { - $plugin = $RCMAIL->plugins->exec_hook('contact_create', array( - 'record' => $a_record, 'source' => $target, 'group' => $target_group)); + $ids = array(); - if (!$plugin['abort']) { - if ($insert_id = $TARGET->insert($plugin['record'], false)) { - $ids[] = $insert_id; - $success++; - } + foreach ($cid as $cid) { + $a_record = $CONTACTS->get_record($cid, true); + + // check if contact exists, if so, we'll need it's ID + $result = $TARGET->search('email', $a_record['email'], true, true); + + // insert contact record + if (!$result->count) { + $plugin = $RCMAIL->plugins->exec_hook('contact_create', array( + '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 if ($plugin['result']) { - $ids = array_merge($ids, $plugin['result']); - $success++; + else { + $record = $result->first(); + $ids[] = $record['ID']; } - } - else { - $record = $result->first(); - $ids[] = $record['ID']; - } } // assign to group if ($target_group && $TARGET->groups && !empty($ids)) { - $plugin = $RCMAIL->plugins->exec_hook('group_addmembers', array( - 'group_id' => $target_group, 'ids' => $ids, 'source' => $target)); + $plugin = $RCMAIL->plugins->exec_hook('group_addmembers', array( + 'group_id' => $target_group, 'ids' => $ids, 'source' => $target)); - if (!$plugin['abort']) { - $TARGET->reset(); - $TARGET->set_group($target_group); + if (!$plugin['abort']) { + $TARGET->reset(); + $TARGET->set_group($target_group); - if (($maxnum = $RCMAIL->config->get('max_group_members', 0)) && ($TARGET->count()->count + count($plugin['ids']) > $maxnum)) { - $OUTPUT->show_message('maxgroupmembersreached', 'warning', array('max' => $maxnum)); - $OUTPUT->send(); - } + if ($maxnum && ($TARGET->count()->count + count($plugin['ids']) > $maxnum)) { + $OUTPUT->show_message('maxgroupmembersreached', 'warning', array('max' => $maxnum)); + $OUTPUT->send(); + } - if (($cnt = $TARGET->add_to_group($target_group, $plugin['ids'])) && $cnt > $success) - $success = $cnt; - } - else if ($plugin['result']) - $success = $plugin['result']; + if (($cnt = $TARGET->add_to_group($target_group, $plugin['ids'])) && $cnt > $success) + $success = $cnt; + } + else if ($plugin['result']) { + $success = $plugin['result']; + } } - } +} - if ($success == 0) +if ($success == 0) $OUTPUT->show_message('copyerror', 'error'); - else +else $OUTPUT->show_message('copysuccess', 'notice', array('nr' => $success)); -} // send response $OUTPUT->send(); - diff --git a/program/steps/addressbook/delete.inc b/program/steps/addressbook/delete.inc index 1cd4f3550..af9bdb10a 100644 --- a/program/steps/addressbook/delete.inc +++ b/program/steps/addressbook/delete.inc @@ -19,42 +19,126 @@ */ -if ($OUTPUT->ajax_call && - ($cid = get_input_value('_cid', RCUBE_INPUT_POST)) && - preg_match('/^[a-zA-Z0-9\+\/=_-]+(,[a-zA-Z0-9\+\/=_-]+)*$/', $cid) -) { +// process ajax requests only +if (!$OUTPUT->ajax_call) + return; + +$cids = rcmail_get_cids(); +$delcnt = 0; + +foreach ($cids as $source => $cid) +{ + $CONTACTS = rcmail_contact_source($source); + + if ($CONTACTS->readonly) { + // 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) { + $OUTPUT->show_message('contactdelerror', 'error'); + $OUTPUT->command('list_contacts'); + } + continue; + } + $plugin = $RCMAIL->plugins->exec_hook('contact_delete', array( - 'id' => $cid, 'source' => get_input_value('_source', RCUBE_INPUT_GPC))); + 'id' => $cid, 'source' => $source)); $deleted = !$plugin['abort'] ? $CONTACTS->delete($cid) : $plugin['result']; if (!$deleted) { $OUTPUT->show_message($plugin['message'] ? $plugin['message'] : 'contactdelerror', 'error'); $OUTPUT->command('list_contacts'); + $OUTPUT->send(); } else { - $OUTPUT->show_message('contactdeleted', 'confirmation'); + $delcnt += $deleted; + } +} - // count contacts for this user - $result = $CONTACTS->count(); +$OUTPUT->show_message('contactdeleted', 'confirmation'); - // update saved search after data changed - if (($search_request = $_REQUEST['_search']) && isset($_SESSION['search'][$search_request])) - $_SESSION['search'][$search_request] = $CONTACTS->refresh_search(); +$page = isset($_SESSION['page']) ? $_SESSION['page'] : 1; - // update message count display - $OUTPUT->set_env('pagecount', ceil($result->count / $CONTACTS->page_size)); - $OUTPUT->command('set_rowcount', rcmail_get_rowcount_text($result->count)); +// update saved search after data changed +if (($search_request = $_REQUEST['_search']) && isset($_SESSION['search'][$search_request])) { + $search = (array)$_SESSION['search'][$search_request]; + $records = array(); - // add new rows from next page (if any) - $pages = ceil(($result->count + $deleted) / $CONTACTS->page_size); - if ($_GET['_from'] != 'show' && $pages > 1 && $CONTACTS->list_page < $pages) - rcmail_js_contacts_list($CONTACTS->list_records(null, -$deleted)); + // Get records from all sources (refresh search) + foreach ($search as $s => $set) { + $source = $RCMAIL->get_address_book($s); + + // reset page + $source->set_page(1); + $source->set_pagesize(9999); + $source->set_search_set($set); + + // get records + $result = $source->list_records(array('name', 'email')); + + if (!$result->count) { + unset($search[$s]); + continue; + } + + while ($row = $result->next()) { + $row['sourceid'] = $s; + $key = $row['name'] . ':' . $row['sourceid']; + $records[$key] = $row; + } + unset($result); + + $search[$s] = $source->get_search_set(); } - // send response - $OUTPUT->send(); + $_SESSION['search'][$search_request] = $search; + + // create resultset object + $count = count($records); + $first = ($page-1) * $CONFIG['pagesize']; + $result = new rcube_result_set($count, $first); + + // get records from the next page to add to the list + $pages = ceil((count($records) + $delcnt) / $CONFIG['pagesize']); + if ($_GET['_from'] != 'show' && $pages > 1 && $page < $pages) { + // sort the records + ksort($records, SORT_LOCALE_STRING); + + $first += $CONFIG['pagesize']; + // create resultset object + $res = new rcube_result_set($count, $first - $delcnt); + + if ($CONFIG['pagesize'] < $count) { + $records = array_slice($records, $first - $delcnt, $delcnt); + } + + $res->records = array_values($records); + $records = $res; + } + else { + unset($records); + } +} +else { + // count contacts for this user + $result = $CONTACTS->count(); + + // get records from the next page to add to the list + $pages = ceil(($result->count + $delcnt) / $CONFIG['pagesize']); + if ($_GET['_from'] != 'show' && $pages > 1 && $page < $pages) { + $CONTACTS->set_page($page); + $records = $CONTACTS->list_records(null, -$delcnt); + } } -exit; +// update message count display +$OUTPUT->set_env('pagecount', ceil($result->count / $CONFIG['pagesize'])); +$OUTPUT->command('set_rowcount', rcmail_get_rowcount_text($result)); + +// add new rows from next page (if any) +if (!empty($records)) { + rcmail_js_contacts_list($records); +} +// send response +$OUTPUT->send(); diff --git a/program/steps/addressbook/edit.inc b/program/steps/addressbook/edit.inc index 96c48704a..05572de2b 100644 --- a/program/steps/addressbook/edit.inc +++ b/program/steps/addressbook/edit.inc @@ -19,15 +19,38 @@ */ +if ($RCMAIL->action == 'edit') { + // Get contact ID and source ID from request + $cids = rcmail_get_cids(); + $source = key($cids); + $cid = array_shift($cids[$source]); + + // Initialize addressbook + $CONTACTS = rcmail_contact_source($source, true); + + // Contact edit + if ($cid && ($record = $CONTACTS->get_record($cid, true))) { + $OUTPUT->set_env('cid', $record['ID']); + } + + // adding not allowed here + if ($CONTACTS->readonly) { + $OUTPUT->show_message('sourceisreadonly'); + rcmail_overwrite_action('show'); + return; + } +} +else { + $source = get_input_value('_source', RCUBE_INPUT_GPC); + + $CONTACTS = $RCMAIL->get_address_book($source); -if (($cid = get_input_value('_cid', RCUBE_INPUT_GPC)) && ($record = $CONTACTS->get_record($cid, true))) - $OUTPUT->set_env('cid', $record['ID']); + // find writable addressbook + if (!$CONTACTS || $CONTACTS->readonly) + $source = rcmail_default_source(true); -// adding not allowed here -if ($CONTACTS->readonly) { - $OUTPUT->show_message('sourceisreadonly'); - rcmail_overwrite_action('show'); - return; + // Initialize addressbook + $CONTACTS = rcmail_contact_source($source, true); } @@ -45,7 +68,7 @@ function rcmail_get_edit_record() $RCMAIL->output->show_message('contactnotfound'); return false; } - + return $record; } @@ -90,7 +113,7 @@ function rcmail_contact_editform($attrib) // add some labels to client $RCMAIL->output->add_label('noemailwarning', 'nonamewarning'); - + // copy (parsed) address template to client if (preg_match_all('/\{([a-z0-9]+)\}([^{]*)/i', $RCMAIL->config->get('address_template', ''), $templ, PREG_SET_ORDER)) $RCMAIL->output->set_env('address_template', $templ); @@ -123,7 +146,7 @@ function rcmail_contact_editform($attrib) ), ), ); - + if (isset($CONTACT_COLTYPES['notes'])) { $form['notes'] = array( 'name' => rcube_label('notes'), @@ -158,11 +181,11 @@ function rcmail_upload_photo_form($attrib) if ($max_postsize && $max_postsize < $max_filesize) $max_filesize = $max_postsize; $max_filesize = show_bytes($max_filesize); - + $hidden = new html_hiddenfield(array('name' => '_cid', 'value' => $GLOBALS['cid'])); $input = new html_inputfield(array('type' => 'file', 'name' => '_photo', 'size' => $attrib['size'])); $button = new html_inputfield(array('type' => 'button')); - + $out = html::div($attrib, $OUTPUT->form_tag(array('name' => 'uploadform', 'method' => 'post', 'enctype' => 'multipart/form-data'), $hidden->show() . @@ -174,7 +197,7 @@ function rcmail_upload_photo_form($attrib) ) ) ); - + $OUTPUT->add_label('addphoto','replacephoto'); $OUTPUT->add_gui_object('uploadbox', $attrib['id']); return $out; @@ -211,12 +234,14 @@ function get_form_tags($attrib) } -$OUTPUT->add_handler('contactedithead', 'rcmail_contact_edithead'); -$OUTPUT->add_handler('contacteditform', 'rcmail_contact_editform'); -$OUTPUT->add_handler('contactphoto', 'rcmail_contact_photo'); -$OUTPUT->add_handler('photouploadform', 'rcmail_upload_photo_form'); +$OUTPUT->add_handlers(array( + 'contactedithead' => 'rcmail_contact_edithead', + 'contacteditform' => 'rcmail_contact_editform', + 'contactphoto' => 'rcmail_contact_photo', + 'photouploadform' => 'rcmail_upload_photo_form', +)); -if (!$CONTACTS->get_result() && $OUTPUT->template_exists('contactadd')) +if ($RCMAIL->action == 'add' && $OUTPUT->template_exists('contactadd')) $OUTPUT->send('contactadd'); // this will be executed if no template for addcontact exists diff --git a/program/steps/addressbook/export.inc b/program/steps/addressbook/export.inc index bfe8e996c..04b98a308 100644 --- a/program/steps/addressbook/export.inc +++ b/program/steps/addressbook/export.inc @@ -6,6 +6,7 @@ | | | This file is part of the Roundcube Webmail client | | Copyright (C) 2008-2011, The Roundcube Dev Team | + | Copyright (C) 2011, Kolab Systems AG | | Licensed under the GNU GPL | | | | PURPOSE: | @@ -13,16 +14,56 @@ | | +-----------------------------------------------------------------------+ | Author: Thomas Bruederli <roundcube@gmail.com> | + | Author: Aleksander Machniak <machniak@kolabsys.com> | +-----------------------------------------------------------------------+ - $Id: $ + $Id$ */ -// get contacts for this user -$CONTACTS->set_page(1); -$CONTACTS->set_pagesize(99999); -$result = $CONTACTS->list_records(null, 0, true); +// Use search result +if (!empty($_REQUEST['_search']) && isset($_SESSION['search'][$_REQUEST['_search']])) +{ + $search = (array)$_SESSION['search'][$_REQUEST['_search']]; + $records = array(); + + // 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 ($row = $result->next()) { + $row['sourceid'] = $s; + $key = $row['name'] . ':' . $row['sourceid']; + $records[$key] = $row; + } + 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 directory/group +else { + $CONTACTS = rcmail_contact_source(null, true); + + // get contacts for this user + $CONTACTS->set_page(1); + $CONTACTS->set_pagesize(99999); + $result = $CONTACTS->list_records(null, 0, true); +} // send downlaod headers send_nocacheing_headers(); @@ -30,25 +71,25 @@ header('Content-Type: text/x-vcard; charset='.RCMAIL_CHARSET); header('Content-Disposition: attachment; filename="rcube_contacts.vcf"'); while ($result && ($row = $result->next())) { - // we already have a vcard record - if ($row['vcard'] && $row['name']) { - echo rcube_vcard::rfc2425_fold($row['vcard']) . "\n"; - } - // copy values into vcard object - else { - $vcard = new rcube_vcard($row['vcard']); - $vcard->reset(); - foreach ($row as $key => $values) { - list($field, $section) = explode(':', $key); - foreach ((array)$values as $value) { - if (is_array($value) || strlen($value)) - $vcard->set($field, $value, strtoupper($section)); - } + // we already have a vcard record + if ($row['vcard'] && $row['name']) { + echo rcube_vcard::rfc2425_fold($row['vcard']) . "\n"; } + // copy values into vcard object + else { + $vcard = new rcube_vcard($row['vcard']); + $vcard->reset(); - echo $vcard->export(true) . "\n"; - } + foreach ($row as $key => $values) { + list($field, $section) = explode(':', $key); + foreach ((array)$values as $value) { + if (is_array($value) || strlen($value)) + $vcard->set($field, $value, strtoupper($section)); + } + } + + echo $vcard->export(true) . "\n"; + } } exit; - diff --git a/program/steps/addressbook/func.inc b/program/steps/addressbook/func.inc index 8b7e641d8..9731d9f09 100644 --- a/program/steps/addressbook/func.inc +++ b/program/steps/addressbook/func.inc @@ -21,46 +21,6 @@ $SEARCH_MODS_DEFAULT = array('name'=>1, 'firstname'=>1, 'surname'=>1, 'email'=>1, '*'=>1); -// select source -$source = get_input_value('_source', RCUBE_INPUT_GPC); - -if (!$RCMAIL->action && !$OUTPUT->ajax_call) { - // add list of address sources to client env - $js_list = $RCMAIL->get_address_sources(); - - // if source is not set use first directory - if (empty($source)) - $source = $js_list[key($js_list)]['id']; - - $search_mods = $RCMAIL->config->get('addressbook_search_mods', $SEARCH_MODS_DEFAULT); - $OUTPUT->set_env('search_mods', $search_mods); - $OUTPUT->set_env('address_sources', $js_list); -} - -// instantiate a contacts object according to the given source -$CONTACTS = $RCMAIL->get_address_book($source); - -$CONTACTS->set_pagesize($CONFIG['pagesize']); - -// set list properties and session vars -if (!empty($_GET['_page'])) - $CONTACTS->set_page(($_SESSION['page'] = intval($_GET['_page']))); -else - $CONTACTS->set_page(isset($_SESSION['page']) ?$_SESSION['page'] : 1); - -if (!empty($_REQUEST['_gid'])) - $CONTACTS->set_group(get_input_value('_gid', RCUBE_INPUT_GPC)); - -// set data source env -$OUTPUT->set_env('source', $source ? $source : '0'); -$OUTPUT->set_env('readonly', $CONTACTS->readonly); -if (!$OUTPUT->ajax_call) { - $search_mods = $RCMAIL->config->get('addressbook_search_mods', $SEARCH_MODS_DEFAULT); - $OUTPUT->set_env('search_mods', $search_mods); - $OUTPUT->set_pagetitle(rcube_label('addressbook')); -} - - // general definition of contact coltypes $CONTACT_COLTYPES = array( 'name' => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('name'), 'category' => 'main'), @@ -96,19 +56,93 @@ $CONTACT_COLTYPES = array( // TODO: define fields for vcards like GEO, KEY ); -// reduce/extend $CONTACT_COLTYPES with specification from the current $CONTACT object -if (is_array($CONTACTS->coltypes)) { - // remove cols not listed by the backend class - $contact_cols = $CONTACTS->coltypes[0] ? array_flip($CONTACTS->coltypes) : $CONTACTS->coltypes; - $CONTACT_COLTYPES = array_intersect_key($CONTACT_COLTYPES, $contact_cols); - // add associative coltypes definition - if (!$CONTACTS->coltypes[0]) { - foreach ($CONTACTS->coltypes as $col => $colprop) - $CONTACT_COLTYPES[$col] = $CONTACT_COLTYPES[$col] ? array_merge($CONTACT_COLTYPES[$col], $colprop) : $colprop; + +// Addressbook UI +if (!$RCMAIL->action && !$OUTPUT->ajax_call) { + // add list of address sources to client env + $js_list = $RCMAIL->get_address_sources(); + + // use first directory by default + $source = $js_list[key($js_list)]['id']; + + // find writeable source + foreach ($js_list as $s) { + if (!$s['readonly']) { + $OUTPUT->set_env('writable_source', $s['id']); + break; + } } + + $search_mods = $RCMAIL->config->get('addressbook_search_mods', $SEARCH_MODS_DEFAULT); + $OUTPUT->set_env('search_mods', $search_mods); + $OUTPUT->set_env('address_sources', $js_list); + + $OUTPUT->set_pagetitle(rcube_label('addressbook')); + + $CONTACTS = rcmail_contact_source($source, true); } -$OUTPUT->set_env('photocol', is_array($CONTACT_COLTYPES['photo'])); + +// instantiate a contacts object according to the given source +function rcmail_contact_source($source=null, $init_env=false) +{ + global $RCMAIL, $OUTPUT, $CONFIG, $CONTACT_COLTYPES; + + if (!strlen($source)) { + $source = get_input_value('_source', RCUBE_INPUT_GPC); + } + + if (!strlen($source)) { + return null; + } + + // Get object + $CONTACTS = $RCMAIL->get_address_book($source); + $CONTACTS->set_pagesize($CONFIG['pagesize']); + + // set list properties and session vars + if (!empty($_GET['_page'])) + $CONTACTS->set_page(($_SESSION['page'] = intval($_GET['_page']))); + else + $CONTACTS->set_page(isset($_SESSION['page']) ? $_SESSION['page'] : 1); + + if (!empty($_REQUEST['_gid'])) + $CONTACTS->set_group(get_input_value('_gid', RCUBE_INPUT_GPC)); + + if (!$init_env) + return $CONTACTS; + + $OUTPUT->set_env('readonly', $CONTACTS->readonly); + $OUTPUT->set_env('source', $source); + + // reduce/extend $CONTACT_COLTYPES with specification from the current $CONTACT object + if (is_array($CONTACTS->coltypes)) { + // remove cols not listed by the backend class + $contact_cols = $CONTACTS->coltypes[0] ? array_flip($CONTACTS->coltypes) : $CONTACTS->coltypes; + $CONTACT_COLTYPES = array_intersect_key($CONTACT_COLTYPES, $contact_cols); + // add associative coltypes definition + if (!$CONTACTS->coltypes[0]) { + foreach ($CONTACTS->coltypes as $col => $colprop) + $CONTACT_COLTYPES[$col] = $CONTACT_COLTYPES[$col] ? array_merge($CONTACT_COLTYPES[$col], $colprop) : $colprop; + } + } + + $OUTPUT->set_env('photocol', is_array($CONTACT_COLTYPES['photo'])); + + return $CONTACTS; +} + + +function rcmail_default_source($writable=false) +{ + global $RCMAIL; + + // get list of address sources + $list = $RCMAIL->get_address_sources($writable); + + // use first directory by default + return $list[key($list)]['id']; +} function rcmail_directory_list($attrib) @@ -230,6 +264,11 @@ function rcmail_js_contacts_list($result, $prefix='') while ($row = $result->next()) { $a_row_cols = array(); + // build contact ID with source ID + if (isset($row['sourceid'])) { + $row['ID'] = $row['ID'].'-'.$row['sourceid']; + } + // format each col foreach ($a_show_cols as $col) $a_row_cols[$col] = Q($row[$col]); @@ -246,7 +285,7 @@ function rcmail_contact_frame($attrib) if (!$attrib['id']) $attrib['id'] = 'rcmcontactframe'; - + $attrib['name'] = $attrib['id']; $OUTPUT->set_env('contentframe', $attrib['name']); @@ -269,12 +308,14 @@ function rcmail_rowcount_display($attrib) } -function rcmail_get_rowcount_text() +function rcmail_get_rowcount_text($result=null) { - global $CONTACTS; - + global $CONTACTS, $CONFIG; + // read nr of contacts - $result = $CONTACTS->get_result(); + if (!$result) { + $result = $CONTACTS->get_result(); + } if (!$result) { $result = $CONTACTS->count(); } @@ -286,7 +327,7 @@ function rcmail_get_rowcount_text() 'name' => 'contactsfromto', 'vars' => array( 'from' => $result->first + 1, - 'to' => min($result->count, $result->first + $CONTACTS->page_size), + 'to' => min($result->count, $result->first + $CONFIG['pagesize']), 'count' => $result->count) )); @@ -303,7 +344,7 @@ function rcmail_get_type_label($type) && ($label = preg_replace('/(\d+)$/', '', $label)) && rcube_label_exists($label)) return rcube_label($label) . ' ' . $m[1]; - + return ucfirst($type); } @@ -322,11 +363,11 @@ function rcmail_contact_form($form, $record, $attrib = null) $del_button = $attrib['deleteicon'] ? html::img(array('src' => $CONFIG['skin_path'] . $attrib['deleteicon'], 'alt' => rcube_label('delete'))) : rcube_label('delete'); unset($attrib['deleteicon']); $out = ''; - + // get default coltypes $coltypes = $GLOBALS['CONTACT_COLTYPES']; $coltype_labels = array(); - + foreach ($coltypes as $col => $prop) { if ($prop['subtypes']) { $subtype_names = array_map('rcmail_get_type_label', $prop['subtypes']); @@ -369,7 +410,7 @@ function rcmail_contact_form($form, $record, $attrib = null) // skip cols unknown to the backend if (!$coltypes[$col]) continue; - + // only string values are expected here if (is_array($record[$col])) $record[$col] = join(' ', $record[$col]); @@ -390,7 +431,7 @@ function rcmail_contact_form($form, $record, $attrib = null) } $content .= html::div($blockname, $fields); } - + if ($edit_mode) $content .= html::p('addfield', $select_add->show(null)); @@ -522,13 +563,13 @@ function rcmail_contact_form($form, $record, $attrib = null) else // row without label $rows .= html::div('row', html::div('contactfield', $val)); } - + // add option to the add-field menu if (!$colprop['limit'] || $coltypes[$field]['count'] < $colprop['limit']) { $select_add->add($colprop['label'], $col); $select_add->_count++; } - + // wrap rows in fieldgroup container $content .= html::tag('fieldset', array('class' => 'contactfieldgroup ' . ($colprop['subtypes'] ? 'contactfieldgroupmulti ' : '') . 'contactcontroller' . $col, 'style' => ($rows ? null : 'display:none')), ($colprop['subtypes'] ? html::tag('legend', null, Q($colprop['label'])) : ' ') . @@ -599,6 +640,48 @@ function rcmail_format_date_col($val) } +/** + * Returns contact ID(s) and source(s) from GET/POST data + * + * @return array List of contact IDs per-source + */ +function rcmail_get_cids() +{ + // contact ID (or comma-separated list of IDs) is provided in two + // forms. If _source is an empty string then the ID is a string + // containing contact ID and source name in form: <ID>-<SOURCE> + + $cid = get_input_value('_cid', RCUBE_INPUT_GPC); + $source = get_input_value('_source', RCUBE_INPUT_GPC); + + if (!preg_match('/^[a-zA-Z0-9\+\/=_-]+(,[a-zA-Z0-9\+\/=_-]+)*$/', $cid)) { + return array(); + } + + $cid = explode(',', $cid); + $got_source = strlen($source); + $result = array(); + + // create per-source contact IDs array + foreach ($cid as $id) { + // if _source is not specified we'll find it from decoded ID + if (!$got_source) { + list ($c, $s) = explode('-', $id, 2); + if (strlen($s)) { + $result[$s][] = $c; + } + else if (strlen($source)) { + $result[$source][] = $c; + } + } + else { + $result[$source][] = $id; + } + } + + return $result; +} + // register UI objects $OUTPUT->add_handlers(array( 'directorylist' => 'rcmail_directory_list', diff --git a/program/steps/addressbook/groups.inc b/program/steps/addressbook/groups.inc index 2517873ce..208df24f5 100644 --- a/program/steps/addressbook/groups.inc +++ b/program/steps/addressbook/groups.inc @@ -19,13 +19,14 @@ */ +$source = get_input_value('_source', RCUBE_INPUT_GPC); +$CONTACTS = rcmail_contact_source($source, true); + if ($CONTACTS->readonly || !$CONTACTS->groups) { $OUTPUT->show_message('sourceisreadonly', 'warning'); $OUTPUT->send(); } -$source = get_input_value('_source', RCUBE_INPUT_GPC); - if ($RCMAIL->action == 'group-addmembers') { if (($gid = get_input_value('_gid', RCUBE_INPUT_POST)) && ($ids = get_input_value('_cid', RCUBE_INPUT_POST))) { $plugin = $RCMAIL->plugins->exec_hook('group_addmembers', array('group_id' => $gid, 'ids' => $ids, 'source' => $source)); diff --git a/program/steps/addressbook/import.inc b/program/steps/addressbook/import.inc index 4583be508..fdac9625f 100644 --- a/program/steps/addressbook/import.inc +++ b/program/steps/addressbook/import.inc @@ -13,9 +13,10 @@ | | +-----------------------------------------------------------------------+ | Author: Thomas Bruederli <roundcube@gmail.com> | + | Author: Aleksander Machniak <machniak@kolabsys.com> | +-----------------------------------------------------------------------+ - $Id: $ + $Id$ */ @@ -26,30 +27,44 @@ function rcmail_import_form($attrib) { global $RCMAIL, $OUTPUT; $target = get_input_value('_target', RCUBE_INPUT_GPC); - + $attrib += array('id' => "rcmImportForm"); - - $abook = new html_hiddenfield(array('name' => '_target', 'value' => $target)); - $form = $abook->show(); + + $writable_books = $RCMAIL->get_address_sources(true); $upload = new html_inputfield(array('type' => 'file', 'name' => '_file', 'id' => 'rcmimportfile', 'size' => 40)); - $form .= html::p(null, html::label('rcmimportfile', rcube_label('importfromfile')) . html::br() . $upload->show()); - + $form = html::p(null, html::label('rcmimportfile', rcube_label('importfromfile')) . $upload->show()); + + // addressbook selector + if (count($writable_books) > 1) { + $select = new html_select(array('name' => '_target', 'id' => 'rcmimporttarget')); + + foreach ($writable_books as $book) + $select->add($book['name'], $book['id']); + + $form .= html::p(null, html::label('rcmimporttarget', rcube_label('importtarget')) + . $select->show($target)); + } + else { + $abook = new html_hiddenfield(array('name' => '_target', 'value' => key($writable_books))); + $form .= $abook->show(); + } + $check_replace = new html_checkbox(array('name' => '_replace', 'value' => 1, 'id' => 'rcmimportreplace')); $form .= html::p(null, $check_replace->show(get_input_value('_replace', RCUBE_INPUT_GPC)) . html::label('rcmimportreplace', rcube_label('importreplace'))); - + $OUTPUT->add_label('selectimportfile','importwait'); $OUTPUT->add_gui_object('importform', $attrib['id']); - + $out = html::p(null, Q(rcube_label('importtext'), 'show')); - + $out .= $OUTPUT->form_tag(array( 'action' => $RCMAIL->url('import'), 'method' => 'post', 'enctype' => 'multipart/form-data') + $attrib, $form); - + return $out; } @@ -60,19 +75,19 @@ function rcmail_import_form($attrib) function rcmail_import_confirm($attrib) { global $IMPORT_STATS; - + $vars = get_object_vars($IMPORT_STATS); $vars['names'] = $vars['skipped_names'] = ''; - + $content = html::p(null, rcube_label(array( 'name' => 'importconfirm', 'nr' => $IMORT_STATS->inserted, 'vars' => $vars, )) . ($IMPORT_STATS->names ? ':' : '.')); - + if ($IMPORT_STATS->names) $content .= html::p('em', join(', ', array_map('Q', $IMPORT_STATS->names))); - + if ($IMPORT_STATS->skipped) { $content .= html::p(null, rcube_label(array( 'name' => 'importconfirmskipped', @@ -81,7 +96,7 @@ function rcmail_import_confirm($attrib) )) . ':'); $content .= html::p('em', join(', ', array_map('Q', $IMPORT_STATS->skipped_names))); } - + return html::div($attrib, $content); } @@ -93,10 +108,10 @@ function rcmail_import_buttons($attrib) { global $IMPORT_STATS, $OUTPUT; $target = get_input_value('_target', RCUBE_INPUT_GPC); - + $attrib += array('type' => 'input'); unset($attrib['name']); - + if (is_object($IMPORT_STATS)) { $attrib['class'] = trim($attrib['class'] . ' mainaction'); $out = $OUTPUT->button(array('command' => 'list', 'prop' => $target, 'label' => 'done') + $attrib); @@ -107,7 +122,7 @@ function rcmail_import_buttons($attrib) $attrib['class'] = trim($attrib['class'] . ' mainaction'); $out .= $OUTPUT->button(array('command' => 'import', 'label' => 'import') + $attrib); } - + return $out; } @@ -137,13 +152,13 @@ if ($_FILES['_file']['tmp_name'] && is_uploaded_file($_FILES['_file']['tmp_name' $IMPORT_STATS->skipped_names = array(); $IMPORT_STATS->count = count($vcards); $IMPORT_STATS->inserted = $IMPORT_STATS->skipped = $IMPORT_STATS->nomail = $IMPORT_STATS->errors = 0; - + if ($replace) $CONTACTS->delete_all(); - + foreach ($vcards as $vcard) { $email = $vcard->email[0]; - + // skip entries without an e-mail address if (empty($email)) { $IMPORT_STATS->nomail++; @@ -152,7 +167,7 @@ if ($_FILES['_file']['tmp_name'] && is_uploaded_file($_FILES['_file']['tmp_name' // We're using UTF8 internally $email = rcube_idn_to_utf8($email); - + if (!$replace && $email) { // compare e-mail address $existing = $CONTACTS->search('email', $email, false, false); @@ -165,10 +180,10 @@ if ($_FILES['_file']['tmp_name'] && is_uploaded_file($_FILES['_file']['tmp_name' continue; } } - + $a_record = $vcard->get_assoc(); $a_record['vcard'] = $vcard->export(); - + $plugin = $RCMAIL->plugins->exec_hook('contact_create', array('record' => $a_record, 'source' => null)); $a_record = $plugin['record']; diff --git a/program/steps/addressbook/list.inc b/program/steps/addressbook/list.inc index 864ad939b..dbc86e20f 100644 --- a/program/steps/addressbook/list.inc +++ b/program/steps/addressbook/list.inc @@ -19,20 +19,68 @@ */ -// set message set for search result +// Use search result if (!empty($_REQUEST['_search']) && isset($_SESSION['search'][$_REQUEST['_search']])) - $CONTACTS->set_search_set($_SESSION['search'][$_REQUEST['_search']]); +{ + $search = (array)$_SESSION['search'][$_REQUEST['_search']]; + $records = array(); -// get contacts for this user -$result = $CONTACTS->list_records(array('name')); + if (!empty($_GET['_page'])) + $page = intval($_GET['_page']); + else + $page = isset($_SESSION['page']) ? $_SESSION['page'] : 1; + + $_SESSION['page'] = $page; + + // 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(9999); + $source->set_search_set($set); + + // get records + $result = $source->list_records(array('name', 'email')); + + while ($row = $result->next()) { + $row['sourceid'] = $s; + $key = $row['name'] . ':' . $row['sourceid']; + $records[$key] = $row; + } + unset($result); + } + + // sort the records + ksort($records, SORT_LOCALE_STRING); + + // create resultset object + $count = count($records); + $first = ($page-1) * $CONFIG['pagesize']; + $result = new rcube_result_set($count, $first); + + // we need only records for current page + if ($CONFIG['pagesize'] < $count) { + $records = array_slice($records, $first, $CONFIG['pagesize']); + } + + $result->records = array_values($records); +} +// List selected directory +else { + $CONTACTS = rcmail_contact_source(null, true); + + // get contacts for this user + $result = $CONTACTS->list_records(array('name')); +} // update message count display -$OUTPUT->set_env('pagecount', ceil($result->count / $CONTACTS->page_size)); -$OUTPUT->command('set_rowcount', rcmail_get_rowcount_text($rowcount)); +$OUTPUT->set_env('pagecount', ceil($result->count / $CONFIG['pagesize'])); +$OUTPUT->command('set_rowcount', rcmail_get_rowcount_text($result)); // create javascript list rcmail_js_contacts_list($result); // send response $OUTPUT->send(); - diff --git a/program/steps/addressbook/mailto.inc b/program/steps/addressbook/mailto.inc index e4f2801f7..5996b9da7 100644 --- a/program/steps/addressbook/mailto.inc +++ b/program/steps/addressbook/mailto.inc @@ -19,33 +19,36 @@ */ -$cid = get_input_value('_cid', RCUBE_INPUT_POST); -$recipients = null; +$cids = rcmail_get_cids(); $mailto = array(); -if ($cid && preg_match('/^[a-z0-9\+\/=_-]+(,[a-z0-9\+\/=_-]+)*$/i', $cid) && $CONTACTS->ready) +foreach ($cids as $source => $cid) { - $CONTACTS->set_page(1); - $CONTACTS->set_pagesize(substr_count($cid, ',')+2); // +2 to skip counting query - $recipients = $CONTACTS->search($CONTACTS->primary_key, $cid); - - while (is_object($recipients) && ($rec = $recipients->iterate())) { - $emails = $CONTACTS->get_col_values('email', $rec, true); - $mailto[] = format_email_recipient($emails[0], $rec['name']); - } + $CONTACTS = $RCMAIL->get_address_book($source); + + if ($CONTACTS->ready) + { + $CONTACTS->set_page(1); + $CONTACTS->set_pagesize(count($cid) + 2); // +2 to skip counting query + $recipients = $CONTACTS->search($CONTACTS->primary_key, $cid, false, true, true, 'email'); + + while (is_object($recipients) && ($rec = $recipients->iterate())) { + $emails = $CONTACTS->get_col_values('email', $rec, true); + $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); - $OUTPUT->redirect(array('task' => 'mail', '_action' => 'compose', '_mailto' => $mailto_id)); + $mailto_str = join(', ', $mailto); + $mailto_id = substr(md5($mailto_str), 0, 16); + $_SESSION['mailto'][$mailto_id] = urlencode($mailto_str); + $OUTPUT->redirect(array('task' => 'mail', '_action' => 'compose', '_mailto' => $mailto_id)); +} +else { + $OUTPUT->show_message('nocontactsfound', 'warning'); } -else - $OUTPUT->show_message('nocontactsfound', 'warning'); - // send response $OUTPUT->send(); - diff --git a/program/steps/addressbook/save.inc b/program/steps/addressbook/save.inc index 0092eb103..19d8f8c7d 100644 --- a/program/steps/addressbook/save.inc +++ b/program/steps/addressbook/save.inc @@ -19,9 +19,11 @@ */ -$cid = get_input_value('_cid', RCUBE_INPUT_POST); +$CONTACTS = rcmail_contact_source(null, true); +$cid = get_input_value('_cid', RCUBE_INPUT_POST); $return_action = empty($cid) ? 'add' : 'edit'; + // cannot edit record if ($CONTACTS->readonly) { $OUTPUT->show_message('contactreadonly', 'error'); @@ -38,19 +40,19 @@ if ($RCMAIL->action == 'upload-photo') { if ($filepath = $_FILES['_photo']['tmp_name']) { // check file type and resize image $imageprop = rcmail::imageprops($_FILES['_photo']['tmp_name']); - + if ($imageprop['width'] && $imageprop['height']) { $maxsize = intval($RCMAIL->config->get('contact_photo_size', 160)); $tmpfname = tempnam($RCMAIL->config->get('temp_dir'), 'rcmImgConvert'); $save_hook = 'attachment_upload'; - + // scale image to a maximum size if (($imageprop['width'] > $maxsize || $imageprop['height'] > $maxsize) && (rcmail::imageconvert(array('in' => $filepath, 'out' => $tmpfname, 'size' => $maxsize.'x'.$maxsize, 'type' => $imageprop['type'])) !== false)) { $filepath = $tmpfname; $save_hook = 'attachment_save'; } - + // save uploaded file in storage backend $attachment = $RCMAIL->plugins->exec_hook($save_hook, array( 'path' => $filepath, @@ -87,10 +89,10 @@ if ($RCMAIL->action == 'upload-photo') { $msg = rcube_label(array('name' => 'filesizeerror', 'vars' => array('size' => show_bytes(parse_bytes($maxsize))))); else $msg = rcube_label('fileuploaderror'); - + $OUTPUT->command('display_message', $msg, 'error'); } - + $OUTPUT->command('photo_upload_end'); $OUTPUT->send('iframe'); } @@ -155,7 +157,7 @@ if (isset($a_record['photo'])) { } else unset($a_record['photo']); - + // cleanup session data $RCMAIL->plugins->exec_hook('attachments_cleanup', array('group' => 'contact')); $RCMAIL->session->remove('contacts'); @@ -240,7 +242,7 @@ else { $CONTACTS->add_to_group($gid, $plugin['ids']); } } - + // add contact row or jump to the page where it should appear $CONTACTS->reset(); $result = $CONTACTS->search($CONTACTS->primary_key, $insert_id); diff --git a/program/steps/addressbook/search.inc b/program/steps/addressbook/search.inc index 498e9b222..a78478f80 100644 --- a/program/steps/addressbook/search.inc +++ b/program/steps/addressbook/search.inc @@ -31,17 +31,17 @@ $OUTPUT->send('contactsearch'); function rcmail_contact_search() { - global $RCMAIL, $OUTPUT, $CONTACTS, $CONTACT_COLTYPES, $SEARCH_MODS_DEFAULT; + global $RCMAIL, $OUTPUT, $CONFIG, $SEARCH_MODS_DEFAULT; $adv = isset($_POST['_adv']); // get fields/values from advanced search form if ($adv) { - foreach ($CONTACT_COLTYPES as $col => $colprop) { - $s = trim(get_input_value('_'.$col, RCUBE_INPUT_POST, true)); - if (strlen($s)) { + foreach (array_keys($_POST) as $key) { + $s = trim(get_input_value($key, RCUBE_INPUT_POST, true)); + if (strlen($s) && preg_match('/^_search_([a-zA-Z0-9_-]+)$/', $key, $m)) { $search[] = $s; - $fields[] = $col; + $fields[] = $m[1]; } } @@ -71,20 +71,77 @@ function rcmail_contact_search() } } + // get sources list + $sources = $RCMAIL->get_address_sources(); + $search_set = array(); + $records = array(); + + foreach ($sources as $s) { + $source = $RCMAIL->get_address_book($s['id']); + + // check if all search fields are supported.... + if (is_array($fields)) { + $cols = $source->coltypes[0] ? array_flip($source->coltypes) : $source->coltypes; + $supported = 0; + + foreach ($fields as $f) { + if (array_key_exists($f, $cols)) { + $supported ++; + } + } + + // ...if not, we can skip this source + if ($supported < count($fields)) { + continue; + } + } + + // reset page + $source->set_page(1); + $source->set_pagesize(9999); + + // get contacts count + $result = $source->search($fields, $search, false, false); + + if (!$result->count) { + continue; + } + + // get records + $result = $source->list_records(array('name', 'email')); + + while ($row = $result->next()) { + $row['sourceid'] = $s['id']; + $key = $row['name'] . ':' . $row['sourceid']; + $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 ($CONFIG['pagesize'] < $count) { + $records = array_slice($records, 0, $CONFIG['pagesize']); + } + + $result->records = array_values($records); + // search request ID $search_request = md5('addr' .(is_array($fields) ? implode($fields, ',') : $fields) .(is_array($search) ? implode($search, ',') : $search)); - // reset page - $CONTACTS->set_page(1); - $_SESSION['page'] = 1; - - // get contacts for this user - $result = $CONTACTS->search($fields, $search); - // save search settings in session - $_SESSION['search'][$search_request] = $CONTACTS->get_search_set(); + $_SESSION['search'][$search_request] = $search_set; + $_SESSION['page'] = 1; if ($adv) $OUTPUT->command('list_contacts_clear'); @@ -99,8 +156,11 @@ function rcmail_contact_search() // update message count display $OUTPUT->command('set_env', 'search_request', $search_request); - $OUTPUT->command('set_env', 'pagecount', ceil($result->count / $CONTACTS->page_size)); - $OUTPUT->command('set_rowcount', rcmail_get_rowcount_text()); + $OUTPUT->command('set_env', 'pagecount', ceil($result->count / $CONFIG['pagesize'])); + $OUTPUT->command('set_rowcount', rcmail_get_rowcount_text($result)); + + // unselect currently selected directory/group + $OUTPUT->command('unselect_directory'); // send response $OUTPUT->send($adv ? 'iframe' : null); @@ -108,7 +168,7 @@ function rcmail_contact_search() function rcmail_contact_search_form($attrib) { - global $RCMAIL, $CONTACTS, $CONTACT_COLTYPES; + global $RCMAIL, $CONTACT_COLTYPES; $i_size = !empty($attrib['size']) ? $attrib['size'] : 30; @@ -130,7 +190,26 @@ function rcmail_contact_search_form($attrib) ), ); - foreach ($CONTACT_COLTYPES as $col => $colprop) + // get supported coltypes from all address sources + $sources = $RCMAIL->get_address_sources(); + $coltypes = array(); + + foreach ($sources as $s) { + $CONTACTS = $RCMAIL->get_address_book($s['id']); + + if (is_array($CONTACTS->coltypes)) { + $contact_cols = $CONTACTS->coltypes[0] ? array_flip($CONTACTS->coltypes) : $CONTACTS->coltypes; + $coltypes = array_merge($coltypes, $contact_cols); + } + } + + // merge supported coltypes with $CONTACT_COLTYPES + foreach ($coltypes as $col => $colprop) { + $coltypes[$col] = $CONTACT_COLTYPES[$col] ? array_merge($CONTACT_COLTYPES[$col], (array)$colprop) : (array)$colprop; + } + + // build form fields list + foreach ($coltypes as $col => $colprop) { if ($colprop['type'] != 'image' && !$colprop['nosearch']) { @@ -142,15 +221,13 @@ function rcmail_contact_search_form($attrib) $colprop['size'] = $i_size; $content = html::div('row', html::div('contactfieldlabel label', Q($label)) - . html::div('contactfieldcontent', rcmail_get_edit_field($col, '', $colprop, $ftype))); + . html::div('contactfieldcontent', rcmail_get_edit_field('search_'.$col, '', $colprop, $ftype))); $form[$category]['content'][] = $content; } } - $hiddenfields = new html_hiddenfield(array( - 'name' => '_source', 'value' => get_input_value('_source', RCUBE_INPUT_GPC))); - $hiddenfields->add(array('name' => '_gid', 'value' => $CONTACTS->group_id)); + $hiddenfields = new html_hiddenfield(); $hiddenfields->add(array('name' => '_adv', 'value' => 1)); $out = $RCMAIL->output->request_form(array( diff --git a/program/steps/addressbook/show.inc b/program/steps/addressbook/show.inc index f62bad3d5..998dee19a 100644 --- a/program/steps/addressbook/show.inc +++ b/program/steps/addressbook/show.inc @@ -19,9 +19,16 @@ */ +// Get contact ID and source ID from request +$cids = rcmail_get_cids(); +$source = key($cids); +$cid = array_shift($cids[$source]); + +// Initialize addressbook source +$CONTACTS = rcmail_contact_source($source, true); // read contact record -if (($cid = get_input_value('_cid', RCUBE_INPUT_GPC)) && ($record = $CONTACTS->get_record($cid, true))) { +if ($cid && ($record = $CONTACTS->get_record($cid, true))) { $OUTPUT->set_env('cid', $record['ID']); } @@ -41,7 +48,7 @@ if ($RCMAIL->action == 'photo') { if (!preg_match('![^a-z0-9/=+-]!i', $data)) $data = base64_decode($data, true); } - + header('Content-Type: ' . rc_image_content_type($data)); echo $data ? $data : file_get_contents('program/blank.gif'); exit; @@ -190,14 +197,15 @@ function rcmail_contact_record_groups($contact_id) $form_end = '</form>'; $RCMAIL->output->add_gui_object('editform', 'form'); - + return $form_start . $table->show() . $form_end; } -//$OUTPUT->framed = $_framed; -$OUTPUT->add_handler('contacthead', 'rcmail_contact_head'); -$OUTPUT->add_handler('contactdetails', 'rcmail_contact_details'); -$OUTPUT->add_handler('contactphoto', 'rcmail_contact_photo'); +$OUTPUT->add_handlers(array( + 'contacthead' => 'rcmail_contact_head', + 'contactdetails' => 'rcmail_contact_details', + 'contactphoto' => 'rcmail_contact_photo', +)); $OUTPUT->send('contact'); |