22 commits - lib/api lib/Auth lib/Auth.php lib/client lib/kolab_api_service.php lib/kolab_client_task.php lib/locale public_html/js

Jeroen van Meeuwen vanmeeuwen at kolabsys.com
Sun Apr 1 18:22:56 CEST 2012


 lib/Auth.php                             |   25 ++-
 lib/Auth/LDAP.php                        |  230 +++++++++++++++++++++++++++----
 lib/api/kolab_api_service_form_value.php |  147 ++++++++++++++++++-
 lib/api/kolab_api_service_group.php      |  155 +++++++++++++++++++-
 lib/api/kolab_api_service_user.php       |  160 ++++++++++++++++++++-
 lib/client/kolab_client_task_group.php   |    4 
 lib/client/kolab_client_task_user.php    |    5 
 lib/kolab_api_service.php                |   14 +
 lib/kolab_client_task.php                |   36 +++-
 lib/locale/en_US.php                     |    2 
 public_html/js/kolab_admin.js            |  116 +++++++++++++++
 11 files changed, 822 insertions(+), 72 deletions(-)

New commits:
commit 1a8ffd90aa802155da1903b6c23ae68224f31b91
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Sun Apr 1 18:18:01 2012 +0200

    Special treatment for the userpassword attribute (#665)
    Use the objectclass attribute values (or actually, 'form_fields') from the original user as they were not offered for editing

diff --git a/lib/api/kolab_api_service_user.php b/lib/api/kolab_api_service_user.php
index 0bce631..847646d 100644
--- a/lib/api/kolab_api_service_user.php
+++ b/lib/api/kolab_api_service_user.php
@@ -177,6 +177,22 @@ class kolab_api_service_user extends kolab_api_service
             $rdn_attr = 'uid';
         }
 
