Branch 'roundcubemail-plugins-kolab-3.1' - 6 commits - plugins/calendar plugins/kolab_addressbook plugins/libkolab plugins/tasklist

Thomas Brüderli bruederli at kolabsys.com
Tue Oct 15 10:32:44 CEST 2013


 plugins/calendar/calendar_ui.js                          |    8 +
 plugins/calendar/config.inc.php.dist                     |    7 
 plugins/calendar/drivers/kolab/kolab_calendar.php        |   21 ++
 plugins/calendar/drivers/kolab/kolab_driver.php          |  118 ++++++++++++---
 plugins/calendar/lib/calendar_ui.php                     |   15 +
 plugins/calendar/localization/de_CH.inc                  |    1 
 plugins/calendar/localization/de_DE.inc                  |    1 
 plugins/calendar/localization/en_US.inc                  |    1 
 plugins/calendar/skins/classic/calendar.css              |    7 
 plugins/calendar/skins/classic/templates/calendar.html   |    4 
 plugins/calendar/skins/larry/calendar.css                |   11 +
 plugins/calendar/skins/larry/templates/calendar.html     |    4 
 plugins/kolab_addressbook/config.inc.php.dist            |    8 +
 plugins/kolab_addressbook/kolab_addressbook.js           |   24 +++
 plugins/kolab_addressbook/kolab_addressbook.php          |    1 
 plugins/kolab_addressbook/lib/kolab_addressbook_ui.php   |    7 
 plugins/kolab_addressbook/lib/rcube_kolab_contacts.php   |   18 ++
 plugins/kolab_addressbook/localization/de_CH.inc         |    2 
 plugins/kolab_addressbook/localization/de_DE.inc         |    2 
 plugins/kolab_addressbook/localization/en_US.inc         |    2 
 plugins/libkolab/lib/kolab_storage.php                   |   31 ++-
 plugins/libkolab/lib/kolab_storage_folder.php            |    2 
 plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php |   83 ++++++++++
 plugins/tasklist/skins/larry/tasklist.css                |    9 +
 plugins/tasklist/tasklist.js                             |    4 
 plugins/tasklist/tasklist_ui.php                         |   13 +
 26 files changed, 353 insertions(+), 51 deletions(-)

New commits:
commit 79b6799a58b6b8945ab1e1de0a0a509910b3ee4d
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Tue Oct 15 10:31:52 2013 +0200

    Limit virtual folder tree to task/calendar main views

diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php
index 3df498d..cffe6b1 100644
--- a/plugins/calendar/drivers/kolab/kolab_driver.php
+++ b/plugins/calendar/drivers/kolab/kolab_driver.php
@@ -109,7 +109,7 @@ class kolab_driver extends calendar_driver
     $calendars = $names = array();
 
     // include virtual folders for a full folder tree
-    if (!$active && !$personal && !$this->rc->output->ajax_call)
+    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());
 
     foreach ($folders as $id => $cal) {
diff --git a/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php b/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php
index 3c6690a..787ced4 100644
--- a/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php
+++ b/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php
@@ -84,7 +84,7 @@ class tasklist_kolab_driver extends tasklist_driver
         $listnames = array();
 
         // include virtual folders for a full folder tree
-        if (!$this->rc->output->ajax_call)
+        if (!$this->rc->output->ajax_call && in_array($this->rc->action, array('index','')))
             $folders = $this->_folder_hierarchy($folders, $delim);
 
         foreach ($folders as $folder) {


commit eef0a950680aa1a498134ee165e203a1a88eb1cc
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Oct 10 17:27:24 2013 +0200

    Show complete folder hierarchy in calendars and tasklist listings with non-clickable virtual parent folders

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, ' » ', 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', ' ') .
+        ($prop['virtual'] ? '' : html::tag('input', array('type' => 'checkbox', 'name' => '_cal[]', 'value' => $id, 'checked' => $prop['active']), '') .
+        html::span('handle', ' ')) .
         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', ' ') .
                 html::span(array('class' => 'listname', 'title' => $title), $prop['name']));
         }


commit c5b3ba8770ec519f13d3fe4bf7873082be368cc9
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Oct 10 17:07:20 2013 +0200

    Also list unsubscribed folders for parent-selector

diff --git a/plugins/libkolab/lib/kolab_storage.php b/plugins/libkolab/lib/kolab_storage.php
index 8c28955..1e44de1 100644
--- a/plugins/libkolab/lib/kolab_storage.php
+++ b/plugins/libkolab/lib/kolab_storage.php
@@ -103,15 +103,16 @@ class kolab_storage
      * Get a list of storage folders for the given data type
      *
      * @param string Data type to list folders for (contact,distribution-list,event,task,note)
