3 commits - lib/client lib/kolab_client_task.php lib/locale public_html/js public_html/skins

Aleksander Machniak machniak at kolabsys.com
Wed Oct 3 13:28:32 CEST 2012


 lib/client/kolab_client_task_settings.php    |  269 +++++++++++++++++++++++----
 lib/kolab_client_task.php                    |   24 +-
 lib/locale/en_US.php                         |   24 ++
 public_html/js/kolab_admin.js                |  250 ++++++++++++++++++++++++-
 public_html/skins/default/images/buttons.png |binary
 public_html/skins/default/style.css          |   59 +++++
 6 files changed, 564 insertions(+), 62 deletions(-)

New commits:
commit a98368884d0294fe5145adab25475f29f977e4c9
Author: Aleksander Machniak <alec at alec.pl>
Date:   Wed Oct 3 13:27:36 2012 +0200

    Object types management - editable

diff --git a/lib/client/kolab_client_task_settings.php b/lib/client/kolab_client_task_settings.php
index 09d7697..1e87de5 100644
--- a/lib/client/kolab_client_task_settings.php
+++ b/lib/client/kolab_client_task_settings.php
@@ -32,9 +32,10 @@ class kolab_client_task_settings extends kolab_client_task
     );
 
     protected $form_element_types = array(
-        'text', 'select', 'multiselect', 'list', 'checkbox', 'password'
+        'text', 'select', 'multiselect', 'list', 'list-autocomplete', 'checkbox', 'password'
     );
 
+
     /**
      * Default action.
      */
@@ -382,9 +383,11 @@ class kolab_client_task_settings extends kolab_client_task
         $title    = $add_mode ? $this->translate('type.add') : $data['name'];
 
         // unset $data for correct form_create() run, we've got already data specified
+        $effective_rights = $data['effective_rights'];
+        $id = $data['id'] ? $data['type'].':'.$data['id'] : null;
         $data = array();
-        // enable delete button
-        $data['effective_rights']['entry'] = array('delete');
+        $data['effective_rights'] = $effective_rights;
+        $data['id'] = $id;
 
         // Create form object and populate with fields
         $form = $this->form_create('type', $attribs, $sections, $fields, $fields_map, $data, $add_mode);
@@ -431,6 +434,7 @@ class kolab_client_task_settings extends kolab_client_task
                 'multiple' => true,
                 'required' => true,
                 'value'    => $data['objectclass'],
+                'onchange' => "kadm.type_attr_class_change(this)",
             ),
             'used_for' => array(
                 'value'   => 'hosted',
@@ -447,7 +451,7 @@ class kolab_client_task_settings extends kolab_client_task
             unset($form_fields['used_for']);
         }
 
-/*
+
         // Get the rights on the entry and attribute level
         $data['effective_rights'] = $this->effective_rights($name, $data['id']);
         $attribute_rights         = $data['effective_rights']['attribute'];
@@ -455,7 +459,7 @@ class kolab_client_task_settings extends kolab_client_task
 
         // See if "administrators" (those who can delete and add back on the entry
         // level) may override the automatically generated contents of auto_form_fields.
-        $admin_auto_fields_rw = $this->config_get('admin_auto_fields_rw', false, Conf::BOOL);
+        //$admin_auto_fields_rw = $this->config_get('admin_auto_fields_rw', false, Conf::BOOL);
 
         foreach ($fields as $idx => $field) {
             if (!array_key_exists($idx, $attribute_rights)) {
@@ -481,7 +485,7 @@ class kolab_client_task_settings extends kolab_client_task
                 }
             }
         }
-*/
+
         // (Re-|Pre-)populate auto_form_fields
         if (!$add_mode) {
             // Add debug information
@@ -500,7 +504,7 @@ class kolab_client_task_settings extends kolab_client_task
         }
 
         // Get object classes
-        $sd = $this->form_element_select_data($fields['objectclass']);
+        $sd = $this->form_element_select_data($fields['objectclass'], null, true);
         $fields['objectclass'] = array_merge($fields['objectclass'], $sd);
 
         // Add entry identifier
@@ -521,39 +525,42 @@ class kolab_client_task_settings extends kolab_client_task
         return $fields;
     }
 
