diff --git a/config/main.inc.php.dist b/config/main.inc.php.dist index b113b41a8..d4246a001 100644 --- a/config/main.inc.php.dist +++ b/config/main.inc.php.dist @@ -680,6 +680,7 @@ $rcmail_config['ldap_public']['Verisign'] = array( 'object_classes' => array("top", "groupOfNames"), 'member_attr' => 'member', // name of the member attribute, e.g. uniqueMember 'name_attr' => 'cn', // attribute to be used as group name + 'member_filter' => '(objectclass=*)', // optional filter to use when querying for group members ), ); */ diff --git a/program/js/app.js b/program/js/app.js index dbb65eea4..0445004f2 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -322,7 +322,7 @@ function rcube_webmail() this.env.contactfolders = $.extend($.extend({}, this.env.address_sources), this.env.contactgroups); this.enable_command('add', 'import', this.env.writable_source); - this.enable_command('list', 'listgroup', 'listsearch', 'advanced-search', true); + this.enable_command('list', 'listgroup', 'pushgroup', 'listsearch', 'advanced-search', true); if (this.gui_objects.contactslist) { this.contact_list = new rcube_list_widget(this.gui_objects.contactslist, @@ -1080,6 +1080,8 @@ function rcube_webmail() } break; + case 'pushgroup': + // TODO: so some magic stuff here case 'listgroup': this.reset_qsearch(); this.list_contacts(props.source, props.id); @@ -4151,6 +4153,7 @@ function rcube_webmail() this.list_contacts_clear = function() { + this.contact_list.data = {}; this.contact_list.clear(true); this.show_contentframe(false); this.enable_command('delete', false); @@ -4160,7 +4163,12 @@ function rcube_webmail() // load contact record this.load_contact = function(cid, action, framed) { - var win, url = {}, target = window; + var win, url = {}, target = window, + rec = this.contact_list.data[cid]; + + if (rec && rec.type == 'group') { + alert('group ' + cid) + } if (win = this.get_frame_window(this.env.contentframe)) { url._framed = 1; @@ -4311,7 +4319,7 @@ function rcube_webmail() }; // add row to contacts list - this.add_contact_row = function(cid, cols, classes) + this.add_contact_row = function(cid, cols, classes, data) { if (!this.gui_objects.contactslist) return false; @@ -4333,6 +4341,8 @@ function rcube_webmail() row.appendChild(col); } + // store data in list member + list.data[cid] = data; list.insert_row(row); this.enable_command('export', list.rowcount > 0); diff --git a/program/lib/Roundcube/rcube_ldap.php b/program/lib/Roundcube/rcube_ldap.php index a2dd163e9..80b85d45b 100644 --- a/program/lib/Roundcube/rcube_ldap.php +++ b/program/lib/Roundcube/rcube_ldap.php @@ -657,6 +657,21 @@ class rcube_ldap extends rcube_addressbook if (empty($entry[$attr])) return $group_members; + // add group record to cache if it isn't yet there + $group_id = self::dn_encode($dn); + $group_cache = $this->cache->get('groups'); + if (!$group_cache[$group_id]) { + $name_attr = $this->prop['groups']['name_attr']; + $group_name = is_array($ldap_data[$i][$name_attr]) ? $ldap_data[$i][$name_attr][0] : $ldap_data[$i][$name_attr]; + + $group_cache[$group_id]['ID'] = $group_id; + $group_cache[$group_id]['dn'] = $dn; + $group_cache[$group_id]['name'] = $group_name; + $group_cache[$group_id]['member_attr'] = $this->get_group_member_attr($entry[$i]['objectclass']); + + $this->cache->set('groups', $group_cache); + } + // read these attributes for all members $attrib = $count ? array('dn') : array_values($this->fieldmap); $attrib[] = 'objectClass'; @@ -664,12 +679,14 @@ class rcube_ldap extends rcube_addressbook $attrib[] = 'uniqueMember'; $attrib[] = 'memberURL'; + $filter = $this->prop['groups']['member_filter'] ? $this->prop['groups']['member_filter'] : '(objectclass=*)'; + for ($i=0; $i < $entry[$attr]['count']; $i++) { if (empty($entry[$attr][$i])) continue; - $result = @ldap_read($this->conn, $entry[$attr][$i], '(objectclass=*)', + $result = @ldap_read($this->conn, $entry[$attr][$i], $filter, $attrib, 0, (int)$this->prop['sizelimit'], (int)$this->prop['timelimit']); $members = @ldap_get_entries($this->conn, $result); @@ -1522,11 +1539,20 @@ class rcube_ldap extends rcube_addressbook */ private function _ldap2result($rec) { - $out = array(); + $out = array('_type' => 'person'); if ($rec['dn']) $out[$this->primary_key] = self::dn_encode($rec['dn']); + // determine record type + if (array_intersect(array('groupofuniquenames','kolabgroupofuniquenames'), (array)$rec['objectclass'])) { + $out['_type'] = 'group'; + $out['readonly'] = true; + if ($this->fieldmap['groupname']) { + $this->fieldmap['name'] = $this->fieldmap['groupname']; + } + } + foreach ($this->fieldmap as $rf => $lf) { for ($i=0; $i < $rec[$lf]['count']; $i++) { @@ -1780,7 +1806,7 @@ class rcube_ldap extends rcube_addressbook for ($i=0; $i < $group_count; $i++) { $group_name = is_array($ldap_data[$i][$name_attr]) ? $ldap_data[$i][$name_attr][0] : $ldap_data[$i][$name_attr]; - $group_id = self::dn_encode($group_name); + $group_id = self::dn_encode($ldap_data[$i]['dn']); $groups[$group_id]['ID'] = $group_id; $groups[$group_id]['dn'] = $ldap_data[$i]['dn']; $groups[$group_id]['name'] = $group_name; diff --git a/program/localization/en_US/labels.inc b/program/localization/en_US/labels.inc index a0b6e6a31..2fce96ca1 100644 --- a/program/localization/en_US/labels.inc +++ b/program/localization/en_US/labels.inc @@ -350,6 +350,7 @@ $labels['lastpage'] = 'Show last page'; $labels['group'] = 'Group'; $labels['groups'] = 'Groups'; +$labels['listgroup'] = 'List group members'; $labels['personaladrbook'] = 'Personal Addresses'; $labels['searchsave'] = 'Save search'; diff --git a/program/steps/addressbook/func.inc b/program/steps/addressbook/func.inc index 80631cd61..f921c8cda 100644 --- a/program/steps/addressbook/func.inc +++ b/program/steps/addressbook/func.inc @@ -310,7 +310,7 @@ function rcmail_contacts_list($attrib) global $CONTACTS, $OUTPUT; // define list of cols to be displayed - $a_show_cols = array('name'); + $a_show_cols = array('name','action'); // add id to message list table if not specified if (!strlen($attrib['id'])) @@ -339,27 +339,51 @@ function rcmail_js_contacts_list($result, $prefix='') return; // define list of cols to be displayed - $a_show_cols = array('name'); + $a_show_cols = array('name','action'); while ($row = $result->next()) { + $row['CID'] = $row['ID']; + $source_id = $OUTPUT->get_env('source'); $a_row_cols = array(); - $classes = array('person'); // org records will follow some day + $classes = array($row['_type'] ? $row['_type'] : 'person'); // build contact ID with source ID if (isset($row['sourceid'])) { $row['ID'] = $row['ID'].'-'.$row['sourceid']; + $source_id = $row['sourceid']; } // format each col foreach ($a_show_cols as $col) { - $val = $col == 'name' ? rcube_addressbook::compose_list_name($row) : $row[$col]; - $a_row_cols[$col] = Q($val); + $val = ''; + switch ($col) { + case 'name': + $val = Q(rcube_addressbook::compose_list_name($row)); + break; + + case 'action': + if ($row['_type'] == 'group') { + $val = html::a(array( + 'href' => '#list', + 'rel' => $row['ID'], + 'title' => rcube_label('listgroup'), + 'onclick' => sprintf("return %s.command('pushgroup',{'source':'%s','id':'%s'},this)", JS_OBJECT_NAME, $source_id, $row['CID']), + ), '»'); + } + break; + + default: + $val = Q($row[$col]); + break; + } + + $a_row_cols[$col] = $val; } if ($row['readonly']) $classes[] = 'readonly'; - $OUTPUT->command($prefix.'add_contact_row', $row['ID'], $a_row_cols, join(' ', $classes)); + $OUTPUT->command($prefix.'add_contact_row', $row['ID'], $a_row_cols, join(' ', $classes), $row); } } diff --git a/skins/larry/addressbook.css b/skins/larry/addressbook.css index ff3951497..bd2b05b26 100644 --- a/skins/larry/addressbook.css +++ b/skins/larry/addressbook.css @@ -141,6 +141,17 @@ font-weight: bold; } +#contacts-table .group td.name { + background-position: 6px -1555px; +} + +#contacts-table .group.selected td.name, +#contacts-table .group.unfocused td.name { + background-position: 6px -1579px; + font-weight: bold; +} + + #contact-frame { position: absolute; top: 0;