3 commits - plugins/kolab_addressbook plugins/kolab_notes plugins/libkolab

Thomas Brüderli bruederli at kolabsys.com
Wed Jun 25 17:12:25 CEST 2014


 plugins/kolab_addressbook/kolab_addressbook.js                |   85 +++
 plugins/kolab_addressbook/kolab_addressbook.php               |  225 ++++++++--
 plugins/kolab_addressbook/lib/kolab_addressbook_ui.php        |   14 
 plugins/kolab_addressbook/lib/rcube_kolab_contacts.php        |   10 
 plugins/kolab_addressbook/localization/en_US.inc              |    8 
 plugins/kolab_addressbook/skins/classic/kolab_addressbook.css |   16 
 plugins/kolab_addressbook/skins/larry/folder_icons.png        |binary
 plugins/kolab_addressbook/skins/larry/kolab_addressbook.css   |   81 +++
 plugins/kolab_notes/kolab_notes.php                           |   20 
 plugins/libkolab/js/folderlist.js                             |   32 -
 10 files changed, 432 insertions(+), 59 deletions(-)

New commits:
commit 32164e30bf6c3fc02143a838ca57bb1865a4d847
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Jun 25 17:09:04 2014 +0200

    Add new folder navigation to address book (#3046).
    This patches/overwrites default functions from Roundcube core. Be careful when updating those!

diff --git a/plugins/kolab_addressbook/kolab_addressbook.js b/plugins/kolab_addressbook/kolab_addressbook.js
index 82b6d9d..c519a67 100644
--- a/plugins/kolab_addressbook/kolab_addressbook.js
+++ b/plugins/kolab_addressbook/kolab_addressbook.js
@@ -87,6 +87,91 @@ if (window.rcmail) {
                 }
             });
         }