+     * @param boolean Enable to return subscribed folders only (null to use configured subscription mode)
      *
      * @return array List of Kolab_Folder objects (folder names in UTF7-IMAP)
      */
-    public static function get_folders($type)
+    public static function get_folders($type, $subscribed = null)
     {
         $folders = $folderdata = array();
 
         if (self::setup()) {
-            foreach ((array)self::list_folders('', '*', $type, null, $folderdata) as $foldername) {
+            foreach ((array)self::list_folders('', '*', $type, $subscribed, $folderdata) as $foldername) {
                 $folders[$foldername] = new kolab_storage_folder($foldername, $folderdata[$foldername]);
             }
         }
@@ -516,7 +517,7 @@ class kolab_storage
     public static function folder_selector($type, $attrs, $current = '')
     {
         // get all folders of specified type
-        $folders = self::get_folders($type);
+        $folders = self::get_folders($type, false);
 
         $delim = self::$imap->get_hierarchy_delimiter();
         $names = array();


commit d0c414e8e8de4c2d3d5a4c8ab56fba0dd8ceaeaa
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Oct 10 16:46:20 2013 +0200

    Make some getter methods available for others

diff --git a/plugins/libkolab/lib/kolab_storage.php b/plugins/libkolab/lib/kolab_storage.php
index ebfddcb..8c28955 100644
--- a/plugins/libkolab/lib/kolab_storage.php
+++ b/plugins/libkolab/lib/kolab_storage.php
@@ -395,11 +395,8 @@ class kolab_storage
         self::setup();
 
         // find custom display name in folder METADATA
-        if (self::$config->get('kolab_custom_display_names', true)) {
-            $metadata = self::$imap->get_metadata($folder, array(self::NAME_KEY_PRIVATE, self::NAME_KEY_SHARED));
-            if (($name = $metadata[$folder][self::NAME_KEY_PRIVATE]) || ($name = $metadata[$folder][self::NAME_KEY_SHARED])) {
-                return $name;
-            }
+        if ($name = self::custom_displayname($folder)) {
+            return $name;
         }
 
         $found     = false;
@@ -468,6 +465,21 @@ class kolab_storage
         return $folder;
     }
 
+    /**
+     * Get custom display name (saved in metadata) for the given folder
+     */
+    public static function custom_displayname($folder)
+    {
+      // find custom display name in folder METADATA
+      if (self::$config->get('kolab_custom_display_names', true)) {
+          $metadata = self::$imap->get_metadata($folder, array(self::NAME_KEY_PRIVATE, self::NAME_KEY_SHARED));
+          if (($name = $metadata[$folder][self::NAME_KEY_PRIVATE]) || ($name = $metadata[$folder][self::NAME_KEY_SHARED])) {
+              return $name;
+          }
+      }
+
+      return false;
+    }
 
     /**
      * Helper method to generate a truncated folder name to display
@@ -482,7 +494,7 @@ class kolab_storage
                 $length = strlen($names[$i] . ' » ');
                 $prefix = substr($name, 0, $length);
                 $count  = count(explode(' » ', $prefix));
-                $name   = str_repeat('  ', $count-1) . '» ' . substr($name, $length);
+                $name   = str_repeat('   ', $count-1) . '» ' . substr($name, $length);
                 break;
             }
         }
diff --git a/plugins/libkolab/lib/kolab_storage_folder.php b/plugins/libkolab/lib/kolab_storage_folder.php
index 294755b..896061e 100644
--- a/plugins/libkolab/lib/kolab_storage_folder.php
+++ b/plugins/libkolab/lib/kolab_storage_folder.php
@@ -92,7 +92,7 @@ class kolab_storage_folder
     /**
      *
      */
-    private function get_folder_info()
+    public function get_folder_info()
     {
         if (!isset($this->info))
             $this->info = $this->imap->folder_info($this->name);


commit 3d494afddb626d60ba7012911772ec44aad36bf3
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Oct 3 12:44:41 2013 +0200

    Add option to display direct CardDAV urls for Kolab address books in the Roundcube UI

diff --git a/plugins/kolab_addressbook/config.inc.php.dist b/plugins/kolab_addressbook/config.inc.php.dist
index 02745c8..0a0decf 100644
--- a/plugins/kolab_addressbook/config.inc.php.dist
+++ b/plugins/kolab_addressbook/config.inc.php.dist
@@ -11,4 +11,12 @@
 */
 $rcmail_config['kolab_addressbook_prio'] = 0;
 
+// Base URL to build fully qualified URIs to access calendars via CALDAV
+// The following replacement variables are supported:
+// %h - Current HTTP host
+// %u - Current webmail user name
+// %n - Folder name
+// %i - Folder UUID
+// $rcmail_config['kolab_addressbook_carddav_url'] = 'http://%h/iRony/addressbooks/%u/%i';
+
 ?>
diff --git a/plugins/kolab_addressbook/kolab_addressbook.js b/plugins/kolab_addressbook/kolab_addressbook.js
index b024fab..5824880 100644
--- a/plugins/kolab_addressbook/kolab_addressbook.js
+++ b/plugins/kolab_addressbook/kolab_addressbook.js
@@ -18,6 +18,7 @@ rcube_webmail.prototype.set_book_actions = function()
 
     this.enable_command('book-create', true);
     this.enable_command('book-edit', 'book-delete', source && sources[source] && sources[source].kolab && sources[source].editable);
+    this.enable_command('book-showurl', source && sources[source] && sources[source].carddavurl);
 };
 
 rcube_webmail.prototype.book_create = function()
@@ -38,6 +39,29 @@ rcube_webmail.prototype.book_delete = function()
     }
 };
 
+rcube_webmail.prototype.book_showurl = function()
+{
+    var source = this.env.source ? this.env.address_sources[this.env.source] : null;
+    if (source && source.carddavurl) {
+        $('div.showurldialog:ui-dialog').dialog('close');
+
+        var $dialog = $('<div>').addClass('showurldialog').append('<p>'+rcmail.gettext('carddavurldescription', 'kolab_addressbook')+'</p>'),
+            textbox = $('<textarea>').addClass('urlbox').css('width', '100%').attr('rows', 2).appendTo($dialog);
+
+          $dialog.dialog({
+            resizable: true,
+            closeOnEscape: true,
+            title: rcmail.gettext('bookshowurl', 'kolab_addressbook'),
+            close: function() {
+              $dialog.dialog("destroy").remove();
+            },
+            width: 520
+          }).show();
+
+          textbox.val(source.carddavurl).select();
+    }
+};
+
 // displays page with book edit/create form
 rcube_webmail.prototype.book_show_contentframe = function(action, framed)
 {
diff --git a/plugins/kolab_addressbook/kolab_addressbook.php b/plugins/kolab_addressbook/kolab_addressbook.php
index d6f0d6d..a3f480c 100644
--- a/plugins/kolab_addressbook/kolab_addressbook.php
+++ b/plugins/kolab_addressbook/kolab_addressbook.php
@@ -117,6 +117,7 @@ class kolab_addressbook extends rcube_plugin
                 'undelete' => $abook->undelete && $undelete,
                 'realname' => rcube_charset::convert($abook->get_realname(), 'UTF7-IMAP'), // IMAP folder name
                 'class_name' => $abook->get_namespace(),
+                'carddavurl' => $abook->get_carddav_url(),
                 'kolab'    => true,
             );
         }
