From 39cafac3f5e9cff676b379c1ecb1c847eec558e2 Mon Sep 17 00:00:00 2001
From: thomascube <thomas@roundcube.net>
Date: Fri, 07 Oct 2011 07:07:23 -0400
Subject: [PATCH] Autocomplete LDAP records when adding contacts from mail (#1488073)

---
 CHANGELOG                             |    1 
 program/include/rcube_addressbook.php |    7 ++-
 program/include/rcube_ldap.php        |   84 ++++++++++++++++++++++++++++++-----------
 program/include/rcube_contacts.php    |    7 ++-
 program/steps/mail/addcontact.inc     |   10 +++++
 5 files changed, 80 insertions(+), 29 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 748d930..f9c1ff0 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
 CHANGELOG Roundcube Webmail
 ===========================
 
+- Autocomplete LDAP records when adding contacts from mail (#1488073)
 - Plugin API: added 'ready' hook (#1488063)
 - Ignore DSN request when it isn't supported by SMTP server (#1487800)
 - Make sure LDAP name fields aren't arrays (#1488108)
diff --git a/program/include/rcube_addressbook.php b/program/include/rcube_addressbook.php
index 94a715b..88f0aa9 100644
--- a/program/include/rcube_addressbook.php
+++ b/program/include/rcube_addressbook.php
@@ -30,7 +30,7 @@
     /** constants for error reporting **/
     const ERROR_READ_ONLY = 1;
     const ERROR_NO_CONNECTION = 2;
-    const ERROR_INCOMPLETE = 3;
+    const ERROR_VALIDATE = 3;
     const ERROR_SAVING = 4;
     const ERROR_SEARCH = 5;
 
@@ -182,15 +182,16 @@
      * If input isn't valid, the message to display can be fetched using get_error()
      *
      * @param array Assoziative array with data to save
+     * @param boolean Attempt to fix/complete record automatically
      * @return boolean True if input is valid, False if not.
      */
-    public function validate($save_data)
+    public function validate(&$save_data, $autofix = false)
     {
         // check validity of email addresses
         foreach ($this->get_col_values('email', $save_data, true) as $email) {
             if (strlen($email)) {
                 if (!check_email(rcube_idn_to_ascii($email))) {
-                    $this->set_error('warning', rcube_label(array('name' => 'emailformaterror', 'vars' => array('email' => $email))));
+                    $this->set_error(self::ERROR_VALIDATE, rcube_label(array('name' => 'emailformaterror', 'vars' => array('email' => $email))));
                     return false;
                 }
             }
diff --git a/program/include/rcube_contacts.php b/program/include/rcube_contacts.php
index 3ad53a6..c810ce6 100644
--- a/program/include/rcube_contacts.php
+++ b/program/include/rcube_contacts.php
@@ -500,16 +500,17 @@
      * If input not valid, the message to display can be fetched using get_error()
      *
      * @param array Assoziative array with data to save
+     * @param boolean Try to fix/complete record automatically
      * @return boolean True if input is valid, False if not.
      */
-    public function validate($save_data)
+    public function validate(&$save_data, $autofix = false)
     {
         // validate e-mail addresses
-        $valid = parent::validate($save_data);
+        $valid = parent::validate($save_data, $autofix);
 
         // 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');
+            $this->set_error(self::ERROR_VALIDATE, 'noemailwarning');
             $valid = false;
         }
 
diff --git a/program/include/rcube_ldap.php b/program/include/rcube_ldap.php
index a3f6dc5..3af343f 100644
--- a/program/include/rcube_ldap.php
+++ b/program/include/rcube_ldap.php
@@ -448,13 +448,6 @@
             return $this->result;
         }
 
-        // add general filter to query
-        if (!empty($this->prop['filter']) && empty($this->filter))
-        {
-            $filter = $this->prop['filter'];
-            $this->set_search_set($filter);
-        }
-
         // query URL is given by the selected group
         if ($this->group_id && $this->group_url)
         {
@@ -463,9 +456,13 @@
             {
                 $this->base_dn = $m[1];
                 $this->prop['scope'] = $m[2];
-                $this->filter = $m[3];
+                $this->filter = $this->filter ? '(&(' . $m[3] . ')(' . $this->filter . '))' : $m[3];
             }
         }
+
+        // add general filter to query
+        if (!empty($this->prop['filter']) && empty($this->filter))
+            $this->set_search_set($this->prop['filter']);
 
         // exec LDAP search if no result resource is stored
         if ($this->conn && !$this->ldap_result)
@@ -689,19 +686,49 @@
      * If input not valid, the message to display can be fetched using get_error()
      *
      * @param array Assoziative array with data to save
-     *
+     * @param boolean Try to fix/complete record automatically
      * @return boolean True if input is valid, False if not.
      */
-    public function validate($save_data)
+    public function validate(&$save_data, $autofix = false)
     {
         // check for name input
         if (empty($save_data['name'])) {
-            $this->set_error('warning', 'nonamewarning');
+            $this->set_error(self::ERROR_VALIDATE, 'nonamewarning');
+            return false;
+        }
+
+        // Verify that the required fields are set.
+        $missing = null;
+        $ldap_data = $this->_map_data($save_data);
+        foreach ($this->prop['required_fields'] as $fld) {
+            if (!isset($ldap_data[$fld])) {
+                $missing[$fld] = 1;
+            }
+        }
+
+        if ($missing) {
+            // try to complete record automatically
+            if ($autofix) {
+                $reverse_map = array_flip($this->fieldmap);
+                $name_parts = preg_split('/[\s,.]+/', $save_data['name']);
+                if ($missing['sn']) {
+                    $sn_field = $reverse_map['sn'];
+                    $save_data[$sn_field] = array_pop ($name_parts);
+                }
+                if ($missing[($fn_field = $this->fieldmap['firstname'])]) {
+                    $save_data['firstname'] = array_shift($name_parts);
+                }
+
+                return $this->validate($save_data, false);
+            }
+
+            // TODO: generate message saying which fields are missing
+            $this->set_error(self::ERROR_VALIDATE, 'formincomplete');
             return false;
         }
 
         // validate e-mail addresses
-        return parent::validate($save_data);
+        return parent::validate($save_data, $autofix);
     }
 
 
@@ -715,17 +742,8 @@
     function insert($save_cols)
     {
         // Map out the column names to their LDAP ones to build the new entry.
-        $newentry = array();
+        $newentry = $this->_map_data($save_cols);
         $newentry['objectClass'] = $this->prop['LDAP_Object_Classes'];
-        foreach ($this->fieldmap as $col => $fld) {
-            $val = $save_cols[$col];
-            if (is_array($val))
-                $val = array_filter($val);  // remove empty entries
-            if ($fld && $val) {
-                // The field does exist, add it to the entry.
-                $newentry[$fld] = $val;
-            } // end if
-        } // end foreach
 
         // Verify that the required fields are set.
         $missing = null;
@@ -738,7 +756,7 @@
         // abort process if requiered fields are missing
         // TODO: generate message saying which fields are missing
         if ($missing) {
-            $this->set_error(self::ERROR_INCOMPLETE, 'formincomplete');
+            $this->set_error(self::ERROR_VALIDATE, 'formincomplete');
             return false;
         }
 
@@ -1052,6 +1070,26 @@
 
 
     /**
+     * Convert a record data set into LDAP field attributes
+     */
+    private function _map_data($save_cols)
+    {
+        $ldap_data = array();
+        foreach ($this->fieldmap as $col => $fld) {
+            $val = $save_cols[$col];
+            if (is_array($val))
+                $val = array_filter($val);  // remove empty entries
+            if ($fld && $val) {
+                // The field does exist, add it to the entry.
+                $ldap_data[$fld] = $val;
+            }
+        }
+        
+        return $ldap_data;
+    }
+
+
+    /**
      * Returns unified attribute name (resolving aliases)
      */
     private static function _attr_name($name)
diff --git a/program/steps/mail/addcontact.inc b/program/steps/mail/addcontact.inc
index b74d95f..dafb276 100644
--- a/program/steps/mail/addcontact.inc
+++ b/program/steps/mail/addcontact.inc
@@ -60,6 +60,16 @@
     $contact['email'] = rcube_idn_to_utf8($contact['email']);
     $contact['name'] = rcube_addressbook::compose_display_name($contact);
 
+    // validate contact record
+    if (!$CONTACTS->validate($contact, true)) {
+      $error = $CONTACTS->get_error();
+      // TODO: show dialog to complete record
+      // if ($error['type'] == rcube_addressbook::ERROR_VALIDATE) { }
+      
+      $OUTPUT->show_message($error['message'] ? $error['message'] : 'errorsavingcontact', 'error');
+      $OUTPUT->send();
+    }
+
     // check for existing contacts
     $existing = $CONTACTS->search('email', $contact['email'], true, false);
 

--
Gitblit v1.9.1