From e848180aaa9640de871796ca1a3e4f8110701fd6 Mon Sep 17 00:00:00 2001 From: thomascube Date: Thu, 12 May 2011 20:18:19 +0000 Subject: [PATCH] Improve display name composition when saving contacts (#1487143), with plugin-support; allow empty names in sql address book, fall back to e-mail address in listing and vcard export --- CHANGELOG | 1 + program/include/rcube_addressbook.php | 37 ++++++++++++++++++++++----- program/include/rcube_contacts.php | 6 ++--- program/include/rcube_ldap.php | 20 +++++++++++++++ program/include/rcube_vcard.php | 11 +++----- program/steps/addressbook/export.inc | 4 +-- program/steps/addressbook/func.inc | 7 +++-- program/steps/addressbook/save.inc | 7 +++-- program/steps/mail/addcontact.inc | 5 +--- 9 files changed, 69 insertions(+), 29 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index caac24157..f960937ed 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Improve display name composition when saving contacts (#1487143) - Fixed handling of folder with name "0" in folder selector - Fix problems with subfolders of INBOX folder on some IMAP servers (#1487725) - Fix handling of folders that doesn't belong to any namespace (#1487637) diff --git a/program/include/rcube_addressbook.php b/program/include/rcube_addressbook.php index e4deea1d1..c580c400d 100644 --- a/program/include/rcube_addressbook.php +++ b/program/include/rcube_addressbook.php @@ -172,18 +172,13 @@ abstract class rcube_addressbook /** * Check the given data before saving. - * If input not valid, the message to display can be fetched using get_error() + * If input isn't valid, the message to display can be fetched using get_error() * * @param array Assoziative array with data to save * @return boolean True if input is valid, False if not. */ public function validate($save_data) { - if (empty($save_data['name'])) { - $this->set_error('warning', 'nonamewarning'); - return false; - } - // check validity of email addresses foreach ($this->get_col_values('email', $save_data, true) as $email) { if (strlen($email)) { @@ -416,6 +411,34 @@ abstract class rcube_addressbook return join(" ", $arr); } - + + + /** + * Compose a valid display name from the given structured contact data + * + * @param array Hash array with contact data as key-value pairs + * @return string Display name + */ + public static function compose_display_name($contact) + { + $contact = rcmail::get_instance()->plugins->exec_hook('contact_displayname', $contact); + $fn = $contact['name']; + + if (!$fn) + $fn = join(' ', array_filter(array($contact['prefix'], $contact['firstname'], $contact['middlename'], $contact['surname'], $contact['suffix']))); + + // use email address part for name + $email = is_array($contact['email']) ? $contact['email'][0] : $contact['email']; + if ($email && (empty($fn) || $fn == $email)) { + list($emailname) = explode('@', $email); + if (preg_match('/(.*)[\.\-\_](.*)/', $emailname, $match)) + $fn = trim(ucfirst($match[1]).' '.ucfirst($match[2])); + else + $fn = ucfirst($emailname); + } + + return $fn; + } + } diff --git a/program/include/rcube_contacts.php b/program/include/rcube_contacts.php index 3c713fe19..8abc76302 100644 --- a/program/include/rcube_contacts.php +++ b/program/include/rcube_contacts.php @@ -184,7 +184,7 @@ class rcube_contacts extends rcube_addressbook " AND c.user_id=?" . ($this->group_id ? " AND m.contactgroup_id=?" : ""). ($this->filter ? " AND (".$this->filter.")" : "") . - " ORDER BY c.name", + " ORDER BY c.name, c.email", $start_row, $length, $this->user_id, @@ -410,10 +410,10 @@ class rcube_contacts extends rcube_addressbook */ public function validate($save_data) { - // check for name input + // validate e-mail addresses $valid = parent::validate($save_data); - // require at least one e-mail address (syntax check is done later in save.inc) + // require at least one e-mail address (syntax check is already done) if ($valid && !array_filter($this->get_col_values('email', $save_data, true))) { $this->set_error('warning', 'noemailwarning'); $valid = false; diff --git a/program/include/rcube_ldap.php b/program/include/rcube_ldap.php index d9f5a104f..f166fd23a 100644 --- a/program/include/rcube_ldap.php +++ b/program/include/rcube_ldap.php @@ -609,6 +609,26 @@ class rcube_ldap extends rcube_addressbook } + /** + * Check the given data before saving. + * If input not valid, the message to display can be fetched using get_error() + * + * @param array Assoziative array with data to save + * @return boolean True if input is valid, False if not. + */ + public function validate($save_data) + { + // check for name input + if (empty($save_data['name'])) { + $this->set_error('warning', 'nonamewarning'); + return false; + } + + // validate e-mail addresses + return parent::validate($save_data); + } + + /** * Create a new contact record * diff --git a/program/include/rcube_vcard.php b/program/include/rcube_vcard.php index 0cb0b2051..4457bf3c9 100644 --- a/program/include/rcube_vcard.php +++ b/program/include/rcube_vcard.php @@ -115,13 +115,6 @@ class rcube_vcard $this->email[0] = $this->email[$pref_index]; $this->email[$pref_index] = $tmp; } - - // make sure displayname is not empty (required by RFC2426) - if (!strlen($this->displayname)) { - // the same method is used in steps/mail/addcontact.inc - $this->displayname = ucfirst(preg_replace('/[\.\-]/', ' ', - substr($this->email[0], 0, strpos($this->email[0], '@')))); - } } @@ -585,6 +578,10 @@ class rcube_vcard while ($type == "N" && is_array($entries[0]) && count($entries[0]) < 5) $entries[0][] = ""; + // make sure FN is not empty (required by RFC2426) + if ($type == "FN" && empty($entries)) + $entries[0] = $data['EMAIL'][0][0]; + foreach((array)$entries as $entry) { $attr = ''; if (is_array($entry)) { diff --git a/program/steps/addressbook/export.inc b/program/steps/addressbook/export.inc index 69f8e2e0b..bfe8e996c 100644 --- a/program/steps/addressbook/export.inc +++ b/program/steps/addressbook/export.inc @@ -5,7 +5,7 @@ | program/steps/addressbook/export.inc | | | | This file is part of the Roundcube Webmail client | - | Copyright (C) 2008-2009, The Roundcube Dev Team | + | Copyright (C) 2008-2011, The Roundcube Dev Team | | Licensed under the GNU GPL | | | | PURPOSE: | @@ -31,7 +31,7 @@ header('Content-Disposition: attachment; filename="rcube_contacts.vcf"'); while ($result && ($row = $result->next())) { // we already have a vcard record - if ($row['vcard']) { + if ($row['vcard'] && $row['name']) { echo rcube_vcard::rfc2425_fold($row['vcard']) . "\n"; } // copy values into vcard object diff --git a/program/steps/addressbook/func.inc b/program/steps/addressbook/func.inc index 837256f89..c36108784 100644 --- a/program/steps/addressbook/func.inc +++ b/program/steps/addressbook/func.inc @@ -346,10 +346,9 @@ function rcmail_contact_form($form, $record, $attrib = null) // render head section with name fields (not a regular list of rows) if ($section == 'head') { $content = ''; - - // TODO: use the save name composition function as in save.inc - $names_arr = array($record['prefix'], $record['firstname'], $record['middlename'], $record['surname'], $record['suffix']); - if ($record['name'] == join(' ', array_filter($names_arr))) + + // unset display name if it is composed from name parts (same composition function as in save.inc) + if ($record['name'] == rcube_addressbook::compose_display_name(array('name' => '') + $record)) unset($record['name']); // group fields diff --git a/program/steps/addressbook/save.inc b/program/steps/addressbook/save.inc index 88fe98cd1..253609780 100644 --- a/program/steps/addressbook/save.inc +++ b/program/steps/addressbook/save.inc @@ -129,9 +129,9 @@ foreach ($GLOBALS['CONTACT_COLTYPES'] as $col => $colprop) { } } +// let a dedicated function or a plugin compose the full name if empty if (empty($a_record['name'])) { - // TODO: let a dedicated function or a plugin compose the full name - $a_record['name'] = join(' ', array_filter(array($a_record['prefix'], $a_record['firstname'], $a_record['middlename'], $a_record['surname'], $a_record['suffix'],))); + $a_record['name'] = rcube_addressbook::compose_display_name($a_record); } @@ -185,6 +185,9 @@ if (!empty($cid)) // define list of cols to be displayed $a_js_cols = array(); $record = $CONTACTS->get_record($newcid ? $newcid : $cid, true); + $record['email'] = reset($CONTACTS->get_col_values('email', $record, true)); + if (!$record['name']) + $record['name'] = $record['email']; foreach (array('name', 'email') as $col) $a_js_cols[] = (string)$record[$col]; diff --git a/program/steps/mail/addcontact.inc b/program/steps/mail/addcontact.inc index 03adcbe21..0baf6cd63 100644 --- a/program/steps/mail/addcontact.inc +++ b/program/steps/mail/addcontact.inc @@ -49,10 +49,7 @@ if (!empty($_POST['_address']) && is_object($CONTACTS)) } $contact['email'] = rcube_idn_to_utf8($contact['email']); - - // use email address part for name - if (empty($contact['name']) || $contact['name'] == $contact['email']) - $contact['name'] = ucfirst(preg_replace('/[\.\-]/', ' ', substr($contact['email'], 0, strpos($contact['email'], '@')))); + $contact['name'] = rcube_addressbook::compose_display_name($contact); // check for existing contacts $existing = $CONTACTS->search('email', $contact['email'], true, false);