diff --git a/plugins/kolab_addressbook/lib/kolab_addressbook_ui.php b/plugins/kolab_addressbook/lib/kolab_addressbook_ui.php
index 7546046..5e4e4d5 100644
--- a/plugins/kolab_addressbook/lib/kolab_addressbook_ui.php
+++ b/plugins/kolab_addressbook/lib/kolab_addressbook_ui.php
@@ -58,6 +58,10 @@ class kolab_addressbook_ui
             $options = array('book-create', 'book-edit', 'book-delete');
             $idx     = 0;
 
+            if ($this->rc->config->get('kolab_addressbook_carddav_url')) {
+              $options[] = 'book-showurl';
+            }
+
             foreach ($options as $command) {
                 $content = html::tag('li', $idx ? null : array('class' => 'separator_above'),
                     $this->plugin->api->output->button(array(
@@ -82,7 +86,8 @@ class kolab_addressbook_ui
             $this->plugin->api->add_content($content, 'groupoptions');
 
             $this->rc->output->add_label('kolab_addressbook.bookdeleteconfirm',
-                'kolab_addressbook.bookdeleting');
+                'kolab_addressbook.bookdeleting', 'kolab_addressbook.bookshowurl',
+                'kolab_addressbook.carddavurldescription');
         }
         // book create/edit form
         else {
diff --git a/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php b/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php
index 365e251..13c7740 100644
--- a/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php
+++ b/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php
@@ -178,6 +178,24 @@ class rcube_kolab_contacts extends rcube_addressbook
         return $this->namespace;
     }
 
+    /**
+     * Compose an URL for CardDAV access to this address book (if configured)
+     */
+    public function get_carddav_url()
+    {
+      $url = null;
+      $rcmail = rcmail::get_instance();
+      if ($template = $rcmail->config->get('kolab_addressbook_carddav_url', null)) {
+        return strtr($template, array(
+          '%h' => $_SERVER['HTTP_HOST'],
+          '%u' => urlencode($rcmail->get_user_name()),
+          '%i' => urlencode($this->storagefolder->get_uid()),
+          '%n' => urlencode($this->imap_folder),
+        ));
+      }
+
+      return false;
+    }
 
     /**
      * Setter for the current group
diff --git a/plugins/kolab_addressbook/localization/de_CH.inc b/plugins/kolab_addressbook/localization/de_CH.inc
index 3439448..130564b 100644
--- a/plugins/kolab_addressbook/localization/de_CH.inc
+++ b/plugins/kolab_addressbook/localization/de_CH.inc
@@ -25,6 +25,8 @@ $labels['bookdelete'] = 'Adressbuch löschen';
 $labels['bookproperties'] = 'Eigenschaften des Adressbuchs';
 $labels['bookname'] = 'Name des Buches';
 $labels['parentbook'] = 'Ãœbergeordnetes Buch';
+$labels['bookshowurl'] = 'CardDAV-URL anzeigen';
+$labels['carddavurldescription'] = 'Benutzen Sie folgende Addresse in einer <a href="http://en.wikipedia.org/wiki/CardDAV" target="_blank">CalDAV</a>-Anwendung um dieses spezifische Adressbuch mit dem Computer oder Mobiltelefon zu synchronisieren.';
 
 $labels['addressbookprio'] = 'Reihenfolge der Adressbücher';
 $labels['personalfirst'] = 'Private(s) Adressbuch/Adressbücher zuerst';
diff --git a/plugins/kolab_addressbook/localization/de_DE.inc b/plugins/kolab_addressbook/localization/de_DE.inc
index 2c2a5d2..8f44c8f 100644
--- a/plugins/kolab_addressbook/localization/de_DE.inc
+++ b/plugins/kolab_addressbook/localization/de_DE.inc
@@ -25,6 +25,8 @@ $labels['bookdelete'] = 'Adressbuch löschen';
 $labels['bookproperties'] = 'Eigenschaften des Adressbuchs';
 $labels['bookname'] = 'Name des Buches';
 $labels['parentbook'] = 'Ãœbergeordnetes Buch';
+$labels['bookshowurl'] = 'CardDAV-URL anzeigen';
+$labels['carddavurldescription'] = 'Benutzen Sie folgende Addresse in einer <a href="http://en.wikipedia.org/wiki/CardDAV" target="_blank">CalDAV</a>-Anwendung um dieses spezifische Adressbuch mit dem Computer oder Mobiltelefon zu synchronisieren.';
 
 $labels['addressbookprio'] = 'Reihenfolge der Adressbücher';
 $labels['personalfirst'] = 'Private(s) Adressbuch/Adressbücher zuerst';
diff --git a/plugins/kolab_addressbook/localization/en_US.inc b/plugins/kolab_addressbook/localization/en_US.inc
index a66426f..c1ab0f5 100644
--- a/plugins/kolab_addressbook/localization/en_US.inc
+++ b/plugins/kolab_addressbook/localization/en_US.inc
@@ -25,6 +25,8 @@ $labels['bookdelete'] = 'Delete address book';
 $labels['bookproperties'] = 'Address book properties';
 $labels['bookname'] = 'Book name';
 $labels['parentbook'] = 'Superior book';
+$labels['bookshowurl'] = 'Show CardDAV URL';
+$labels['carddavurldescription'] = 'Copy this address to a <a href="http://en.wikipedia.org/wiki/CardDAV" target="_blank">CardDAV</a> client application to fully synchronize this specific address book with your computer or mobile device.';
 
 $labels['addressbookprio'] = 'Address book(s) selection/behaviour';
 $labels['personalfirst'] = 'Personal address book(s) first';


commit a604dbd3e47168ea31ca13b73fdce843774c9818
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Oct 3 12:07:02 2013 +0200

    Add option to display direct CalDAV urls for calendars in the UI

diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js
index 2747641..81e350f 100644
--- a/plugins/calendar/calendar_ui.js
+++ b/plugins/calendar/calendar_ui.js
@@ -2009,6 +2009,14 @@ function rcube_calendar_ui(settings)
         $dialog.dialog('close');
 
       if (calendar.feedurl) {
+        if (calendar.caldavurl) {
+          $('#caldavurl').val(calendar.caldavurl);
+          $('#calendarcaldavurl').show();
+        }
+        else {
+          $('#calendarcaldavurl').hide();
+        }
+
         $dialog.dialog({
           resizable: true,
           closeOnEscape: true,
diff --git a/plugins/calendar/config.inc.php.dist b/plugins/calendar/config.inc.php.dist
index a8d5da8..56748bf 100644
--- a/plugins/calendar/config.inc.php.dist
+++ b/plugins/calendar/config.inc.php.dist
@@ -119,5 +119,12 @@ $rcmail_config['calendar_itip_smtp_user'] = 'smtpauth';
 // SMTP password used to send (anonymous) itip messages
 $rcmail_config['calendar_itip_smtp_pass'] = '123456';
 
+// Base URL to build fully qualified URIs to access calendars via CALDAV
+// The following replacement variables are supported:
+// %h - Current HTTP host
+// %u - Current webmail user name
+// %n - Calendar name
+// %i - Calendar UUID
+// $rcmail_config['calendar_caldav_url'] = 'http://%h/iRony/calendars/%u/%i';
 
 ?>
\ No newline at end of file
diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php
index 1cf7107..877c3f5 100644
--- a/plugins/calendar/drivers/kolab/kolab_calendar.php
+++ b/plugins/calendar/drivers/kolab/kolab_calendar.php
@@ -156,6 +156,24 @@ class kolab_calendar
   }
 
   /**
+   * Compose an URL for CalDAV access to this calendar (if configured)
+   */
+  public function get_caldav_url()
+  {
+    $url = null;
+    if ($template = $this->cal->rc->config->get('calendar_caldav_url', null)) {
+      return strtr($template, array(
+        '%h' => $_SERVER['HTTP_HOST'],
+        '%u' => urlencode($this->cal->rc->get_user_name()),
+        '%i' => urlencode($this->storage->get_uid()),
+        '%n' => urlencode($this->imap_folder),
+      ));
+    }
+
+    return false;
+  }
+
+  /**
    * Return the corresponding kolab_storage_folder instance
    */
   public function get_folder()
diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php
index 77b6a0a..05fd0e4 100644
--- a/plugins/calendar/drivers/kolab/kolab_driver.php
+++ b/plugins/calendar/drivers/kolab/kolab_driver.php
@@ -123,6 +123,7 @@ class kolab_driver extends calendar_driver
         '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(),
       );
     }
 
