summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Bruederli <bruederli@kolabsys.com>2013-10-10 15:27:24 (GMT)
committerThomas Bruederli <bruederli@kolabsys.com>2013-10-15 08:32:29 (GMT)
commiteef0a950680aa1a498134ee165e203a1a88eb1cc (patch)
treebc7bb7021125f236259cc3faf9a1cece9480b702
parentc5b3ba8770ec519f13d3fe4bf7873082be368cc9 (diff)
downloadroundcubemail-plugins-kolab-eef0a950680aa1a498134ee165e203a1a88eb1cc.tar.gz
Show complete folder hierarchy in calendars and tasklist listings with non-clickable virtual parent folders
-rw-r--r--plugins/calendar/drivers/kolab/kolab_calendar.php3
-rw-r--r--plugins/calendar/drivers/kolab/kolab_driver.php119
-rw-r--r--plugins/calendar/lib/calendar_ui.php15
-rw-r--r--plugins/calendar/skins/classic/calendar.css4
-rw-r--r--plugins/calendar/skins/larry/calendar.css8
-rw-r--r--plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php83
-rw-r--r--plugins/tasklist/skins/larry/tasklist.css9
-rw-r--r--plugins/tasklist/tasklist.js4
-rw-r--r--plugins/tasklist/tasklist_ui.php13
9 files changed, 219 insertions, 39 deletions
diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php
index 877c3f5..d238e90 100644
--- a/plugins/calendar/drivers/kolab/kolab_calendar.php
+++ b/plugins/calendar/drivers/kolab/kolab_calendar.php
@@ -33,6 +33,7 @@ class kolab_calendar
public $alarms = false;
public $categories = array();
public $storage;
+ public $name;
private $cal;
private $events = array();
@@ -48,7 +49,7 @@ class kolab_calendar
$this->cal = $calendar;
if (strlen($imap_folder))
- $this->imap_folder = $imap_folder;
+ $this->imap_folder = $this->name = $imap_folder;
// ID is derrived from folder name
$this->id = kolab_storage::folder_id($this->imap_folder);
diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php
index 05fd0e4..3df498d 100644
--- a/plugins/calendar/drivers/kolab/kolab_driver.php
+++ b/plugins/calendar/drivers/kolab/kolab_driver.php
@@ -105,31 +105,79 @@ class kolab_driver extends calendar_driver
}
}
- $calendars = $this->filter_calendars(false, $active, $personal);
- $names = array();
-
- foreach ($calendars as $id => $cal) {
- $name = kolab_storage::folder_displayname($cal->get_name(), $names);
-
- $calendars[$id] = array(
- 'id' => $cal->id,
- 'name' => $name,
- 'editname' => $cal->get_foldername(),
- 'color' => $cal->get_color(),
- 'readonly' => $cal->readonly,
- 'showalarms' => $cal->alarms,
- 'class_name' => $cal->get_namespace(),
- 'default' => $cal->storage->default,
- 'active' => $cal->storage->is_active(),
- 'owner' => $cal->get_owner(),
- 'children' => true, // TODO: determine if that folder indeed has child folders
- 'caldavurl' => $cal->get_caldav_url(),
- );
+ $folders = $this->filter_calendars(false, $active, $personal);
+ $calendars = $names = array();
+
+ // include virtual folders for a full folder tree
+ if (!$active && !$personal && !$this->rc->output->ajax_call)
+ $folders = $this->_folder_hierarchy($folders, $this->rc->get_storage()->get_hierarchy_delimiter());
+
+ foreach ($folders as $id => $cal) {
+ $fullname = $cal->get_name();
+ $name = kolab_storage::folder_displayname($fullname, $names);
+
+ // special handling for virtual folders
+ if ($cal->virtual) {
+ $calendars[$cal->id] = array(
+ 'id' => $cal->id,
+ 'name' => $name,
+ 'virtual' => true,
+ );
+ }
+ else {
+ $calendars[$cal->id] = array(
+ 'id' => $cal->id,
+ 'name' => $name,
+ 'altname' => $fullname,
+ 'editname' => $cal->get_foldername(),
+ 'color' => $cal->get_color(),
+ 'readonly' => $cal->readonly,
+ 'showalarms' => $cal->alarms,
+ 'class_name' => $cal->get_namespace(),
+ 'default' => $cal->storage->default,
+ 'active' => $cal->storage->is_active(),
+ 'owner' => $cal->get_owner(),
+ 'children' => true, // TODO: determine if that folder indeed has child folders
+ 'caldavurl' => $cal->get_caldav_url(),
+ );
+ }
}
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
@@ -1041,7 +1089,7 @@ class kolab_driver extends calendar_driver
// Disable folder name input
if (!empty($options) && ($options['norename'] || $options['protected'])) {
$input_name = new html_hiddenfield(array('name' => 'name', 'id' => 'calendar-name'));
- $formfields['name']['value'] = Q(str_replace($delim, ' &raquo; ', kolab_storage::object_name($folder)))
+ $formfields['name']['value'] = kolab_storage::object_name($folder)
. $input_name->show($folder);
}
@@ -1229,3 +1277,32 @@ 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/calendar/lib/calendar_ui.php b/plugins/calendar/lib/calendar_ui.php
index 23a335a..9ea93ef 100644
--- a/plugins/calendar/lib/calendar_ui.php
+++ b/plugins/calendar/lib/calendar_ui.php
@@ -194,21 +194,24 @@ class calendar_ui
$prop['attachments'] = $this->cal->driver->attachments;
$prop['undelete'] = $this->cal->driver->undelete;
$prop['feedurl'] = $this->cal->get_url(array('_cal' => $this->cal->ical_feed_hash($id) . '.ics', 'action' => 'feed'));
- $jsenv[$id] = $prop;
+
+ if (!$prop['virtual'])
+ $jsenv[$id] = $prop;
$html_id = html_identifier($id);
$class = 'cal-' . asciiwords($id, true);
- $listname = html_entity_decode($prop['name'], ENT_COMPAT, RCMAIL_CHARSET);
- $title = strlen($listname) > 25 ? $listname : '';
+ $title = !empty($prop['altname']) && $prop['altname'] != $prop['name'] ? html_entity_decode($prop['altname'], ENT_COMPAT, RCMAIL_CHARSET) : '';
- if ($prop['readonly'])
+ if ($prop['virtual'])
+ $class .= ' virtual';
+ else if ($prop['readonly'])
$class .= ' readonly';
if ($prop['class_name'])
$class .= ' '.$prop['class_name'];
$li .= html::tag('li', array('id' => 'rcmlical' . $html_id, 'class' => $class),
- html::tag('input', array('type' => 'checkbox', 'name' => '_cal[]', 'value' => $id, 'checked' => $prop['active']), '') .
- html::span('handle', '&nbsp;') .
+ ($prop['virtual'] ? '' : html::tag('input', array('type' => 'checkbox', 'name' => '_cal[]', 'value' => $id, 'checked' => $prop['active']), '') .
+ html::span('handle', '&nbsp;')) .
html::span(array('class' => 'calname', 'title' => $title), $prop['name']));
}
diff --git a/plugins/calendar/skins/classic/calendar.css b/plugins/calendar/skins/classic/calendar.css
index 835bdac..c646b0d 100644
--- a/plugins/calendar/skins/classic/calendar.css
+++ b/plugins/calendar/skins/classic/calendar.css
@@ -164,6 +164,10 @@ pre {
background-position: 0 -92px;
}
+#calendarslist li.virtual span.calname {
+ color: #666;
+}
+
#calfeedurl,
#caldavurl {
width: 98%;
diff --git a/plugins/calendar/skins/larry/calendar.css b/plugins/calendar/skins/larry/calendar.css
index 8775b2a..48160ad 100644
--- a/plugins/calendar/skins/larry/calendar.css
+++ b/plugins/calendar/skins/larry/calendar.css
@@ -157,6 +157,10 @@ pre {
position: relative;
}
+#calendarslist li.virtual {
+ padding-top: 2px;
+}
+
#calendarslist li label {
display: block;
}
@@ -225,6 +229,10 @@ pre {
background-position: right -92px;
}
+#calendarslist li.virtual span.calname {
+ color: #aaa;
+}
+
#calfeedurl,
#caldavurl {
width: 98%;
diff --git a/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php b/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php
index 90772ac..3c6690a 100644
--- a/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php
+++ b/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php
@@ -80,21 +80,36 @@ class tasklist_kolab_driver extends tasklist_driver
}
$delim = $this->rc->get_storage()->get_hierarchy_delimiter();
+ $prefs = $this->rc->config->get('kolab_tasklists', array());
$listnames = array();
- $prefs = $this->rc->config->get('kolab_tasklists', array());
+ // include virtual folders for a full folder tree
+ if (!$this->rc->output->ajax_call)
+ $folders = $this->_folder_hierarchy($folders, $delim);
foreach ($folders as $folder) {
$utf7name = $folder->name;
- $this->folders[$folder->name] = $folder;
$path_imap = explode($delim, $utf7name);
$editname = rcube_charset::convert(array_pop($path_imap), 'UTF7-IMAP'); // pop off raw name part
$path_imap = join($delim, $path_imap);
- $name = kolab_storage::folder_displayname(kolab_storage::object_name($utf7name), $listnames);
+ $fullname = kolab_storage::object_name($utf7name);
+ $name = kolab_storage::folder_displayname($fullname, $listnames);
+
+ // special handling for virtual folders
+ if ($folder->virtual) {
+ $list_id = kolab_storage::folder_id($utf7name);
+ $this->lists[$list_id] = array(
+ 'id' => $list_id,
+ 'name' => $name,
+ 'virtual' => true,
+ );
+ continue;
+ }
if ($folder->get_namespace() == 'personal') {
+ $norename = false;
$readonly = false;
$alarms = true;
}
@@ -105,16 +120,20 @@ class tasklist_kolab_driver extends tasklist_driver
if (strpos($rights, 'i') !== false)
$readonly = false;
}
+ $info = $folder->get_folder_info();
+ $norename = $readonly || $info['norename'] || $info['protected'];
}
$list_id = kolab_storage::folder_id($utf7name);
$tasklist = array(
'id' => $list_id,
'name' => $name,
+ 'altname' => $fullname,
'editname' => $editname,
'color' => $folder->get_color('0000CC'),
'showalarms' => isset($prefs[$list_id]['showalarms']) ? $prefs[$list_id]['showalarms'] : $alarms,
- 'editable' => !$readonly,
+ 'editable' => !$readionly,
+ 'norename' => $norename,
'active' => $folder->is_active(),
'parentfolder' => $path_imap,
'default' => $folder->default,
@@ -123,9 +142,42 @@ class tasklist_kolab_driver extends tasklist_driver
);
$this->lists[$tasklist['id']] = $tasklist;
$this->folders[$tasklist['id']] = $folder;
+ $this->folders[$folder->name] = $folder;
+ }
+ }
+
+ /**
+ * 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
*/
@@ -848,3 +900,26 @@ 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;
+ }
+}
+
diff --git a/plugins/tasklist/skins/larry/tasklist.css b/plugins/tasklist/skins/larry/tasklist.css
index 5582bad..173704d 100644
--- a/plugins/tasklist/skins/larry/tasklist.css
+++ b/plugins/tasklist/skins/larry/tasklist.css
@@ -195,6 +195,11 @@ body.attachmentwin #topnav .topright {
white-space: nowrap;
}
+#tasklists li.virtual {
+ padding-top: 4px;
+ height: 16px;
+}
+
#tasklists li label {
display: block;
}
@@ -240,6 +245,10 @@ body.attachmentwin #topnav .topright {
background-position: right -214px;
}
+#tasklists li.virtual span.listname {
+ color: #aaa;
+}
+
#tasklists li input {
position: absolute;
top: 5px;
diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js
index 7d91e07..bfe19d5 100644
--- a/plugins/tasklist/tasklist.js
+++ b/plugins/tasklist/tasklist.js
@@ -1413,7 +1413,7 @@ function rcube_tasklist_ui(settings)
list = { name:'', editable:true, showalarms:true };
// fill edit form
- var name = $('#taskedit-tasklistame').prop('disabled', !list.editable).val(list.editname || list.name),
+ var name = $('#taskedit-tasklistame').prop('disabled', list.norename||false).val(list.editname || list.name),
alarms = $('#taskedit-showalarms').prop('checked', list.showalarms).get(0),
parent = $('#taskedit-parentfolder').val(list.parentfolder);
@@ -1465,7 +1465,7 @@ function rcube_tasklist_ui(settings)
function list_remove(id)
{
var list = me.tasklists[id];
- if (list && list.editable && confirm(rcmail.gettext(list.children ? 'deletelistconfirmrecursive' : 'deletelistconfirm', 'tasklist'))) {
+ if (list && !list.norename && confirm(rcmail.gettext(list.children ? 'deletelistconfirmrecursive' : 'deletelistconfirm', 'tasklist'))) {
saving_lock = rcmail.set_busy(true, 'tasklist.savingdata');
rcmail.http_post('tasklist', { action:'remove', l:{ id:list.id } });
return true;
diff --git a/plugins/tasklist/tasklist_ui.php b/plugins/tasklist/tasklist_ui.php
index 66a7ab0..99d0875 100644
--- a/plugins/tasklist/tasklist_ui.php
+++ b/plugins/tasklist/tasklist_ui.php
@@ -100,20 +100,23 @@ class tasklist_ui
$prop['undelete'] = $this->plugin->driver->undelete;
$prop['sortable'] = $this->plugin->driver->sortable;
$prop['attachments'] = $this->plugin->driver->attachments;
- $jsenv[$id] = $prop;
+
+ if (!$prop['virtual'])
+ $jsenv[$id] = $prop;
$html_id = html_identifier($id);
$class = 'tasks-' . asciiwords($id, true);
- $listname = html_entity_decode($prop['name'], ENT_COMPAT, RCMAIL_CHARSET);
- $title = strlen($listname) > 25 ? $listname : '';
+ $title = !empty($prop['altname']) && $prop['altname'] != $prop['name'] ? html_entity_decode($prop['altname'], ENT_COMPAT, RCMAIL_CHARSET) : '';
- if (!$prop['editable'])
+ if ($prop['virtual'])
+ $class .= ' virtual';
+ else if (!$prop['editable'])
$class .= ' readonly';
if ($prop['class_name'])
$class .= ' '.$prop['class_name'];
$li .= html::tag('li', array('id' => 'rcmlitasklist' . $html_id, 'class' => $class),
- html::tag('input', array('type' => 'checkbox', 'name' => '_list[]', 'value' => $id, 'checked' => $prop['active'])) .
+ ($prop['virtual'] ? '' : html::tag('input', array('type' => 'checkbox', 'name' => '_list[]', 'value' => $id, 'checked' => $prop['active']))) .
html::span('handle', '&nbsp;') .
html::span(array('class' => 'listname', 'title' => $title), $prop['name']));
}