From 32164e30bf6c3fc02143a838ca57bb1865a4d847 Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Wed, 25 Jun 2014 17:09:04 +0200 Subject: [PATCH] Add new folder navigation to address book (#3046). This patches/overwrites default functions from Roundcube core. Be careful when updating those! --- .../kolab_addressbook/kolab_addressbook.js | 85 +++++++ .../kolab_addressbook/kolab_addressbook.php | 225 +++++++++++++++--- .../lib/kolab_addressbook_ui.php | 14 +- .../lib/rcube_kolab_contacts.php | 10 + .../kolab_addressbook/localization/en_US.inc | 8 + .../skins/classic/kolab_addressbook.css | 16 +- .../skins/larry/folder_icons.png | Bin 3224 -> 1328 bytes .../skins/larry/kolab_addressbook.css | 81 +++++++ 8 files changed, 404 insertions(+), 35 deletions(-) diff --git a/plugins/kolab_addressbook/kolab_addressbook.js b/plugins/kolab_addressbook/kolab_addressbook.js index 82b6d9dd..c519a673 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); + $('') + .insertBefore(container.parent()); + + $('' + + rcmail.gettext('findaddressbooks', 'kolab_addressbook') + '') + .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 530580bb..7bada56b 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
  • 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 c7c1a7f5..898dd8cd 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 2e93d462..8bd18865 100644 --- a/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php +++ b/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php @@ -198,6 +198,16 @@ class rcube_kolab_contacts extends rcube_addressbook return $this->storagefolder->get_parent(); } + /** + * 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) */ diff --git a/plugins/kolab_addressbook/localization/en_US.inc b/plugins/kolab_addressbook/localization/en_US.inc index c1ab0f5e..11a45817 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 da3abed2..30b95939 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 62805ad24bc8b71173c9ee146718e150a6c3bd9e..07674ab098f0d4d545a3a72a9b35364704d3aa6a 100644 GIT binary patch delta 1322 zcmV+_1=aeP8L$eF7k{t_1^@s6ZX9g4000E{Nkl+wy2~D zi$)D@>SPzuYzsoUCCBF>F9gf|($^TzFZt(f-}997f1dX|Z-3u%A|eQaAP9mW2!bF8 zB22t~{Qbz7;~zw^<8z`We&fBQsJN2~S$tX+B&amNk8v`EOf&22L5OKxMW9OD^H> z+3_W1r3G|#&40OJPkuBd{lzqvOvz>(uhc{zK9Lw5uhK*-%IhHCV%Hs!DP9PxfwUrn zl+Ql1&JG{yO5h#!>BVDuwGHGKE&{8MSHo(+>b?}KGB<+O<^pwX3-H(csA+5mWn}~K zcz6wT^~5S!(wyebRpRG)JnSo=U^CUQ6$q6=j1{oBy??Rt@_NX$wyhFB&*Nb=@YnS8 z!Kq*nrh+qTCr9- zh-yG~PJdQa$)1ic+CW)W3(Ar*m8b@?YMP+fGXQ$;6)5cJ1(VaAFRFpOCKs6cK7|YY zpMn0;RevaKcAXQ|fT_c`Y{1|fg#4!V^P(E)9vUt%^b9Vq!1a+)aaZ8Z*xh%}yZaYc z!PxDGAAY%iSX2Xp!?*T-`}2M1xN#fC#vkhHT%P@+B!2UwlTpXy8HW-JiVM_ctFHap z7g>BeCh3ZG{5+3`D{kLY zrv{R8&%TmUQUSTPHYj5Io);jmetsV?`aW5F&(HHXAIHbitpgk9vR9`=j0; z^?&{df*=TjAP9mW2!bF8g7{aVh7~oes9{A7D{5F#!-^VK1VIpl*XxyV?e_1OOeUFL zub1q^KrfSHH_6RrvqW2Lz}D8*f}PC%9#deKtEf^KtFXoG`E9!&lrdEcO4zZ1%l;8{ zogFLN+-S4eB-=6IV3G>j(Pe08XpmI5b$=pzo4I;#`xG( zzZFdtRZo(0@{voHTY#Cy3e;L!@r=`p+18IStGotN@{4e3OdjZQrGX(~xh0#_g+nm3 zYTVS+)U#V70X)r;vIkg9uWdmsn|&TLkZbS4Op62AlQk+TEa)7#iW3tP7@8oP7k?dJ z_BE1Ag;@3mk8{yXfj^%i_RU2mBa_@xNF5eZ8lv(*Cp{O0XIM!f?yyZ{Ad+gxbt z8NlxDZpl`vKW6DZAiQ2SwiU|lZGW?>T053`{rKzS3G5mE3NstqQB%{j>2BajK}Z}e zD6n*HE6JTsXF^709lEa%6kh*b-*P3xf7 zf2HfoZvr?wJIj-23w0q~SXdZ+@aQqRe0{jl3P^Vn3)zmPd^hlWa1^@s6 literal 3224 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 KMNUMnLSTaFoA_G* diff --git a/plugins/kolab_addressbook/skins/larry/kolab_addressbook.css b/plugins/kolab_addressbook/skins/larry/kolab_addressbook.css index 2cfac3b6..00f34e8a 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 {