diff --git a/plugins/calendar/localization/de_CH.inc b/plugins/calendar/localization/de_CH.inc
index cd3399f..b580fc2 100644
--- a/plugins/calendar/localization/de_CH.inc
+++ b/plugins/calendar/localization/de_CH.inc
@@ -76,6 +76,7 @@ $labels['onemonthback'] = '1 Monat zurück';
 $labels['nmonthsback'] = '$nr Monate zurück';
 $labels['showurl'] = 'URL anzeigen';
 $labels['showurldescription'] = 'Über die folgende Adresse können Sie mit einem beliebigen Kalenderprogramm Ihren Kalender abrufen (nur lesend), sofern dieses das iCal-Format unterstützt.';
+$labels['caldavurldescription'] = 'Benutzen Sie folgende Addresse in einer <a href="http://de.wikipedia.org/wiki/CalDAV" target="_blank">CalDAV</a>-Anwendung (wie z.B. Evolution oder Mozilla Thunderbird) um diesen spezifischen Kalender mit dem Computer oder Mobiltelefon zu synchronisieren.';
 
 // agenda view
 $labels['listrange'] = 'Angezeigter Bereich:';
diff --git a/plugins/calendar/localization/de_DE.inc b/plugins/calendar/localization/de_DE.inc
index cb96b0d..60e6ad2 100644
--- a/plugins/calendar/localization/de_DE.inc
+++ b/plugins/calendar/localization/de_DE.inc
@@ -76,6 +76,7 @@ $labels['onemonthback'] = '1 Monat zurück';
 $labels['nmonthsback'] = '$nr Monate zurück';
 $labels['showurl'] = 'URL anzeigen';
 $labels['showurldescription'] = 'Über die folgende Adresse können Sie mit einem beliebigen Kalenderprogramm Ihren Kalender abrufen (nur lesend), sofern dieses das iCal-Format unterstützt.';