+    /**
+     * Type attributes table
+     */
     private function type_form_attributes($data)
     {
         $attributes = array();
         $rows       = array();
+        $attr_table = array();
         $table      = array(
+            'id'    => 'type_attr_table',
             'class' => 'list',
         );
         $cells      = array(
             'name' => array(
-                'class' => 'name',
                 'body'  => $this->translate('attribute.name'),
             ),
             'type' => array(
-                'class' => 'type',
                 'body'  => $this->translate('attribute.type'),
             ),
+            'readonly' => array(
+                'body'  => $this->translate('attribute.readonly'),
+            ),
             'optional' => array(
-                'class' => 'optional',
                 'body'  => $this->translate('attribute.optional'),
             ),
-            'auto' => array(
-                'class' => 'auto',
-                'body'  => $this->translate('attribute.auto'),
+            'value' => array(
+                'body'  => $this->translate('attribute.value'),
             ),
-            'static' => array(
-                'class' => 'default',
-                'body'  => $this->translate('attribute.static'),
+            'actions' => array(
             ),
-//            'actions' => array(
-//                'class' => 'actions',
-//            ),
         );
 
+        foreach ($cells as $idx => $cell) {
+            $cells[$idx]['class'] = $idx;
+        }
+
         // get attributes list from $data
         if (!empty($data) && count($data) > 1) {
             $attributes = array_merge(
@@ -565,43 +572,229 @@ class kolab_client_task_settings extends kolab_client_task
             $attributes = array_unique($attributes);
         }
 
+        // get all available attributes
+        $available = $this->type_attributes($data['objectclass']);
+
         // table header
         $table['head'] = array(array('cells' => $cells));
-/*
-        // attribute row elements
-        $cells['type']['element'] = array(
-            'type'    => kolab_form::INPUT_SELECT,
-            'options' => $this->form_element_types,
-        );
-        $cells['optional']['element'] = array(
-            'type'  => kolab_form::INPUT_CHECKBOX,
-            'value' => 1,
-        );
-*/
+
         $yes = $this->translate('yes');
+        $no  = '';
         // defined attributes
         foreach ($attributes as $attr) {
-            $row = $cells;
+            $row          = $cells;
+            $type         = $data['attributes']['form_fields'][$attr]['type'];
+            $optional     = $data['attributes']['form_fields'][$attr]['optional'];
+            $autocomplete = $data['attributes']['form_fields'][$attr]['autocomplete'];
+            $valtype      = 'normal';
+            $value        = '';
+
+            if ($type == 'list' && $autocomplete) {
+                $type = 'list-autocomplete';
+            }
+
+            if ($data['attributes']['fields'][$attr]) {
+                $valtype = 'static';
+                $_data   = $data['attributes']['fields'][$attr];
+                $value   = $this->translate('attribute.value.static') . ': ' . kolab_html::escape($_data);
+            }
+            else if (isset($data['attributes']['auto_form_fields'][$attr])) {
+                $valtype = 'auto';
+                if (is_array($data['attributes']['auto_form_fields'][$attr]['data'])) {
+                    $_data = implode(',', $data['attributes']['auto_form_fields'][$attr]['data']);
+                }
+                else {
+                    $_data = '';
+                }
+                $value = $this->translate('attribute.value.auto') . ': ' . kolab_html::escape($_data);
 
-            $type     = $data['attributes']['form_fields'][$attr]['type'];
-            $optional = $data['attributes']['form_fields'][$attr]['optional'];
+                if (empty($data['attributes']['form_fields'][$attr])) {
+                    $valtype = 'auto-readonly';
+                }
+            }
 
             // set cell content
-            $row['name']['body']     = $attr;
-            $row['static']['body']   = kolab_html::escape($data['attributes']['fields'][$attr]);
-            $row['auto']['body']     = isset($data['attributes']['fields'][$attr]) ? $yes : '';
+            $row['name']['body']     = !empty($available[$attr]) ? $available[$attr] : $attr;
             $row['type']['body']     = !empty($type) ? $type : 'text';
-            $row['optional']['body'] = $optional ? $yes : '';
+            $row['value']['body']    = $value;
+            $row['readonly']['body'] = $valtype == 'auto-readonly' ? $yes : $no;
+            $row['optional']['body'] = $optional ? $yes : $no;
+            $row['actions']['body']  = 
+                kolab_html::a(array('href' => '#delete', 'onclick' => "kadm.type_attr_delete('$attr')",
+                    'class' => 'button delete', 'title' => $this->translate('delete')))
+                . kolab_html::a(array('href' => '#edit', 'onclick' => "kadm.type_attr_edit('$attr')",
+                    'class' => 'button edit', 'title' => $this->translate('edit')));
+
+            $rows[] = array(
+                'id'    => 'attr_table_row_' . $attr,
+                'cells' => $row,
+            );
 
-            $rows[] = array('cells' => $row);
+            // data array for the UI
+            $attr_table[$attr] = array(
+                'type'     => !empty($type) ? $type : 'text',
+                'valtype'  => $valtype,
+                'optional' => $optional,
+                'maxcount' => $data['attributes']['form_fields'][$attr]['maxcount'],
+                'data'     => $_data,
+                'values'   => $data['attributes']['form_fields'][$attr]['values'],
+            );
         }
 
+        // edit form
+        $rows[] = array(
+            'cells' => array(
+                array(
+                    'body'    => $this->type_form_attributes_form($available),
+                    'colspan' => count($cells),
+                ),
+            ),
+            'id' => 'type_attr_form',
+        );
+
         $table['body'] = $rows;
 
+        // sort attr_table by attribute name
+        ksort($attr_table);
+
+        // set environment variables
+        $this->output->set_env('attr_table', $attr_table);
+        $this->output->set_env('yes_label', $yes);
+        $this->output->set_env('no_label', $no);
+        $this->output->add_translation('attribute.value.auto', 'attribute.value.static',
+            'attribute.key.invalid');
+
+        // Add attribute link
+        $link = kolab_html::a(array(
+            'href' => '#add_attr', 'class' => 'add_attr',
+            'onclick' => "kadm.type_attr_add()",
+            'content' =>  $this->translate('attribute.add')), true);
+
+        return kolab_html::table($table) . $link;
+    }
+
+    /**
+     * Attributes edit form
+     */
+    private function type_form_attributes_form($attributes)
+    {
+        // build form
+        $rows = array();
+        $form = array(
+            'name' => array(
+                'type' => kolab_form::INPUT_SELECT,
+                'options' => $attributes,
+            ),
+            'type' => array(
+                'type' => kolab_form::INPUT_SELECT,
+                'options' => array_combine($this->form_element_types, $this->form_element_types),
+                'onchange' => 'kadm.type_attr_type_change(this)',
+            ),
+            'options' => array(
+                'type'      => kolab_form::INPUT_TEXTAREA,
+                'data-type' => kolab_form::TYPE_LIST,
+            ),
+            'maxcount' => array(
+                'type' => kolab_form::INPUT_TEXT,
+                'size' => 5,
+            ),
+            'value' => array(
+                'type'  => kolab_form::INPUT_SELECT,
+                'options' => array(
+                    'normal'        => $this->translate('attribute.value.normal'),
+                    'auto'          => $this->translate('attribute.value.auto'),
+                    'auto-readonly' => $this->translate('attribute.value.auto-readonly'),
+                    'static'        => $this->translate('attribute.value.static'),
+                ),
+                'onchange' => 'kadm.type_attr_value_change(this)',
+            ),
+            'optional' => array(
+                'type'  => kolab_form::INPUT_CHECKBOX,
+                'value' => 1,
+            ),
+        );
+
+        foreach ($form as $idx => $element) {
+            $element['name'] = 'attr_' . $idx;
+            $body = kolab_form::get_element($element);
+
+            if ($idx == 'value') {
+                $body .= kolab_form::get_element(array(
+                    'name' => 'attr_data',
+                    'type' => kolab_form::INPUT_TEXT,
+                ));
+            }
+
+            $rows[] = array(
+                'id' => 'attr_form_row_' . $idx,
+                'cells' => array(
+                    array(
+                        'class' => 'label',
+                        'body'  => $this->translate('attribute.' . $idx),
+                    ),
+                    array(
+                        'class' => 'value',
+                        'body'  => $body,
+                    ),
+                ),
+            );
+        }
+
+        $rows[] = array(
+            'cells' => array(
+                array(
+                    'colspan' => 2,
+                    'class' => 'formbuttons',
+                    'body' => kolab_html::input(array(
+                        'type'    => 'button',
+                        'value'   => $this->translate('button.save'),
+                        'onclick' => "kadm.type_attr_save()",
+                    ))
+                    . kolab_html::input(array(
+                        'type'    => 'button',
+                        'value'   => $this->translate('button.cancel'),
+                        'onclick' => "kadm.type_attr_cancel()",
+                    )),
+                ),
+            ),
+        );
+
+        $table = array(
+            'class' => 'form',
+            'body'  => $rows,
+        );
+
         return kolab_html::table($table);
     }
 
     /**
+     * Returns list of LDAP attributes for specified opject classes.
+     */
+    public function type_attributes($object_class = null)
+    {
+        $post_data = array(
+            'attributes' => array('attribute'),
+            'classes'    => $object_class,
+        );
+
+        // get all available attributes
+        $response   = $this->api->post('form_value.select_options', null, $post_data);
+        $response   = $response->get('attribute');
+        $attributes = array();
+
+        // convert to hash array
+        if (!empty($response['list'])) {
+            $attributes = array_combine(array_map('strtolower', $response['list']), $response['list']);
+        }
+
+        $this->output->set_env('attributes', $attributes);
+        // @TODO: check if all required attributes are used
+//        $this->output->set_env('attributes_required', $attributes['required']);
+
+        return $attributes;
+    }
+
+    /**
      * Users search form.
      *
      * @return string HTML output of the form
diff --git a/lib/kolab_client_task.php b/lib/kolab_client_task.php
index 13a90f1..fec0404 100644
--- a/lib/kolab_client_task.php
+++ b/lib/kolab_client_task.php
@@ -279,10 +279,10 @@ class kolab_client_task
         // Run security checks
         $this->input_checks();
 
-        $action = $this->get_input('action', 'GET');
+        $this->action = $this->get_input('action', 'GET');
 
-        if ($action) {
-            $method = 'action_' . $action;
+        if ($this->action) {
+            $method = 'action_' . $this->action;
             if (method_exists($this, $method)) {
                 $this->$method();
             }
@@ -817,10 +817,11 @@ class kolab_client_task
      *
      * @param array $field Field attributes
      * @param array $data  Attribute values
+     * @param bool  $lc    Convert option values to lower-case
      *
      * @return array Options/Default definition
      */
-    protected function form_element_select_data($field, $data = array())
+    protected function form_element_select_data($field, $data = array(), $lc = false)
     {
         $options = array();
         $default = null;
@@ -836,7 +837,12 @@ class kolab_client_task
         }
 
         if (!empty($field['values'])) {
-            $options = array_combine($field['values'], $field['values']);
+            if ($lc) {
+                $options = array_combine(array_map('strtolower', $field['values']), $field['values']);
+            }
+            else {
+                $options = array_combine($field['values'], $field['values']);
+            }
 
             // Exceptions
             if ($field['name'] == 'ou') {
@@ -1237,7 +1243,7 @@ class kolab_client_task
 
         if ($writeable) {
             $form->add_button(array(
-                'value'   => kolab_html::escape($this->translate('submit.button')),
+                'value'   => kolab_html::escape($this->translate('button.submit')),
                 'onclick' => "kadm.{$name}_save()",
             ));
         }
@@ -1245,7 +1251,7 @@ class kolab_client_task
         if (!empty($data['id']) && in_array('delete', $data['effective_rights']['entry'])) {
             $id = $data['id'];
             $form->add_button(array(
-                'value'   => kolab_html::escape($this->translate('delete.button')),
+                'value'   => kolab_html::escape($this->translate('button.delete')),
                 'onclick' => "kadm.{$name}_delete('{$id}')",
             ));
         }
diff --git a/lib/locale/en_US.php b/lib/locale/en_US.php
index 9f5561e..23d81d4 100644
--- a/lib/locale/en_US.php
+++ b/lib/locale/en_US.php
@@ -7,16 +7,29 @@ $LANG['about.support'] = 'Professional support is available from <a href="http:/
 $LANG['about.technology'] = 'Technology';
 $LANG['about.warranty'] = 'It comes with absolutely <b>no warranties</b> and is typically run entirely self supported. You can find help & information on the community <a href="http://kolab.org">web site</a> & <a href="http://wiki.kolab.org">wiki</a>.';
 
-$LANG['attribute.auto'] = 'Auto-generated';
+$LANG['attribute.add'] = 'Add attribute';
 $LANG['attribute.static'] = 'Static value';
 $LANG['attribute.name'] = 'Attribute';
 $LANG['attribute.optional'] = 'Optional';
+$LANG['attribute.maxcount'] = 'Max. count';
+$LANG['attribute.readonly'] = 'Read-only';
 $LANG['attribute.type'] = 'Field type';
+$LANG['attribute.value'] = 'Value';
+$LANG['attribute.value.auto'] = 'Generated';
+$LANG['attribute.value.auto-readonly'] = 'Generated (read-only)';
+$LANG['attribute.value.normal'] = 'Normal';
+$LANG['attribute.value.static'] = 'Static';
+$LANG['attribute.options'] = 'Options';
+$LANG['attribute.key.invalid'] = 'Type key contains forbidden characters!';
+
+$LANG['button.cancel'] = 'Cancel';
+$LANG['button.delete'] = 'Delete';
+$LANG['button.save'] = 'Save';
+$LANG['button.submit'] = 'Submit';
 
 $LANG['creatorsname'] = 'Created by';
 $LANG['days'] = 'days';
 $LANG['debug'] = 'Debug info';
-$LANG['delete.button'] = 'Delete';
 $LANG['deleting'] = 'Deleting data...';
 
 $LANG['domain.add'] = 'Add Domain';
@@ -140,12 +153,13 @@ $LANG['signup.wronguid'] = 'Invalid Username!';
 $LANG['signup.wrongmailalternateaddress'] = 'Please provide a valid Email Address!';
 $LANG['signup.footer'] = 'This is a service offered by <a href="http://kolabsys.com">Kolab Systems</a>.';
 
-$LANG['submit.button'] = 'Submit';
-
 $LANG['type.add'] = 'Add Object Type';
+$LANG['type.add.success'] = 'Object type created successfully.';
 $LANG['type.attributes'] = 'Attributes';
 $LANG['type.description'] = 'Description';
+$LANG['type.delete.success'] = 'Object type deleted successfully.';
 $LANG['type.domain'] = 'Domain';
+$LANG['type.edit.success'] = 'Object type updated successfully.';
 $LANG['type.group'] = 'Group';
 $LANG['type.list'] = 'Object Types List';
 $LANG['type.key'] = 'Key';
@@ -240,3 +254,5 @@ $LANG['user.userpassword2'] = 'Confirm password';
 $LANG['user.uidnumber'] = 'User ID number';
 
 $LANG['welcome'] = 'Welcome to the Kolab Groupware Server Maintenance';
+
+$LANG['yes'] = 'yes';
diff --git a/public_html/js/kolab_admin.js b/public_html/js/kolab_admin.js
index c58a592..e4bf81e 100644
--- a/public_html/js/kolab_admin.js
+++ b/public_html/js/kolab_admin.js
@@ -1648,9 +1648,6 @@ function kolab_admin()
 
   this.type_delete = function(id)
   {
-    // @TODO:
-    return alert('Not implemented');
-
     this.set_busy(true, 'deleting');
     this.api_post('type.delete', this.type_id_parse(id), 'type_delete_response');
   };
@@ -1667,16 +1664,14 @@ function kolab_admin()
       page -= 1;
 
     this.display_message('type.delete.success');
-    this.command('type.list', {page: page});
+    this.command('settings.type_list', {page: page});
     this.set_watermark('taskcontent');
   };
 
   this.type_save = function(reload, section)
   {
-    // @TODO:
-    return alert('Not implemented');
-
-    var data = this.serialize_form('#'+this.env.form_id),
+    var i, attr, request = {},
+      data = this.serialize_form('#'+this.env.form_id),
       action = data.id ? 'edit' : 'add';
 
     if (reload) {
@@ -1692,8 +1687,57 @@ function kolab_admin()
       return;
     }
 
+    if (data.key.match(/[^a-z_-]/)) {
+      this.display_message('attribute.key.invalid', 'error');
+      return;
+    }
+
+    request.id = data.id;
+    request.key = data.key;
+    request.name = data.name;
+    request.type = data.type;
+    request.description = data.description;
+    request.used_for = data.used_for;
+    request.attributes = {fields: {}, form_fields: {}, auto_form_fields: {}};
+    request.attributes.fields.objectclass = data.objectclass;
+
+    // Build attributes array compatible with the API format
+    // @TODO: use attr_table format
+    for (i in this.env.attr_table) {
+      attr = this.env.attr_table[i];
+      data = {};
+
+      if (attr.valtype == 'static') {
+        request.attributes.fields[i] = attr.data;
+        continue;
+      }
+
+      if (attr.type == 'list-autocomplete') {
+        data.type = 'list';
+        data.autocomplete = true;
+      }
+      else if (attr.type != 'text')
+        data.type = attr.type;
+
+      if ((attr.type == 'select' || attr.type == 'multiselect') && attr.values)
+        data.values = attr.values;
+
+      if (attr.optional)
+        data.optional = true;
+      if (attr.maxcount)
+        data.maxcount = attr.maxcount;
+
+      if (attr.valtype == 'normal' || attr.valtype == 'auto')
+        request.attributes.form_fields[i] = data;
+      if (attr.valtype == 'auto' || attr.valtype == 'auto-readonly') {
+        if (attr.data)
+          data.data = attr.data.split(/,/);
+        request.attributes.auto_form_fields[i] = data;
+      }
+    }
+
     this.set_busy(true, 'saving');
-    this.api_post('type.' + action, data, 'type_' + action + '_response');
+    this.api_post('type.' + action, request, 'type_' + action + '_response');
   };
 
   this.type_add_response = function(response)
@@ -1716,12 +1760,199 @@ function kolab_admin()
     this.set_watermark('taskcontent');
   };
 
+  /*********************************************************/
+  /*********       Various helper methods          *********/
+  /*********************************************************/
+
+  // Parses object type identifier
   this.type_id_parse = function(id)
   {
     var id = String(id).split(':');
     return {type: id[0], id: id[1]};
   };
 
+  // Removes attribute row
+  this.type_attr_delete = function(attr)
+  {
+    $('#attr_table_row_' + attr).remove();
+    $('select[name="attr_name"] > option[value="'+attr+'"]').show();
+
+    delete this.env.attr_table[attr];
+    this.type_attr_cancel();
+  };
+
+  // Displays attribute edition form
+  this.type_attr_edit = function(attr)
+  {
+    var form = $('#type_attr_form');
+
+    form.detach();
+    $('#attr_table_row_'+attr).after(form);
+    this.type_attr_form_init(attr);
+    form.slideDown(400);
+  };
+
+  // Displays attribute addition form
+  this.type_attr_add = function()
+  {
+    var form = $('#type_attr_form');
+
+    form.detach();
+    $('#type_attr_table > tbody').append(form);
+    this.type_attr_form_init();
+    form.slideDown(400);
+  };
+
+  // Saves attribute form, create/update attribute row
+  this.type_attr_save = function()
+  {
+    var attr, row, value = '', data = {},
+      form_data = this.serialize_form('#'+this.env.form_id),
+      name_select = $('select[name="attr_name"]');
+
+    // read attribute form data
+    data.type = form_data.attr_type;
+    data.valtype = form_data.attr_value;
+    data.optional = form_data.attr_optional;
+    data.data = data.valtype != 'normal' ? form_data.attr_data : null;
+    data.maxcount = data.type == 'list' || data.type == 'list-autocomplete' ? form_data.attr_maxcount : 0;
+    data.values = data.type == 'select' || data.type == 'multiselect' ? form_data.attr_options : [];
+
+    if (name_select.is(':visible')) {
+      // new attribute
+      attr = name_select.val();
+      row = $('<tr><td class="name"></td><td class="type"></td><td class="readonly"></td>'
+        +'<td class="optional"></td><td class="value"></td><td class="actions">'
+        +'<a class="button delete" title="delete" onclick="kadm.type_attr_delete(\''+attr+'\')" href="#delete"></a>'
+        +'<a class="button edit" title="edit" onclick="kadm.type_attr_edit(\''+attr+'\')" href="#edit"></a></td></tr>')
+        .attr('id', 'attr_table_row_' + attr).appendTo('#type_attr_table > tbody');
+    }
+    else {
+      // edited attribute
+      attr = $('span', name_select.parent()).text().toLowerCase();
+      row = $('#attr_table_row_' + attr);
+    }
+
+    if (data.valtype != 'normal') {
+      value = this.t('attribute.value.' + (data.valtype == 'static' ? 'static' : 'auto')) + ': ' + data.data;
+    }
+
+    // Update table row
+    $('td.name', row).text(this.env.attributes[attr]);
+    $('td.type', row).text(data.type);
+    $('td.readonly', row).text(data.valtype == 'auto-readonly' ? this.env.yes_label : this.env.no_label);
+    $('td.optional', row).text(data.optional ? this.env.yes_label : this.env.no_label);
+    $('td.value', row).text(value);
+
+    // Update env data
+    this.env.attr_table[attr] = data;
+
+    this.type_attr_cancel();
+  };
+
+  // Hide attribute form
+  this.type_attr_cancel = function()
+  {
+    $('#type_attr_form').hide();
+  };
+
+  this.type_attr_form_init = function(attr)
+  {
+    var name_select = $('select[name="attr_name"]'),
+      data = attr ? this.env.attr_table[attr] : {},
+      type = data.type ? data.type : 'text';
+
+    $('select[name="attr_type"]').val(type);
+    $('select[name="attr_value"]').val(attr ? data.valtype : 'normal');
+    $('input[name="attr_optional"]').attr('checked', attr ? data.optional : false);
+    $('input[name="attr_data"]').val(attr ? data.data : '');
+    $('input[name="attr_maxcount"]').val(data.maxcount ? data.maxcount : '');
+    $('textarea[name="attr_options"]').val(data.values ? data.values.join("\n") : '');
+    this.form_element_update({name: 'attr_options'});
+
+    $('span', name_select.parent()).remove();
+    this.type_attr_type_change('select[name="attr_type"]');
+    this.type_attr_value_change('select[name="attr_value"]');
+
+    if (attr) {
+      name_select.hide().val(attr);
+      $('<span></span>').text(this.env.attributes[attr] ? this.env.attributes[attr] : attr).appendTo(name_select.parent());
+      return;
+    }
+
+    this.type_attr_select_init();
+    name_select.show();
+  };
+
+  // Initialize attribute name selector
+  this.type_attr_select_init = function()
+  {
+    var select = $('select[name="attr_name"]'),
+      options = $('option', select);
+
+    options.each(function() {
+      if (kadm.env.attr_table[this.value])
+        $(this).attr('disabled', true);
+    });
+    options.not(':disabled').first().attr('selected', true);
+  };
+
+  // Update attribute form on value type change
+  this.type_attr_value_change = function(elem)
+  {
+    var type = $(elem).val();
+    $('input[name="attr_data"]')[type != 'normal' ? 'show' : 'hide']();
+    $('#attr_form_row_optional')[type != 'static' ? 'show' : 'hide']();
+    $('#attr_form_row_readonly')[type != 'static' ? 'show' : 'hide']();
+  };
+
+  // Update attribute form on type change
+  this.type_attr_type_change = function(elem)
+  {
+    var type = $(elem).val();
+    $('#attr_form_row_maxcount')[type == 'list' || type == 'list-autocomplete' ? 'show' : 'hide']();
+    $('#attr_form_row_options')[type == 'select' || type == 'multiselect' ? 'show' : 'hide']();
+  };
+
+  // Update attributes list on object classes change
+  this.type_attr_class_change = function(field)
+  {
+    var data = {attributes: 'attribute', classes: this.type_object_classes(field)};
+    this.api_post('form_value.select_options', data, 'type_attr_class_change_response');
+    this.type_attr_cancel();
+  };
+
+  // Update attributes list on object classes change - API response handler
+  this.type_attr_class_change_response = function(response)
+  {
+    if (!this.api_response(response))
+      return;
+
+    var i, lc, list = response.result.attribute.list,
+      required = response.result.attribute.required,
+      select = $('select[name="attr_name"]');
+
+    this.env.attributes = {};
+    select.empty();
+
+    for (i in list) {
+      lc = list[i].toLowerCase()
+      this.env.attributes[list[i].toLowerCase()] = list[i];
+      $('<option>').text(list[i]).val(lc).appendTo(select);
+    }
+  };
+
+  // Return selected objectclasses array
+  this.type_object_classes = function(field)
+  {
+    var classes = [];
+    $('option:selected', $(field)).each(function() {
+      classes.push(this.value);
+    });
+    return classes;
+  };
+
+  // Password generation - request
   this.generate_password = function(fieldname)
   {
     this.env.password_field = fieldname;
@@ -1730,6 +1961,7 @@ function kolab_admin()
     this.api_post('form_value.generate', {attributes: [fieldname]}, 'generate_password_response');
   };
 
+  // Password generation - response handler
   this.generate_password_response = function(response)
   {
     if (!this.api_response(response))
diff --git a/public_html/skins/default/style.css b/public_html/skins/default/style.css
index 201e7f6..cf597ee 100644
--- a/public_html/skins/default/style.css
+++ b/public_html/skins/default/style.css
@@ -117,11 +117,13 @@ table.form td {
 }
 
 table.form tr.required input,
+table.form tr.required select,
 table.form tr.required textarea {
   background-color: #f0f9ff;
 }
 
 table.form tr input.error,
+table.form tr select.error,
 table.form tr textarea.error {
   background-color: #f5e3e3;
 }
@@ -472,6 +474,7 @@ input.inactive {
 
 .formbuttons {
   text-align: center;
+  white-space: nowrap;
 }
 
 .formbuttons input {
@@ -548,6 +551,21 @@ pre.debug {
     -o-box-shadow: 0 2px 6px 0 #333;
 }
 
+a.button {
+  display: inline-block;
+  width: 18px;
+  height: 18px;
+  background: url(images/buttons.png) 0 0 no-repeat;
+}
+
+a.button.edit {
+  background-position: -81px 0;
+}
+
+a.button.delete {
+  background-position: -1px 0;
+}
+
 /********* Form smart inputs *********/
 
 span.listarea {
@@ -944,3 +962,40 @@ td.no {
   font-weight: bold;
   text-align: center;
 }
+
+/**** Settings ****/
+
+table.form table.list td {
+  padding: 2px 4px;
+}
+
+#type_attr_table td.actions {
+  width: 40px;
+  padding: 0;
+  white-space: nowrap;
+}
+
+#type_attr_table thead td {
+  white-space: nowrap;
+}
+
+#type_attr_table tfoot span {
+  cursor: pointer;
+}
+
+#type_attr_table td.readonly {
+  color: #514949;
+}
+
+#type_attr_form {
+  display: none;
+}
+
+#type_attr_form table.form td {
+  border: none;
+}
+
+a.add_attr {
+  padding-left: 2px;
+  padding-top: 2px;
+}


commit 9f5c62f030f174ebb95d0a3544c33610c6820bf4
Author: Aleksander Machniak <alec at alec.pl>
Date:   Fri Sep 28 08:14:53 2012 +0200

    Add edit button for future use

diff --git a/public_html/skins/default/images/buttons.png b/public_html/skins/default/images/buttons.png
index 586851e..d206367 100644
Binary files a/public_html/skins/default/images/buttons.png and b/public_html/skins/default/images/buttons.png differ
diff --git a/public_html/skins/default/style.css b/public_html/skins/default/style.css
index db3e596..201e7f6 100644
--- a/public_html/skins/default/style.css
+++ b/public_html/skins/default/style.css
@@ -629,11 +629,11 @@ span.listelement span.actions span.reset {
 }
 
 span.listelement span.actions span.add {
-  background-position: -43px -2px;
+  background-position: -41px -2px;
 }
 
 span.listelement span.actions span.search {
-  background-position: -65px -1px;
+  background-position: -61px -1px;
   cursor: default;
 }
 


commit 9209287971a6683cd28547669cd60994e9feeb1d
Author: Aleksander Machniak <alec at alec.pl>
Date:   Thu Sep 27 15:03:57 2012 +0200

    Fix PHP warning when capability response is empty/malformed

diff --git a/lib/kolab_client_task.php b/lib/kolab_client_task.php
index 4326b9a..13a90f1 100644
--- a/lib/kolab_client_task.php
+++ b/lib/kolab_client_task.php
@@ -504,11 +504,11 @@ class kolab_client_task
 
         $menu = array();
         $task = $this->get_task();
-        $caps = $this->capabilities();
+        $caps = (array) $this->get_capability('actions');
 
         foreach ($this->menu as $idx => $label) {
             if (in_array($task, array('domain', 'group', 'resource', 'role', 'user'))) {
-                if (!array_key_exists($task . "." . $idx, $caps['actions'])) {
+                if (!array_key_exists($task . "." . $idx, $caps)) {
                     continue;
                 }
             }





More information about the commits mailing list