diff --git a/plugins/kolab_folders/kolab_folders.php b/plugins/kolab_folders/kolab_folders.php index 1ca29803..126ec7f9 100644 --- a/plugins/kolab_folders/kolab_folders.php +++ b/plugins/kolab_folders/kolab_folders.php @@ -72,6 +72,11 @@ class kolab_folders extends rcube_plugin // ACL plugin hooks $this->add_hook('acl_rights_simple', array($this, 'acl_rights_simple')); $this->add_hook('acl_rights_supported', array($this, 'acl_rights_supported')); + + // Resolving other user folder names + $this->add_hook('render_mailboxlist', array($this, 'render_folderlist')); + $this->add_hook('render_folder_selector', array($this, 'render_folderlist')); + $this->add_hook('folders_list', array($this, 'render_folderlist')); } /** @@ -709,6 +714,7 @@ class kolab_folders extends rcube_plugin * Static getter for default folder of the given type * * @param string $type Folder type + * * @return string Folder name */ public static function default_folder($type) @@ -781,4 +787,74 @@ class kolab_folders extends rcube_plugin ); } } + + /** + * Handler for various folders list widgets (hooks) + * + * @param array $args Hash array with hook parameters + * + * @return array Hash array with modified hook parameters + */ + public function render_folderlist($args) + { + $storage = $this->rc->get_storage(); + $ns_other = $storage->get_namespace('other'); + $is_fl = $this->rc->plugins->is_processing('folders_list'); + + foreach ((array) $ns_other as $root) { + $delim = $root[1]; + $prefix = rtrim($root[0], $delim); + $length = strlen($prefix); + + if (!$length) { + continue; + } + + // folders_list hook mode + if ($is_fl) { + foreach ((array) $args['list'] as $folder_name => $folder) { + if (strpos($folder_name, $root[0]) === 0 && !substr_count($folder_name, $root[1], $length+1)) { + if ($name = $this->folder_id2username(substr($folder_name, $length+1))) { + $old = $args['list'][$folder_name]['display']; + $content = $args['list'][$folder_name]['content']; + + $name = rcube::Q($name); + $content = str_replace(">$old<", ">$name<", $content); + + $args['list'][$folder_name]['display'] = $name; + $args['list'][$folder_name]['content'] = $content; + } + } + } + + // TODO: Re-sort the list + } + // render_* hooks mode + else if (!empty($args['list'][$prefix]) && !empty($args['list'][$prefix]['folders'])) { + $map = array(); + foreach ($args['list'][$prefix]['folders'] as $folder_name => $folder) { + if ($name = $this->folder_id2username($folder_name)) { + $args['list'][$prefix]['folders'][$folder_name]['name'] = $name; + } + + $map[$folder_name] = $name ?: $args['list'][$prefix]['folders'][$folder_name]['name']; + } + + // Re-sort the list + uasort($map, 'strcoll'); + $args['list'][$prefix]['folders'] = array_replace($map, $args['list'][$prefix]['folders']); + } + } + + return $args; + } + + /** + * Map other user (imap) folder identifier to user name + * @see kolab_storage::folder_id2user() + */ + private static function folder_id2username($uid) + { + return kolab_storage::folder_id2user($uid, true); + } } diff --git a/plugins/libkolab/config.inc.php.dist b/plugins/libkolab/config.inc.php.dist index a2c15e8c..a4a7a7a6 100644 --- a/plugins/libkolab/config.inc.php.dist +++ b/plugins/libkolab/config.inc.php.dist @@ -64,6 +64,18 @@ $config['kolab_users_id_attrib'] = null; // Use these attributes when searching users in LDAP $config['kolab_users_search_attrib'] = array('cn','mail','alias'); +// Which property of the LDAP user record to use as a display name. +// Defaults to the 'kolab_auth_name' configuration option. +$config['kolab_users_name_field'] = null; + +// Type of cache for uid-to-user map. Supported: 'db', 'apc', 'memcache' and 'memcached'. +// Note: This stores only other user folder identifier to user attributes map. +$config['kolab_users_cache'] = null; + +// lifetime of shared folder mapping cache +// possible units: s, m, h, d, w +$config['kolab_users_cache_ttl'] = '10d'; + // JSON-RPC endpoint configuration of the Bonnie web service providing historic data for groupware objects $config['kolab_bonnie_api'] = array( 'uri' => 'https://:8080/api/rpc', diff --git a/plugins/libkolab/lib/kolab_storage.php b/plugins/libkolab/lib/kolab_storage.php index 39d6ff11..61a2fa74 100644 --- a/plugins/libkolab/lib/kolab_storage.php +++ b/plugins/libkolab/lib/kolab_storage.php @@ -46,12 +46,14 @@ class kolab_storage private static $ready = false; private static $with_tempsubs = true; private static $subscriptions; + private static $ldapcache = array(); private static $typedata = array(); private static $states; private static $config; private static $imap; private static $ldap; + // Default folder names private static $default_folders = array( 'event' => 'Calendar', @@ -354,7 +356,6 @@ class kolab_storage return ''; } - /** * Deletes IMAP folder * @@ -421,7 +422,6 @@ class kolab_storage return false; } - /** * Renames IMAP folder * @@ -457,7 +457,6 @@ class kolab_storage return $success; } - /** * Rename or Create a new IMAP folder. * @@ -552,7 +551,6 @@ class kolab_storage return $result ? $folder : false; } - /** * Getter for human-readable name of Kolab object (folder) * with kolab_custom_display_names support. @@ -637,20 +635,18 @@ class kolab_storage if (!$found && !empty($namespace['other'])) { foreach ($namespace['other'] as $ns) { if (strlen($ns[0]) && strpos($folder, $ns[0]) === 0) { - // remove namespace prefix + // remove namespace prefix and extract username $folder = substr($folder, strlen($ns[0])); $delim = $ns[1]; - // get username - $pos = strpos($folder, $delim); - if ($pos) { - $prefix = '('.substr($folder, 0, $pos).')'; - $folder = substr($folder, $pos+1); - } - else { - $prefix = '('.$folder.')'; - $folder = ''; - } + // get username part and map it to user name + $pos = strpos($folder, $delim); + $fid = $pos ? substr($folder, 0, $pos) : $folder; + $fid = self::folder_id2user($fid, true); + $fid = str_replace($delim, '', $fid); + + $prefix = "($fid)"; + $folder = $pos ? substr($folder, $pos + 1) : ''; $found = true; $folder_ns = 'other'; break; @@ -729,7 +725,6 @@ class kolab_storage return $name; } - /** * Creates a SELECT field with folders list * @@ -812,7 +807,6 @@ class kolab_storage return $select; } - /** * Returns a list of folder names * @@ -976,7 +970,6 @@ class kolab_storage return $folders; } - /** * Sort the given list of kolab folders by namespace/name * @@ -1006,7 +999,6 @@ class kolab_storage return $out; } - /** * Check the folder tree and add the missing parents as virtual folders * @@ -1082,7 +1074,6 @@ class kolab_storage return $_folders; } - /** * Returns folder types indexed by folder name * @@ -1149,7 +1140,6 @@ class kolab_storage return self::$typedata[$prefix]; } - /** * Callback for array_map to select the correct annotation value */ @@ -1165,7 +1155,6 @@ class kolab_storage return null; } - /** * Returns type of IMAP folder * @@ -1197,7 +1186,6 @@ class kolab_storage return 'mail'; } - /** * Sets folder content-type. * @@ -1220,7 +1208,6 @@ class kolab_storage return $success; } - /** * Check subscription status of this folder * @@ -1242,7 +1229,6 @@ class kolab_storage ($temp && in_array($folder, (array)$_SESSION['kolab_subscribed_folders'])); } - /** * Change subscription status of this folder * @@ -1273,7 +1259,6 @@ class kolab_storage return false; } - /** * Change subscription status of this folder * @@ -1301,7 +1286,6 @@ class kolab_storage return false; } - /** * Check activation status of this folder * @@ -1316,7 +1300,6 @@ class kolab_storage return in_array($folder, $active_folders); } - /** * Change activation status of this folder * @@ -1331,7 +1314,6 @@ class kolab_storage return self::set_state($folder, true); } - /** * Change activation status of this folder * @@ -1347,7 +1329,6 @@ class kolab_storage return self::set_state($folder, false); } - /** * Return list of active folders */ @@ -1379,7 +1360,6 @@ class kolab_storage return self::$states; } - /** * Update list of active folders */ @@ -1504,7 +1484,6 @@ class kolab_storage } } - /** * * @param mixed $query Search value (or array of field => value pairs) @@ -1544,7 +1523,6 @@ class kolab_storage return $results; } - /** * Returns a list of IMAP folders shared by the given user * @@ -1584,7 +1562,6 @@ class kolab_storage return $folders; } - /** * Get a list of (virtual) top-level folders from the other users namespace * @@ -1635,7 +1612,6 @@ class kolab_storage return $folders; } - /** * Handler for user_delete plugin hooks * @@ -1673,4 +1649,79 @@ class kolab_storage return $metadata[$folder]; } } + + /** + * Get user attributes for specified other user (imap) folder identifier. + * Note: This uses LDAP config/code from kolab_auth. + * + * @param string $folder_id Folder name w/o path (imap user identifier) + * @param bool $as_string Return configured display name attribute value + * + * @return array User attributes + * @see self::ldap() + */ + public static function folder_id2user($folder_id, $as_string = false) + { + static $domain, $cache, $name_attr; + + $rcube = rcube::get_instance(); + + if ($domain === null) { + list(, $domain) = explode('@', $rcube->get_user_name()); + } + + if ($name_attr === null) { + $name_attr = (array) (self::$config->get('kolab_users_name_field', self::$config->get('kolab_auth_name')) ?: 'name'); + } + + $token = $folder_id; + if ($domain && strpos($find, '@') === false) { + $token .= '@' . $domain; + } + + if ($cache === null) { + $cache = $rcube->get_cache_shared('kolab_users') ?: false; + } + + // use value cached in memory for repeated lookups + if (!$cache && array_key_exists($token, self::$ldapcache)) { + $user = self::$ldapcache[$token]; + } + + if (empty($user) && $cache) { + $user = $cache->get($token); + } + + if (empty($user) && ($ldap = kolab_storage::ldap())) { + $user = $ldap->get_user_record($token, $_SESSION['imap_host']); + + if (!empty($user)) { + $keys = array('displayname', 'name', 'mail'); // supported keys + $user = array_intersect_key($user, array_flip($keys)); + + if (!empty($user)) { + if ($cache) { + $cache->set($token, $user); + } + else { + self::$ldapcache[$token] = $user; + } + } + } + } + + if (!empty($user)) { + if ($as_string) { + foreach ($name_attr as $attr) { + if ($name = $user[$attr]) { + return $name; + } + } + + return $user['displayname'] ?: $user['name']; + } + + return $user; + } + } } diff --git a/plugins/libkolab/lib/kolab_storage_folder_user.php b/plugins/libkolab/lib/kolab_storage_folder_user.php index ec751fdb..707ce817 100644 --- a/plugins/libkolab/lib/kolab_storage_folder_user.php +++ b/plugins/libkolab/lib/kolab_storage_folder_user.php @@ -23,8 +23,6 @@ */ class kolab_storage_folder_user extends kolab_storage_folder_virtual { - protected static $ldapcache = array(); - public $ldaprec; public $type; @@ -36,21 +34,13 @@ class kolab_storage_folder_user extends kolab_storage_folder_virtual parent::__construct($name, kolab_storage::object_prettyname($name), 'other', $parent); if (!empty($ldaprec)) { - self::$ldapcache[$name] = $this->ldaprec = $ldaprec; + $this->ldaprec = $ldaprec; } - // use value cached in memory for repeated lookups - else if (array_key_exists($name, self::$ldapcache)) { - $this->ldaprec = self::$ldapcache[$name]; - } - // lookup user in LDAP and set $this->ldaprec - else if ($ldap = kolab_storage::ldap()) { - // get domain from current user - list(,$domain) = explode('@', rcube::get_instance()->get_user_name()); - $this->ldaprec = $ldap->get_user_record(parent::get_foldername($this->name) . '@' . $domain, $_SESSION['imap_host']); + else { + $this->ldaprec = kolab_storage::folder_id2user(parent::get_foldername($this->name)); if (!empty($this->ldaprec)) { $this->ldaprec['kolabtargetfolder'] = $name; } - self::$ldapcache[$name] = $this->ldaprec; } }