+$labels['caldavurldescription'] = 'Benutzen Sie folgende Addresse in einer <a href="http://de.wikipedia.org/wiki/CalDAV" target="_blank">CalDAV</a>-Anwendung (wie z.B. Evolution oder Mozilla Thunderbird) um diesen spezifischen Kalender mit dem Computer oder Mobiltelefon zu synchronisieren.';
 
 // agenda view
 $labels['listrange'] = 'Angezeigter Bereich:';
diff --git a/plugins/calendar/localization/en_US.inc b/plugins/calendar/localization/en_US.inc
index 3bf52ba..9a631b2 100644
--- a/plugins/calendar/localization/en_US.inc
+++ b/plugins/calendar/localization/en_US.inc
@@ -77,6 +77,7 @@ $labels['onemonthback'] = '1 month back';
 $labels['nmonthsback'] = '$nr months back';
 $labels['showurl'] = 'Show calendar URL';
 $labels['showurldescription'] = 'Use the following address to access (read only) your calendar from other applications. You can copy and paste this into any calendar software that supports the iCal format.';
+$labels['caldavurldescription'] = 'Copy this address to a <a href="http://en.wikipedia.org/wiki/CalDAV" target="_blank">CalDAV</a> client application (e.g. Evolution or Mozilla Thunderbird) to fully synchronize this specific calendar with your computer or mobile device.';
 
 // agenda view
 $labels['listrange'] = 'Range to display:';
