summaryrefslogtreecommitdiff
path: root/program/steps/addressbook
diff options
context:
space:
mode:
authorAndy Wermke <andy@dev.next-step-software.com>2013-04-04 16:10:23 +0200
committerAndy Wermke <andy@dev.next-step-software.com>2013-04-04 16:10:23 +0200
commit92cd7f34b07e86062f2c024039e3309768b48ce6 (patch)
tree63b9f39280ebcab80742d9f2b4db6a139c1791e1 /program/steps/addressbook
parent029d18f13bcf01aa2f1f08dbdfc6400c081bf7cb (diff)
parent443b92a7ee19e321b350750240e0fc54ec5be357 (diff)
Merge branch 'master' of https://github.com/roundcube/roundcubemail
Diffstat (limited to 'program/steps/addressbook')
-rw-r--r--program/steps/addressbook/delete.inc2
-rw-r--r--program/steps/addressbook/export.inc79
-rw-r--r--program/steps/addressbook/func.inc67
-rw-r--r--program/steps/addressbook/groups.inc13
-rw-r--r--program/steps/addressbook/import.inc11
-rw-r--r--program/steps/addressbook/list.inc2
-rw-r--r--program/steps/addressbook/save.inc2
-rw-r--r--program/steps/addressbook/search.inc8
8 files changed, 135 insertions, 49 deletions
diff --git a/program/steps/addressbook/delete.inc b/program/steps/addressbook/delete.inc
index 81b8a0970..56118583c 100644
--- a/program/steps/addressbook/delete.inc
+++ b/program/steps/addressbook/delete.inc
@@ -93,7 +93,7 @@ if (($search_request = $_REQUEST['_search']) && isset($_SESSION['search'][$searc
while ($row = $result->next()) {
$row['sourceid'] = $s;
- $key = rcmail_contact_key($row, $sort_col);
+ $key = rcube_addressbook::compose_contact_key($row, $sort_col);
$records[$key] = $row;
}
unset($result);
diff --git a/program/steps/addressbook/export.inc b/program/steps/addressbook/export.inc
index 850795c85..761f26b75 100644
--- a/program/steps/addressbook/export.inc
+++ b/program/steps/addressbook/export.inc
@@ -40,11 +40,31 @@ if (!empty($_REQUEST['_search']) && isset($_SESSION['search'][$_REQUEST['_search
// get records
$result = $source->list_records();
- while ($row = $result->next()) {
- $row['sourceid'] = $s;
- $key = rcmail_contact_key($row, $sort_col);
- $records[$key] = $row;
+ while ($record = $result->next()) {
+ // because vcard_map is per-source we need to create vcard here
+ if (empty($record['vcard']) || empty($record['name'])) {
+ $vcard = new rcube_vcard();
+ $vcard->extend_fieldmap($source->vcard_map);
+ $vcard->load($record['vcard']);
+ $vcard->reset();
+
+ foreach ($record 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));
+ }
+ }
+ }
+
+ $record['vcard'] = $vcard->export(true);
+ }
+
+ $record['sourceid'] = $s;
+ $key = rcube_addressbook::compose_contact_key($record, $sort_col);
+ $records[$key] = $record;
}
+
unset($result);
}
@@ -56,6 +76,51 @@ if (!empty($_REQUEST['_search']) && isset($_SESSION['search'][$_REQUEST['_search
$result = new rcube_result_set($count);
$result->records = array_values($records);
}
+// selected contacts
+else if (!empty($_REQUEST['_cid'])) {
+ $sort_col = $RCMAIL->config->get('addressbook_sort_col', 'name');
+ $records = array();
+
+ // Selected contact IDs (with multi-source support)
+ $cids = rcmail_get_cids();
+
+ foreach ($cids as $s => $ids) {
+ $source = $RCMAIL->get_address_book($s);
+ $result = $source->search('ID', $ids, 1, true, true);
+
+ while ($record = $result->next()) {
+ // because vcard_map is per-source we need to create vcard here
+ if (empty($record['vcard']) || empty($record['name'])) {
+ $vcard = new rcube_vcard();
+ $vcard->extend_fieldmap($source->vcard_map);
+ $vcard->load($record['vcard']);
+ $vcard->reset();
+
+ foreach ($record 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));
+ }
+ }
+ }
+
+ $record['vcard'] = $vcard->export(true);
+ }
+
+ $record['sourceid'] = $s;
+ $key = rcube_addressbook::compose_contact_key($record, $sort_col);
+ $records[$key] = $record;
+ }
+ }
+
+ ksort($records, SORT_LOCALE_STRING);
+
+ // create resultset object
+ $count = count($records);
+ $result = new rcube_result_set($count);
+ $result->records = array_values($records);
+}
// selected directory/group
else {
$CONTACTS = rcmail_contact_source(null, true);
@@ -68,12 +133,14 @@ else {
// send downlaod headers
header('Content-Type: text/x-vcard; charset='.RCMAIL_CHARSET);
-header('Content-Disposition: attachment; filename="rcube_contacts.vcf"');
+header('Content-Disposition: attachment; filename="contacts.vcf"');
while ($result && ($row = $result->next())) {
// we already have a vcard record
if ($row['vcard'] && $row['name']) {
- $row['vcard'] = preg_replace('/\r?\n/', rcube_vcard::$eol, $row['vcard']);
+ // fix folding and end-of-line chars
+ $row['vcard'] = preg_replace('/\r|\n\s+/', '', $row['vcard']);
+ $row['vcard'] = preg_replace('/\n/', rcube_vcard::$eol, $row['vcard']);
echo rcube_vcard::rfc2425_fold($row['vcard']) . rcube_vcard::$eol;
}
// copy values into vcard object
diff --git a/program/steps/addressbook/func.inc b/program/steps/addressbook/func.inc
index fded9a819..ffc0b3b92 100644
--- a/program/steps/addressbook/func.inc
+++ b/program/steps/addressbook/func.inc
@@ -26,7 +26,7 @@ $CONTACT_COLTYPES = array(
'name' => array('type' => 'text', 'size' => 40, 'maxlength' => 50, 'limit' => 1, 'label' => rcube_label('name'), 'category' => 'main'),
'firstname' => array('type' => 'text', 'size' => 19, 'maxlength' => 50, 'limit' => 1, 'label' => rcube_label('firstname'), 'category' => 'main'),
'surname' => array('type' => 'text', 'size' => 19, 'maxlength' => 50, 'limit' => 1, 'label' => rcube_label('surname'), 'category' => 'main'),
- 'email' => array('type' => 'text', 'size' => 40, 'maxlength' => 50, 'label' => rcube_label('email'), 'subtypes' => array('home','work','other'), 'category' => 'main'),
+ 'email' => array('type' => 'text', 'size' => 40, 'maxlength' => 254, 'label' => rcube_label('email'), 'subtypes' => array('home','work','other'), 'category' => 'main'),
'middlename' => array('type' => 'text', 'size' => 19, 'maxlength' => 50, 'limit' => 1, 'label' => rcube_label('middlename'), 'category' => 'main'),
'prefix' => array('type' => 'text', 'size' => 8, 'maxlength' => 20, 'limit' => 1, 'label' => rcube_label('nameprefix'), 'category' => 'main'),
'suffix' => array('type' => 'text', 'size' => 8, 'maxlength' => 20, 'limit' => 1, 'label' => rcube_label('namesuffix'), 'category' => 'main'),
@@ -187,7 +187,7 @@ function rcmail_directory_list($attrib)
$jsdata = array();
$line_templ = html::tag('li', array(
- 'id' => 'rcmli%s', 'class' => '%s'),
+ 'id' => 'rcmli%s', 'class' => '%s', 'noclose' => true),
html::a(array('href' => '%s',
'rel' => '%s',
'onclick' => "return ".JS_OBJECT_NAME.".command('list','%s',this)"), '%s'));
@@ -213,7 +213,7 @@ function rcmail_directory_list($attrib)
$name = !empty($source['name']) ? $source['name'] : $id;
$out .= sprintf($line_templ,
- html_identifier($id),
+ rcube_utils::html_identifier($id, true),
$class_name,
Q(rcmail_url(null, array('_source' => $id))),
$source['id'],
@@ -224,10 +224,11 @@ function rcmail_directory_list($attrib)
$groupdata = rcmail_contact_groups($groupdata);
$jsdata = $groupdata['jsdata'];
$out = $groupdata['out'];
+ $out .= '</li>';
}
$line_templ = html::tag('li', array(
- 'id' => 'rcmliS%s', 'class' => '%s'),
+ 'id' => 'rcmli%s', 'class' => '%s'),
html::a(array('href' => '#', 'rel' => 'S%s',
'onclick' => "return ".JS_OBJECT_NAME.".command('listsearch', '%s', this)"), '%s'));
@@ -245,14 +246,17 @@ function rcmail_directory_list($attrib)
$class_name .= ' ' . $source['class_name'];
$out .= sprintf($line_templ,
- html_identifier($id),
+ rcube_utils::html_identifier('S'.$id, true),
$class_name,
$id,
$js_id, (!empty($source['name']) ? Q($source['name']) : Q($id)));
}
$OUTPUT->set_env('contactgroups', $jsdata);
+ $OUTPUT->set_env('collapsed_abooks', (string)$RCMAIL->config->get('collapsed_abooks',''));
$OUTPUT->add_gui_object('folderlist', $attrib['id']);
+ $OUTPUT->include_script('treelist.js');
+
// add some labels to client
$OUTPUT->add_label('deletegroupconfirm', 'groupdeleting', 'addingmember', 'removingmember');
@@ -264,19 +268,25 @@ function rcmail_contact_groups($args)
{
global $RCMAIL;
+ $groups_html = '';
$groups = $RCMAIL->get_address_book($args['source'])->list_groups();
+ $js_id = $RCMAIL->JQ($args['source']);
if (!empty($groups)) {
$line_templ = html::tag('li', array(
- 'id' => 'rcmliG%s', 'class' => 'contactgroup'),
+ 'id' => 'rcmli%s', 'class' => 'contactgroup'),
html::a(array('href' => '#',
'rel' => '%s:%s',
'onclick' => "return ".JS_OBJECT_NAME.".command('listgroup',{'source':'%s','id':'%s'},this)"), '%s'));
+ // append collapse/expand toggle and open a new <ul>
+ $is_collapsed = strpos($RCMAIL->config->get('collapsed_abooks',''), '&'.rawurlencode($args['source']).'&') !== false;
+ $args['out'] .= html::div('treetoggle ' . ($is_collapsed ? 'collapsed' : 'expanded'), '&nbsp;');
+
$jsdata = array();
foreach ($groups as $group) {
- $args['out'] .= sprintf($line_templ,
- html_identifier($args['source'] . $group['ID']),
+ $groups_html .= sprintf($line_templ,
+ rcube_utils::html_identifier('G' . $args['source'] . $group['ID'], true),
$args['source'], $group['ID'],
$args['source'], $group['ID'], Q($group['name'])
);
@@ -286,6 +296,10 @@ function rcmail_contact_groups($args)
}
}
+ $args['out'] .= html::tag('ul',
+ array('class' => 'groups', 'style' => ($is_collapsed ? "display:none;" : null)),
+ $groups_html);
+
return $args;
}
@@ -733,30 +747,12 @@ function rcmail_format_date_col($val)
}
-function rcmail_contact_key($row, $sort_col)
-{
- $key = $row[$sort_col] . ':' . $row['sourceid'];
-
- // add email to a key to not skip contacts with the same name (#1488375)
- if (!empty($row['email'])) {
- if (is_array($row['email'])) {
- $key .= ':' . implode(':', $row['email']);
- }
- else {
- $key .= ':' . $row['email'];
- }
- }
-
- return $key;
-}
-
-
/**
* Returns contact ID(s) and source(s) from GET/POST data
*
* @return array List of contact IDs per-source
*/
-function rcmail_get_cids()
+function rcmail_get_cids($filter = null)
{
// 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
@@ -765,6 +761,10 @@ function rcmail_get_cids()
$cid = get_input_value('_cid', RCUBE_INPUT_GPC);
$source = (string) get_input_value('_source', RCUBE_INPUT_GPC);
+ if (is_array($cid)) {
+ return $cid;
+ }
+
if (!preg_match('/^[a-zA-Z0-9\+\/=_-]+(,[a-zA-Z0-9\+\/=_-]+)*$/', $cid)) {
return array();
}
@@ -775,24 +775,29 @@ function rcmail_get_cids()
// create per-source contact IDs array
foreach ($cid as $id) {
- // if _source is not specified we'll find it from decoded ID
+ // extract source ID from contact ID (it's there in search mode)
+ // see #1488959 and #1488862 for reference
if (!$got_source) {
if ($sep = strrpos($id, '-')) {
$contact_id = substr($id, 0, $sep);
- $source_id = substr($id, $sep+1);
+ $source_id = (string) substr($id, $sep+1);
if (strlen($source_id)) {
- $result[(string)$source_id][] = $contact_id;
+ $result[$source_id][] = $contact_id;
}
}
}
else {
+ if (substr($id, -($got_source+1)) == "-$source") {
+ $id = substr($id, 0, -($got_source+1));
+ }
$result[$source][] = $id;
}
}
- return $result;
+ return $filter !== null ? $result[$filter] : $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 b70453889..3b9288a2b 100644
--- a/program/steps/addressbook/groups.inc
+++ b/program/steps/addressbook/groups.inc
@@ -20,7 +20,7 @@
*/
$source = get_input_value('_source', RCUBE_INPUT_GPC);
-$CONTACTS = rcmail_contact_source($source, true);
+$CONTACTS = rcmail_contact_source($source);
if ($CONTACTS->readonly || !$CONTACTS->groups) {
$OUTPUT->show_message('sourceisreadonly', 'warning');
@@ -28,11 +28,11 @@ if ($CONTACTS->readonly || !$CONTACTS->groups) {
}
if ($RCMAIL->action == 'group-addmembers') {
- if (($gid = get_input_value('_gid', RCUBE_INPUT_POST)) && ($ids = get_input_value('_cid', RCUBE_INPUT_POST))) {
+ if (($gid = get_input_value('_gid', RCUBE_INPUT_POST)) && ($ids = rcmail_get_cids($source))) {
$plugin = $RCMAIL->plugins->exec_hook('group_addmembers', array('group_id' => $gid, 'ids' => $ids, 'source' => $source));
$CONTACTS->set_group($gid);
- $num2add = count(explode(',', $plugin['ids']));
+ $num2add = count($plugin['ids']);
if (!$plugin['abort']) {
if (($maxnum = $RCMAIL->config->get('max_group_members', 0)) && ($CONTACTS->count()->count + $num2add > $maxnum)) {
@@ -55,7 +55,7 @@ if ($RCMAIL->action == 'group-addmembers') {
}
else if ($RCMAIL->action == 'group-delmembers') {
- if (($gid = get_input_value('_gid', RCUBE_INPUT_POST)) && ($ids = get_input_value('_cid', RCUBE_INPUT_POST))) {
+ if (($gid = get_input_value('_gid', RCUBE_INPUT_POST)) && ($ids = rcmail_get_cids($source))) {
$plugin = $RCMAIL->plugins->exec_hook('group_delmembers', array('group_id' => $gid, 'ids' => $ids, 'source' => $source));
if (!$plugin['abort'])
@@ -63,10 +63,11 @@ else if ($RCMAIL->action == 'group-delmembers') {
else
$result = $plugin['result'];
- if ($result){
+ if ($result) {
$OUTPUT->show_message('contactremovedfromgroup');
$OUTPUT->command('remove_group_contacts',array('source' => $source, 'gid' => $gid));
- }else{
+ }
+ else {
$OUTPUT->show_message($plugin['message'] ? $plugin['message'] : 'errorsaving', 'error');
}
}
diff --git a/program/steps/addressbook/import.inc b/program/steps/addressbook/import.inc
index df07d64bc..72da15078 100644
--- a/program/steps/addressbook/import.inc
+++ b/program/steps/addressbook/import.inc
@@ -209,6 +209,15 @@ if (is_array($_FILES['_file'])) {
foreach ($vcards as $vcard) {
$a_record = $vcard->get_assoc();
+ // Generate contact's display name (must be before validation), the same we do in save.inc
+ if (empty($a_record['name'])) {
+ $a_record['name'] = rcube_addressbook::compose_display_name($a_record, true);
+ // Reset it if equals to email address (from compose_display_name())
+ if ($a_record['name'] == $a_record['email'][0]) {
+ $a_record['name'] = '';
+ }
+ }
+
// skip invalid (incomplete) entries
if (!$CONTACTS->validate($a_record, true)) {
$IMPORT_STATS->invalid++;
@@ -250,7 +259,7 @@ if (is_array($_FILES['_file'])) {
if ($success) {
$IMPORT_STATS->inserted++;
- $IMPORT_STATS->names[] = $vcard->displayname ? $vcard->displayname : $email;
+ $IMPORT_STATS->names[] = $a_record['name'] ? $a_record['name'] : $email;
}
else {
$IMPORT_STATS->errors++;
diff --git a/program/steps/addressbook/list.inc b/program/steps/addressbook/list.inc
index 06a1e10a3..1bb28658b 100644
--- a/program/steps/addressbook/list.inc
+++ b/program/steps/addressbook/list.inc
@@ -49,7 +49,7 @@ if (!empty($_REQUEST['_search']) && isset($_SESSION['search'][$_REQUEST['_search
while ($row = $result->next()) {
$row['sourceid'] = $s;
- $key = rcmail_contact_key($row, $sort_col);
+ $key = rcube_addressbook::compose_contact_key($row, $sort_col);
$records[$key] = $row;
}
unset($result);
diff --git a/program/steps/addressbook/save.inc b/program/steps/addressbook/save.inc
index 901ea0190..8cab6e817 100644
--- a/program/steps/addressbook/save.inc
+++ b/program/steps/addressbook/save.inc
@@ -82,7 +82,7 @@ if (empty($a_record['name'])) {
// do input checks (delegated to $CONTACTS instance)
if (!$CONTACTS->validate($a_record)) {
$err = (array)$CONTACTS->get_error();
- $OUTPUT->show_message($err['message'] ? $err['message'] : 'formincomplete', 'warning');
+ $OUTPUT->show_message($err['message'] ? Q($err['message']) : 'formincomplete', 'warning');
$GLOBALS['EDIT_RECORD'] = $a_record; // store submitted data to be used in edit form
rcmail_overwrite_action($return_action);
return;
diff --git a/program/steps/addressbook/search.inc b/program/steps/addressbook/search.inc
index 851325070..d153c255a 100644
--- a/program/steps/addressbook/search.inc
+++ b/program/steps/addressbook/search.inc
@@ -184,7 +184,7 @@ function rcmail_contact_search()
while ($row = $result->next()) {
$row['sourceid'] = $s['id'];
- $key = rcmail_contact_key($row, $sort_col);
+ $key = rcube_addressbook::compose_contact_key($row, $sort_col);
$records[$key] = $row;
}
@@ -300,9 +300,13 @@ function rcmail_contact_search_form($attrib)
$label = isset($colprop['label']) ? $colprop['label'] : rcube_label($col);
$category = $colprop['category'] ? $colprop['category'] : 'other';
- if ($ftype == 'text')
+ // load jquery UI datepicker for date fields
+ if ($colprop['type'] == 'date')
+ $colprop['class'] .= ($colprop['class'] ? ' ' : '') . 'datepicker';
+ else if ($ftype == 'text')
$colprop['size'] = $i_size;
+
$content = html::div('row', html::div('contactfieldlabel label', Q($label))
. html::div('contactfieldcontent', rcmail_get_edit_field('search_'.$col, '', $colprop, $ftype)));