diff options
author | Aleksander Machniak <machniak@kolabsys.com> | 2014-03-04 14:43:24 +0100 |
---|---|---|
committer | Aleksander Machniak <machniak@kolabsys.com> | 2014-03-04 14:43:24 +0100 |
commit | 2a8ac19a13e682bd1e52b2364702414031f36767 (patch) | |
tree | 95d9aa20b06fd1d74be8944acf95abd3ddf95157 /lib | |
parent | 8e09ba6962f63bfcb0dcd6eee92250b352bec895 (diff) | |
download | webadmin-2a8ac19a13e682bd1e52b2364702414031f36767.tar.gz |
Add support for organizational unit hierarchy
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Auth/LDAP.php | 8 | ||||
-rw-r--r-- | lib/api/kolab_api_service_ou.php | 8 | ||||
-rw-r--r-- | lib/client/kolab_client_task_ou.php | 159 | ||||
-rw-r--r-- | lib/ext/Net/LDAP3.php | 41 | ||||
-rw-r--r-- | lib/kolab_api_service.php | 9 | ||||
-rw-r--r-- | lib/kolab_client_task.php | 49 | ||||
-rw-r--r-- | lib/kolab_form.php | 2 | ||||
-rw-r--r-- | lib/kolab_html.php | 2 | ||||
-rw-r--r-- | lib/locale/en_US.php | 1 |
9 files changed, 238 insertions, 41 deletions
diff --git a/lib/Auth/LDAP.php b/lib/Auth/LDAP.php index 25bef97..83eb859 100644 --- a/lib/Auth/LDAP.php +++ b/lib/Auth/LDAP.php @@ -723,7 +723,13 @@ class LDAP extends Net_LDAP3 { public function organizationalunit_add($attrs, $typeid = null) { - $base_dn = $this->entry_base_dn('ou', $typeid); + if (!empty($attrs['base_dn'])) { + $base_dn = $attrs['base_dn']; + unset($attrs['base_dn']); + } + else { + $base_dn = $this->entry_base_dn('ou', $typeid); + } // TODO: The rdn is configurable as well. // Use [$type_str . "_"]ou_rdn_attr diff --git a/lib/api/kolab_api_service_ou.php b/lib/api/kolab_api_service_ou.php index 015d736..7e9eddc 100644 --- a/lib/api/kolab_api_service_ou.php +++ b/lib/api/kolab_api_service_ou.php @@ -167,9 +167,15 @@ class kolab_api_service_ou extends kolab_api_service $result = $auth->organizationalunit_info($getdata['id']); // normalize result - $result = $this->parse_result_attributes('ou', $result); + $result = $this->parse_result_attributes('ou', $result, $dn); if ($result) { + // get base_dn "attribute" for the API client + $dn = substr($dn, strlen($result['ou']) + 4); + if (strpos($dn, 'ou=') === 0) { + $result['base_dn'] = $dn; + } + return $result; } diff --git a/lib/client/kolab_client_task_ou.php b/lib/client/kolab_client_task_ou.php index 67c1df3..e1d80a8 100644 --- a/lib/client/kolab_client_task_ou.php +++ b/lib/client/kolab_client_task_ou.php @@ -78,6 +78,55 @@ class kolab_client_task_ou extends kolab_client_task $this->output->set_object('taskcontent', $output); } + /** + * OU list result handler, tree list builder + */ + protected function list_result_handler($result, $head, $foot, $table_class) + { + $result = $this->ou_list_sort($result); + $result = $this->ou_list_build_tree($result, $max_tree_level); + + foreach ($result as $idx => $item) { + $class = array('selectable'); + $tree = ''; + $style = ''; + + if ($item['level']) { + $tree .= '<span class="level" style="width:' . ($item['level'] * 16) . 'px"></span>'; + $style = 'display:none'; + } + + if ($item['has_children']) { + $tree .= '<span class="expando"></span>'; + } + else if ($max_tree_level) { + $tree .= '<span class="spacer"></span>'; + } + + $i++; + $cells = array(); + $cells[] = array( + 'class' => 'name', + 'body' => $tree . kolab_html::escape($item['name']), + 'onclick' => "kadm.command('ou.info', '$idx')", + ); + + $rows[] = array( + 'id' => $i, + 'class' => implode(' ', $class), + 'cells' => $cells, + 'style' => $style, + 'data-level' => !empty($item['level']) ? $item['level'] : '', + ); + } + + if ($max_tree_level) { + $this->output->command('tree_list_init'); + } + + return array($rows, $head, '', $table_class . ' tree'); + } + private function ou_form($attribs, $data = array()) { if (empty($attribs['id'])) { @@ -95,6 +144,7 @@ class kolab_client_task_ou extends kolab_client_task 'type_id' => 'system', 'type_id_name' => 'system', 'ou' => 'system', + 'base_dn' => 'system', 'description' => 'system', ); @@ -122,6 +172,17 @@ class kolab_client_task_ou extends kolab_client_task $fields['type_id']['type'] = kolab_form::INPUT_HIDDEN; } + $ou_roots = array_merge(array(''), $this->ou_list()); + + // Add OU root selector + $fields['base_dn'] = array( + 'section' => 'system', + 'type' => kolab_form::INPUT_SELECT, + 'options' => $ou_roots, + 'escaped' => true, + 'default' => $data['base_dn'], + ); + // Create mode if ($add_mode) { // Page title @@ -146,4 +207,102 @@ class kolab_client_task_ou extends kolab_client_task return $form->output(); } + + private function ou_list() + { + $result = $this->api_get('ous.list', null, array('page_size' => 999)); + $list = (array) $result->get('list'); + + $sorted = $this->ou_list_sort($list); + + return $this->ou_list_build($sorted); + } + + private function ou_list_sort($list) + { + // build the list for sorting + foreach (array_keys($list) as $dn) { + $name = kolab_utils::dn2ufn($dn); + $name = explode(',', $name); + + $sort_name = array_pop($name) . ',' . implode(',', array_reverse($name)); + + $list[$dn] = mb_strtolower($sort_name); + } + + // sort + asort($list, SORT_LOCALE_STRING); + + return $list; + } + + private function ou_list_build($list) + { + // @TODO: this code assumes parent always exist + foreach (array_keys($list) as $dn) { + $item = $this->parse_dn($dn); + $list[$dn] = str_repeat(' ', $item['level']) . kolab_html::escape($item['name']); + } + + return $list; + } + + /** + * Builds a tree from OUs list + */ + private function ou_list_build_tree($list, &$max_level) + { + $last = ''; + $result = array(); + + foreach (array_keys($list) as $dn) { + $item = $this->parse_dn($dn); + + if ($item['level']) { + // create a parent unit entry if it does not exist (search result) + $parent = $this->domain_dn($item['domain']); + + foreach ($item['path'] as $sub) { + $parent = "ou=$sub,$parent"; + if (!isset($result[$parent])) { + $result[$parent] = $this->parse_dn($parent); + $last = $parent; + } + + $result[$parent]['has_children'] = true; + } + } + + $result[$dn] = $item; + $last = $dn; + $max_level = max($max_level, $item['level']); + } + + return $result; + } + + /** + * Parse OU DN string into an array + */ + private function parse_dn($dn) + { + $path = kolab_utils::dn2ufn($dn); + $path = explode(', ', $path); + $domain = array_pop($path); // remove domain + + return array( + 'name' => array_shift($path), + 'path' => array_reverse($path), + 'level' => count($path), + 'domain' => $domain, + ); + } + + /** + * Converts domain name into DN string + */ + private function domain_dn($domain) + { + return "dc=" . implode(',dc=', explode('.', $domain)); + } } diff --git a/lib/ext/Net/LDAP3.php b/lib/ext/Net/LDAP3.php index 52d8626..674f261 100644 --- a/lib/ext/Net/LDAP3.php +++ b/lib/ext/Net/LDAP3.php @@ -1228,8 +1228,7 @@ class Net_LDAP3 // TODO: Get $rdn_attr - we have type_id in $new_attrs $dn_components = ldap_explode_dn($subject_dn, 0); $rdn_components = explode('=', $dn_components[0]); - - $rdn_attr = $rdn_components[0]; + $rdn_attr = $rdn_components[0]; $this->_debug("Net_LDAP3::modify_entry() using rdn attribute: " . $rdn_attr); @@ -1363,6 +1362,11 @@ class Net_LDAP3 } foreach ($new_attrs as $attr => $value) { + // OU's parent base dn + if ($attr == 'base_dn') { + continue; + } + if (is_array($value)) { if (count($value) == 1) { $new_attrs[$attr] = $value[0]; @@ -1420,21 +1424,28 @@ class Net_LDAP3 $old_ou = implode(',', $subject_dn_components); } - if ((!empty($old_ou) || !empty($new_ou)) && strtolower($old_ou) !== strtolower($new_ou)) { - // object is an organizational unit - if (strpos($subject_dn, 'ou=' . $old_ou) === 0) { - $mod_array['rename']['new_parent'] = substr($subject_dn, strlen('ou=' . $old_ou) + 1); - if (empty($mod_array['rename']['dn']) || empty($mod_array['rename']['new_rdn'])) { - $mod_array['rename']['dn'] = $subject_dn; - $mod_array['rename']['new_rdn'] = 'ou=' . $new_ou; + // object is an organizational unit + if (strpos($subject_dn, 'ou=' . $old_ou) === 0) { + $root = substr($subject_dn, strlen($old_ou) + 4); // remove ou=*, + + if ((!empty($new_attrs['base_dn']) && strtolower($new_attrs['base_dn']) !== strtolower($root)) + || (strtolower($old_ou) !== strtolower($new_ou)) + ) { + if (!empty($new_attrs['base_dn'])) { + $root = $new_attrs['base_dn']; } + + $mod_array['rename']['new_parent'] = $root; + $mod_array['rename']['dn'] = $subject_dn; + $mod_array['rename']['new_rdn'] = 'ou=' . $new_ou; } - else { - $mod_array['rename']['new_parent'] = $new_ou; - if (empty($mod_array['rename']['dn']) || empty($mod_array['rename']['new_rdn'])) { - $mod_array['rename']['dn'] = $subject_dn; - $mod_array['rename']['new_rdn'] = $rdn_attr . '=' . $new_attrs[$rdn_attr]; - } + } + // not OU object, but changed ou attribute + else if ((!empty($old_ou) || !empty($new_ou)) && strtolower($old_ou) !== strtolower($new_ou)) { + $mod_array['rename']['new_parent'] = $new_ou; + if (empty($mod_array['rename']['dn']) || empty($mod_array['rename']['new_rdn'])) { + $mod_array['rename']['dn'] = $subject_dn; + $mod_array['rename']['new_rdn'] = $rdn_attr . '=' . $new_attrs[$rdn_attr]; } } diff --git a/lib/kolab_api_service.php b/lib/kolab_api_service.php index 0e01a9e..4733347 100644 --- a/lib/kolab_api_service.php +++ b/lib/kolab_api_service.php @@ -347,6 +347,12 @@ abstract class kolab_api_service } } + // OU's parent attribute + if ($object_name == 'ou' && !empty($attribs['base_dn'])) { + // @TODO: validate? + $result['base_dn'] = $attribs['base_dn']; + } + $result = array_merge($result, $special_attr_validate); Log::trace("parse_input_attributes result (merge of \$result and \$special_attr_validate)", $result); @@ -452,10 +458,11 @@ abstract class kolab_api_service * * @param string $object_name Name of the object (user, group, etc.) * @param array $attrs Entry attributes + * @param string $dn Will be filled with object base DN * * @return array Entry attributes */ - protected function parse_result_attributes($object_name, $attrs = array()) + protected function parse_result_attributes($object_name, $attrs = array(), &$dn = null) { //console("parse_result_attributes($object_name, \$attrs = ", $attrs); diff --git a/lib/kolab_client_task.php b/lib/kolab_client_task.php index c9bcae9..35eec94 100644 --- a/lib/kolab_client_task.php +++ b/lib/kolab_client_task.php @@ -1298,7 +1298,7 @@ class kolab_client_task $field['suffix'] = kolab_html::escape($this->translate($field['suffix'])); } */ - if (!empty($field['options'])) { + if (!empty($field['options']) && empty($field['escaped'])) { foreach ($field['options'] as $opt_idx => $option) { if (is_array($option)) { $field['options'][$opt_idx]['content'] = kolab_html::escape($this->translate($option['content'])); @@ -1527,6 +1527,8 @@ class kolab_client_task $cols = array('name'); $i = 0; + $table_class = 'list'; + // table header $head[0]['cells'][] = array('class' => 'name', 'body' => $this->translate($task . '.list')); @@ -1555,29 +1557,34 @@ class kolab_client_task // table body if (!empty($result)) { - foreach ($result as $idx => $item) { - if (!is_array($item)) { - continue; - } + if (method_exists($this, 'list_result_handler')) { + list($rows, $head, $foot, $table_class) = $this->list_result_handler($result, $head, $foot, $table_class); + } + else { + foreach ($result as $idx => $item) { + if (!is_array($item)) { + continue; + } - $class = array('selectable'); + $class = array('selectable'); - if (method_exists($this, 'list_item_handler')) { - $item = $this->list_item_handler($item, $class); - } - else { - $item = array_shift($item); - } + if (method_exists($this, 'list_item_handler')) { + $item = $this->list_item_handler($item, $class); + } + else { + $item = array_shift($item); + } - if (empty($item)) { - continue; - } + if (empty($item)) { + continue; + } - $i++; - $cells = array(); - $cells[] = array('class' => 'name', 'body' => kolab_html::escape($item), - 'onclick' => "kadm.command('$task.info', '$idx')"); - $rows[] = array('id' => $i, 'class' => implode(' ', $class), 'cells' => $cells); + $i++; + $cells = array(); + $cells[] = array('class' => 'name', 'body' => kolab_html::escape($item), + 'onclick' => "kadm.command('$task.info', '$idx')"); + $rows[] = array('id' => $i, 'class' => implode(' ', $class), 'cells' => $cells); + } } } else { @@ -1588,7 +1595,7 @@ class kolab_client_task $table = kolab_html::table(array( 'id' => $task . 'list', - 'class' => 'list', + 'class' => $table_class, 'head' => $head, 'body' => $rows, 'foot' => $foot, diff --git a/lib/kolab_form.php b/lib/kolab_form.php index 2fe3e9c..6ce7332 100644 --- a/lib/kolab_form.php +++ b/lib/kolab_form.php @@ -319,7 +319,7 @@ class kolab_form $attribs['size'] = 5; } - $content = kolab_html::select($attribs, true); + $content = kolab_html::select($attribs, empty($attribs['escaped'])); break; case self::INPUT_CUSTOM: diff --git a/lib/kolab_html.php b/lib/kolab_html.php index 1107d4b..2317bd0 100644 --- a/lib/kolab_html.php +++ b/lib/kolab_html.php @@ -460,7 +460,7 @@ class kolab_html } // ignore empty values - if ($value === null) { + if ($value === null || $value === '') { continue; } diff --git a/lib/locale/en_US.php b/lib/locale/en_US.php index c89fd2c..d4e28cd 100644 --- a/lib/locale/en_US.php +++ b/lib/locale/en_US.php @@ -147,6 +147,7 @@ $LANG['ou.list'] = 'Organizational Unit List'; $LANG['ou.norecords'] = 'No organizational unit records found!'; $LANG['ou.system'] = 'Details'; $LANG['ou.type_id'] = 'Unit Type'; +$LANG['ou.base_dn'] = 'Parent Unit'; $LANG['resource.add'] = 'Add Resource'; $LANG['resource.add.success'] = 'Resource created successfully.'; |