+
+        // append search form for address books
+        if (rcmail.gui_objects.folderlist) {
+            var container = $(rcmail.gui_objects.folderlist);
+            $('<div class="listsearchbox" style="display:none">' +
+                '<div class="searchbox" role="search" aria-labelledby="aria-labelfoldersearchform" aria-controls="' + rcmail.gui_objects.folderlist.id + '">' +
+                    '<h3 id="aria-label-labelfoldersearchform" class="voice">' + rcmail.gettext('foldersearchform', 'kolab_addressbook') + '" /></h3>' +
+                    '<label for="addressbooksearch" class="voice">' + rcmail.gettext('searchterms', 'kolab_addressbook') + '</label>' +
+                    '<input type="text" name="q" id="addressbooksearch" placeholder="' + rcmail.gettext('findaddressbooks', 'kolab_addressbook') + '" />' +
+                    '<a class="iconbutton searchicon"></a>' +
+                    '<a href="#reset" onclick="return rcmail.command(\'reset-listsearch\',null,this,event)" id="directorylistsearch-reset" class="iconbutton reset" title="' + rcmail.gettext('resetsearch') + '">' +
+                        rcmail.gettext('resetsearch') + '</a>' +
+                '</div>' +
+            '</div>')
+            .insertBefore(container.parent());
+
+            $('<a href="#search" class="iconbutton search" title="' + rcmail.gettext('findaddressbooks', 'kolab_addressbook') + '" tabindex="0">' +
+                rcmail.gettext('findaddressbooks', 'kolab_addressbook') + '</a>')
+                .appendTo('#directorylistbox h2.boxtitle')
+                .click(function(e){
+                    var box = $('#directorylistbox .listsearchbox'),
+                        dir = box.is(':visible') ? -1 : 1;
+
+                    box.slideToggle({
+                        duration: 160,
+                        progress: function(animation, progress) {
+                            if (dir < 0) progress = 1 - progress;
+                            $('#directorylistbox .scroller').css('top', (34 + 34 * progress) + 'px');
+                        },
+                        complete: function() {
+                            box.toggleClass('expanded');
+                            if (box.is(':visible')) {
+                                box.find('input[type=text]').focus();
+                            }
+                            else {
+                                $('#directorylistsearch-reset').click();
+                            }
+                        }
+                    });
+                });
+
+
+            // remove event handlers set by the regular treelist widget
+            rcmail.treelist.container.off('click mousedown focusin focusout');
+
+            // re-initialize folderlist widget
+            // copy form app.js with additional parameters
+            var widget_class = window.kolab_folderlist || rcube_treelist_widget;
+            rcmail.treelist = new widget_class(rcmail.gui_objects.folderlist, {
+                selectable: true,
+                id_prefix: 'rcmli',
+                id_encode: rcmail.html_identifier_encode,
+                id_decode: rcmail.html_identifier_decode,
+                searchbox: '#addressbooksearch',
+                search_action: 'plugin.book-search',
+                search_sources: [ 'folders', 'users' ],
+                search_title: rcmail.gettext('listsearchresults','kolab_addressbook'),
+                check_droptarget: function(node) { return !node.virtual && rcmail.check_droptarget(node.id) }
+            });
+
+            rcmail.treelist
+                .addEventListener('collapse',  function(node) { rcmail.folder_collapsed(node) })
+                .addEventListener('expand',    function(node) { rcmail.folder_collapsed(node) })
+                .addEventListener('select',    function(node) { rcmail.triggerEvent('selectfolder', { folder:node.id, prefix:'rcmli' }) })
+                .addEventListener('subscribe', function(node) {
+                    var source;
+                    if ((source = rcmail.env.address_sources[node.id])) {
+                        source.subscribed = node.subscribed || false;
+                        rcmail.http_post('plugin.book-subscribe', { _source:node.id, _permanent:source.subscribed?1:0 });
+                    }
+                })
+                .addEventListener('insert-item', function(data) {
+                    // register new address source
+                    rcmail.env.address_sources[data.id] = rcmail.env.contactfolders[data.id] = data.data;
+                    if (data.data.subscribed)
+                        rcmail.http_post('plugin.book-subscribe', { _source:data.id, _permanent:1 });
+                    // TODO: load groups and add them to the list
+                })
+                .addEventListener('search-complete', function(data) {
+                    if (data.length)
+                        rcmail.display_message(rcmail.gettext('nraddressbooksfound','kolab_addressbook').replace('$nr', data.length), 'voice');
+                    else
+                        rcmail.display_message(rcmail.gettext('noaddressbooksfound','kolab_addressbook'), 'info');
+                });
+        }
     });
     rcmail.addEventListener('listupdate', function() {
         rcmail.set_book_actions();
diff --git a/plugins/kolab_addressbook/kolab_addressbook.php b/plugins/kolab_addressbook/kolab_addressbook.php
index 530580b..7bada56 100644
--- a/plugins/kolab_addressbook/kolab_addressbook.php
+++ b/plugins/kolab_addressbook/kolab_addressbook.php
@@ -66,6 +66,8 @@ class kolab_addressbook extends rcube_plugin
             // Plugin actions
             $this->register_action('plugin.book', array($this, 'book_actions'));
             $this->register_action('plugin.book-save', array($this, 'book_save'));
+            $this->register_action('plugin.book-search', array($this, 'book_search'));
+            $this->register_action('plugin.book-subscribe', array($this, 'book_subscribe'));
 
             // Load UI elements
             if ($this->api->output->type == 'html') {
@@ -133,19 +135,34 @@ class kolab_addressbook extends rcube_plugin
      */
     protected function abook_prop($id, $abook)
     {
-        return array(
-            'id'       => $id,
-            'name'     => $abook->get_name(),
-            'listname' => $abook->get_foldername(),
-            'readonly' => $abook->readonly,
-            'editable' => $abook->editable,
-            'groups'   => $abook->groups,
-            'undelete' => $abook->undelete && $this->rc->config->get('undo_timeout'),
-            'realname' => rcube_charset::convert($abook->get_realname(), 'UTF7-IMAP'), // IMAP folder name
-            'group'    => $abook->get_namespace(),
-            'carddavurl' => $abook->get_carddav_url(),
-            'kolab'    => true,
-        );
+        if ($abook->virtual) {
+            return array(
+                'id'       => $id,
+                'name'     => $abook->get_name(),
+                'listname' => $abook->get_foldername(),
+                'group'    => $abook instanceof kolab_storage_folder_user ? 'user' : $abook->get_namespace(),
+                'readonly' => true,
+                'editable' => false,
+                'kolab'    => true,
+                'virtual'  => true,
+            );
+        }
+        else {
+            return array(
+                'id'       => $id,
+                'name'     => $abook->get_name(),
+                'listname' => $abook->get_foldername(),
+                'readonly' => $abook->readonly,
+                'editable' => $abook->editable,
+                'groups'   => $abook->groups,
+                'undelete' => $abook->undelete && $this->rc->config->get('undo_timeout'),
+                'realname' => rcube_charset::convert($abook->get_realname(), 'UTF7-IMAP'), // IMAP folder name
+                'group'    => $abook->get_namespace(),
+                'subscribed' => $abook->is_subscribed(),
+                'carddavurl' => $abook->get_carddav_url(),
+                'kolab'    => true,
+            );
+        }
     }
 
     /**
@@ -167,7 +184,8 @@ class kolab_addressbook extends rcube_plugin
         kolab_storage::folder_hierarchy($this->folders, $tree);
         $out .= $this->folder_tree_html($tree, $sources, $jsdata);
 
-        $this->rc->output->set_env('contactgroups', $jsdata);
+        $this->rc->output->set_env('contactgroups', array_filter($jsdata, function($src){ return $src['type'] == 'group'; }));
+        $this->rc->output->set_env('address_sources', array_filter($jsdata, function($src){ return $src['type'] != 'group'; }));
 
         $args['content'] = html::tag('ul', $args, $out, html::$common_attrib);
         return $args;
@@ -185,16 +203,11 @@ class kolab_addressbook extends rcube_plugin
             $is_collapsed = strpos($this->rc->config->get('collapsed_abooks',''), '&'.rawurlencode($id).'&') !== false;
 
             if ($folder->virtual) {
-                $source = array(
-                    'id'       => $folder->id,
-                    'name'     => $folder->get_name(),
-                    'listname' => $folder->get_foldername(),
-                    'group'    => $folder->get_namespace(),
-                    'readonly' => true,
-                    'editable' => false,
-                    'kolab'    => true,
-                    'virtual'  => true,
-                );
+                $source = $this->abook_prop($folder->id, $folder);
+            }
+            else if (empty($source)) {
+                $this->sources[$id] = new rcube_kolab_contacts($folder->name);
+                $source = $this->abook_prop($id, $this->sources[$id]);
             }
 
             $content = $this->addressbook_list_item($id, $source, $jsdata);
@@ -219,11 +232,15 @@ class kolab_addressbook extends rcube_plugin
     /**
      *
      */
-    protected function addressbook_list_item($id, $source, &$jsdata, $checkbox = false)
+    protected function addressbook_list_item($id, $source, &$jsdata, $search_mode = false)
     {
         $folder = $this->folders[$id];
         $current = rcube_utils::get_input_value('_source', rcube_utils::INPUT_GPC);
 
+        if (!$source['virtual']) {
+            $jsdata[$id] = $source;
+        }
+
         // set class name(s)
         $classes = array($source['group'] ?: '000', 'addressbook');
         if ($current === $id)
@@ -236,19 +253,48 @@ class kolab_addressbook extends rcube_plugin
             $classes[] = $source['class_name'];
 
         $name = !empty($source['listname']) ? $source['listname'] : (!empty($source['name']) ? $source['name'] : $id);
+        $label_id = 'kabt:' . $id;
+        $inner = ($source['virtual'] ?
+            html::a(array('tabindex' => '0'), $name) :
+            html::a(array(
+                    'href' => $this->rc->url(array('_source' => $id)),
+                    'rel' => $source['id'],
+                    'id' => $label_id,
+                    'onclick' => "return " . rcmail_output::JS_OBJECT_NAME.".command('list','" . rcube::JQ($id) . "',this)",
+                ), $name)
+        );
+
+        if (isset($source['subscribed'])) {
+            $inner .= html::span(array(
+                'class' => 'subscribed',
+                'title' => $this->gettext('foldersubscribe'),
+                'role' => 'checkbox',
+                'aria-checked' => $source['subscribed'] ? 'true' : 'false',
+            ), '');
+        }
+
+        // don't wrap in <li> but add a checkbox for search results listing
+        if ($search_mode) {
+            $jsdata[$id]['group'] = join(' ', $classes);
+
+            if (!$source['virtual']) {
+                $inner .= html::tag('input', array(
+                    'type' => 'checkbox',
+                    'name' => '_source[]',
+                    'value' => $id,
+                    'checked' => $prop['active'],
+                    'aria-labelledby' => $label_id,
+                ));
+            }
+            return html::div(null, $inner);
+        }
+
         $out .= html::tag('li', array(
                 'id' => 'rcmli' . rcube_utils::html_identifier($id, true),
                 'class' => join(' ', $classes), 
                 'noclose' => true,
             ),
-            ($source['virtual'] ?
-                html::a(array('tabindex' => '0'), $name) :
-                html::a(array(
-                        'href' => $this->rc->url(array('_source' => $id)),
-                        'rel' => $source['id'],
-                        'onclick' => "return " . rcmail_output::JS_OBJECT_NAME.".command('list','" . rcube::JQ($id) . "',this)",
-                    ), $name)
-            )
+            html::div($source['subscribed'] ? 'subscribed' : null, $inner)
         );
 
         $groupdata = array('out' => '', 'jsdata' => $jsdata, 'source' => $id);
@@ -596,6 +642,119 @@ class kolab_addressbook extends rcube_plugin
         $this->ui->book_edit();
     }
 
+    /**
+     *
+     */
+    public function book_search()
+    {
+        $results = array();
+        $query = rcube_utils::get_input_value('q', RCUBE_INPUT_GPC);
+        $source = rcube_utils::get_input_value('source', RCUBE_INPUT_GPC);
+
+        kolab_storage::$encode_ids = true;
+        $search_more_results = false;
+        $this->sources = array();
+        $this->folders = array();
+    
+        // find unsubscribed IMAP folders that have "event" type
+        if ($source == 'folders') {
+            foreach ((array)kolab_storage::search_folders('contact', $query, array('other')) as $folder) {
+                $this->folders[$folder->id] = $folder;
+                $this->sources[$folder->id] = new rcube_kolab_contacts($folder->name);
+            }
+        }
+        // search other user's namespace via LDAP
+        else if ($source == 'users') {
+            $limit = $this->rc->config->get('autocomplete_max', 15) * 2;  // we have slightly more space, so display twice the number
+            foreach (kolab_storage::search_users($query, 0, array(), $limit * 10) as $user) {
+                $folders = array();
+                // search for contact folders shared by this user
+                foreach (kolab_storage::list_user_folders($user, 'contact', false) as $foldername) {
+                    $folders[] = new kolab_storage_folder($foldername, 'contact');
+                }
+
+                if (count($folders)) {
+                    $userfolder = new kolab_storage_folder_user($user['kolabtargetfolder'], '', $user);
+                    $this->folders[$userfolder->id] = $userfolder;
+                    $this->sources[$userfolder->id] = $userfolder;
+
+                    foreach ($folders as $folder) {
+                        $this->folders[$folder->id] = $folder;
+                        $this->sources[$folder->id] = new rcube_kolab_contacts($folder->name);;
+                        $count++;
+                    }
+                }
+
+                if ($count >= $limit) {
+                    $search_more_results = true;
+                    break;
+                }
+            }
+        }
+
+        $delim = $this->rc->get_storage()->get_hierarchy_delimiter();
+
+        // build results list
+        foreach ($this->sources as $id => $source) {
+            $folder = $this->folders[$id];
+            $imap_path = explode($delim, $folder->name);
+
+            // find parent
+            do {
+              array_pop($imap_path);
+              $parent_id = kolab_storage::folder_id(join($delim, $imap_path));
+            }
+            while (count($imap_path) > 1 && !$this->folders[$parent_id]);
+
+            // restore "real" parent ID
+            if ($parent_id && !$this->folders[$parent_id]) {
+                $parent_id = kolab_storage::folder_id($folder->get_parent());
+            }
+
+            $prop = $this->abook_prop($id, $source);
+            $prop['parent'] = $parent_id;
+            unset($prop['group']);
+
+            $html = $this->addressbook_list_item($id, $prop, $jsdata, true);
+            $prop += (array)$jsdata[$id];
+            $prop['html'] = $html;
+
+            $results[] = $prop;
+        }
+
+        // report more results available
+        if ($search_more_results) {
+            $this->rc->output->show_message('autocompletemore', 'info');
+        }
+
+        $this->rc->output->command('multi_thread_http_response', $results, rcube_utils::get_input_value('_reqid', RCUBE_INPUT_GPC));
+    }
+
+    /**
+     *
+     */
+    public function book_subscribe()
+    {
+        $success = false;
+        $id = rcube_utils::get_input_value('_source', RCUBE_INPUT_GPC);
+
+        if ($id && ($folder = kolab_storage::get_folder(kolab_storage::id_decode($id)))) {
+            if (isset($_POST['_permanent']))
+                $success |= $folder->subscribe(intval($_POST['_permanent']));
+            if (isset($_POST['_active']))
+                $success |= $folder->activate(intval($_POST['_active']));
+        }
+        
+        if ($success) {
+            $this->rc->output->show_message('successfullysaved', 'confirmation');
+        }
+        else {
+            $this->rc->output->show_message($this->gettext('errorsaving'), 'error');
+        }
+
+        $this->rc->output->send();
+    }
+
 
     /**
      * Handler for address book delete action (AJAX)
diff --git a/plugins/kolab_addressbook/lib/kolab_addressbook_ui.php b/plugins/kolab_addressbook/lib/kolab_addressbook_ui.php
index c7c1a7f..898dd8c 100644
--- a/plugins/kolab_addressbook/lib/kolab_addressbook_ui.php
+++ b/plugins/kolab_addressbook/lib/kolab_addressbook_ui.php
@@ -54,6 +54,11 @@ class kolab_addressbook_ui
             // Include stylesheet (for directorylist)
             $this->plugin->include_stylesheet($this->plugin->local_skin_path().'/kolab_addressbook.css');
 
+            // include kolab folderlist widget if available
+            if (is_readable($this->plugin->api->dir . 'libkolab/js/folderlist.js')) {
+                $this->plugin->api->include_script('libkolab/js/folderlist.js');
+            }
+
             // Add actions on address books
             $options = array('book-create', 'book-edit', 'book-delete');
             $idx     = 0;
@@ -91,7 +96,14 @@ class kolab_addressbook_ui
                 'kolab_addressbook.carddavurldescription',
                 'kolab_addressbook.bookedit',
                 'kolab_addressbook.bookdelete',
-                'kolab_addressbook.bookshowurl');
+                'kolab_addressbook.bookshowurl',
+                'kolab_addressbook.findaddressbooks',
+                'kolab_addressbook.searchterms',
+                'kolab_addressbook.foldersearchform',
+                'kolab_addressbook.listsearchresults',
+                'kolab_addressbook.nraddressbooksfound',
+                'kolab_addressbook.noaddressbooksfound',
+                'resetsearch');
         }
         // 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 2e93d46..8bd1886 100644
--- a/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php
+++ b/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php
@@ -199,6 +199,16 @@ class rcube_kolab_contacts extends rcube_addressbook
     }
 
     /**
+     * Check subscription status of this folder
+     *
+     * @return boolean True if subscribed, false if not
+     */
+    public function is_subscribed()
+    {
+        return kolab_storage::folder_is_subscribed($this->imap_folder);
+    }
+
+    /**
      * Compose an URL for CardDAV access to this address book (if configured)
      */
     public function get_carddav_url()
diff --git a/plugins/kolab_addressbook/localization/en_US.inc b/plugins/kolab_addressbook/localization/en_US.inc
index c1ab0f5..11a4581 100644
--- a/plugins/kolab_addressbook/localization/en_US.inc
+++ b/plugins/kolab_addressbook/localization/en_US.inc
@@ -34,6 +34,14 @@ $labels['globalfirst'] = 'Global address book(s) first';
 $labels['personalonly'] = 'Personal address book(s) only';
 $labels['globalonly'] = 'Global address book(s) only';
 
+$labels['findaddressbooks'] = 'Find address books';
+$labels['searchterms'] = 'Search terms';
+$labels['listsearchresults'] = 'Additional address books';
+$labels['foldersearchform'] = 'Address book search form';
+$labels['foldersubscribe'] = 'List permanently';
+$labels['nraddressbooksfound'] = '$nr address books found';
+$labels['noaddressbooksfound'] = 'No address books found';
+
 $messages['bookdeleteconfirm']  = 'Do you really want to delete the selected address book and all contacts in it?';
 $messages['bookdeleting'] = 'Deleting address book...';
 $messages['booksaving'] = 'Saving address book...';
diff --git a/plugins/kolab_addressbook/skins/classic/kolab_addressbook.css b/plugins/kolab_addressbook/skins/classic/kolab_addressbook.css
index da3abed..30b9593 100644
--- a/plugins/kolab_addressbook/skins/classic/kolab_addressbook.css
+++ b/plugins/kolab_addressbook/skins/classic/kolab_addressbook.css
@@ -1,6 +1,19 @@
+#directorylistbox ul.treelist li.virtual {
+	background-image: none !important;
+}
+
+#directorylistbox ul.treelist li.virtual > div a {
+	color: #aaa;
+	background-image: none;
+}
+
+#directorylistbox ul.treelist li.virtual > .treetoggle {
+	top: -2px !important;
+}
+
+/*
 #directorylist li.addressbook.readonly
 {
-  /* don't use 'background' to not reset background color */
   background-image: url(kolab_folders.png);
   background-position: 5px 0px;
   background-repeat: no-repeat;
