From e848180aaa9640de871796ca1a3e4f8110701fd6 Mon Sep 17 00:00:00 2001
From: thomascube <thomas@roundcube.net>
Date: Thu, 12 May 2011 16:18:19 -0400
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_ldap.php        |   20 ++++++++++
 program/steps/addressbook/save.inc    |    7 ++-
 program/steps/addressbook/func.inc    |    7 +--
 program/include/rcube_vcard.php       |   11 ++---
 program/steps/addressbook/export.inc  |    4 +-
 program/include/rcube_contacts.php    |    6 +-
 program/steps/mail/addcontact.inc     |    5 --
 9 files changed, 69 insertions(+), 29 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index caac241..f960937 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 e4deea1..c580c40 100644
--- a/program/include/rcube_addressbook.php
+++ b/program/include/rcube_addressbook.php
@@ -172,18 +172,13 @@
 
     /**
      * 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 @@
         
         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 3c713fe..8abc763 100644
--- a/program/include/rcube_contacts.php
+++ b/program/include/rcube_contacts.php
@@ -184,7 +184,7 @@
                 " 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 @@
      */
     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 d9f5a10..f166fd2 100644
--- a/program/include/rcube_ldap.php
+++ b/program/include/rcube_ldap.php
@@ -610,6 +610,26 @@
 
 
     /**
+     * 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
     *
     * @param array    Hash array with save data
diff --git a/program/include/rcube_vcard.php b/program/include/rcube_vcard.php
index 0cb0b20..4457bf3 100644
--- a/program/include/rcube_vcard.php
+++ b/program/include/rcube_vcard.php
@@ -115,13 +115,6 @@
       $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 @@
       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 69f8e2e..bfe8e99 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 @@
 
 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 837256f..c361087 100644
--- a/program/steps/addressbook/func.inc
+++ b/program/steps/addressbook/func.inc
@@ -346,10 +346,9 @@
         // 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 88fe98c..2536097 100644
--- a/program/steps/addressbook/save.inc
+++ b/program/steps/addressbook/save.inc
@@ -129,9 +129,9 @@
   }
 }
 
+// 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 @@
     // 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 03adcbe..0baf6cd 100644
--- a/program/steps/mail/addcontact.inc
+++ b/program/steps/mail/addcontact.inc
@@ -49,10 +49,7 @@
     }
 
     $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);

--
Gitblit v1.9.1