From 43b287c470a9ce67cd10a80b8080cf6b6681dc06 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 21 Nov 2013 10:58:16 +0100 Subject: [PATCH] Fix folders sorting issues (Bug #2210) - move some shared code to kolab_storage - this requires recent Roundcube Framework (f13ae32a) --- .../calendar/drivers/kolab/kolab_driver.php | 64 +-------- plugins/libkolab/lib/kolab_storage.php | 122 ++++++++++++++---- .../drivers/kolab/tasklist_kolab_driver.php | 57 +------- 3 files changed, 101 insertions(+), 142 deletions(-) diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php index d68d1772..f673f6c3 100644 --- a/plugins/calendar/drivers/kolab/kolab_driver.php +++ b/plugins/calendar/drivers/kolab/kolab_driver.php @@ -110,7 +110,7 @@ class kolab_driver extends calendar_driver // include virtual folders for a full folder tree if (!$active && !$personal && !$this->rc->output->ajax_call && in_array($this->rc->action, array('index',''))) - $folders = $this->_folder_hierarchy($folders, $this->rc->get_storage()->get_hierarchy_delimiter()); + $folders = kolab_storage::folder_hierarchy($folders); foreach ($folders as $id => $cal) { $fullname = $cal->get_name(); @@ -148,39 +148,6 @@ class kolab_driver extends calendar_driver return $calendars; } - /** - * Check the folder tree and add the missing parents as virtual folders - */ - private function _folder_hierarchy($folders, $delim) - { - $parents = array(); - $existing = array_map(function($folder){ return $folder->get_name(); }, $folders); - foreach ($folders as $id => $folder) { - $path = explode($delim, $folder->name); - array_pop($path); - - // skip top folders or ones with a custom displayname - if (count($path) <= 1 || kolab_storage::custom_displayname($folder->name)) - continue; - - while (count($path) > 1 && ($parent = join($delim, $path))) { - if (!in_array($parent, $existing) && !$parents[$parent]) { - $name = kolab_storage::object_name($parent, $folder->get_namespace()); - $parents[$parent] = new virtual_kolab_calendar($name, $folder->get_namespace()); - $parents[$parent]->id = kolab_storage::folder_id($parent); - } - array_pop($path); - } - } - - // add virtual parents to the list and sort again - if (count($parents)) { - $folders = kolab_storage::sort_folders(array_merge($folders, array_values($parents))); - } - - return $folders; - } - /** * Get list of calendars according to specified filters * @@ -1281,32 +1248,3 @@ class kolab_driver extends calendar_driver } } - - -/** - * Helper class that represents a virtual IMAP folder - * with a subset of the kolab_calendar API. - */ -class virtual_kolab_calendar -{ - public $name; - public $namespace; - public $virtual = true; - - public function __construct($name, $ns) - { - $this->name = $name; - $this->namespace = $ns; - } - - public function get_name() - { - return $this->name; - } - - public function get_namespace() - { - return $this->namespace; - } -} - diff --git a/plugins/libkolab/lib/kolab_storage.php b/plugins/libkolab/lib/kolab_storage.php index 5f8b9c65..a95a59e9 100644 --- a/plugins/libkolab/lib/kolab_storage.php +++ b/plugins/libkolab/lib/kolab_storage.php @@ -433,7 +433,7 @@ class kolab_storage // get username $pos = strpos($folder, $delim); if ($pos) { - $prefix = '('.substr($folder, 0, $pos).') '; + $prefix = '('.substr($folder, 0, $pos).')'; $folder = substr($folder, $pos+1); } else { @@ -525,8 +525,8 @@ class kolab_storage */ public static function folder_selector($type, $attrs, $current = '') { - // get all folders of specified type - $folders = self::get_folders($type, false); + // get all folders of specified type (sorted) + $folders = self::get_folders($type, true); $delim = self::$imap->get_hierarchy_delimiter(); $names = array(); @@ -540,13 +540,15 @@ class kolab_storage // Filter folders list foreach ($folders as $c_folder) { $name = $c_folder->name; + // skip current folder and it's subfolders if ($len && ($name == $current || strpos($name, $current.$delim) === 0)) { continue; } // always show the parent of current folder - if ($p_len && $name == $parent) { } + if ($p_len && $name == $parent) { + } // skip folders where user have no rights to create subfolders else if ($c_folder->get_owner() != $_SESSION['username']) { $rights = $c_folder->get_myrights(); @@ -555,17 +557,14 @@ class kolab_storage } } + // Make sure parent folder is listed (might be skipped e.g. if it's namespace root) + if ($p_len && !isset($names[$parent]) && strpos($name, $parent.$delim) === 0) { + $names[$parent] = self::object_name($parent); + } + $names[$name] = self::object_name($name); } - // Make sure parent folder is listed (might be skipped e.g. if it's namespace root) - if ($p_len && !isset($names[$parent])) { - $names[$parent] = self::object_name($parent); - } - - // Sort folders list - asort($names, SORT_LOCALE_STRING); - // Build SELECT field of parent folder $attrs['is_escaped'] = true; $select = new html_select($attrs); @@ -643,7 +642,8 @@ class kolab_storage unset($folderdata[$folder]); } } - return array_keys($folderdata); + + return self::$imap->sort_folder_list(array_keys($folderdata), true); } // Get folders list @@ -683,29 +683,78 @@ class kolab_storage */ public static function sort_folders($folders) { - $pad = ' '; + $pad = ' '; + $out = array(); $nsnames = array('personal' => array(), 'shared' => array(), 'other' => array()); + foreach ($folders as $folder) { $folders[$folder->name] = $folder; $ns = $folder->get_namespace(); $nsnames[$ns][$folder->name] = strtolower(html_entity_decode(self::object_name($folder->name, $ns), ENT_COMPAT, RCUBE_CHARSET)) . $pad; // decode » } - $names = array(); - foreach ($nsnames as $ns => $dummy) { - asort($nsnames[$ns], SORT_LOCALE_STRING); - $names += $nsnames[$ns]; - } - - $out = array(); - foreach ($names as $utf7name => $name) { - $out[] = $folders[$utf7name]; + // $folders is a result of get_folders() we can assume folders were already sorted + foreach (array_keys($nsnames) as $ns) { + // asort($nsnames[$ns], SORT_LOCALE_STRING); + foreach (array_keys($nsnames[$ns]) as $utf7name) { + $out[] = $folders[$utf7name]; + } } return $out; } + /** + * Check the folder tree and add the missing parents as virtual folders + * + * @param array $folders Folders list + * + * @return array Folders list + */ + public static function folder_hierarchy($folders) + { + $_folders = array(); + $existing = array_map(function($folder){ return $folder->get_name(); }, $folders); + $delim = rcube::get_instance()->get_storage()->get_hierarchy_delimiter(); + + foreach ($folders as $idx => $folder) { + $path = explode($delim, $folder->name); + array_pop($path); + + // skip top folders or ones with a custom displayname + if (count($path) <= 1 || kolab_storage::custom_displayname($folder->name)) { + } + else { + $parents = array(); + + while (count($path) > 1 && ($parent = join($delim, $path))) { + $name = kolab_storage::object_name($parent, $folder->get_namespace()); + if (!in_array($name, $existing)) { + $parents[$parent] = new virtual_kolab_storage_folder($name, $folder->get_namespace()); + $parents[$parent]->id = kolab_storage::folder_id($parent); + $existing[] = $name; + } + + array_pop($path); + } + + if (!empty($parents)) { + $parents = array_reverse(array_values($parents)); + foreach ($parents as $parent) { + $_folders[] = $parent; + } + } + } + + $_folders[] = $folder; + unset($folders[$idx]); + } + + return $_folders; + } + + /** * Returns folder types indexed by folder name * @@ -1047,3 +1096,30 @@ class kolab_storage } } + +/** + * Helper class that represents a virtual IMAP folder + * with a subset of the kolab_storage_folder API. + */ +class virtual_kolab_storage_folder +{ + public $name; + public $namespace; + public $virtual = true; + + public function __construct($name, $ns) + { + $this->name = $name; + $this->namespace = $ns; + } + + public function get_namespace() + { + return $this->namespace; + } + + public function get_name() + { + return $this->name; + } +} diff --git a/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php b/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php index c92ca553..625ca389 100644 --- a/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php +++ b/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php @@ -85,7 +85,7 @@ class tasklist_kolab_driver extends tasklist_driver // include virtual folders for a full folder tree if (!$this->rc->output->ajax_call && in_array($this->rc->action, array('index',''))) - $folders = $this->_folder_hierarchy($folders, $delim); + $folders = kolab_storage::folder_hierarchy($folders); foreach ($folders as $folder) { $utf7name = $folder->name; @@ -148,38 +148,6 @@ class tasklist_kolab_driver extends tasklist_driver } } - /** - * Check the folder tree and add the missing parents as virtual folders - */ - private function _folder_hierarchy($folders, $delim) - { - $parents = array(); - $existing = array_map(function($folder){ return $folder->name; }, $folders); - foreach ($folders as $id => $folder) { - $path = explode($delim, $folder->name); - array_pop($path); - - // skip top folders or ones with a custom displayname - if (count($path) <= 1 || kolab_storage::custom_displayname($folder->name)) - continue; - - while (count($path) > 1 && ($parent = join($delim, $path))) { - if (!in_array($parent, $existing) && !$parents[$parent]) { - $parents[$parent] = new virtual_kolab_storage_folder($parent, $folder->get_namespace()); - } - array_pop($path); - } - } - - // add virtual parents to the list and sort again - if (count($parents)) { - $folders = kolab_storage::sort_folders(array_merge($folders, array_values($parents))); - } - - return $folders; - } - - /** * Get a list of available task lists from this source */ @@ -906,26 +874,3 @@ class tasklist_kolab_driver extends tasklist_driver } } - -/** - * Helper class that represents a virtual IMAP folder - * with a subset of the kolab_storage_folder API. - */ -class virtual_kolab_storage_folder -{ - public $name; - public $namespace; - public $virtual = true; - - public function __construct($name, $ns) - { - $this->name = $name; - $this->namespace = $ns; - } - - public function get_namespace() - { - return $this->namespace; - } -} -