@@ -33,3 +46,4 @@
   background-position: 5px -36px;
   background-repeat: no-repeat;
 }
+*/
\ No newline at end of file
diff --git a/plugins/kolab_addressbook/skins/larry/folder_icons.png b/plugins/kolab_addressbook/skins/larry/folder_icons.png
index 62805ad..07674ab 100644
Binary files a/plugins/kolab_addressbook/skins/larry/folder_icons.png and b/plugins/kolab_addressbook/skins/larry/folder_icons.png differ
diff --git a/plugins/kolab_addressbook/skins/larry/kolab_addressbook.css b/plugins/kolab_addressbook/skins/larry/kolab_addressbook.css
index 2cfac3b..00f34e8 100644
--- a/plugins/kolab_addressbook/skins/larry/kolab_addressbook.css
+++ b/plugins/kolab_addressbook/skins/larry/kolab_addressbook.css
@@ -1,4 +1,79 @@
 
+#directorylistbox .listsearchbox + .scroller {
+	top: 34px;
+}
+
+#directorylistbox .listsearchbox.expanded + .scroller {
+	top: 68px;
+}
+
+#directorylistbox .boxtitle a.iconbutton.search {
+	position: absolute;
+	top: 8px;
+	right: 8px;
+	width: 16px;
+	cursor: pointer;
+	background-position: -2px -317px;
+}
+
+#directorylistbox ul.treelist li.virtual {
+	background-image: none !important;
+}
+
+#directorylistbox ul.treelist li.virtual > div a {
+	color: #aaa;
+	background-image: none;
+	height: 16px;
+	padding-top: 3px;
+	padding-bottom: 3px;
+}
+
+#directorylistbox ul.treelist li.virtual > .treetoggle {
+	top: 6px !important;
+}
+
+#directorylistbox ul.treelist li input {
+	position: absolute;
+	top: 4px;
+	left: 10px;
+}
+
+#directorylistbox ul.treelist ul li input {
+	left: 36px;
+}
+
+#directorylist li input {
+	display: none;
+}
+
+#directorylistbox ul.treelist div span.subscribed {
+	display: inline-block;
+	position: absolute;
+	top: 4px;
+	right: 5px;
+	height: 16px;
+	width: 16px;
+	padding: 0;
+	background: url(folder_icons.png) -100px 0 no-repeat;
+	overflow: hidden;
+	text-indent: -5000px;
+	cursor: pointer;
+}
+
+#directorylistbox ul.treelist div > span.subscribed:focus,
+#directorylistbox ul.treelist div:hover > span.subscribed {
+	background-position: 2px -160px;
+}
+
+#directorylistbox ul.treelist div.subscribed span.subscribed {
+	background-position: -16px -160px;
+}
+
+#directorylistbox ul.treelist div span.subscribed:focus {
+	border-radius: 3px;
+	outline: 2px solid rgba(30,150,192, 0.5);
+}
+
 #directorylist li.addressbook.readonly,
 #directorylist li.addressbook.shared,
 #directorylist li.addressbook.other {