+        // Obtain the original user's information.
+        $auth = Auth::get_instance();
+        $auth->connect();
+
+        // Now that values have been re-generated where necessary, compare
+        // the new group attributes to the original group attributes.
+        $_user = $auth->user_find_by_attribute(Array($unique_attr => $postdata[$unique_attr]));
+
+        if (!$_user) {
+            console("Could not find user");
+            return false;
+        }
+
+        $_user_dn = key($_user);
+        $_user = $this->user_info(Array('user' => $_user_dn), Array());
+
         if (isset($uta['form_fields'])) {
             foreach ($uta['form_fields'] as $key => $value) {
                 if (
@@ -194,11 +210,26 @@ class kolab_api_service_user extends kolab_api_service
         if (isset($uta['auto_form_fields'])) {
             foreach ($uta['auto_form_fields'] as $key => $value) {
                 if (empty($postdata[$key])) {
-                    if (!array_key_exists('optional', $value) || !$value['optional']) {
-                        $postdata['attributes'] = array($key);
-                        $res                    = $form_service->generate($getdata, $postdata);
-                        $postdata[$key]         = $res[$key];
-                        $user_attributes[$key]  = $postdata[$key];
+                    switch ($key) {
+                        case "userpassword":
+                            if (!empty($postdata['userpassword']) && !empty($postdata['userpassword2'])) {
+                                if ($postdata['userpassword'] === $postdata['userpassword2']) {
+                                    $user_password = $postdata['userpassword'];
+                                } else {
+                                    throw new Exception("Password mismatch");
+                                }
+                            } else {
+                                $user_attributes[$key] = $_user[$key];
+                            }
+                            break;
+                        default:
+                            if (!array_key_exists('optional', $value) || !$value['optional']) {
+                                $postdata['attributes'] = array($key);
+                                $res                    = $form_service->generate($getdata, $postdata);
+                                $postdata[$key]         = $res[$key];
+                                $user_attributes[$key]  = $postdata[$key];
+                            }
+                            break;
                     }
                 } else {
                     $user_attributes[$key] = $postdata[$key];
@@ -206,34 +237,17 @@ class kolab_api_service_user extends kolab_api_service
             }
         }
 
+        // The user did not edit these.
+        // They're not in $postdata.
+        // Only the original user object has the right ones
         if (isset($uta['fields'])) {
             foreach ($uta['fields'] as $key => $value) {
-                if (empty($postdata[$key])) {
-                    $user_attributes[$key] = $uta['fields'][$key];
-                } else {
-                    $user_attributes[$key] = $postdata[$key];
-                }
+                console("Setting $key from original user's value", $_user[$key]);
+                $user_attributes[$key] = $_user[$key];
             }
 
             $user_attributes[$unique_attr] = $postdata[$unique_attr];
-        }
-
-        $auth = Auth::get_instance();
-        $auth->connect();
-
-        // Now that values have been re-generated where necessary, compare
-        // the new group attributes to the original group attributes.
-        $_user = $auth->user_find_by_attribute(Array($unique_attr => $postdata[$unique_attr]));
-
-        if (!$_user) {
-            console("Could not find user");
-            return false;
-        }
-
-        $_user_dn = key($_user);
-        $_user = $this->user_info(Array('user' => $_user_dn), Array());
-
-        
+        }     
 
         // We should start throwing stuff over the fence here.
         $result = $auth->modify_entry($_user_dn, $_user, $user_attributes);


commit 3136ff4ecec60ea95554b45354abaf1a337a25dd
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Sun Apr 1 17:06:34 2012 +0200

    Supply the unique attribute to form submissions as data, so that form_value service methods such as generate_uid and generate_primary_mail can take into account whether the existing result found for a value generated is actually the same LDAP object as is being edited. Resolves #666.

diff --git a/lib/Auth/LDAP.php b/lib/Auth/LDAP.php
index 506e3e5..e748347 100644
--- a/lib/Auth/LDAP.php
+++ b/lib/Auth/LDAP.php
@@ -245,8 +245,11 @@ class LDAP
 
     public function get_attribute($subject_dn, $attribute)
     {
-        $result = ldap_read($this->conn, $subject_dn, '(objectclass=*)', (array)($attribute));
-        console($result);
+        $result = $this->search($subject_dn, '(objectclass=*)', (array)($attribute));
+        $result = self::normalize_result($result);
+        $dn = key($result);
+        $attr = key($result[$dn]);
+        return $result[$dn][$attr];
     }
 
     public function get_attributes($subject_dn, $attributes)
diff --git a/lib/api/kolab_api_service_form_value.php b/lib/api/kolab_api_service_form_value.php
index 843792b..7e03140 100644
--- a/lib/api/kolab_api_service_form_value.php
+++ b/lib/api/kolab_api_service_form_value.php
@@ -198,10 +198,16 @@ class kolab_api_service_form_value extends kolab_api_service
 
     private function generate_cn($postdata, $attribs = array())
     {
+        $conf = Conf::get_instance();
+        $unique_attr = $conf->get('unique_attribute');
+        if (!$unique_attr) {
+            $unique_attr = 'nsuniqueid';
+        }
+
         if (isset($attribs['auto_form_fields']) && isset($attribs['auto_form_fields']['cn'])) {
             // Use Data Please
             foreach ($attribs['auto_form_fields']['cn']['data'] as $key) {
-                if (!isset($postdata[$key])) {
+                if (!isset($postdata[$key]) && !($key == $unique_attr)) {
                     throw new Exception("Key not set: " . $key, 12356);
                 }
             }
@@ -215,10 +221,16 @@ class kolab_api_service_form_value extends kolab_api_service
 
     private function generate_displayname($postdata, $attribs = array())
     {
+        $conf = Conf::get_instance();
+        $unique_attr = $conf->get('unique_attribute');
+        if (!$unique_attr) {
+            $unique_attr = 'nsuniqueid';
+        }
+
         if (isset($attribs['auto_form_fields']) && isset($attribs['auto_form_fields']['displayname'])) {
             // Use Data Please
             foreach ($attribs['auto_form_fields']['displayname']['data'] as $key) {
-                if (!isset($postdata[$key])) {
+                if (!isset($postdata[$key]) && !($key == $unique_attr)) {
                     throw new Exception("Key not set: " . $key, 12356);
                 }
             }
@@ -284,10 +296,16 @@ class kolab_api_service_form_value extends kolab_api_service
 
     private function generate_homedirectory($postdata, $attribs = array())
     {
+        $conf = Conf::get_instance();
+        $unique_attr = $conf->get('unique_attribute');
+        if (!$unique_attr) {
+            $unique_attr = 'nsuniqueid';
+        }
+
         if (isset($attribs['auto_form_fields']) && isset($attribs['auto_form_fields']['homedirectory'])) {
             // Use Data Please
             foreach ($attribs['auto_form_fields']['homedirectory']['data'] as $key) {
-                if (!isset($postdata[$key])) {
+                if (!isset($postdata[$key]) && !($key == $unique_attr)) {
                     throw new Exception("Key not set: " . $key, 12356);
                 }
             }
@@ -338,10 +356,16 @@ class kolab_api_service_form_value extends kolab_api_service
 
     private function generate_primary_mail($postdata, $attribs = array())
     {
+        $conf = Conf::get_instance();
+        $unique_attr = $conf->get('unique_attribute');
+        if (!$unique_attr) {
+            $unique_attr = 'nsuniqueid';
+        }
+
         if (isset($attribs['auto_form_fields']) && isset($attribs['auto_form_fields']['mail'])) {
             // Use Data Please
             foreach ($attribs['auto_form_fields']['mail']['data'] as $key) {
-                if (!isset($postdata[$key])) {
+                if (!isset($postdata[$key]) && !($key == $unique_attr)) {
                     throw new Exception("Key not set: " . $key, 12356);
                 }
             }
@@ -360,10 +384,16 @@ class kolab_api_service_form_value extends kolab_api_service
 
     private function generate_primary_mail_group($postdata, $attribs = array())
     {
+        $conf = Conf::get_instance();
+        $unique_attr = $conf->get('unique_attribute');
+        if (!$unique_attr) {
+            $unique_attr = 'nsuniqueid';
+        }
+
         if (isset($attribs['auto_form_fields']) && isset($attribs['auto_form_fields']['mail'])) {
             // Use Data Please
             foreach ($attribs['auto_form_fields']['mail']['data'] as $key) {
-                if (!isset($postdata[$key])) {
+                if (!isset($postdata[$key]) && !($key == $unique_attr)) {
                     throw new Exception("Key not set: " . $key, 12356);
                 }
             }
@@ -376,6 +406,12 @@ class kolab_api_service_form_value extends kolab_api_service
 
     private function generate_secondary_mail($postdata, $attribs = array())
     {
+        $conf = Conf::get_instance();
+        $unique_attr = $conf->get('unique_attribute');
+        if (!$unique_attr) {
+            $unique_attr = 'nsuniqueid';
+        }
+
         $secondary_mail_address = Array();
 
         if (isset($attribs['auto_form_fields'])) {
@@ -388,7 +424,7 @@ class kolab_api_service_form_value extends kolab_api_service
             }
 
             foreach ($attribs['auto_form_fields'][$secondary_mail_key]['data'] as $key) {
-                if (!isset($postdata[$key])) {
+                if (!isset($postdata[$key]) && !($key == $unique_attr)) {
                     throw new Exception("Key not set: " . $key, 456789);
                 }
             }
@@ -407,10 +443,16 @@ class kolab_api_service_form_value extends kolab_api_service
 
     private function generate_uid($postdata, $attribs = array())
     {
+        $conf = Conf::get_instance();
+        $unique_attr = $conf->get('unique_attribute');
+        if (!$unique_attr) {
+            $unique_attr = 'nsuniqueid';
+        }
+
         if (isset($attribs['auto_form_fields']) && isset($attribs['auto_form_fields']['uid'])) {
             // Use Data Please
             foreach ($attribs['auto_form_fields']['uid']['data'] as $key) {
-                if (!isset($postdata[$key])) {
+                if (!isset($postdata[$key]) && !($key == $unique_attr)) {
                     throw new Exception("Key not set: " . $key, 12356);
                 }
             }
@@ -432,7 +474,14 @@ class kolab_api_service_form_value extends kolab_api_service
             $auth = Auth::get_instance($_SESSION['user']->get_domain());
 
             $x = 2;
-            while ($auth->user_find_by_attribute(array('uid' => $uid))) {
+            while (($user_found = $auth->user_find_by_attribute(array('uid' => $uid)))) {
+                $user_found_dn = key($user_found);
+                $user_found_unique_attr = $auth->get_attribute($user_found_dn, $unique_attr);
+                console("user that i found info", $user_found_unique_attr);
+                if ($user_found_unique_attr == $postdata[$unique_attr]) {
+                    break;
+                }
+
                 $uid = $orig_uid . $x;
                 $x++;
             }
diff --git a/lib/kolab_api_service.php b/lib/kolab_api_service.php
index f1b0b95..26d9e39 100644
--- a/lib/kolab_api_service.php
+++ b/lib/kolab_api_service.php
@@ -150,6 +150,12 @@ abstract class kolab_api_service
             return $this->cache['object_types'][$object_name];
         }
 
+        $conf = Conf::get_instance();
+        $unique_attr = $conf->get('unique_attribute');
+        if (!$unique_attr) {
+            $unique_attr = 'nsuniqueid';
+        }
+
         $sql_result   = $this->db->query("SELECT * FROM {$object_name}_types");
         $object_types = array();
 
@@ -160,6 +166,14 @@ abstract class kolab_api_service
                 if ($key != "id") {
                     if ($key == "attributes") {
                         $object_types[$row['id']][$key] = json_decode($value, true);
+                        // TODO: Insert unique_attr to attributes, auto_form_fields, $attribute, $data here.
+                        if (array_key_exists('auto_form_fields', $object_types[$row['id']][$key])) {
+                            foreach ($object_types[$row['id']][$key]['auto_form_fields'] as $attribute => $data) {
+                                if (array_key_exists('data', $data) && !in_array($unique_attr, $data['data'])) {
+                                    $object_types[$row['id']][$key]['auto_form_fields'][$attribute]['data'][] = $unique_attr;
+                                }
+                            }
+                        }
                     }
                     else {
                         $object_types[$row['id']][$key] = $value;


commit 687f5e1088d27fe5d8aad7478254f4281f347a84
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Sun Apr 1 02:37:35 2012 +0200

    Attributes posted and also "static" forms should be preserved (as posted)

diff --git a/lib/api/kolab_api_service_group.php b/lib/api/kolab_api_service_group.php
index 3e82f0d..9ff1835 100644
--- a/lib/api/kolab_api_service_group.php
+++ b/lib/api/kolab_api_service_group.php
@@ -91,7 +91,11 @@ class kolab_api_service_group extends kolab_api_service
 
         if (isset($gta['fields'])) {
             foreach ($gta['fields'] as $key => $value) {
-                $group_attributes[$key] = $gta['fields'][$key];
+                if (empty($postdata[$key])) {
+                    $group_attributes[$key] = $gta['fields'][$key];
+                } else {
+                    $group_attributes[$key] = $postdata[$key];
+                }
             }
         }
 
@@ -193,7 +197,11 @@ class kolab_api_service_group extends kolab_api_service
 
         if (isset($gta['fields'])) {
             foreach ($gta['fields'] as $key => $value) {
-                $group_attributes[$key] = $gta['fields'][$key];
+                if (empty($postdata[$key])) {
+                    $group_attributes[$key] = $gta['fields'][$key];
+                } else {
+                    $group_attributes[$key] = $postdata[$key];
+                }
             }
 
             $group_attributes[$unique_attr] = $postdata[$unique_attr];
diff --git a/lib/api/kolab_api_service_user.php b/lib/api/kolab_api_service_user.php
index a5c58e2..0bce631 100644
--- a/lib/api/kolab_api_service_user.php
+++ b/lib/api/kolab_api_service_user.php
@@ -98,7 +98,11 @@ class kolab_api_service_user extends kolab_api_service
 
         if (isset($uta['fields'])) {
             foreach ($uta['fields'] as $key => $value) {
-                $user_attributes[$key] = $uta['fields'][$key];
+                if (empty($postdata[$key])) {
+                    $user_attributes[$key] = $uta['fields'][$key];
+                } else {
+                    $user_attributes[$key] = $uta['fields'][$key];
+                }
             }
         }
 
@@ -204,7 +208,11 @@ class kolab_api_service_user extends kolab_api_service
 
         if (isset($uta['fields'])) {
             foreach ($uta['fields'] as $key => $value) {
-                $user_attributes[$key] = $uta['fields'][$key];
+                if (empty($postdata[$key])) {
+                    $user_attributes[$key] = $uta['fields'][$key];
+                } else {
+                    $user_attributes[$key] = $postdata[$key];
+                }
             }
 
             $user_attributes[$unique_attr] = $postdata[$unique_attr];


commit 5c72e4a7010e681dc18cae6f8ade0225cc21c53b
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Sun Apr 1 00:18:13 2012 +0200

    Correct the comparison of an attribute being marked as optional

diff --git a/lib/api/kolab_api_service_group.php b/lib/api/kolab_api_service_group.php
index 700ff25..3e82f0d 100644
--- a/lib/api/kolab_api_service_group.php
+++ b/lib/api/kolab_api_service_group.php
@@ -64,7 +64,7 @@ class kolab_api_service_group extends kolab_api_service
                 error_log("form field $key");
                 if (
                         (!isset($postdata[$key]) || empty($postdata[$key]) &&
-                        !(array_key_exists('optional', $value) || $value['optional'])
+                        (!array_key_exists('optional', $value) || !$value['optional'])
                     ) {
                     throw new Exception("Missing input value for $key", 345);
                 }
@@ -77,7 +77,7 @@ class kolab_api_service_group extends kolab_api_service
         if (isset($gta['auto_form_fields'])) {
             foreach ($gta['auto_form_fields'] as $key => $value) {
                 if (empty($postdata[$key])) {
-                    if (!array_key_exists('optional', $value) || $value['optional']) {
+                    if (!array_key_exists('optional', $value) || !$value['optional']) {
                         $postdata['attributes'] = array($key);
                         $res                    = $form_service->generate($getdata, $postdata);
                         $postdata[$key]         = $res[$key];
@@ -166,7 +166,7 @@ class kolab_api_service_group extends kolab_api_service
             foreach ($gta['form_fields'] as $key => $value) {
                 if (
                         (!isset($postdata[$key]) || empty($postdata[$key])) &&
-                        !(array_key_exists('optional', $value) && $value['optional'])
+                        (!array_key_exists('optional', $value) || !$value['optional'])
                     ) {
                     throw new Exception("Missing input value for $key", 345);
                 }
@@ -179,7 +179,7 @@ class kolab_api_service_group extends kolab_api_service
         if (isset($gta['auto_form_fields'])) {
             foreach ($gta['auto_form_fields'] as $key => $value) {
                 if (empty($postdata[$key])) {
-                    if (!array_key_exists('optional', $value) || $value['optional']) {
+                    if (!array_key_exists('optional', $value) || !$value['optional']) {
                         $postdata['attributes'] = array($key);
                         $res                    = $form_service->generate($getdata, $postdata);
                         $postdata[$key]         = $res[$key];
diff --git a/lib/api/kolab_api_service_user.php b/lib/api/kolab_api_service_user.php
index 21276e2..a5c58e2 100644
--- a/lib/api/kolab_api_service_user.php
+++ b/lib/api/kolab_api_service_user.php
@@ -59,6 +59,8 @@ class kolab_api_service_user extends kolab_api_service
      */
     public function user_add($getdata, $postdata)
     {
+        console("user_add()", $postdata);
+
         $uta             = $this->object_type_attributes('user', $postdata['type_id']);
         $form_service    = $this->controller->get_service('form_value');
         $user_attributes = array();
@@ -67,7 +69,7 @@ class kolab_api_service_user extends kolab_api_service
             foreach ($uta['form_fields'] as $key => $value) {
                 if (
                         (!isset($postdata[$key]) || empty($postdata[$key])) &&
-                        !(array_key_exists('optional', $value) && $value['optional'])
+                        (!array_key_exists('optional', $value) || !$value['optional'])
                     ) {
                     throw new Exception("Missing input value for $key", 345);
                 }
@@ -80,26 +82,28 @@ class kolab_api_service_user extends kolab_api_service
         if (isset($uta['auto_form_fields'])) {
             foreach ($uta['auto_form_fields'] as $key => $value) {
                 if (empty($postdata[$key])) {
-                    if (!array_key_exists('optional', $value) || $value['optional']) {
+                    console("Key $key empty in \$postdata");
+                    // If the attribute is marked as optional, however...
+                    if (!array_key_exists('optional', $value) || !$value['optional']) {
                         $postdata['attributes'] = array($key);
                         $res                    = $form_service->generate($getdata, $postdata);
                         $postdata[$key]         = $res[$key];
                         $user_attributes[$key] = $postdata[$key];
                     }
+                } else {
+                    $user_attributes[$key] = $postdata[$key];
                 }
             }
         }
 
         if (isset($uta['fields'])) {
             foreach ($uta['fields'] as $key => $value) {
-                if (!isset($postdata[$key]) || empty($postdata[$key])) {
-                    $user_attributes[$key] = $uta['fields'][$key];
-                } else {
-                    $user_attributes[$key] = $postdata[$key];
-                }
+                $user_attributes[$key] = $uta['fields'][$key];
             }
         }
 
+        console("user_add()", $user_attributes);
+
         $auth = Auth::get_instance();
         $result = $auth->user_add($user_attributes, $postdata['type_id']);
 
@@ -137,6 +141,8 @@ class kolab_api_service_user extends kolab_api_service
 
     public function user_edit($getdata, $postdata)
     {
+        console("\$postdata to user_edit()", $postdata);
+
         $uta             = $this->object_type_attributes('user', $postdata['type_id']);
         $form_service    = $this->controller->get_service('form_value');
         $user_attributes = array();
@@ -171,7 +177,7 @@ class kolab_api_service_user extends kolab_api_service
             foreach ($uta['form_fields'] as $key => $value) {
                 if (
                         (!isset($postdata[$key]) || empty($postdata[$key])) &&
-                        !(array_key_exists('optional', $value) && $value['optional']) 
+                        (!array_key_exists('optional', $value) || !$value['optional']) 
                     ) {
                     throw new Exception("Missing input value for $key", 345);
                 }
@@ -184,7 +190,7 @@ class kolab_api_service_user extends kolab_api_service
         if (isset($uta['auto_form_fields'])) {
             foreach ($uta['auto_form_fields'] as $key => $value) {
                 if (empty($postdata[$key])) {
-                    if (!array_key_exists('optional', $value) || $value['optional']) {
+                    if (!array_key_exists('optional', $value) || !$value['optional']) {
                         $postdata['attributes'] = array($key);
                         $res                    = $form_service->generate($getdata, $postdata);
                         $postdata[$key]         = $res[$key];


commit f5e7a053fe74f4b00d7a68c6ea06472c400bdba6
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Sat Mar 31 23:55:24 2012 +0200

    Improve handling of optional form fields

diff --git a/lib/api/kolab_api_service_group.php b/lib/api/kolab_api_service_group.php
index e7fef8a..700ff25 100644
--- a/lib/api/kolab_api_service_group.php
+++ b/lib/api/kolab_api_service_group.php
@@ -62,7 +62,10 @@ class kolab_api_service_group extends kolab_api_service
         if (isset($gta['form_fields'])) {
             foreach ($gta['form_fields'] as $key => $value) {
                 error_log("form field $key");
-                if (!isset($postdata[$key]) || $postdata[$key] === '') {
+                if (
+                        (!isset($postdata[$key]) || empty($postdata[$key]) &&
+                        !(array_key_exists('optional', $value) || $value['optional'])
+                    ) {
                     throw new Exception("Missing input value for $key", 345);
                 }
                 else {
@@ -73,10 +76,14 @@ class kolab_api_service_group extends kolab_api_service
 
         if (isset($gta['auto_form_fields'])) {
             foreach ($gta['auto_form_fields'] as $key => $value) {
-                if (!isset($postdata[$key])) {
-                    throw new Exception("Key not set: " . $key, 12356);
-                }
-                else {
+                if (empty($postdata[$key])) {
+                    if (!array_key_exists('optional', $value) || $value['optional']) {
+                        $postdata['attributes'] = array($key);
+                        $res                    = $form_service->generate($getdata, $postdata);
+                        $postdata[$key]         = $res[$key];
+                        $group_attributes[$key]  = $postdata[$key];
+                    }
+                } else {
                     $group_attributes[$key] = $postdata[$key];
                 }
             }
@@ -84,12 +91,7 @@ class kolab_api_service_group extends kolab_api_service
 
         if (isset($gta['fields'])) {
             foreach ($gta['fields'] as $key => $value) {
-                if (!isset($postdata[$key]) || empty($postdata[$key])) {
-                    $group_attributes[$key] = $gta['fields'][$key];
-                }
-                else {
-                    $group_attributes[$key] = $postdata[$key];
-                }
+                $group_attributes[$key] = $gta['fields'][$key];
             }
         }
 
@@ -162,7 +164,10 @@ class kolab_api_service_group extends kolab_api_service
 
         if (isset($gta['form_fields'])) {
             foreach ($gta['form_fields'] as $key => $value) {
-                if (!isset($postdata[$key]) || empty($postdata[$key])) {
+                if (
+                        (!isset($postdata[$key]) || empty($postdata[$key])) &&
+                        !(array_key_exists('optional', $value) && $value['optional'])
+                    ) {
                     throw new Exception("Missing input value for $key", 345);
                 }
                 else {
@@ -174,21 +179,21 @@ class kolab_api_service_group extends kolab_api_service
         if (isset($gta['auto_form_fields'])) {
             foreach ($gta['auto_form_fields'] as $key => $value) {
                 if (empty($postdata[$key])) {
-                    $postdata['attributes'] = array($key);
-                    $res                    = $form_service->generate($getdata, $postdata);
-                    $postdata[$key]         = $res[$key];
+                    if (!array_key_exists('optional', $value) || $value['optional']) {
+                        $postdata['attributes'] = array($key);
+                        $res                    = $form_service->generate($getdata, $postdata);
+                        $postdata[$key]         = $res[$key];
+                        $group_attributes[$key]  = $postdata[$key];
+                    }
+                } else {
+                    $group_attributes[$key] = $postdata[$key];
                 }
-                $group_attributes[$key] = $postdata[$key];
             }
         }
 
         if (isset($gta['fields'])) {
             foreach ($gta['fields'] as $key => $value) {
-                if (!isset($postdata[$key]) || empty($postdata[$key])) {
-                    $group_attributes[$key] = $gta['fields'][$key];
-                } else {
-                    $group_attributes[$key] = $postdata[$key];
-                }
+                $group_attributes[$key] = $gta['fields'][$key];
             }
 
             $group_attributes[$unique_attr] = $postdata[$unique_attr];
diff --git a/lib/api/kolab_api_service_user.php b/lib/api/kolab_api_service_user.php
index 4695fdf..21276e2 100644
--- a/lib/api/kolab_api_service_user.php
+++ b/lib/api/kolab_api_service_user.php
@@ -65,7 +65,10 @@ class kolab_api_service_user extends kolab_api_service
 
         if (isset($uta['form_fields'])) {
             foreach ($uta['form_fields'] as $key => $value) {
-                if (!isset($postdata[$key]) || empty($postdata[$key])) {
+                if (
+                        (!isset($postdata[$key]) || empty($postdata[$key])) &&
+                        !(array_key_exists('optional', $value) && $value['optional'])
+                    ) {
                     throw new Exception("Missing input value for $key", 345);
                 }
                 else {
@@ -77,11 +80,13 @@ class kolab_api_service_user extends kolab_api_service
         if (isset($uta['auto_form_fields'])) {
             foreach ($uta['auto_form_fields'] as $key => $value) {
                 if (empty($postdata[$key])) {
-                    $postdata['attributes'] = array($key);
-                    $res                    = $form_service->generate($getdata, $postdata);
-                    $postdata[$key]         = $res[$key];
+                    if (!array_key_exists('optional', $value) || $value['optional']) {
+                        $postdata['attributes'] = array($key);
+                        $res                    = $form_service->generate($getdata, $postdata);
+                        $postdata[$key]         = $res[$key];
+                        $user_attributes[$key] = $postdata[$key];
+                    }
                 }
-                $user_attributes[$key] = $postdata[$key];
             }
         }
 
@@ -164,7 +169,10 @@ class kolab_api_service_user extends kolab_api_service
 
         if (isset($uta['form_fields'])) {
             foreach ($uta['form_fields'] as $key => $value) {
-                if (!isset($postdata[$key]) || empty($postdata[$key])) {
+                if (
+                        (!isset($postdata[$key]) || empty($postdata[$key])) &&
+                        !(array_key_exists('optional', $value) && $value['optional']) 
+                    ) {
                     throw new Exception("Missing input value for $key", 345);
                 }
                 else {
@@ -176,21 +184,21 @@ class kolab_api_service_user extends kolab_api_service
         if (isset($uta['auto_form_fields'])) {
             foreach ($uta['auto_form_fields'] as $key => $value) {
                 if (empty($postdata[$key])) {
-                    $postdata['attributes'] = array($key);
-                    $res                    = $form_service->generate($getdata, $postdata);
-                    $postdata[$key]         = $res[$key];
+                    if (!array_key_exists('optional', $value) || $value['optional']) {
+                        $postdata['attributes'] = array($key);
+                        $res                    = $form_service->generate($getdata, $postdata);
+                        $postdata[$key]         = $res[$key];
+                        $user_attributes[$key]  = $postdata[$key];
+                    }
+                } else {
+                    $user_attributes[$key] = $postdata[$key];
                 }
-                $user_attributes[$key] = $postdata[$key];
             }
         }
 
         if (isset($uta['fields'])) {
             foreach ($uta['fields'] as $key => $value) {
-                if (!isset($postdata[$key]) || empty($postdata[$key])) {
-                    $user_attributes[$key] = $uta['fields'][$key];
-                } else {
-                    $user_attributes[$key] = $postdata[$key];
-                }
+                $user_attributes[$key] = $uta['fields'][$key];
             }
 
             $user_attributes[$unique_attr] = $postdata[$unique_attr];


commit 9abe372a0aa14187f1a3e098d2f2b7c99f746c09
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Sat Mar 31 23:52:46 2012 +0200

    Only replace attributes that are not empty.
    Delete attributes that are empty.

diff --git a/lib/Auth/LDAP.php b/lib/Auth/LDAP.php
index 2a26c0a..506e3e5 100644
--- a/lib/Auth/LDAP.php
+++ b/lib/Auth/LDAP.php
@@ -401,8 +401,13 @@ class LDAP
                     if ($attr === $rdn_attr) {
                         $mod_array['rename'][$subject_dn] = $rdn_attr . '=' . $new_attrs[$attr];
                     } else {
-                        console("Adding to replace: $attr");
-                        $mod_array['replace'][$attr] = (array)($new_attrs[$attr]);
+                        if (empty($new_attrs[$attr])) {
+                            console("Adding to del: $attr");
+                            $mod_array['del'][$attr] = (array)($old_attr_value);
+                        } else {
+                            console("Adding to replace: $attr");
+                            $mod_array['replace'][$attr] = (array)($new_attrs[$attr]);
+                        }
                     }
                 } else {
                     console("Attribute $attr unchanged");
@@ -416,14 +421,23 @@ class LDAP
 
         foreach ($new_attrs as $attr => $value) {
             if (array_key_exists($attr, $old_attrs)) {
-                if (!($old_attrs[$attr] === $value) && !($attr === $rdn_attr)) {
-                    if (!array_key_exists($attr, $mod_array['replace'])) {
-                        console("Adding to replace(2): $attr");
-                        $mod_array['replace'][$attr] = $value;
+                if (empty($value)) {
+                    if (!array_key_exists($attr, $mod_array['del'])) {
+                        console("Adding to del(2): $attr");
+                        $mod_array['del'][$attr] = (array)($old_attrs[$attr]);
+                    }
+                } else {
+                    if (!($old_attrs[$attr] === $value) && !($attr === $rdn_attr)) {
+                        if (!array_key_exists($attr, $mod_array['replace'])) {
+                            console("Adding to replace(2): $attr");
+                            $mod_array['replace'][$attr] = $value;
+                        }
                     }
                 }
             } else {
-                $mod_array['add'][$attr] = $value;
+                if (!empty($value)) {
+                    $mod_array['add'][$attr] = $value;
+                }
             }
         }
 
@@ -453,6 +467,16 @@ class LDAP
             return false;
         }
 
+        if (is_array($attributes['del']) && !empty($attributes['del'])) {
+            $result = ldap_mod_del($this->conn, $subject_dn, $attributes['del']);
+        }
+
+        if (!$result) {
+            console("Failed to delete the following attributes", $attributes['del']);
+            return false;
+        }
+
+
         if (is_array($attributes['add']) && !empty($attributes['add'])) {
             $result = ldap_mod_add($this->conn, $subject_dn, $attributes['add']);
         }


commit 4b7bcf0bcd74ccfe367fdd12c2f7dbccfc1408c1
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Sat Mar 31 20:45:11 2012 +0200

    Add nsroledn attribute to 'system' tab

diff --git a/lib/client/kolab_client_task_user.php b/lib/client/kolab_client_task_user.php
index f4ef0ea..1672136 100644
--- a/lib/client/kolab_client_task_user.php
+++ b/lib/client/kolab_client_task_user.php
@@ -233,6 +233,7 @@ class kolab_client_task_user extends kolab_client_task
             'gidnumber'                 => 'system',
             'homedirectory'             => 'system',
             'nsrole'                    => 'system',
+            'nsroledn'                  => 'system',
 
             'mailquota'                 => 'config',
             'cyrususerquota'            => 'config',


commit 4ed16adbfc19a22818341270e2fe1c862c27cdcf
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Sat Mar 31 20:44:52 2012 +0200

    Add function user_edit()

diff --git a/lib/api/kolab_api_service_user.php b/lib/api/kolab_api_service_user.php
index 222d5a7..4695fdf 100644
--- a/lib/api/kolab_api_service_user.php
+++ b/lib/api/kolab_api_service_user.php
@@ -40,7 +40,7 @@ class kolab_api_service_user extends kolab_api_service
         return array(
             'add' => 'w',
             'delete' => 'w',
-//            'edit' => 'w',
+            'edit' => 'w',
 //            'find' => 'r',
 //            'find_by_any_attribute' => 'r',
 //            'find_by_attribute' => 'r',
@@ -130,6 +130,99 @@ class kolab_api_service_user extends kolab_api_service
         return false;
     }
 
+    public function user_edit($getdata, $postdata)
+    {
+        $uta             = $this->object_type_attributes('user', $postdata['type_id']);
+        $form_service    = $this->controller->get_service('form_value');
+        $user_attributes = array();
+
+        // Get the type "key" string for the next few settings.
+        if ($postdata['type_id'] == null) {
+            $type_str = 'user';
+        }
+        else {
+            $db   = SQL::get_instance();
+            $_key = $db->fetch_assoc($db->query("SELECT `key` FROM user_types WHERE id = ?", $postdata['type_id']));
+            $type_str = $_key['key'];
+        }
+
+        $conf = Conf::get_instance();
+
+        $unique_attr = $conf->get('unique_attribute');
+        if (!$unique_attr) {
+            $unique_attr = 'nsuniqueid';
+        }
+        // TODO: "rdn" is somewhat LDAP specific, but not used as something
+        // LDAP specific...?
+        $rdn_attr = $conf->get($type_str . '_user_name_attribute');
+        if (!$rdn_attr) {
+            $rdn_attr = $conf->get('user_name_attribute');
+        }
+        if (!$rdn_attr) {
+            $rdn_attr = 'uid';
+        }
+
+        if (isset($uta['form_fields'])) {
+            foreach ($uta['form_fields'] as $key => $value) {
+                if (!isset($postdata[$key]) || empty($postdata[$key])) {
+                    throw new Exception("Missing input value for $key", 345);
+                }
+                else {
+                    $user_attributes[$key] = $postdata[$key];
+                }
+            }
+        }
+
+        if (isset($uta['auto_form_fields'])) {
+            foreach ($uta['auto_form_fields'] as $key => $value) {
+                if (empty($postdata[$key])) {
+                    $postdata['attributes'] = array($key);
+                    $res                    = $form_service->generate($getdata, $postdata);
+                    $postdata[$key]         = $res[$key];
+                }
+                $user_attributes[$key] = $postdata[$key];
+            }
+        }
+
+        if (isset($uta['fields'])) {
+            foreach ($uta['fields'] as $key => $value) {
+                if (!isset($postdata[$key]) || empty($postdata[$key])) {
+                    $user_attributes[$key] = $uta['fields'][$key];
+                } else {
+                    $user_attributes[$key] = $postdata[$key];
+                }
+            }
+
+            $user_attributes[$unique_attr] = $postdata[$unique_attr];
+        }
+
+        $auth = Auth::get_instance();
+        $auth->connect();
+
+        // Now that values have been re-generated where necessary, compare
+        // the new group attributes to the original group attributes.
+        $_user = $auth->user_find_by_attribute(Array($unique_attr => $postdata[$unique_attr]));
+
+        if (!$_user) {
+            console("Could not find user");
+            return false;
+        }
+
+        $_user_dn = key($_user);
+        $_user = $this->user_info(Array('user' => $_user_dn), Array());
+
+        
+
+        // We should start throwing stuff over the fence here.
+        $result = $auth->modify_entry($_user_dn, $_user, $user_attributes);
+
+        if ($result) {
+            return true;
+        }
+
+        return false;
+
+    }
     /**
      * User information.
      *
@@ -189,6 +282,8 @@ class kolab_api_service_user extends kolab_api_service
             }
         }
 
+        console($result);
+
         if ($result) {
             return $result;
         }


commit 3fac2ceac61e1b0f0c16ce124860cfbc4c36c2ef
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Sat Mar 31 20:44:04 2012 +0200

    Inverse form_value.list_options for nsrole and nsroledn, and issue a warning when using 'nsrole'

diff --git a/lib/api/kolab_api_service_form_value.php b/lib/api/kolab_api_service_form_value.php
index d3aedd3..843792b 100644
--- a/lib/api/kolab_api_service_form_value.php
+++ b/lib/api/kolab_api_service_form_value.php
@@ -516,6 +516,12 @@ class kolab_api_service_form_value extends kolab_api_service
 
     private function list_options_nsrole($postdata, $attribs = array())
     {
+        error_log("Listing options for attribute 'nsrole', while the expected attribute to use is 'nsroledn'");
+        return $this->list_options_nsroledn($postdata, $attribs);
+    }
+
+    private function list_options_nsroledn($postdata, $attribs = Array())
+    {
         $service = $this->controller->get_service('roles');
 
         $keyword = array('value' => $postdata['search']);
@@ -540,8 +546,4 @@ class kolab_api_service_form_value extends kolab_api_service
         return $list;
     }
 
-    private function list_options_nsroledn($postdata, $attribs = Array())
-    {
-        return $this->list_options_nsrole($postdata, $attribs);
-    }
 }


commit c6fcc6f7f6c40757e244485e16663e7b384ed08e
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Sat Mar 31 20:42:01 2012 +0200

    'nsrole', as an attribute that can actually be added is called 'nsroledn' - provide that form_value.list_options function as well

diff --git a/lib/api/kolab_api_service_form_value.php b/lib/api/kolab_api_service_form_value.php
index f601bd9..d3aedd3 100644
--- a/lib/api/kolab_api_service_form_value.php
+++ b/lib/api/kolab_api_service_form_value.php
@@ -539,4 +539,9 @@ class kolab_api_service_form_value extends kolab_api_service
 
         return $list;
     }
+
+    private function list_options_nsroledn($postdata, $attribs = Array())
+    {
+        return $this->list_options_nsrole($postdata, $attribs);
+    }
 }


commit 32cd43db6279424d14ebda304ed11356ef3d05b4
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Sat Mar 31 20:41:30 2012 +0200

    Discover what the RDN is by using the old LDAP object

diff --git a/lib/Auth/LDAP.php b/lib/Auth/LDAP.php
index abf328b..2a26c0a 100644
--- a/lib/Auth/LDAP.php
+++ b/lib/Auth/LDAP.php
@@ -373,8 +373,17 @@ class LDAP
 
     public function modify_entry($subject_dn, $old_attrs, $new_attrs)
     {
+        console($old_attrs);
+
         // TODO: Get $rdn_attr - we have type_id in $new_attrs
-        $rdn_attr = 'cn';
+        $dn_components = ldap_explode_dn($subject_dn, 0);
+        $rdn_components = explode('=', $dn_components[0]);
+
+        $rdn_attr = $rdn_components[0];
+
+        console($rdn_attr);
+
+//        return;
 
         $mod_array = Array(
                 "add"       => Array(), // For use with ldap_mod_add()
@@ -439,15 +448,19 @@ class LDAP
             $result = ldap_mod_replace($this->conn, $subject_dn, $attributes['replace']);
         }
 
-        if (!$result)
+        if (!$result) {
+            console("Failed to replace the following attributes", $attributes['replace']);
             return false;
+        }
 
         if (is_array($attributes['add']) && !empty($attributes['add'])) {
             $result = ldap_mod_add($this->conn, $subject_dn, $attributes['add']);
         }
 
-        if (!$result)
+        if (!$result) {
+            console("Failed to add the following attributes", $attributes['add']);
             return false;
+        }
 
         if (is_array($attributes['rename']) && !empty($attributes['rename'])) {
             $olddn = key($attributes['rename']);
@@ -455,13 +468,15 @@ class LDAP
             $result = ldap_rename($this->conn, $olddn, $newrdn, NULL, true);
         }
 
-        if (!$result)
+        if (!$result) {
             return false;
+        }
 
-        if ($result)
+        if ($result) {
             return true;
-        else
+        } else {
             return false;
+        }
     }
 
     public function user_add($attrs, $type = null)


commit 857248878c83ac2a7a84ff43f1bafef403d4ad71
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Sat Mar 31 20:41:00 2012 +0200

    Deprecate Auth::modify_entry_attributes

diff --git a/lib/Auth.php b/lib/Auth.php
index e4bbc8d..75e78e1 100644
--- a/lib/Auth.php
+++ b/lib/Auth.php
@@ -256,11 +256,6 @@ class Auth {
         return $this->_auth[$_SESSION['user']->get_domain()]->modify_entry($subject, $attrs, $_attrs);
     }
 
-    public function modify_entry_attributes($subject, $mod_array)
-    {
-        return $this->_auth[$_SESSION['user']->get_domain()]->modify_entry_attributes($subject, $mod_array);
-    }
-
     public function primary_for_valid_domain($domain)
     {
         $this->domains = $this->list_domains();


commit eb79ffcceaccd909f7b0cd09b025b2dfb6c426e0
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Sat Mar 31 19:24:47 2012 +0200

    Generate the uid and preseed it into the recipient policy if it does not already exist.

diff --git a/lib/api/kolab_api_service_form_value.php b/lib/api/kolab_api_service_form_value.php
index fe3cf6c..f601bd9 100644
--- a/lib/api/kolab_api_service_form_value.php
+++ b/lib/api/kolab_api_service_form_value.php
@@ -346,6 +346,12 @@ class kolab_api_service_form_value extends kolab_api_service
                 }
             }
 
+            if (array_key_exists('uid', $attribs['auto_form_fields'])) {
+                if (!array_key_exists('uid', $postdata)) {
+                    $postdata['uid'] = $this->generate_uid($postdata, $attribs);
+                }
+            }
+
             $primary_mail = kolab_recipient_policy::primary_mail($postdata);
 
             return $primary_mail;
@@ -387,6 +393,12 @@ class kolab_api_service_form_value extends kolab_api_service
                 }
             }
 
+            if (array_key_exists('uid', $attribs['auto_form_fields'])) {
+                if (!array_key_exists('uid', $postdata)) {
+                    $postdata['uid'] = $this->generate_uid($postdata, $attribs);
+                }
+            }
+
             $secondary_mail = kolab_recipient_policy::secondary_mail($postdata);
 
             return $secondary_mail;


commit a727b5b890b7f7937c8df542289cafc7534e7f61
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Sat Mar 31 14:50:02 2012 +0200

    Add 'edit' to capabilities

diff --git a/lib/api/kolab_api_service_group.php b/lib/api/kolab_api_service_group.php
index 76e8cfd..e7fef8a 100644
--- a/lib/api/kolab_api_service_group.php
+++ b/lib/api/kolab_api_service_group.php
@@ -40,6 +40,7 @@ class kolab_api_service_group extends kolab_api_service
         return array(
             'add'          => 'w',
             'delete'       => 'w',
+            'edit'         => 'w',
             'info'         => 'r',
             'members_list' => 'r',
         );


commit 8ef5f5b7efb8f2aafd120c7963ce26be2bb138f5
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Sat Mar 31 14:41:32 2012 +0200

    Actually generate the uidnumber and gidnumber

diff --git a/lib/api/kolab_api_service_form_value.php b/lib/api/kolab_api_service_form_value.php
index 6e39a9b..fe3cf6c 100644
--- a/lib/api/kolab_api_service_form_value.php
+++ b/lib/api/kolab_api_service_form_value.php
@@ -243,12 +243,42 @@ class kolab_api_service_form_value extends kolab_api_service
 
     private function generate_gidnumber($postdata, $attribs = array())
     {
-        if (isset($attribs['auto_form_fields']) && isset($attribs['auto_form_fields']['uidnumber'])) {
+        if (isset($attribs['auto_form_fields']) && isset($attribs['auto_form_fields']['gidnumber'])) {
             $auth = Auth::get_instance($_SESSION['user']->get_domain());
+            $conf = Conf::get_instance();
 
             // TODO: Take a policy to use a known group ID, a known group (by name?)
             // and/or create user private groups.
-            return 500;
+
+            $search = Array(
+                    'params' => Array(
+                            'objectclass' => Array(
+                                    'type' => 'exact',
+                                    'value' => 'posixgroup',
+                                ),
+                        ),
+                );
+
+            $groups = $auth->list_groups(NULL, Array('gidnumber'), $search);
+
+            $highest_gidnumber = $conf->get('gidnumber_lower_barrier');
+            if (!$highest_gidnumber) {
+                $highest_gidnumber = 1000;
+            }
+
+            foreach ($groups as $dn => $attributes) {
+                if (!array_key_exists('gidnumber', $attributes)) {
+                    continue;
+                }
+
+                if ($attributes['gidnumber'] > $highest_gidnumber) {
+                    $highest_gidnumber = $attributes['gidnumber'];
+                }
+            }
+
+            //$users = $auth->list_users();
+            //console($groups);
+            return ($highest_gidnumber + 1);
         }
     }
 
@@ -403,9 +433,34 @@ class kolab_api_service_form_value extends kolab_api_service
     {
         if (isset($attribs['auto_form_fields']) && isset($attribs['auto_form_fields']['uidnumber'])) {
             $auth = Auth::get_instance($_SESSION['user']->get_domain());
+            $conf = Conf::get_instance();
+
+            $search = Array(
+                    'params' => Array(
+                            'objectclass' => Array(
+                                    'type' => 'exact',
+                                    'value' => 'posixaccount',
+                                ),
+                        ),
+                );
+
+            $users = $auth->list_users(NULL, Array('uidnumber'), $search);
+
+            $highest_uidnumber = $conf->get('uidnumber_lower_barrier');
+            if (!$highest_uidnumber) {
+                $highest_uidnumber = 1000;
+            }
 
-            // TODO: Actually poll $auth for users with a uidNumber set, and take the next one.
-            return 500;
+            foreach ($users as $dn => $attributes) {
+                if (!array_key_exists('uidnumber', $attributes)) {
+                    continue;
+                }
+
+                if ($attributes['uidnumber'] > $highest_uidnumber) {
+                    $highest_uidnumber = $attributes['uidnumber'];
+                }
+            }
+            return ($highest_uidnumber + 1);
         }
     }
 


commit ccaca4003972445d8f3301ed670901da84e601ed
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Sat Mar 31 13:58:27 2012 +0200

    Also translate the edit success notification

diff --git a/lib/client/kolab_client_task_group.php b/lib/client/kolab_client_task_group.php
index eb20069..0e04e4a 100644
--- a/lib/client/kolab_client_task_group.php
+++ b/lib/client/kolab_client_task_group.php
@@ -271,7 +271,7 @@ class kolab_client_task_group extends kolab_client_task
 
         $form->set_title(kolab_html::escape($title));
 
-        $this->output->add_translation('group.add.success', 'group.delete.success');
+        $this->output->add_translation('group.add.success', 'group.edit.success', 'group.delete.success');
 
         return $form->output();
     }
diff --git a/lib/client/kolab_client_task_user.php b/lib/client/kolab_client_task_user.php
index d1e5b9f..f4ef0ea 100644
--- a/lib/client/kolab_client_task_user.php
+++ b/lib/client/kolab_client_task_user.php
@@ -312,7 +312,7 @@ class kolab_client_task_user extends kolab_client_task
         $form->set_title(kolab_html::escape($title));
 
         $this->output->add_translation('user.password.mismatch',
-            'user.add.success', 'user.delete.success');
+            'user.add.success', 'user.edit.success', 'user.delete.success');
 
         return $form->output();
     }


commit f5a58c5eaa14f741cb582e9a1b2b60bcb9811c99
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Sat Mar 31 13:56:06 2012 +0200

    Move to use configuration value 'unique_attribute' and 'user_name_attribute' and 'group_name_attribute' instead of it's short equivalents (write once read many times)
    Move some of the LDAP logic in modifying entries back to where it belongs in Auth::LDAP instead of the lib/api/ files

diff --git a/lib/Auth/LDAP.php b/lib/Auth/LDAP.php
index bd5f6a2..abf328b 100644
--- a/lib/Auth/LDAP.php
+++ b/lib/Auth/LDAP.php
@@ -371,11 +371,92 @@ class LDAP
         return $roles;
     }
 
+    public function modify_entry($subject_dn, $old_attrs, $new_attrs)
+    {
+        // TODO: Get $rdn_attr - we have type_id in $new_attrs
+        $rdn_attr = 'cn';
+
+        $mod_array = Array(
+                "add"       => Array(), // For use with ldap_mod_add()
+                "del"       => Array(), // For use with ldap_mod_del()
+                "replace"   => Array(), // For use with ldap_mod_replace()
+                "rename"    => Array(), // For use with ldap_rename()
+            );
+
+        // Compare each attribute value of the old attrs with the corresponding value
+        // in the new attrs, if any.
+        foreach ($old_attrs as $attr => $old_attr_value) {
+            if (array_key_exists($attr, $new_attrs)) {
+                if (!($new_attrs[$attr] === $old_attr_value)) {
+                    console("Attribute $attr changed from", $old_attr_value, "to", $new_attrs[$attr]);
+                    if ($attr === $rdn_attr) {
+                        $mod_array['rename'][$subject_dn] = $rdn_attr . '=' . $new_attrs[$attr];
+                    } else {
+                        console("Adding to replace: $attr");
+                        $mod_array['replace'][$attr] = (array)($new_attrs[$attr]);
+                    }
+                } else {
+                    console("Attribute $attr unchanged");
+                }
+            } else {
+                // TODO: Since we're not shipping the entire object back and forth, and only post
+                // part of the data... we don't know what is actually removed (think modifiedtimestamp, etc.)
+                console("Group attribute $attr not mentioned in \$new_attrs..., but not explicitly removed... by assumption");
+            }
+        }
+
+        foreach ($new_attrs as $attr => $value) {
+            if (array_key_exists($attr, $old_attrs)) {
+                if (!($old_attrs[$attr] === $value) && !($attr === $rdn_attr)) {
+                    if (!array_key_exists($attr, $mod_array['replace'])) {
+                        console("Adding to replace(2): $attr");
+                        $mod_array['replace'][$attr] = $value;
+                    }
+                }
+            } else {
+                $mod_array['add'][$attr] = $value;
+            }
+        }
+
+        console($mod_array);
+
+        $result = $this->modify_entry_attributes($subject_dn, $mod_array);
+
+        if ($result) {
+            return $mod_array;
+        }
+
+    }
+
     public function modify_entry_attributes($subject_dn, $attributes)
     {
         $this->_bind($_SESSION['user']->user_bind_dn, $_SESSION['user']->user_bind_pw);
 
-        $result = ldap_mod_replace($this->conn, $subject_dn, $attributes['replace']);
+        // Opportunities to set false include failed ldap commands.
+        $result = true;
+
+        if (is_array($attributes['replace']) && !empty($attributes['replace'])) {
+            $result = ldap_mod_replace($this->conn, $subject_dn, $attributes['replace']);
+        }
+
+        if (!$result)
+            return false;
+
+        if (is_array($attributes['add']) && !empty($attributes['add'])) {
+            $result = ldap_mod_add($this->conn, $subject_dn, $attributes['add']);
+        }
+
+        if (!$result)
+            return false;
+
+        if (is_array($attributes['rename']) && !empty($attributes['rename'])) {
+            $olddn = key($attributes['rename']);
+            $newrdn = $attributes['rename'][$olddn];
+            $result = ldap_rename($this->conn, $olddn, $newrdn, NULL, true);
+        }
+
+        if (!$result)
+            return false;
 
         if ($result)
             return true;
diff --git a/lib/api/kolab_api_service_group.php b/lib/api/kolab_api_service_group.php
index d2103fa..76e8cfd 100644
--- a/lib/api/kolab_api_service_group.php
+++ b/lib/api/kolab_api_service_group.php
@@ -132,13 +132,32 @@ class kolab_api_service_group extends kolab_api_service
         $gta             = $this->object_type_attributes('group', $postdata['type_id']);
         $form_service    = $this->controller->get_service('form_value');
         $group_attributes = array();
-    
+
+        // Get the type "key" string for the next few settings.
+        if ($postdata['type_id'] == null) {
+            $type_str = 'group';
+        }
+        else {
+            $db   = SQL::get_instance();
+            $_key = $db->fetch_assoc($db->query("SELECT `key` FROM group_types WHERE id = ?", $postdata['type_id']));
+            $type_str = $_key['key'];
+        }
+
         $conf = Conf::get_instance();
 
-        $unique_attr = $conf->get('unique_attr');
+        $unique_attr = $conf->get('unique_attribute');
         if (!$unique_attr) {
             $unique_attr = 'nsuniqueid';
         }
+        // TODO: "rdn" is somewhat LDAP specific, but not used as something
+        // LDAP specific...?
+        $rdn_attr = $conf->get($type_str . '_group_name_attribute');
+        if (!$rdn_attr) {
+            $rdn_attr = $conf->get('group_name_attribute');
+        }
+        if (!$rdn_attr) {
+            $rdn_attr = 'cn';
+        }
 
         if (isset($gta['form_fields'])) {
             foreach ($gta['form_fields'] as $key => $value) {
@@ -189,41 +208,11 @@ class kolab_api_service_group extends kolab_api_service
         $_group_dn = key($_group);
         $_group = $this->group_info(Array('group' => $_group_dn), Array());
 
-        $mod_array = Array(
-                "add" => Array(),
-                "del" => Array(),
-                "replace" => Array(),
-            );
-
-        foreach ($_group as $_group_attr => $_group_value) {
-            if (array_key_exists($_group_attr, $group_attributes)) {
-                if (!($group_attributes[$_group_attr] === $_group_value)) {
-                    console("Attribute $_group_attr changed from", $_group_value, "to", $group_attributes[$_group_attr]);
-                    $mod_array['replace'][$_group_attr] = (array)($_group_value);
-                }
-            } else {
-                // TODO: Since we're not shipping the entire object back and forth, and only post
-                // part of the data... we don't know what is actually removed (think modifiedtimestamp, etc.)
-                console("Group attribute not mentioned, but not explicitly removed... by assumption");
-            }
-        }
-
-        foreach ($group_attributes as $attr => $value) {
-            if (array_key_exists($attr, $_group)) {
-                if (!($_group[$attr] === $value)) {
-                    $mod_array['replace'][$attr] = $value;
-                }
-            } else {
-                $mod_array['add'][$attr] = $value;
-            }
-        }
-
-        console($mod_array);
-
-        $result = $auth->modify_entry_attributes($_group_dn, $mod_array);
+        // We should start throwing stuff over the fence here.
+        $result = $auth->modify_entry($_group_dn, $_group, $group_attributes);
 
         if ($result) {
-            return $mod_array;
+            return true;
         }
 
         return false;
@@ -271,7 +260,7 @@ class kolab_api_service_group extends kolab_api_service
             }
 
             // Insert the persistent, unique attribute
-            $unique_attr = $conf->get('unique_attr');
+            $unique_attr = $conf->get('unique_attribute');
             if (!$unique_attr) {
                 $unique_attr = 'nsuniqueid';
             }
diff --git a/lib/api/kolab_api_service_user.php b/lib/api/kolab_api_service_user.php
index 5810732..222d5a7 100644
--- a/lib/api/kolab_api_service_user.php
+++ b/lib/api/kolab_api_service_user.php
@@ -172,7 +172,7 @@ class kolab_api_service_user extends kolab_api_service
             }
 
             // Insert the persistent, unique attribute
-            $unique_attr = $conf->get('unique_attr');
+            $unique_attr = $conf->get('unique_attribute');
             if (!$unique_attr) {
                 $unique_attr = 'nsuniqueid';
             }
diff --git a/lib/kolab_client_task.php b/lib/kolab_client_task.php
index b0ffba3..14fe71f 100644
--- a/lib/kolab_client_task.php
+++ b/lib/kolab_client_task.php
@@ -837,7 +837,7 @@ class kolab_client_task
             );
 
         if (!$add_mode) {
-            $unique_attr = $this->config->get('unique_attr');
+            $unique_attr = $this->config->get('unique_attribute');
             if (!$unique_attr) {
                 $unique_attr = 'nsuniqueid';
             }


commit b8bba11531399482b2c24ab273649e3156e66859
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Sat Mar 31 13:55:07 2012 +0200

    Create a function Auth::modify_entry() to get things edited in one go.

diff --git a/lib/Auth.php b/lib/Auth.php
index b72b7bc..e4bbc8d 100644
--- a/lib/Auth.php
+++ b/lib/Auth.php
@@ -251,6 +251,11 @@ class Auth {
         return $roles;
     }
 
+    public function modify_entry($subject, $attrs, $_attrs)
+    {
+        return $this->_auth[$_SESSION['user']->get_domain()]->modify_entry($subject, $attrs, $_attrs);
+    }
+
     public function modify_entry_attributes($subject, $mod_array)
     {
         return $this->_auth[$_SESSION['user']->get_domain()]->modify_entry_attributes($subject, $mod_array);


commit cf23f6dfe62ab042331dd754b304d1968a568e66
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Sat Mar 31 12:47:29 2012 +0200

    Be a little more intelligent about where to add groups of a certain type using standard configuration section/item resolving mechanisms
    Lowercase special attributes such as 'objectclass' so we get to know whether anything submitted is actually different

diff --git a/lib/Auth/LDAP.php b/lib/Auth/LDAP.php
index a75323d..bd5f6a2 100644
--- a/lib/Auth/LDAP.php
+++ b/lib/Auth/LDAP.php
@@ -522,15 +522,10 @@ class LDAP
         }
 
         // Check if the user_type has a specific base DN specified.
-        $base_dn = $this->conf->get($this->domain, $type_str . "_group_base_dn");
+        $base_dn = $this->conf->get($type_str . "_group_base_dn");
         // If not, take the regular user_base_dn
         if (!$base_dn)
-            $base_dn = $this->conf->get($this->domain, "group_base_dn");
-
-        // If no user_base_dn either, take the user type specific from the parent
-        // configuration
-        if (!$base_dn)
-            $base_dn = $this->conf->get('ldap', $type_str . "_group_base_dn");
+            $base_dn = $this->conf->get("group_base_dn");
 
         // TODO: The rdn is configurable as well.
         // Use [$type_str . "_"]user_rdn_attr
@@ -722,7 +717,14 @@ class LDAP
             for ($y = 0; $y < $__result[$x]["count"]; $y++) {
                 $attr = $__result[$x][$y];
                 if ($__result[$x][$attr]["count"] == 1) {
-                    $result[$dn][$attr] = $__result[$x][$attr][0];
+                    switch ($attr) {
+                        case "objectclass":
+                            $result[$dn][$attr] = strtolower($__result[$x][$attr][0]);
+                            break;
+                        default:
+                            $result[$dn][$attr] = $__result[$x][$attr][0];
+                            break;
+                    }
                 }
                 else {
                     $result[$dn][$attr] = array();
@@ -732,7 +734,14 @@ class LDAP
                             $result[$dn]['primary_domain'] = $__result[$x][$attr][$z];
                         }
 
-                        $result[$dn][$attr][] = $__result[$x][$attr][$z];
+                        switch ($attr) {
+                            case "objectclass":
+                                $result[$dn][$attr][] = strtolower($__result[$x][$attr][$z]);
+                                break;
+                            default:
+                                $result[$dn][$attr][] = $__result[$x][$attr][$z];
+                                break;
+                        }
                     }
                 }
             }
@@ -875,8 +884,8 @@ class LDAP
         // Always bind with the session credentials
         $this->_bind($_SESSION['user']->user_bind_dn, $_SESSION['user']->user_bind_pw);
 
-//        console("Entry DN", $entry_dn);
-//        console("Attributes", $attributes);
+        //console("Entry DN", $entry_dn);
+        //console("Attributes", $attributes);
 
         foreach ($attributes as $attr_name => $attr_value) {
             if (empty($attr_value)) {


commit a8cb5e9802720d8d1b5931154f25f3afd3b96d5d
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Sat Mar 31 12:44:48 2012 +0200

    Update kolab_admin.js to realize the difference between selecting to add a user, and submitting a form to add a user
    Same for groups

diff --git a/public_html/js/kolab_admin.js b/public_html/js/kolab_admin.js
index 8279b0c..fe398f8 100644
--- a/public_html/js/kolab_admin.js
+++ b/public_html/js/kolab_admin.js
@@ -1024,6 +1024,72 @@ function kolab_admin()
     this.command('user.list', {page: page});
   };
 
+  this.user_add = function(reload, section)
+  {
+    var data = this.serialize_form('#'+this.env.form_id);
+
+    if (reload || reload == '') {
+      // TODO: Not needed?
+      data.section = section;
+
+      this.http_post('user.add');
+      return;
+    }
+
+    this.form_error_clear();
+
+    // check password
+    if (data.userpassword != data.userpassword2) {
+      this.display_message('user.password.mismatch', 'error');
+      this.form_value_error('userpassword2');
+      return;
+    }
+
+    this.set_busy(true, 'saving');
+    this.api_post('user.add', data, 'user_add_response');
+  };
+
+  this.user_add_response = function(response)
+  {
+    if (!this.api_response(response))
+      return;
+
+    this.display_message('user.add.success');
+    this.command('user.list', {page: this.env.list_page});
+  };
+
+  this.user_edit = function(reload, section)
+  {
+    var data = this.serialize_form('#'+this.env.form_id);
+
+    if (reload) {
+      data.section = section;
+      this.http_post('user.edit', {data: data});
+      return;
+    }
+
+    this.form_error_clear();
+
+    // check password
+    if (data.userpassword != data.userpassword2) {
+      this.display_message('user.password.mismatch', 'error');
+      this.form_value_error('userpassword2');
+      return;
+    }
+
+    this.set_busy(true, 'saving');
+    this.api_post('user.edit', data, 'user_edit_response');
+  };
+
+  this.user_edit_response = function(response)
+  {
+    if (!this.api_response(response))
+      return;
+
+    this.display_message('user.edit.success');
+    this.command('user.list', {page: this.env.list_page});
+  };
+
   this.user_save = function(reload, section)
   {
     var data = this.serialize_form('#'+this.env.form_id);
@@ -1097,9 +1163,9 @@ function kolab_admin()
   {
     var data = this.serialize_form('#'+this.env.form_id);
 
-    if (reload) {
+    if (reload || reload == '') {
       data.section = section;
-      this.http_post('group.add', {data: data});
+      this.http_post('group.add');
       return;
     }
 


commit 4884ed6a5424034daff81dad0b09234a7303baed
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Sat Mar 31 11:10:06 2012 +0200

    Rename function user_attributes to get_attributes so it can sensibly be used for users, groups and other objects
    Make sure the values in ldap modifications arrays are arrays

diff --git a/lib/Auth.php b/lib/Auth.php
index 936a8db..b72b7bc 100644
--- a/lib/Auth.php
+++ b/lib/Auth.php
@@ -176,6 +176,11 @@ class Auth {
         return $this->_auth[$_SESSION['user']->get_domain()]->get_attribute($subject, $attribute);
     }
 
+    public function get_attributes($subject, $attributes)
+    {
+        return $this->_auth[$_SESSION['user']->get_domain()]->get_attributes($subject, $attributes);
+    }
+
     public function group_add($attributes, $type=NULL)
     {
         return $this->_auth[$_SESSION['user']->get_domain()]->group_add($attributes, $type);
@@ -288,11 +293,6 @@ class Auth {
         return $this->_auth[$_SESSION['user']->get_domain()]->user_find_by_attribute($attribute);
     }
 
-    public function user_attributes($user, $attributes)
-    {
-        return $this->_auth[$_SESSION['user']->get_domain()]->user_attributes($user, $attributes);
-    }
-
     public function user_info($userdata)
     {
         return $this->_auth[$_SESSION['user']->get_domain()]->user_info($userdata);
diff --git a/lib/Auth/LDAP.php b/lib/Auth/LDAP.php
index 1801328..a75323d 100644
--- a/lib/Auth/LDAP.php
+++ b/lib/Auth/LDAP.php
@@ -249,6 +249,19 @@ class LDAP
         console($result);
     }
 
+    public function get_attributes($subject_dn, $attributes)
+    {
+        $result = $this->search($subject_dn, '(objectclass=*)', $attributes);
+        $result = self::normalize_result($result);
+
+        if (!empty($result)) {
+            $result = array_pop($result);
+            return $result;
+        }
+
+        return false;
+    }
+
     public function group_find_by_attribute($attribute)
     {
         if (empty($attribute) || !is_array($attribute) || count($attribute) > 1) {
@@ -455,19 +468,6 @@ class LDAP
      *
      *
      */
-    public function user_attributes($user_dn, $attributes)
-    {
-        $result = $this->search($user_dn, '(objectclass=*)', $attributes);
-        $result = self::normalize_result($result);
-
-        if (!empty($result)) {
-            $result = array_pop($result);
-            return $result;
-        }
-
-        return false;
-    }
-
     public function user_info($user)
     {
         $is_dn = ldap_explode_dn($user, 1);
diff --git a/lib/api/kolab_api_service_group.php b/lib/api/kolab_api_service_group.php
index 329521e..d2103fa 100644
--- a/lib/api/kolab_api_service_group.php
+++ b/lib/api/kolab_api_service_group.php
@@ -199,7 +199,7 @@ class kolab_api_service_group extends kolab_api_service
             if (array_key_exists($_group_attr, $group_attributes)) {
                 if (!($group_attributes[$_group_attr] === $_group_value)) {
                     console("Attribute $_group_attr changed from", $_group_value, "to", $group_attributes[$_group_attr]);
-                    $mod_array['replace'][$_group_attr] = $_group_value;
+                    $mod_array['replace'][$_group_attr] = (array)($_group_value);
                 }
             } else {
                 // TODO: Since we're not shipping the entire object back and forth, and only post
@@ -281,7 +281,7 @@ class kolab_api_service_group extends kolab_api_service
             }
 
             if (!empty($attrs)) {
-                $attrs = $auth->user_attributes($result['entrydn'], $attrs);
+                $attrs = $auth->get_attributes($result['entrydn'], $attrs);
                 if (!empty($attrs)) {
                     $result = array_merge($result, $attrs);
                 }
diff --git a/lib/api/kolab_api_service_user.php b/lib/api/kolab_api_service_user.php
index 111456e..5810732 100644
--- a/lib/api/kolab_api_service_user.php
+++ b/lib/api/kolab_api_service_user.php
@@ -182,7 +182,7 @@ class kolab_api_service_user extends kolab_api_service
             }
 
             if (!empty($attrs)) {
-                $attrs = $auth->user_attributes($result['entrydn'], $attrs);
+                $attrs = $auth->get_attributes($result['entrydn'], $attrs);
                 if (!empty($attrs)) {
                     $result = array_merge($result, $attrs);
                 }


commit a228de1f80379cc29b58bf2673fd4e3d56e953cd
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Sat Mar 31 01:30:37 2012 +0200

    Take a stab at making groups editable (and users, ultimately)

diff --git a/lib/Auth.php b/lib/Auth.php
index dfafc06..936a8db 100644
--- a/lib/Auth.php
+++ b/lib/Auth.php
@@ -171,11 +171,21 @@ class Auth {
         return $this->_auth[$_SESSION['user']->get_domain()]->find_user_groups($member_dn);
     }
 
+    public function get_attribute($subject, $attribute)
+    {
+        return $this->_auth[$_SESSION['user']->get_domain()]->get_attribute($subject, $attribute);
+    }
+
     public function group_add($attributes, $type=NULL)
     {
         return $this->_auth[$_SESSION['user']->get_domain()]->group_add($attributes, $type);
     }
 
+    public function group_find_by_attribute($attribute)
+    {
+        return $this->_auth[$_SESSION['user']->get_domain()]->group_find_by_attribute($attribute);
+    }
+
     public function group_info($groupdata)
     {
         return $this->_auth[$_SESSION['user']->get_domain()]->group_info($groupdata);
@@ -236,6 +246,11 @@ class Auth {
         return $roles;
     }
 
+    public function modify_entry_attributes($subject, $mod_array)
+    {
+        return $this->_auth[$_SESSION['user']->get_domain()]->modify_entry_attributes($subject, $mod_array);
+    }
+
     public function primary_for_valid_domain($domain)
     {
         $this->domains = $this->list_domains();
diff --git a/lib/Auth/LDAP.php b/lib/Auth/LDAP.php
index 73701b7..1801328 100644
--- a/lib/Auth/LDAP.php
+++ b/lib/Auth/LDAP.php
@@ -243,6 +243,44 @@ class LDAP
         return $attributes;
     }
 
+    public function get_attribute($subject_dn, $attribute)
+    {
+        $result = ldap_read($this->conn, $subject_dn, '(objectclass=*)', (array)($attribute));
+        console($result);
+    }
+
+    public function group_find_by_attribute($attribute)
+    {
+        if (empty($attribute) || !is_array($attribute) || count($attribute) > 1) {
+            return false;
+        }
+
+        if (empty($attribute[key($attribute)])) {
+            return false;
+        }
+
+        $filter = "(&";
+
+        foreach ($attribute as $key => $value) {
+            $filter .= "(" . $key . "=" . $value . ")";
+        }
+
+        $filter .= ")";
+
+        $base_dn = $this->domain_root_dn($this->domain);
+
+        $result = self::normalize_result($this->search($base_dn, $filter, array_keys($attribute)));
+
+        if (count($result) > 0) {
+            error_log("Results found: " . implode(', ', array_keys($result)));
+            return $result;
+        }
+        else {
+            error_log("No result");
+            return false;
+        }
+    }
+
     public function list_domains()
     {
         $domains = $this->domains_list();
@@ -320,6 +358,18 @@ class LDAP
         return $roles;
     }
 
+    public function modify_entry_attributes($subject_dn, $attributes)
+    {
+        $this->_bind($_SESSION['user']->user_bind_dn, $_SESSION['user']->user_bind_pw);
+
+        $result = ldap_mod_replace($this->conn, $subject_dn, $attributes['replace']);
+
+        if ($result)
+            return true;
+        else
+            return false;
+    }
+
     public function user_add($attrs, $type = null)
     {
         if ($type == null) {
diff --git a/lib/api/kolab_api_service_group.php b/lib/api/kolab_api_service_group.php
index 33a71a7..329521e 100644
--- a/lib/api/kolab_api_service_group.php
+++ b/lib/api/kolab_api_service_group.php
@@ -127,6 +127,109 @@ class kolab_api_service_group extends kolab_api_service
         return FALSE;
     }
 
+    public function group_edit($getdata, $postdata)
+    {
+        $gta             = $this->object_type_attributes('group', $postdata['type_id']);
+        $form_service    = $this->controller->get_service('form_value');
+        $group_attributes = array();
+    
+        $conf = Conf::get_instance();
+
+        $unique_attr = $conf->get('unique_attr');
+        if (!$unique_attr) {
+            $unique_attr = 'nsuniqueid';
+        }
+
+        if (isset($gta['form_fields'])) {
+            foreach ($gta['form_fields'] as $key => $value) {
+                if (!isset($postdata[$key]) || empty($postdata[$key])) {
+                    throw new Exception("Missing input value for $key", 345);
+                }
+                else {
+                    $group_attributes[$key] = $postdata[$key];
+                } 
+            }
+        }
+
+        if (isset($gta['auto_form_fields'])) {
+            foreach ($gta['auto_form_fields'] as $key => $value) {
+                if (empty($postdata[$key])) {
+                    $postdata['attributes'] = array($key);
+                    $res                    = $form_service->generate($getdata, $postdata);
+                    $postdata[$key]         = $res[$key];
+                }
+                $group_attributes[$key] = $postdata[$key];
+            }
+        }
+
+        if (isset($gta['fields'])) {
+            foreach ($gta['fields'] as $key => $value) {
+                if (!isset($postdata[$key]) || empty($postdata[$key])) {
+                    $group_attributes[$key] = $gta['fields'][$key];
+                } else {
+                    $group_attributes[$key] = $postdata[$key];
+                }
+            }
+
+            $group_attributes[$unique_attr] = $postdata[$unique_attr];
+        }
+
+        $auth = Auth::get_instance();
+        $auth->connect();
+
+        // Now that values have been re-generated where necessary, compare
+        // the new group attributes to the original group attributes.
+        $_group = $auth->group_find_by_attribute(Array($unique_attr => $postdata[$unique_attr]));
+
+        if (!$_group) {
+            console("Could not find group");
+            return false;
+        }
+
+        $_group_dn = key($_group);
+        $_group = $this->group_info(Array('group' => $_group_dn), Array());
+
+        $mod_array = Array(
+                "add" => Array(),
+                "del" => Array(),
+                "replace" => Array(),
+            );
+
+        foreach ($_group as $_group_attr => $_group_value) {
+            if (array_key_exists($_group_attr, $group_attributes)) {
+                if (!($group_attributes[$_group_attr] === $_group_value)) {
+                    console("Attribute $_group_attr changed from", $_group_value, "to", $group_attributes[$_group_attr]);
+                    $mod_array['replace'][$_group_attr] = $_group_value;
+                }
+            } else {
+                // TODO: Since we're not shipping the entire object back and forth, and only post
+                // part of the data... we don't know what is actually removed (think modifiedtimestamp, etc.)
+                console("Group attribute not mentioned, but not explicitly removed... by assumption");
+            }
+        }
+
+        foreach ($group_attributes as $attr => $value) {
+            if (array_key_exists($attr, $_group)) {
+                if (!($_group[$attr] === $value)) {
+                    $mod_array['replace'][$attr] = $value;
+                }
+            } else {
+                $mod_array['add'][$attr] = $value;
+            }
+        }
+
+        console($mod_array);
+
+        $result = $auth->modify_entry_attributes($_group_dn, $mod_array);
+
+        if ($result) {
+            return $mod_array;
+        }
+
+        return false;
+
+    }
+
     /**
      * Group information.
      *
@@ -142,6 +245,7 @@ class kolab_api_service_group extends kolab_api_service
         }
 
         $auth   = Auth::get_instance();
+        $conf   = Conf::get_instance();
         $result = $auth->group_info($getdata['group']);
 
         // normalize result
@@ -152,6 +256,38 @@ class kolab_api_service_group extends kolab_api_service
         // add group type id to the result                                                                                                                       
         $result['type_id'] = $this->object_type_id('group', $result['objectclass']);
 
+        // Search for attributes associated with the type_id that are not part
+        // of the results returned earlier. Example: nsrole / nsroledn / aci, etc.
+        if ($result['type_id']) {
+            $uta   = $this->object_type_attributes('group', $result['type_id']);
+            $attrs = array();
+
+            foreach ($uta as $field_type => $attributes) {
+                foreach ($attributes as $attribute => $data) {
+                    if (!array_key_exists($attribute, $result)) {
+                        $attrs[] = $attribute;
+                    }
+                }
+            }
+
+            // Insert the persistent, unique attribute
+            $unique_attr = $conf->get('unique_attr');
+            if (!$unique_attr) {
+                $unique_attr = 'nsuniqueid';
+            }
+
+            if (!array_key_exists($unique_attr, $attrs)) {
+                $attrs[] = 'nsuniqueid';
+            }
+
+            if (!empty($attrs)) {
+                $attrs = $auth->user_attributes($result['entrydn'], $attrs);
+                if (!empty($attrs)) {
+                    $result = array_merge($result, $attrs);
+                }
+            }
+        }
+
         //console($result);
 
         if ($result) {
diff --git a/lib/api/kolab_api_service_user.php b/lib/api/kolab_api_service_user.php
index 855518f..111456e 100644
--- a/lib/api/kolab_api_service_user.php
+++ b/lib/api/kolab_api_service_user.php
@@ -145,6 +145,7 @@ class kolab_api_service_user extends kolab_api_service
         }
 
         $auth   = Auth::get_instance();
+        $conf   = Conf::get_instance();
         $user   = $getdata['user'];
         $result = $auth->user_info($user);
 
@@ -170,6 +171,16 @@ class kolab_api_service_user extends kolab_api_service
                 }
             }
 
+            // Insert the persistent, unique attribute
+            $unique_attr = $conf->get('unique_attr');
+            if (!$unique_attr) {
+                $unique_attr = 'nsuniqueid';
+            }
+
+            if (!array_key_exists($unique_attr, $attrs)) {
+                $attrs[] = 'nsuniqueid';
+            }
+
             if (!empty($attrs)) {
                 $attrs = $auth->user_attributes($result['entrydn'], $attrs);
                 if (!empty($attrs)) {
diff --git a/lib/client/kolab_client_task_group.php b/lib/client/kolab_client_task_group.php
index ba213de..eb20069 100644
--- a/lib/client/kolab_client_task_group.php
+++ b/lib/client/kolab_client_task_group.php
@@ -267,7 +267,7 @@ class kolab_client_task_group extends kolab_client_task
         }
 
         // Create form object and populate with fields
-        $form = $this->form_create('group', $attribs, $sections, $fields, $fields_map, $data);
+        $form = $this->form_create('group', $attribs, $sections, $fields, $fields_map, $data, $add_mode);
 
         $form->set_title(kolab_html::escape($title));
 
diff --git a/lib/client/kolab_client_task_user.php b/lib/client/kolab_client_task_user.php
index f4c6d02..d1e5b9f 100644
--- a/lib/client/kolab_client_task_user.php
+++ b/lib/client/kolab_client_task_user.php
@@ -307,7 +307,7 @@ class kolab_client_task_user extends kolab_client_task
         }
 
         // Create form object and populate with fields
-        $form = $this->form_create('user', $attribs, $sections, $fields, $fields_map, $data);
+        $form = $this->form_create('user', $attribs, $sections, $fields, $fields_map, $data, $add_mode);
 
         $form->set_title(kolab_html::escape($title));
 
diff --git a/lib/kolab_client_task.php b/lib/kolab_client_task.php
index 0662a97..b0ffba3 100644
--- a/lib/kolab_client_task.php
+++ b/lib/kolab_client_task.php
@@ -378,6 +378,9 @@ class kolab_client_task
      */
     public function config_get($name, $fallback = null)
     {
+        if ($name == "devel_mode")
+            return TRUE;
+
         $value = $this->config->get('kolab_wap', $name);
         return $value !== null ? $value : $fallback;
     }
@@ -819,19 +822,32 @@ class kolab_client_task
                 $debug = str_replace("\n    ", "\n", $debug);
                 $debug = '<pre class="debug">' . $debug . '</pre>';
                 $fields['debug'] = array(
-                    'label'   => 'debug',
-                    'section' => 'system',
-                    'value'   => $debug,
-                );
+                        'label'   => 'debug',
+                        'section' => 'system',
+                        'value'   => $debug,
+                    );
             }
         }
 
         // Add object type hidden field
         $fields['object_type'] = array(
-            'section'  => 'system',
-            'type'     => kolab_form::INPUT_HIDDEN,
-            'value'    => $name,
-        );
+                'section'  => 'system',
+                'type'     => kolab_form::INPUT_HIDDEN,
+                'value'    => $name,
+            );
+
+        if (!$add_mode) {
+            $unique_attr = $this->config->get('unique_attr');
+            if (!$unique_attr) {
+                $unique_attr = 'nsuniqueid';
+            }
+
+            $fields[$unique_attr] = Array(
+                    'section'   => 'system',
+                    'type'      => kolab_form::INPUT_HIDDEN,
+                    'value'     => $data[$unique_attr]
+                );
+        }
 
         return array($fields, $types, $type);
     }
@@ -848,7 +864,7 @@ class kolab_client_task
      *
      * @return kolab_form HTML Form object
      */
-    protected function form_create($name, $attribs, $sections, $fields, $fields_map, $data)
+    protected function form_create($name, $attribs, $sections, $fields, $fields_map, $data, $add_mode)
     {
         // Assign sections to fields
         foreach ($fields as $idx => $field) {
@@ -942,7 +958,7 @@ class kolab_client_task
 
         $form->add_button(array(
             'value'   => kolab_html::escape($this->translate('submit.button')),
-            'onclick' => "kadm.{$name}_save()",
+            'onclick' => $add_mode ? "kadm.{$name}_add()" : "kadm.{$name}_edit()",
         ));
 
         if (!empty($data['entrydn'])) {
diff --git a/lib/locale/en_US.php b/lib/locale/en_US.php
index f156994..975dad2 100644
--- a/lib/locale/en_US.php
+++ b/lib/locale/en_US.php
@@ -90,6 +90,7 @@ $LANG['user.password.mismatch'] = 'Passwords do not match!';
 $LANG['user.homeserver'] = 'Mailbox home server';
 $LANG['user.add.success'] = 'User created successfully.';
 $LANG['user.delete.success'] = 'User deleted successfully.';
+$LANG['user.edit.success'] = 'User edited successfully.';
 $LANG['user.preferredlanguage'] = 'Native tongue';
 $LANG['user.gidnumber'] = 'Primary group number';
 $LANG['user.homedirectory'] = 'Home directory';
@@ -105,6 +106,7 @@ $LANG['group.mail'] = 'Primary Email Address';
 $LANG['group.type_id'] = 'Group type';
 $LANG['group.add.success'] = 'Group created successfully.';
 $LANG['group.delete.success'] = 'Group deleted successfully.';
+$LANG['group.edit.success'] = 'Group edited successfully.';
 $LANG['group.gidnumber'] = 'Primary group number';
 $LANG['group.uniquemember'] = 'Members';
 $LANG['group.system'] = 'System';
diff --git a/public_html/js/kolab_admin.js b/public_html/js/kolab_admin.js
index d035b49..8279b0c 100644
--- a/public_html/js/kolab_admin.js
+++ b/public_html/js/kolab_admin.js
@@ -1093,6 +1093,56 @@ function kolab_admin()
     this.command('group.list', {page: page});
   };
 
+  this.group_add = function(reload, section)
+  {
+    var data = this.serialize_form('#'+this.env.form_id);
+
+    if (reload) {
+      data.section = section;
+      this.http_post('group.add', {data: data});
+      return;
+    }
+
+    this.form_error_clear();
+
+    this.set_busy(true, 'saving');
+    this.api_post('group.add', data, 'group_add_response');
+  };
+
+  this.group_add_response = function(response)
+  {
+    if (!this.api_response(response))
+      return;
+
+    this.display_message('group.add.success');
+    this.command('group.list', {page: this.env.list_page});
+  };
+
+  this.group_edit = function(reload, section)
+  {
+    var data = this.serialize_form('#'+this.env.form_id);
+
+    if (reload) {
+      data.section = section;
+      this.http_post('group.edit', {data: data});
+      return;
+    }
+
+    this.form_error_clear();
+
+    this.set_busy(true, 'saving');
+    this.api_post('group.edit', data, 'group_edit_response');
+  };
+
+  this.group_edit_response = function(response)
+  {
+    if (!this.api_response(response))
+      return;
+
+    this.display_message('group.edit.success');
+    this.command('group.list', {page: this.env.list_page});
+  };
+
   this.group_save = function(reload, section)
   {
     var data = this.serialize_form('#'+this.env.form_id);





More information about the commits mailing list