summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG2
-rw-r--r--config/main.inc.php.dist1
-rw-r--r--program/js/app.js87
-rw-r--r--program/localization/en_US/labels.inc1
-rw-r--r--program/localization/pl_PL/labels.inc1
-rw-r--r--program/steps/addressbook/copy.inc112
-rw-r--r--program/steps/addressbook/delete.inc126
-rw-r--r--program/steps/addressbook/edit.inc61
-rw-r--r--program/steps/addressbook/export.inc85
-rw-r--r--program/steps/addressbook/func.inc209
-rw-r--r--program/steps/addressbook/groups.inc5
-rw-r--r--program/steps/addressbook/import.inc65
-rw-r--r--program/steps/addressbook/list.inc62
-rw-r--r--program/steps/addressbook/mailto.inc41
-rw-r--r--program/steps/addressbook/save.inc18
-rw-r--r--program/steps/addressbook/search.inc119
-rw-r--r--program/steps/addressbook/show.inc22
17 files changed, 733 insertions, 284 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 00556ddf8..19a73b2c1 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -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');