diff --git a/plugins/calendar/skins/classic/calendar.css b/plugins/calendar/skins/classic/calendar.css
index 117c2ce..835bdac 100644
--- a/plugins/calendar/skins/classic/calendar.css
+++ b/plugins/calendar/skins/classic/calendar.css
@@ -164,7 +164,8 @@ pre {
     background-position: 0 -92px;
 }
 
-#calfeedurl {
+#calfeedurl,
+#caldavurl {
 	width: 98%;
 	background: #fbfbfb;
 	padding: 4px;
diff --git a/plugins/calendar/skins/classic/templates/calendar.html b/plugins/calendar/skins/classic/templates/calendar.html
index a78e76f..ba80ca3 100644
--- a/plugins/calendar/skins/classic/templates/calendar.html
+++ b/plugins/calendar/skins/classic/templates/calendar.html
@@ -150,6 +150,10 @@
 <div id="calendarurlbox" class="uidialog">
   <p><roundcube:label name="calendar.showurldescription" /></p>
   <textarea id="calfeedurl" rows="2" readonly="readonly"></textarea>
+  <div id="calendarcaldavurl" style="display:none">
+    <p><roundcube:label name="calendar.caldavurldescription" html="yes" /></p>
+    <textarea id="caldavurl" rows="2" readonly="readonly"></textarea>
+  </div>
 </div>
 
 <div id="calendartoolbar">
diff --git a/plugins/calendar/skins/larry/calendar.css b/plugins/calendar/skins/larry/calendar.css
index b11abfa..8775b2a 100644
--- a/plugins/calendar/skins/larry/calendar.css
+++ b/plugins/calendar/skins/larry/calendar.css
@@ -225,7 +225,8 @@ pre {
 	background-position: right -92px;
 }
 
-#calfeedurl {
+#calfeedurl,
+#caldavurl {
 	width: 98%;
 	background: #fbfbfb;
 	padding: 4px;
diff --git a/plugins/calendar/skins/larry/templates/calendar.html b/plugins/calendar/skins/larry/templates/calendar.html
index bc9beca..79ce5ad 100644
--- a/plugins/calendar/skins/larry/templates/calendar.html
+++ b/plugins/calendar/skins/larry/templates/calendar.html
@@ -164,6 +164,10 @@
 <div id="calendarurlbox" class="uidialog">
 	<p><roundcube:label name="calendar.showurldescription" /></p>
 	<textarea id="calfeedurl" rows="2" readonly="readonly"></textarea>
+	<div id="calendarcaldavurl" style="display:none">
+		<p><roundcube:label name="calendar.caldavurldescription" html="yes" /></p>
+		<textarea id="caldavurl" rows="2" readonly="readonly"></textarea>
+	</div>
 </div>
 
 <roundcube:object name="plugin.calendar_css" />





More information about the commits mailing list