@@ -15,6 +90,7 @@
 	background-position: 98% -52px;
 }
 
+#directorylist li.addressbook.personal.readonly,
 #directorylist li.addressbook.other.readonly {
 	background-position: 98% -77px;
 }
@@ -27,6 +103,11 @@
 	background-position: 98% -130px;
 }
 
+#directorylist li.addressbook.virtual.user {
+	background-image: url(folder_icons.png) !important;
+	background-position: 98% -52px;
+}
+
 #directorylist li.addressbook.readonly a,
 #directorylist li.addressbook.shared a,
 #directorylist li.addressbook.other a {


commit 22d3e7553bb0e18630bc473035c6572a27367bba
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Jun 25 17:08:07 2014 +0200

    Small code cleanup: remove unused variables and arguments

diff --git a/plugins/kolab_notes/kolab_notes.php b/plugins/kolab_notes/kolab_notes.php
index ed50af7..4c816a2 100644
--- a/plugins/kolab_notes/kolab_notes.php
+++ b/plugins/kolab_notes/kolab_notes.php
@@ -157,10 +157,8 @@ class kolab_notes extends rcube_plugin
             array_unshift($folders, $default_folder);
         }
 
-        $delim = $this->rc->get_storage()->get_hierarchy_delimiter();
-
         foreach ($folders as $folder) {
-            $item = $this->folder_props($folder, $delim);
+            $item = $this->folder_props($folder);
             $this->lists[$item['id']] = $item;
             $this->folders[$item['id']] = $folder;
             $this->folders[$folder->name] = $folder;
@@ -243,7 +241,7 @@ class kolab_notes extends rcube_plugin
             }
             else {
                 if (!$this->lists[$list_id]) {
-                    $this->lists[$list_id] = $this->folder_props($folder, $delim);
+                    $this->lists[$list_id] = $this->folder_props($folder);
                     $this->folders[$list_id] = $folder;
                 }
                 $this->lists[$list_id]['parent'] = $parent_id;
@@ -270,13 +268,11 @@ class kolab_notes extends rcube_plugin
         $this->search_more_results = false;
         $this->lists = $this->folders = array();
 
-        $delim = $this->rc->get_storage()->get_hierarchy_delimiter();
-
         // find unsubscribed IMAP folders that have "event" type
         if ($source == 'folders') {
             foreach ((array)kolab_storage::search_folders('note', $query, array('other')) as $folder) {
                 $this->folders[$folder->id] = $folder;
-                $this->lists[$folder->id] = $this->folder_props($folder, $delim);
+                $this->lists[$folder->id] = $this->folder_props($folder);
             }
         }
         // search other user's namespace via LDAP
@@ -284,7 +280,7 @@ class kolab_notes extends rcube_plugin
             $limit = $this->rc->config->get('autocomplete_max', 15) * 2;  // we have slightly more space, so display twice the number
             foreach (kolab_storage::search_users($query, 0, array(), $limit * 10) as $user) {
                 $folders = array();
-                // search for tasks folders shared by this user
+                // search for note folders shared by this user
                 foreach (kolab_storage::list_user_folders($user, 'note', false) as $foldername) {
                     $folders[] = new kolab_storage_folder($foldername, 'note');
                 }
@@ -292,11 +288,11 @@ class kolab_notes extends rcube_plugin
                 if (count($folders)) {
                     $userfolder = new kolab_storage_folder_user($user['kolabtargetfolder'], '', $user);
                     $this->folders[$userfolder->id] = $userfolder;
-                    $this->lists[$userfolder->id] = $this->folder_props($userfolder, $delim, array());
+                    $this->lists[$userfolder->id] = $this->folder_props($userfolder);
 
                     foreach ($folders as $folder) {
                         $this->folders[$folder->id] = $folder;
-                        $this->lists[$folder->id] = $this->folder_props($folder, $delim, array());
+                        $this->lists[$folder->id] = $this->folder_props($folder);
                         $count++;
                     }
                 }
@@ -315,7 +311,7 @@ class kolab_notes extends rcube_plugin
     /**
      * Derive list properties from the given kolab_storage_folder object
      */
-    protected function folder_props($folder, $delim)
+    protected function folder_props($folder)
     {
         if ($folder->get_namespace() == 'personal') {
             $norename = false;
@@ -362,7 +358,7 @@ class kolab_notes extends rcube_plugin
             $folder = kolab_storage::get_folder(kolab_storage::id_decode($id));
             if ($folder->type) {
                 $this->folders[$id] = $folder;
-                $this->lists[$id] = $this->folder_props($folder, $this->rc->get_storage()->get_hierarchy_delimiter());
+                $this->lists[$id] = $this->folder_props($folder);
             }
         }
 


commit 5b2f441008b151fce8410d5a54fe73ce07d52888
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Jun 25 16:27:18 2014 +0200

    Allow subscribe buttons to be <span> elements and item IDs to be encoded

diff --git a/plugins/libkolab/js/folderlist.js b/plugins/libkolab/js/folderlist.js
index 2c12ab6..543673b 100644
--- a/plugins/libkolab/js/folderlist.js
+++ b/plugins/libkolab/js/folderlist.js
@@ -53,6 +53,8 @@ function kolab_folderlist(node, p)
 
               search_results_widget = new rcube_treelist_widget('<ul>', {
                   id_prefix: p.id_prefix,
+                  id_encode: p.id_encode,
+                  id_decode: p.id_decode,
                   selectable: false
               });
               // copy classes from main list
@@ -61,11 +63,13 @@ function kolab_folderlist(node, p)
               // register click handler on search result's checkboxes to select the given item for listing
               search_results_widget.container
                   .appendTo(search_results_container)
-                  .on('click', 'input[type=checkbox], a.subscribed', function(e) {
-                      var li = $(this).closest('li'),
-                          id = li.attr('id').replace(new RegExp('^'+p.id_prefix), '')
-                          node = search_results_widget.get_node(id),
-                          has_children = node.children && node.children.length;
+                  .on('click', 'input[type=checkbox], a.subscribed, span.subscribed', function(e) {
+                      var node, has_children, li = $(this).closest('li'),
+                          id = li.attr('id').replace(new RegExp('^'+p.id_prefix), '');
+                      if (p.id_decode)
+                          id = p.id_decode(id);
+                      node = search_results_widget.get_node(id),
+                      has_children = node.children && node.children.length;
 
                       // activate + subscribe
                       if ($(e.target).hasClass('subscribed')) {
@@ -83,8 +87,8 @@ function kolab_folderlist(node, p)
                       add_result2list(id, li, true);
 
                       if (has_children) {
-                          li.find('input[type=checkbox]').first().prop('disabled', true).get(0).checked = true;
-                          li.find('a.subscribed').first().hide();
+                          li.find('input[type=checkbox]').first().prop('disabled', true).prop('checked', true);
+                          li.find('a.subscribed, span.subscribed').first().hide();
                       }
                       else {
                           li.remove();
@@ -106,13 +110,14 @@ function kolab_folderlist(node, p)
                   id: prop.id,
                   classes: [ prop.group || '' ],
                   html: item,
-                  collapsed: true
+                  collapsed: true,
+                  virtual: prop.virtual
               }, prop.parent);
 
               // disable checkbox if item already exists in main list
               if (me.get_node(prop.id) && !me.get_node(prop.id).virtual) {
-                  item.find('input[type=checkbox]').first().prop('disabled', true).get(0).checked = true;
-                  item.find('a.subscribed').hide();
+                  item.find('input[type=checkbox]').first().prop('disabled', true).prop('checked', true);
+                  item.find('a.subscribed, span.subscribed').hide();
               }
           }
 
@@ -131,7 +136,7 @@ function kolab_folderlist(node, p)
 
         // find parent node and insert at the right place
         if (parent_id && me.get_node(parent_id)) {
-            dom_node.children('span,a').first().html(Q(prop.editname));
+            dom_node.children('span,a').first().html(Q(prop.editname || prop.listname));
         }
         else if (parent_id && search_results[parent_id]) {
             // copy parent tree from search results
@@ -217,11 +222,14 @@ function kolab_folderlist(node, p)
         }
     });
 
-    this.container.on('click', 'a.subscribed', function(e){
+    this.container.on('click', 'a.subscribed, span.subscribed', function(e){
         var li = $(this).closest('li'),
             id = li.attr('id').replace(new RegExp('^'+p.id_prefix), ''),
             div = li.children().first();
 
+        if (p.id_decode)
+            id = p.id_decode(id);
+
         div.toggleClass('subscribed');
         $(this).attr('aria-checked', div.hasClass('subscribed') ? 'true' : 'false');
         me.triggerEvent('subscribe', { id: id, subscribed: div.hasClass('subscribed'), item: li });




More information about the commits mailing list