From 1a4562ff045edeba35159fcb4c68b00cc239ddc1 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Mon, 15 Feb 2016 16:02:18 +0100 Subject: [PATCH 001/483] Fix bug where editor height is very small When editing a note attached to email (#4600) --- plugins/kolab_notes/skins/larry/templates/dialogview.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/kolab_notes/skins/larry/templates/dialogview.html b/plugins/kolab_notes/skins/larry/templates/dialogview.html index b42af928..03ec2224 100644 --- a/plugins/kolab_notes/skins/larry/templates/dialogview.html +++ b/plugins/kolab_notes/skins/larry/templates/dialogview.html @@ -50,7 +50,7 @@ $(document).ready(function(e){ // fixes issue when toolbar is not ready yet and content // area height is set to 0, wait and try again later... - if (h < 0) + if (h < 40) setTimeout(function() { layout_view(); }, 100); } From ef640cded17b3bdd13e3bb307afde7c90e1a7db7 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Mon, 22 Feb 2016 19:45:38 +0100 Subject: [PATCH 002/483] Fix logging of chwala communication errors --- plugins/kolab_files/lib/kolab_files_engine.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/kolab_files/lib/kolab_files_engine.php b/plugins/kolab_files/lib/kolab_files_engine.php index da5aae88..2828953b 100644 --- a/plugins/kolab_files/lib/kolab_files_engine.php +++ b/plugins/kolab_files/lib/kolab_files_engine.php @@ -797,7 +797,7 @@ class kolab_files_engine $quota = $body['result']; } else { - throw new Exception($body['reason']); + throw new Exception($body['reason'] ?: "Failed to get quota. Status: $status"); } } catch (Exception $e) { @@ -1076,7 +1076,7 @@ class kolab_files_engine $files[] = $attach_name; } else { - throw new Exception($body['reason']); + throw new Exception($body['reason'] ?: "Failed to post file_upload. Status: $status"); } } catch (Exception $e) { @@ -1165,7 +1165,7 @@ class kolab_files_engine $file_params = $body['result']; } else { - throw new Exception($body['reason']); + throw new Exception($body['reason'] ?: "Failed to get file_info. Status: $status"); } } catch (Exception $e) { @@ -1321,7 +1321,7 @@ class kolab_files_engine $this->file_data = $body['result']; } else { - throw new Exception($body['reason']); + throw new Exception($body['reason'] ?: "Failed to get file_info. Status: $status"); } } catch (Exception $e) { @@ -1375,7 +1375,7 @@ class kolab_files_engine $mimetypes = $body['result']; } else { - throw new Exception($body['reason']); + throw new Exception($body['reason'] ?: "Failed to get mimetypes. Status: $status"); } } catch (Exception $e) { @@ -1407,7 +1407,7 @@ class kolab_files_engine $sources = $body['result']; } else { - throw new Exception($body['reason']); + throw new Exception($body['reason'] ?: "Failed to get folder_types. Status: $status"); } } catch (Exception $e) { From 6fc46f682337ba76b123dd504686521417261905 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Mon, 22 Feb 2016 19:54:58 +0100 Subject: [PATCH 003/483] Add some labels to the client-side even if ext sources fetching failed --- plugins/kolab_files/lib/kolab_files_engine.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/kolab_files/lib/kolab_files_engine.php b/plugins/kolab_files/lib/kolab_files_engine.php index 2828953b..2ce540bd 100644 --- a/plugins/kolab_files/lib/kolab_files_engine.php +++ b/plugins/kolab_files/lib/kolab_files_engine.php @@ -101,6 +101,9 @@ class kolab_files_engine // get list of external sources $this->get_external_storage_drivers(); + + // these labels may be needed even if fetching ext sources failed + $this->plugin->add_label('folderauthtitle', 'authenticating'); } if ($list_widget) { From ffdc0454e0625cf1478d0c74bbe38d365e040f5f Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 25 Feb 2016 18:16:54 +0100 Subject: [PATCH 004/483] Performance: Load tags UI (and initialize configuration folder/cache) on when it's needed --- plugins/kolab_tags/kolab_tags.php | 4 -- plugins/kolab_tags/lib/kolab_tags_engine.php | 44 +++++++++++--------- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/plugins/kolab_tags/kolab_tags.php b/plugins/kolab_tags/kolab_tags.php index c3a18469..730d50cd 100644 --- a/plugins/kolab_tags/kolab_tags.php +++ b/plugins/kolab_tags/kolab_tags.php @@ -79,10 +79,6 @@ class kolab_tags extends rcube_plugin return; } - if ($this->rc->action == 'print') { - return; - } - if ($engine = $this->engine()) { $engine->ui(); } diff --git a/plugins/kolab_tags/lib/kolab_tags_engine.php b/plugins/kolab_tags/lib/kolab_tags_engine.php index b1158fc7..b9840494 100644 --- a/plugins/kolab_tags/lib/kolab_tags_engine.php +++ b/plugins/kolab_tags/lib/kolab_tags_engine.php @@ -47,26 +47,32 @@ class kolab_tags_engine public function ui() { // set templates of Files UI and widgets - if ($this->rc->task == 'mail') { - $this->plugin->add_texts('localization/'); - - $this->plugin->include_stylesheet($this->plugin->local_skin_path().'/style.css'); - $this->plugin->include_script('kolab_tags.js'); - $this->rc->output->add_label('cancel', 'save'); - $this->plugin->add_label('tags', 'add', 'edit', 'delete', 'saving', - 'nameempty', 'nameexists', 'colorinvalid', 'untag', 'tagname', - 'tagcolor', 'tagsearchnew', 'newtag'); - - $this->rc->output->add_handlers(array( - 'plugin.taglist' => array($this, 'taglist'), - )); - - $ui = $this->rc->output->parse('kolab_tags.ui', false, false); - $this->rc->output->add_footer($ui); - - // load miniColors - jqueryui::miniColors(); + if ($this->rc->task != 'mail') { + return; } + + if ($this->rc->action && !in_array($this->rc->action, array('show', 'preview'))) { + return; + } + + $this->plugin->add_texts('localization/'); + + $this->plugin->include_stylesheet($this->plugin->local_skin_path().'/style.css'); + $this->plugin->include_script('kolab_tags.js'); + $this->rc->output->add_label('cancel', 'save'); + $this->plugin->add_label('tags', 'add', 'edit', 'delete', 'saving', + 'nameempty', 'nameexists', 'colorinvalid', 'untag', 'tagname', + 'tagcolor', 'tagsearchnew', 'newtag'); + + $this->rc->output->add_handlers(array( + 'plugin.taglist' => array($this, 'taglist'), + )); + + $ui = $this->rc->output->parse('kolab_tags.ui', false, false); + $this->rc->output->add_footer($ui); + + // load miniColors + jqueryui::miniColors(); } /** From e6a9819c3268400c1ce53d1f316ac6372232fa7b Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 25 Feb 2016 18:40:54 +0100 Subject: [PATCH 005/483] Performance: Don't load tasklist UI elements when not needed --- plugins/tasklist/tasklist_ui.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/tasklist/tasklist_ui.php b/plugins/tasklist/tasklist_ui.php index c67710b8..17b13e91 100644 --- a/plugins/tasklist/tasklist_ui.php +++ b/plugins/tasklist/tasklist_ui.php @@ -44,6 +44,10 @@ class tasklist_ui return; } + if ($this->rc->action && !in_array($this->rc->action, array('show', 'preview', 'print', 'index'))) { + return; + } + // add taskbar button $this->plugin->add_button(array( 'command' => 'tasks', From 6f8465e9a84b6e57e20c210e4d11d3c0d5d24b4c Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 25 Feb 2016 19:33:59 +0100 Subject: [PATCH 006/483] Fix duplicated event after editing an event with tasklist change (#5302) --- plugins/tasklist/tasklist.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/plugins/tasklist/tasklist.php b/plugins/tasklist/tasklist.php index 6b43e167..9e0b3a41 100644 --- a/plugins/tasklist/tasklist.php +++ b/plugins/tasklist/tasklist.php @@ -252,12 +252,16 @@ class tasklist extends rcube_plugin $rec = $this->prepare_task($rec); $clone = $this->handle_recurrence($rec, $this->driver->get_task($rec)); if ($success = $this->driver->edit_task($rec)) { - $refresh[] = $this->driver->get_task($rec); + $new_task = $this->driver->get_task($rec); + $new_task['tempid'] = $rec['id']; + $refresh[] = $new_task; $this->cleanup_task($rec); // add clone from recurring task if ($clone && $this->driver->create_task($clone)) { - $refresh[] = $this->driver->get_task($clone); + $new_clone = $this->driver->get_task($clone); + $new_clone['tempid'] = $clone['id']; + $refresh[] = $new_clone; $this->driver->clear_alarms($rec['id']); } @@ -268,6 +272,7 @@ class tasklist extends rcube_plugin if ($this->driver->move_task($child)) { $r = $this->driver->get_task($child); if ((bool)($filter & self::FILTER_MASK_COMPLETE) == $this->driver->is_complete($r)) { + $r['tempid'] = $cid; $refresh[] = $r; } } From 4794dde5ded5337afcf317894e4ce6f4254b13dc Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 25 Feb 2016 20:44:01 +0100 Subject: [PATCH 007/483] Fix height of Participants table header --- plugins/calendar/skins/larry/calendar.css | 2 +- plugins/tasklist/skins/larry/tasklist.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/calendar/skins/larry/calendar.css b/plugins/calendar/skins/larry/calendar.css index 50c5910b..f2087fe4 100644 --- a/plugins/calendar/skins/larry/calendar.css +++ b/plugins/calendar/skins/larry/calendar.css @@ -1112,7 +1112,7 @@ td.topalign { #eventedit .edit-attendees-table th.invite, #eventedit .edit-attendees-table td.invite { - width: 44px; + width: 50px; padding: 2px; } diff --git a/plugins/tasklist/skins/larry/tasklist.css b/plugins/tasklist/skins/larry/tasklist.css index a8748ede..fc7982c8 100644 --- a/plugins/tasklist/skins/larry/tasklist.css +++ b/plugins/tasklist/skins/larry/tasklist.css @@ -957,7 +957,7 @@ a.morelink:hover { #taskedit .edit-attendees-table th.invite, #taskedit .edit-attendees-table td.invite { - width: 48px; + width: 50px; padding: 2px; } From 261a78c505ce8574e0005a03b2350207c666be60 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 26 Feb 2016 11:18:41 +0100 Subject: [PATCH 008/483] Fix bug where read-only folder was displayed as read-write in delegation form and it wasn't possible to unset ACL on a folder (#5347) --- plugins/kolab_delegation/kolab_delegation.js | 4 ++-- .../kolab_delegation/kolab_delegation_engine.php | 16 +++++++--------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/plugins/kolab_delegation/kolab_delegation.js b/plugins/kolab_delegation/kolab_delegation.js index 01d6d30b..c1bbe9b6 100644 --- a/plugins/kolab_delegation/kolab_delegation.js +++ b/plugins/kolab_delegation/kolab_delegation.js @@ -188,8 +188,8 @@ rcube_webmail.prototype.delegate_save = function() } data.folders = {}; - $('input.read:checked').each(function(i, elem) { - data.folders[elem.value] = 1; + $('input.read').each(function(i, elem) { + data.folders[elem.value] = this.checked ? 1 : 0; }); $('input.write:checked').each(function(i, elem) { data.folders[elem.value] = 2; diff --git a/plugins/kolab_delegation/kolab_delegation_engine.php b/plugins/kolab_delegation/kolab_delegation_engine.php index 88e4563b..e1a62299 100644 --- a/plugins/kolab_delegation/kolab_delegation_engine.php +++ b/plugins/kolab_delegation/kolab_delegation_engine.php @@ -105,6 +105,9 @@ class kolab_delegation_engine if ($r) { $storage->set_acl($folder_name, $uid, $r); } + else { + $storage->delete_acl($folder_name, $uid); + } if (!empty($folders) && isset($folders[$folder_name])) { unset($folders[$folder_name]); @@ -863,12 +866,10 @@ class kolab_delegation_engine /** * Compares two ACLs (according to supported rights) * - * @todo: this is stolen from acl plugin, move to rcube_storage/rcube_imap - * * @param array $acl1 ACL rights array (or string) * @param array $acl2 ACL rights array (or string) * - * @param int Comparision result, 2 - full match, 1 - partial match, 0 - no match + * @param bool True if $acl1 contains all rights from $acl2 */ function acl_compare($acl1, $acl2) { @@ -884,12 +885,9 @@ class kolab_delegation_engine $cnt1 = count($res); $cnt2 = count($acl2); - if ($cnt1 == $cnt2) - return 2; - else if ($cnt1) - return 1; - else - return 0; + if ($cnt1 >= $cnt2) { + return true; + } } /** From 5b9c5b6f5ffa155f23e2f4dab4b1c218dcb86539 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Mon, 29 Feb 2016 10:46:14 +0100 Subject: [PATCH 009/483] Remove kolab_zpush plugin (T1042) --- plugins/kolab_zpush/LICENSE | 661 ------------------ plugins/kolab_zpush/README | 5 - plugins/kolab_zpush/kolab_zpush.js | 181 ----- plugins/kolab_zpush/kolab_zpush.php | 349 --------- plugins/kolab_zpush/kolab_zpush_ui.php | 169 ----- plugins/kolab_zpush/localization/de_CH.inc | 29 - plugins/kolab_zpush/localization/de_DE.inc | 29 - plugins/kolab_zpush/localization/en_US.inc | 33 - plugins/kolab_zpush/localization/pl_PL.inc | 29 - plugins/kolab_zpush/package.xml | 72 -- .../kolab_zpush/skins/classic/alarm-clock.png | Bin 841 -> 0 bytes plugins/kolab_zpush/skins/classic/config.css | 119 ---- .../skins/classic/deviceactions.png | Bin 1233 -> 0 bytes .../kolab_zpush/skins/classic/foldertypes.png | Bin 2291 -> 0 bytes .../skins/classic/pointer-left.gif | Bin 173 -> 0 bytes .../kolab_zpush/skins/classic/synchronize.png | Bin 836 -> 0 bytes .../skins/classic/templates/config.html | 63 -- .../kolab_zpush/skins/larry/alarm-clock.png | Bin 841 -> 0 bytes plugins/kolab_zpush/skins/larry/config.css | 135 ---- .../kolab_zpush/skins/larry/foldertypes.png | Bin 2291 -> 0 bytes .../kolab_zpush/skins/larry/pointer-left.png | Bin 1283 -> 0 bytes .../kolab_zpush/skins/larry/synchronize.png | Bin 836 -> 0 bytes .../skins/larry/templates/config.html | 71 -- 23 files changed, 1945 deletions(-) delete mode 100644 plugins/kolab_zpush/LICENSE delete mode 100644 plugins/kolab_zpush/README delete mode 100644 plugins/kolab_zpush/kolab_zpush.js delete mode 100644 plugins/kolab_zpush/kolab_zpush.php delete mode 100644 plugins/kolab_zpush/kolab_zpush_ui.php delete mode 100644 plugins/kolab_zpush/localization/de_CH.inc delete mode 100644 plugins/kolab_zpush/localization/de_DE.inc delete mode 100644 plugins/kolab_zpush/localization/en_US.inc delete mode 100644 plugins/kolab_zpush/localization/pl_PL.inc delete mode 100644 plugins/kolab_zpush/package.xml delete mode 100755 plugins/kolab_zpush/skins/classic/alarm-clock.png delete mode 100644 plugins/kolab_zpush/skins/classic/config.css delete mode 100644 plugins/kolab_zpush/skins/classic/deviceactions.png delete mode 100644 plugins/kolab_zpush/skins/classic/foldertypes.png delete mode 100644 plugins/kolab_zpush/skins/classic/pointer-left.gif delete mode 100755 plugins/kolab_zpush/skins/classic/synchronize.png delete mode 100644 plugins/kolab_zpush/skins/classic/templates/config.html delete mode 100755 plugins/kolab_zpush/skins/larry/alarm-clock.png delete mode 100644 plugins/kolab_zpush/skins/larry/config.css delete mode 100644 plugins/kolab_zpush/skins/larry/foldertypes.png delete mode 100644 plugins/kolab_zpush/skins/larry/pointer-left.png delete mode 100755 plugins/kolab_zpush/skins/larry/synchronize.png delete mode 100644 plugins/kolab_zpush/skins/larry/templates/config.html diff --git a/plugins/kolab_zpush/LICENSE b/plugins/kolab_zpush/LICENSE deleted file mode 100644 index dba13ed2..00000000 --- a/plugins/kolab_zpush/LICENSE +++ /dev/null @@ -1,661 +0,0 @@ - GNU AFFERO GENERAL PUBLIC LICENSE - Version 3, 19 November 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU Affero General Public License is a free, copyleft license for -software and other kinds of works, specifically designed to ensure -cooperation with the community in the case of network server software. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -our General Public Licenses are intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - Developers that use our General Public Licenses protect your rights -with two steps: (1) assert copyright on the software, and (2) offer -you this License which gives you legal permission to copy, distribute -and/or modify the software. - - A secondary benefit of defending all users' freedom is that -improvements made in alternate versions of the program, if they -receive widespread use, become available for other developers to -incorporate. Many developers of free software are heartened and -encouraged by the resulting cooperation. However, in the case of -software used on network servers, this result may fail to come about. -The GNU General Public License permits making a modified version and -letting the public access it on a server without ever releasing its -source code to the public. - - The GNU Affero General Public License is designed specifically to -ensure that, in such cases, the modified source code becomes available -to the community. It requires the operator of a network server to -provide the source code of the modified version running there to the -users of that server. Therefore, public use of a modified version, on -a publicly accessible server, gives the public access to the source -code of the modified version. - - An older license, called the Affero General Public License and -published by Affero, was designed to accomplish similar goals. This is -a different license, not a version of the Affero GPL, but Affero has -released a new version of the Affero GPL which permits relicensing under -this license. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU Affero General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Remote Network Interaction; Use with the GNU General Public License. - - Notwithstanding any other provision of this License, if you modify the -Program, your modified version must prominently offer all users -interacting with it remotely through a computer network (if your version -supports such interaction) an opportunity to receive the Corresponding -Source of your version by providing access to the Corresponding Source -from a network server at no charge, through some standard or customary -means of facilitating copying of software. This Corresponding Source -shall include the Corresponding Source for any work covered by version 3 -of the GNU General Public License that is incorporated pursuant to the -following paragraph. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the work with which it is combined will remain governed by version -3 of the GNU General Public License. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU Affero General Public License from time to time. Such new versions -will be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU Affero General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU Affero General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU Affero General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If your software can interact with users remotely through a computer -network, you should also make sure that it provides a way for users to -get its source. For example, if your program is a web application, its -interface could display a "Source" link that leads users to an archive -of the code. There are many ways you could offer source, and different -solutions will be better for different programs; see section 13 for the -specific requirements. - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU AGPL, see -. diff --git a/plugins/kolab_zpush/README b/plugins/kolab_zpush/README deleted file mode 100644 index ccd513ae..00000000 --- a/plugins/kolab_zpush/README +++ /dev/null @@ -1,5 +0,0 @@ -Icons by Fugue Icons - -Copyright (C) 2010 Yusuke Kamiyamane. All rights reserved. -The icons are licensed under a Creative Commons Attribution -3.0 license. diff --git a/plugins/kolab_zpush/kolab_zpush.js b/plugins/kolab_zpush/kolab_zpush.js deleted file mode 100644 index 599911a8..00000000 --- a/plugins/kolab_zpush/kolab_zpush.js +++ /dev/null @@ -1,181 +0,0 @@ -/** - * Client scripts for the Kolab Z-Push configuration utitlity - * - * @version 0.2 - * @author Thomas Bruederli - * - * Copyright (C) 2011, Kolab Systems AG - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -function kolab_zpush_config() -{ - /* private members */ - var me = this; - var http_lock = null; - var active_device = null; - - - /* constructor */ - var devicelist = new rcube_list_widget(rcmail.gui_objects.devicelist, - { multiselect:true, draggable:false, keyboard:true }); - devicelist.addEventListener('select', select_device); - devicelist.init(); - - rcmail.register_command('plugin.save-config', save_config); - rcmail.register_command('plugin.delete-device', delete_device_config); - rcmail.addEventListener('plugin.zpush_data_ready', device_data_ready); - rcmail.addEventListener('plugin.zpush_save_complete', save_complete); - - $('input.alarm').change(function(e){ if (this.checked) $('#'+this.id.replace(/_alarm/, '')).prop('checked', this.checked); }); - $('input.subscription').change(function(e){ if (!this.checked) $('#'+this.id+'_alarm').prop('checked', false); }); - $(window).bind('resize', resize_ui); - - $('.subscriptionblock thead td.subscription img, .subscriptionblock thead td.alarm img').click(function(e){ - var $this = $(this); - var classname = $this.parent().get(0).className; - var check = !($this.data('checked') || false); - $this.css('cursor', 'pointer').data('checked', check) - .closest('table').find('input.'+classname).prop('checked', check).change(); - }); - - // select the one and only device from list - if (rcmail.env.devicecount == 1) { - for (var imei in rcmail.env.devices) - break; - devicelist.select(imei); - } - - /* private methods */ - function select_device(list) - { - active_device = list.get_single_selection(); - rcmail.enable_command('plugin.save-config', 'plugin.delete-device', true); - - if (active_device) { - http_lock = rcmail.set_busy(true, 'loading'); - rcmail.http_request('plugin.zpushjson', { cmd:'load', id:active_device }, http_lock); - } - - $('#introtext').hide(); - } - - // callback from server after loading device data - function device_data_ready(data) - { - // reset form first - $('input.alarm:checked').prop('checked', false); - $('input.subscription:checked').prop('checked', false).change(); - - if (data.id && data.id == active_device) { - $('#config-device-alias').val(data.devicealias); - $('#config-device-mode').val(data.syncmode); - $('#config-device-laxpic').prop('checked', data.laxpic ? true : false); - - $('input.subscription').each(function(i, elem){ - var key = elem.value; - elem.checked = data.subscribed[key] ? true : false; - }).change(); - $('input.alarm').each(function(i, elem){ - var key = elem.value; - elem.checked = data.subscribed[key] == 2; - }); - - $('#configform, #prefs-box .boxtitle').show(); - resize_ui(); - } - else { - $('#configform, #prefs-box .boxtitle').hide(); - } - } - - // submit current configuration form to server - function save_config() - { - // TODO: validate device info - var data = { - cmd: 'save', - id: active_device, - devicealias: $('#config-device-alias').val(), - syncmode: $('#config-device-mode option:selected').val(), - laxpic: $('#config-device-laxpic').get(0).checked ? 1 : 0 - }; - - data.subscribed = {}; - $('input.subscription:checked').each(function(i, elem){ - data.subscribed[elem.value] = 1; - }); - $('input.alarm:checked').each(function(i, elem){ - if (data.subscribed[elem.value]) - data.subscribed[elem.value] = 2; - }); - - http_lock = rcmail.set_busy(true, 'kolab_zpush.savingdata'); - rcmail.http_post('plugin.zpushjson', data, http_lock); - } - - // callback function when saving has completed - function save_complete(p) - { - if (p.success && p.devicealias) { - $('#devices-table tr.selected span.devicealias').html(p.devicealias); - rcmail.env.devices[p.id].ALIAS = p.devicealias; - } - } - - // handler for delete commands - function delete_device_config() - { - if (active_device && confirm(rcmail.gettext('devicedeleteconfirm', 'kolab_zpush'))) { - http_lock = rcmail.set_busy(true, 'kolab_zpush.savingdata'); - rcmail.http_post('plugin.zpushjson', { cmd:'delete', id:active_device }, http_lock); - } - } - - // handler for window resize events: sets max-height of folders list scroll container - function resize_ui() - { - if (active_device) { - var h = $(window).height(); - var pos = $('#foldersubscriptions').offset(); - $('#foldersubscriptions').css('max-height', (h - pos.top - 90) + 'px'); - } - } -} - - -window.rcmail && rcmail.addEventListener('init', function(evt) { - var ACTION_CONFIG = 'plugin.zpushconfig'; - - // add button to tabs list - var tab = $('').attr('id', 'settingstabpluginzpushconfig').addClass('tablink'); - var button = $('').attr('href', rcmail.env.comm_path+'&_action=plugin.zpushconfig').html(rcmail.gettext('tabtitle', 'kolab_zpush')).appendTo(tab); - rcmail.add_element(tab, 'tabs'); - - if (rcmail.env.action == ACTION_CONFIG) - new kolab_zpush_config(); -}); - - -// extend jQuery -(function($){ - $.fn.serializeJSON = function(){ - var json = {}; - jQuery.map($(this).serializeArray(), function(n, i) { - json[n['name']] = n['value']; - }); - return json; - }; -})(jQuery); diff --git a/plugins/kolab_zpush/kolab_zpush.php b/plugins/kolab_zpush/kolab_zpush.php deleted file mode 100644 index 6107f2c0..00000000 --- a/plugins/kolab_zpush/kolab_zpush.php +++ /dev/null @@ -1,349 +0,0 @@ - - * - * Copyright (C) 2012, Kolab Systems AG - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -class kolab_zpush extends rcube_plugin -{ - public $task = 'settings'; - public $urlbase; - - private $rc; - private $ui; - private $cache; - private $devices; - private $folders; - private $folders_meta; - private $root_meta; - - const ROOT_MAILBOX = 'INBOX'; - const CTYPE_KEY = '/shared/vendor/kolab/folder-type'; - const ACTIVESYNC_KEY = '/private/vendor/kolab/activesync'; - - /** - * Plugin initialization. - */ - public function init() - { - $this->rc = rcube::get_instance(); - - $this->require_plugin('jqueryui'); - $this->add_texts('localization/', true); - - $this->include_script('kolab_zpush.js'); - - $this->register_action('plugin.zpushconfig', array($this, 'config_view')); - $this->register_action('plugin.zpushjson', array($this, 'json_command')); - - if ($this->rc->action == 'plugin.zpushconfig') { - $this->require_plugin('libkolab'); - } - } - - - /** - * Establish IMAP connection - */ - public function init_imap() - { - $storage = $this->rc->get_storage(); - - // @TODO: Metadata is already cached by rcube storage, get rid of cache here - - $this->cache = $this->rc->get_cache('zpush', 'db', 900); - $this->cache->expunge(); - - if ($meta = $storage->get_metadata(self::ROOT_MAILBOX, self::ACTIVESYNC_KEY)) { - // clear cache if device config changed - if (($oldmeta = $this->cache->read('devicemeta')) && $oldmeta != $meta) - $this->cache->remove(); - - $this->root_meta = $this->unserialize_metadata($meta[self::ROOT_MAILBOX][self::ACTIVESYNC_KEY]); - $this->cache->remove('devicemeta'); - $this->cache->write('devicemeta', $meta); - } - } - - - /** - * Handle JSON requests - */ - public function json_command() - { - $storage = $this->rc->get_storage(); - $cmd = rcube_utils::get_input_value('cmd', rcube_utils::INPUT_GPC); - $imei = rcube_utils::get_input_value('id', rcube_utils::INPUT_GPC); - - switch ($cmd) { - case 'load': - $result = array(); - $devices = $this->list_devices(); - if ($device = $devices[$imei]) { - $result['id'] = $imei; - $result['devicealias'] = $device['ALIAS']; - $result['syncmode'] = intval($device['MODE']); - $result['laxpic'] = intval($device['LAXPIC']); - $result['subscribed'] = array(); - - foreach ($this->folders_meta() as $folder => $meta) { - if ($meta[$imei]['S']) - $result['subscribed'][$folder] = intval($meta[$imei]['S']); - } - - $this->rc->output->command('plugin.zpush_data_ready', $result); - } - else { - $this->rc->output->show_message($this->gettext('devicenotfound'), 'error'); - } - break; - - case 'save': - $syncmode = intval(rcube_utils::get_input_value('syncmode', rcube_utils::INPUT_POST)); - $laxpic = intval(rcube_utils::get_input_value('laxpic', rcube_utils::INPUT_POST)); - $devicealias = rcube_utils::get_input_value('devicealias', rcube_utils::INPUT_POST, true); - $subsciptions = rcube_utils::get_input_value('subscribed', rcube_utils::INPUT_POST); - $devices = $this->list_devices(); - $err = false; - - if ($device = $devices[$imei]) { - // update device config if changed - if ($devicealias != $this->root_meta['DEVICE'][$imei]['ALIAS'] || - $syncmode != $this->root_meta['DEVICE'][$imei]['MODE'] || - $laxpic != $this->root_meta['DEVICE'][$imei]['LAXPIC'] || - $subsciptions[self::ROOT_MAILBOX] != $this->root_meta['FOLDER'][$imei]['S']) { - $this->root_meta['DEVICE'][$imei]['MODE'] = $syncmode; - $this->root_meta['DEVICE'][$imei]['ALIAS'] = $devicealias; - $this->root_meta['DEVICE'][$imei]['LAXPIC'] = $laxpic; - $this->root_meta['FOLDER'][$imei]['S'] = intval($subsciptions[self::ROOT_MAILBOX]); - - $err = !$storage->set_metadata(self::ROOT_MAILBOX, - array(self::ACTIVESYNC_KEY => $this->serialize_metadata($this->root_meta))); - - // update cached meta data - if (!$err) { - $this->cache->remove('devicemeta'); - $this->cache->write('devicemeta', $storage->get_metadata(self::ROOT_MAILBOX, self::ACTIVESYNC_KEY)); - } - } - // iterate over folders list and update metadata if necessary - foreach ($this->folders_meta() as $folder => $meta) { - // skip root folder (already handled above) - if ($folder == self::ROOT_MAILBOX) - continue; - - if ($subsciptions[$folder] != $meta[$imei]['S']) { - $meta[$imei]['S'] = intval($subsciptions[$folder]); - $this->folders_meta[$folder] = $meta; - unset($meta['TYPE']); - - // read metadata first - $folderdata = $storage->get_metadata($folder, array(self::ACTIVESYNC_KEY)); - if ($asyncdata = $folderdata[$folder][self::ACTIVESYNC_KEY]) - $metadata = $this->unserialize_metadata($asyncdata); - $metadata['FOLDER'] = $meta; - - $err |= !$storage->set_metadata($folder, array(self::ACTIVESYNC_KEY => $this->serialize_metadata($metadata))); - } - } - - // update cache - $this->cache->remove('folders'); - $this->cache->write('folders', $this->folders_meta); - - $this->rc->output->command('plugin.zpush_save_complete', array('success' => !$err, 'id' => $imei, 'devicealias' => rcube::Q($devicealias))); - } - - if ($err) - $this->rc->output->show_message($this->gettext('savingerror'), 'error'); - else - $this->rc->output->show_message($this->gettext('successfullysaved'), 'confirmation'); - - break; - - case 'delete': - $devices = $this->list_devices(); - - if ($device = $devices[$imei]) { - unset($this->root_meta['DEVICE'][$imei], $this->root_meta['FOLDER'][$imei]); - - // update annotation and cached meta data - if ($success = $storage->set_metadata(self::ROOT_MAILBOX, array(self::ACTIVESYNC_KEY => $this->serialize_metadata($this->root_meta)))) { - $this->cache->remove('devicemeta'); - $this->cache->write('devicemeta', $storage->get_metadata(self::ROOT_MAILBOX, self::ACTIVESYNC_KEY)); - - // remove device annotation in every folder - foreach ($this->folders_meta() as $folder => $meta) { - // skip root folder (already handled above) - if ($folder == self::ROOT_MAILBOX) - continue; - - if (isset($meta[$imei])) { - $type = $meta['TYPE']; // remember folder type - unset($meta[$imei], $meta['TYPE']); - - // read metadata first and update FOLDER property - $folderdata = $storage->get_metadata($folder, array(self::ACTIVESYNC_KEY)); - if ($asyncdata = $folderdata[$folder][self::ACTIVESYNC_KEY]) - $metadata = $this->unserialize_metadata($asyncdata); - $metadata['FOLDER'] = $meta; - - if ($storage->set_metadata($folder, array(self::ACTIVESYNC_KEY => $this->serialize_metadata($metadata)))) { - $this->folders_meta[$folder] = $metadata; - $this->folders_meta[$folder]['TYPE'] = $type; - } - } - } - - // update cache - $this->cache->remove('folders'); - $this->cache->write('folders', $this->folders_meta); - } - } - - if ($success) { - $this->rc->output->show_message($this->gettext('successfullydeleted'), 'confirmation'); - $this->rc->output->redirect(array('action' => 'plugin.zpushconfig')); // reload UI - } - else - $this->rc->output->show_message($this->gettext('savingerror'), 'error'); - - break; - } - - $this->rc->output->send(); - } - - - /** - * Render main UI for device configuration - */ - public function config_view() - { - require_once $this->home . '/kolab_zpush_ui.php'; - - $storage = $this->rc->get_storage(); - - // checks if IMAP server supports any of METADATA, ANNOTATEMORE, ANNOTATEMORE2 - if (!($storage->get_capability('METADATA') || $storage->get_capability('ANNOTATEMORE') || $storage->get_capability('ANNOTATEMORE2'))) { - $this->rc->output->show_message($this->gettext('notsupported'), 'error'); - } - - $this->ui = new kolab_zpush_ui($this); - - $this->register_handler('plugin.devicelist', array($this->ui, 'device_list')); - $this->register_handler('plugin.deviceconfigform', array($this->ui, 'device_config_form')); - $this->register_handler('plugin.foldersubscriptions', array($this->ui, 'folder_subscriptions')); - - $this->rc->output->set_env('devicecount', count($this->list_devices())); - $this->rc->output->send('kolab_zpush.config'); - } - - - /** - * List known devices - * - * @return array Device list as hash array - */ - public function list_devices() - { - if (!isset($this->devices)) { - $this->init_imap(); - $this->devices = (array)$this->root_meta['DEVICE']; - } - - return $this->devices; - } - - - /** - * Get list of all folders available for sync - * - * @return array List of mailbox folders - */ - public function list_folders() - { - if (!isset($this->folders)) { - // read cached folder meta data - if ($cached_folders = $this->cache->read('folders')) { - $this->folders_meta = $cached_folders; - $this->folders = array_keys($this->folders_meta); - } - // fetch folder data from server - else { - $storage = $this->rc->get_storage(); - $this->folders = $storage->list_folders(); - - foreach ($this->folders as $folder) { - $folderdata = $storage->get_metadata($folder, array(self::ACTIVESYNC_KEY, self::CTYPE_KEY)); - $foldertype = explode('.', $folderdata[$folder][self::CTYPE_KEY]); - - if ($asyncdata = $folderdata[$folder][self::ACTIVESYNC_KEY]) { - $metadata = $this->unserialize_metadata($asyncdata); - $this->folders_meta[$folder] = $metadata['FOLDER']; - } - $this->folders_meta[$folder]['TYPE'] = !empty($foldertype[0]) ? $foldertype[0] : 'mail'; - } - - // cache it! - $this->cache->write('folders', $this->folders_meta); - } - } - - return $this->folders; - } - - /** - * Getter for folder metadata - * - * @return array Hash array with meta data for each folder - */ - public function folders_meta() - { - if (!isset($this->folders_meta)) - $this->list_folders(); - - return $this->folders_meta; - } - - /** - * Helper method to decode saved IMAP metadata - */ - private function unserialize_metadata($str) - { - if (!empty($str)) - return @json_decode(base64_decode($str), true); - - return null; - } - - /** - * Helper method to encode IMAP metadata for saving - */ - private function serialize_metadata($data) - { - if (is_array($data)) - return base64_encode(json_encode($data)); - - return ''; - } - -} diff --git a/plugins/kolab_zpush/kolab_zpush_ui.php b/plugins/kolab_zpush/kolab_zpush_ui.php deleted file mode 100644 index 780426d9..00000000 --- a/plugins/kolab_zpush/kolab_zpush_ui.php +++ /dev/null @@ -1,169 +0,0 @@ - - * - * Copyright (C) 2011, Kolab Systems AG - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -class kolab_zpush_ui -{ - private $rc; - private $config; - - public function __construct($config) - { - $this->config = $config; - $this->rc = rcube::get_instance(); - - $skin = $this->rc->config->get('skin'); - $this->config->include_stylesheet('skins/' . $skin . '/config.css'); - $this->rc->output->include_script('list.js'); - $this->skin_path = $this->config->urlbase . 'skins/' . $skin . '/'; - } - - - public function device_list($attrib = array()) - { - $attrib += array('id' => 'devices-list'); - - $devices = $this->config->list_devices(); - $table = new html_table(); - - foreach ($devices as $id => $device) { - $name = $device['ALIAS'] ? $device['ALIAS'] : $id; - $table->add_row(array('id' => 'rcmrow' . $id)); - $table->add(null, html::span('devicealias', rcube::Q($name)) . html::span('devicetype', rcube::Q($device['TYPE']))); - } - - $this->rc->output->add_gui_object('devicelist', $attrib['id']); - $this->rc->output->set_env('devices', $devices); - - return $table->show($attrib); - } - - - public function device_config_form($attrib = array()) - { - $table = new html_table(array('cols' => 2)); - - $field_id = 'config-device-alias'; - $input = new html_inputfield(array('name' => 'devicealias', 'id' => $field_id, 'size' => 40)); - $table->add('title', html::label($field_id, $this->config->gettext('devicealias'))); - $table->add(null, $input->show()); - - $field_id = 'config-device-mode'; - $select = new html_select(array('name' => 'syncmode', 'id' => $field_id)); - $select->add(array($this->config->gettext('modeauto'), $this->config->gettext('modeflat'), $this->config->gettext('modefolder')), array('-1', '0', '1')); - $table->add('title', html::label($field_id, $this->config->gettext('syncmode'))); - $table->add(null, $select->show('-1')); - - $field_id = 'config-device-laxpic'; - $checkbox = new html_checkbox(array('name' => 'laxpic', 'value' => '1', 'id' => $field_id)); - $table->add('title', $this->config->gettext('imageformat')); - $table->add(null, html::label($field_id, $checkbox->show() . ' ' . $this->config->gettext('laxpiclabel'))); - - if ($attrib['form']) - $this->rc->output->add_gui_object('editform', $attrib['form']); - - return $table->show($attrib); - } - - - public function folder_subscriptions($attrib = array()) - { - if (!$attrib['id']) - $attrib['id'] = 'foldersubscriptions'; - - // group folders by type (show only known types) - $folder_groups = array('mail' => array(), 'contact' => array(), 'event' => array(), 'task' => array()); - $folder_meta = $this->config->folders_meta(); - foreach ($this->config->list_folders() as $folder) { - $type = $folder_meta[$folder]['TYPE'] ? $folder_meta[$folder]['TYPE'] : 'mail'; - if (is_array($folder_groups[$type])) - $folder_groups[$type][] = $folder; - } - - // build block for every folder type - foreach ($folder_groups as $type => $group) { - if (empty($group)) - continue; - $attrib['type'] = $type; - $html .= html::div('subscriptionblock', - html::tag('h3', $type, $this->config->gettext($type)) . - $this->folder_subscriptions_block($group, $attrib)); - } - - $this->rc->output->add_gui_object('subscriptionslist', $attrib['id']); - - return html::div($attrib, $html); - } - - public function folder_subscriptions_block($a_folders, $attrib) - { - $alarms = ($attrib['type'] == 'event' || $attrib['type'] == 'task'); - - $table = new html_table(array('cellspacing' => 0)); - $table->add_header('subscription', $attrib['syncicon'] ? html::img(array('src' => $this->skin_path . $attrib['syncicon'], 'title' => $this->config->gettext('synchronize'))) : ''); - $table->add_header('alarm', $alarms && $attrib['alarmicon'] ? html::img(array('src' => $this->skin_path . $attrib['alarmicon'], 'title' => $this->config->gettext('withalarms'))) : ''); - $table->add_header('foldername', $this->config->gettext('folder')); - - $checkbox_sync = new html_checkbox(array('name' => 'subscribed[]', 'class' => 'subscription')); - $checkbox_alarm = new html_checkbox(array('name' => 'alarm[]', 'class' => 'alarm')); - - $names = array(); - foreach ($a_folders as $folder) { - $foldername = $origname = preg_replace('/^INBOX »\s+/', '', kolab_storage::object_name($folder)); - - // find folder prefix to truncate (the same code as in kolab_addressbook plugin) - for ($i = count($names)-1; $i >= 0; $i--) { - if (strpos($foldername, $names[$i].' » ') === 0) { - $length = strlen($names[$i].' » '); - $prefix = substr($foldername, 0, $length); - $count = count(explode(' » ', $prefix)); - $foldername = str_repeat('  ', $count-1) . '» ' . substr($foldername, $length); - break; - } - } - - $names[] = $origname; - $classes = array('mailbox'); - - if ($folder_class = $this->rc->folder_classname($folder)) { - $foldername = $this->rc->gettext($folder_class); - $classes[] = $folder_class; - } - - $folder_id = 'rcmf' . rcube_utils::html_identifier($folder); - - $table->add_row(); - $table->add('subscription', $checkbox_sync->show('', array('value' => $folder, 'id' => $folder_id))); - - if ($alarms) - $table->add('alarm', $checkbox_alarm->show('', array('value' => $folder, 'id' => $folder_id.'_alarm'))); - else - $table->add('alarm', ''); - - $table->add(join(' ', $classes), html::label($folder_id, rcube::Q($foldername))); - } - - return $table->show(); - } - -} diff --git a/plugins/kolab_zpush/localization/de_CH.inc b/plugins/kolab_zpush/localization/de_CH.inc deleted file mode 100644 index 74445a49..00000000 --- a/plugins/kolab_zpush/localization/de_CH.inc +++ /dev/null @@ -1,29 +0,0 @@ -
Um ein neues Gerät anzumelden, verbinden Sie dieses zuerst mit dem Server. Eine Anleitung dazu finden Sie im
Wiki. Anschliessend laden Sie diese Seite neu und das Gerät wird hier aufgelistet.'; -$labels['savingdata'] = 'Daten werden gespeichert...'; -$labels['savingerror'] = 'Fehler beim Speichern'; -$labels['notsupported'] = 'Ihr Server unterstützt keine Activesync-Konfiguration'; -$labels['devicedeleteconfirm'] = 'Wollen Sie wirklich alle Einstellungen für dieses Gerät löschen?'; -$labels['successfullydeleted'] = 'Die Geräteinstellungen wurden erfolgreich gelöscht'; -?> diff --git a/plugins/kolab_zpush/localization/de_DE.inc b/plugins/kolab_zpush/localization/de_DE.inc deleted file mode 100644 index 74445a49..00000000 --- a/plugins/kolab_zpush/localization/de_DE.inc +++ /dev/null @@ -1,29 +0,0 @@ -
Um ein neues Gerät anzumelden, verbinden Sie dieses zuerst mit dem Server. Eine Anleitung dazu finden Sie im Wiki. Anschliessend laden Sie diese Seite neu und das Gerät wird hier aufgelistet.'; -$labels['savingdata'] = 'Daten werden gespeichert...'; -$labels['savingerror'] = 'Fehler beim Speichern'; -$labels['notsupported'] = 'Ihr Server unterstützt keine Activesync-Konfiguration'; -$labels['devicedeleteconfirm'] = 'Wollen Sie wirklich alle Einstellungen für dieses Gerät löschen?'; -$labels['successfullydeleted'] = 'Die Geräteinstellungen wurden erfolgreich gelöscht'; -?> diff --git a/plugins/kolab_zpush/localization/en_US.inc b/plugins/kolab_zpush/localization/en_US.inc deleted file mode 100644 index 56e2790a..00000000 --- a/plugins/kolab_zpush/localization/en_US.inc +++ /dev/null @@ -1,33 +0,0 @@ -
In order to register a device, please connect it to the server first, using the instructions in the Wiki. Afterwards the device should become available for configuration here.'; -$labels['savingdata'] = 'Saving data...'; -$labels['savingerror'] = 'Failed to save configuration'; -$labels['notsupported'] = 'Your server does not support metadata/annotations'; -$labels['devicedeleteconfirm'] = 'Do you really want to delete the configuration for this device?'; -$labels['successfullydeleted'] = 'The device configuration was successfully removed'; - -?> \ No newline at end of file diff --git a/plugins/kolab_zpush/localization/pl_PL.inc b/plugins/kolab_zpush/localization/pl_PL.inc deleted file mode 100644 index 64d63854..00000000 --- a/plugins/kolab_zpush/localization/pl_PL.inc +++ /dev/null @@ -1,29 +0,0 @@ -
Aby zarejestrować urządzenie najpierw podłącz je do serwera według instrukcji z Wiki. Po tym procesie urządzenie powinno dać się skonfigurować tutaj.'; -$labels['savingdata'] = 'Zapisywanie danych...'; -$labels['savingerror'] = 'Nie udało się zapisać konfiguracji'; -$labels['notsupported'] = 'Twój serwer nie obsługuje metadanych (metadata/annotations).'; -$labels['devicedeleteconfirm'] = 'Czy na pewno chcesz usunąć konfigurację dla tego urządzenia?'; -$labels['successfullydeleted'] = 'Konfiguracja urządzenia została pomyślnie usunięta.'; -?> diff --git a/plugins/kolab_zpush/package.xml b/plugins/kolab_zpush/package.xml deleted file mode 100644 index ffa27d0b..00000000 --- a/plugins/kolab_zpush/package.xml +++ /dev/null @@ -1,72 +0,0 @@ - - - kolab_zpush - http://git.kolab.org/roundcubemail-plugins-kolab/ - Z-Push configuration utility for Kolab accounts - - - Thomas Bruederli - thomasb - bruederli@kolabsys.com - yes - - 2012-05-14 - - 1.0 - 1.0 - - - stable - - GNU AGPL - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 5.2.1 - - - 1.7.0 - - - kolab_core - http://kolabsys.com - - - jqueryui - pear.roundcube.net - - - - - diff --git a/plugins/kolab_zpush/skins/classic/alarm-clock.png b/plugins/kolab_zpush/skins/classic/alarm-clock.png deleted file mode 100755 index 518bdc1166b8d585f27c33d2066f04a3f9834f9b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 841 zcmV-P1GfB$P)q$gGRCwBylFLsMQ543%&P-dTw1pyr zpeWF^lt4uUB1S+G0t-b$!lHykqkjT!T=>`+b>$k_un`CeF@(^N7=sXk3jsqYuM{j$ zpi)2{0j7^>X~%m9A!ywACEq;mIlpu6oI9%CCk%LvDuZKD2Ot~A@*c&|Z+{)Hq%Ski zn2~?QLlN1}Nl|2~)QQipmOCoaFBh66DU5;so&&YyZHQxYpJq#}@JQ5V7uVD^7`562 zY^+W#xrVz-rKtFdI|#}T^KaGHCJ8q4s&@sWqhmN%*vMP#mA5sIElRt*pB{K-M$p&W zgR-*g5YF2Q%%%1G*N+`*Trh$XnPD=gU1OM^_adqK4Xk!Ml65*vPfp@WQ4vgOR##!-7NBc1X0ccxTyuajl;va@Er&ZRRayBrOK9RjD5RLm z?RH~jdmDc%x3CIs=ZksLriHW4sTOnRu*JT-5Q_mUO~^`3RT2*m4^N+|j^c6r*xf~< zF*A1egKBFbAB&uMjs$>+nqd$-1K+8sL3MRC9yB(>U@$1w>-8uuFGqPDJZVjr&ba=;-P?-GNNJABlkV`2vLVE3=^mh`&Xh&P|SGm2FZbrKBh@ zy`Na0oD3~Fjc{Mzrozwy5jt(4f2+NHA;ISpW4{ZW6gjIni6w1ph`zSA19w^fT0zjrvit|kk{QX%%YjLUV>+8U}fi7AzZCsS=07?_nZLn2Bde0{8v^Ko2Tt~skz|cV7z)0WFNY~KZ%Gk)tz(4^Clz_GsrKDK}xwt{?0`hE?GD=Dctn~HE z%ggo3jrH=2()A53EiFN27#ZmTRp=I1=9MH?=;jqG!%T2VElw`VEGWs$&r<-In3$Ab zT4JjNbScCOxdm`z^NOLt1Pn0!io^naLp=kKmtYEgeeo;J&4sHjE(uCSxEHIz#UYgi zsro^w#rdU0$-sz9QwCX8VC7ttnpl!w6q28x0}I7~jQo=P;*9(P1?ON>1>eNv%sdbu ztlrnx$}_LHBrz{J)zigR321^|W@d_&i<6*(J3ovn(~mttdZN0qkX~Ox$iU#c3W? zZwhX=nBml`4|I$^C}NQ!8YToxJs>7L*#bH6grAxROzlO$WId-N?L7knqpPQjV@SoV zH`5mO9dZz8+b*u7e?VGBD*WOpE+LUu9uq87wjESD|Imx6%UGquORFHEQZUB%{ro5U zQ#M;B+%93f==twVnf2|unRn;LuGirD>UH|_9*;>bwJXdwvOURlj7nv93B1z$+F4O# zgLO0C1v}r%lRx(`Br$DoO5%0es9URjYzOm0ez6PcTiV(J+NC!6-_u=nV2hW}agi+N z;wANYEq@f!PH#Kn^RY(H*ms-cMAM6{X8#>HR>X;#_q^RbW7dSaa(<=>^TdRAG5n04 zahzSC%ICurt0Lw@m5!%h)N*s&mk8EA(O8!-v9r-|S=a>IlE!`S92tz#&a@`4IlFmp ze+KK?v%0%3i!D3v!*N7(7Gp`|GrOePYbV+n7f0N1o02)HGe5fH)b2KW3GY% t3Pn4-mg}GX=6N;e?9mmBl_~p~co^84D+&W=O67qH8BbR~mvv4FO#q{ysgM8w diff --git a/plugins/kolab_zpush/skins/classic/foldertypes.png b/plugins/kolab_zpush/skins/classic/foldertypes.png deleted file mode 100644 index 4950296a906a82b571650afd4974eed21fb9dced..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2291 zcmVTcL`lmza&Oa)$ZLj0?6m{E!a|0^2*Yj93;$}H!Ew$=qNsJYQpbp zYPN=5^Lf2e5rLe!co74`!|?k3Hx<@lUAAG{Fp$^l)mSYSeCw{e@X`767#JG*Vh|c* z9oA(Vww(gDuxV{*r4vx%{g zo}|?Z+bJNoTjLAP$nEjKJ~D!|q$I@Clg|(sx21)JFq_TjBli-mR_Jv)XxWBrXUu5a z<-#Ck;e!|K%C4n?fOD5k+(~RqD zYebhQp-}n>sATWnz59B4`?Ky?un>9q`3MXJaYMjL0CMP8?<2+|{q;wuf4QD9)CIVB z=gys3ot>Td!y8q&@_9#~a{U1?Rt^Y|mt{k4dFys7ws2VpW>FgVbM zu@MKxM;#brq2D7TgRl>Di!eyj997Tg`kp;|o^EPxrCAVD66b8$v>wAl4xFk#h2s0~ zCn~Yxx39kgjaJ4yZ3bjrd{VR-#@q0eh#nO4V_L8gF%Pk;UT4w|wfz^}*qt@(; z1|)CYx)qtkNv=s3CR~%ue%QBfA6i>mv48*m0G}I8M6-H~`!w)KQgXO_RO#PBZ`@Zo zYlq)@kCY5Bn9QPhcvgOqJ_XM4G2D0WGQ9TMIIdiJx8urZuh^!iznxrrF)P3D<+3f? z5ucnXXjRfkWKN>^L@D@v9%yMsvd&Kr&??cgX5O%2gS*c@M9N+mO-4wvhLmL$kOIF~ zCLk>)40ia96G_p4lB=q!e!;H2QB?!IfipzJDG`_wSOz|NfJc(Wj66#|#H6FCA7^I* zwUKLU^A;~XR`$$u$Vf>f44J^>;INN241V6ZP!hbj(b5f_*?1+m1Qm6#nYRnS^Y9~Q zH5ysG*F^mW7qegJ`Ju3TBp$a5AN=Fka_Vd#438R!Ef_34xapFECh)C?*}A@6UBVq10ja#3Ag zUPGxQwW}W*W$ZHLygZE-p{Q(kK(BCNMMX(*Z7o__T2Q=VMbUy)tBTY>#dUR4W2;ve zwUw9aBi5QL@Y~=Y8-txT;*C!}iMaH1cxm^hZt5}CB|Ad|kRwo(aUQ!J-=w``c4np+ z?4q50LSo|7YldD?EMk{Ox?~p@5$qhglDweYW@DS2Acj`;Qk8jO`e(UhG@Px#dYp1C z1jIXAKhZ?>@#DDG*N1tu=`7p0Q4Dfl7xn$LtSr1KRRcwkKkr=K1hj6=8f@FV8GZfz z;y%>poR|pT2Y0zqXQU17{yVz5@K$v-RxDp0-sN)}qfCYG12fV_%qU1QVn!ZPOif9_ z&$e&J`zKC_`;d%M`a{22mu-|NK-!RNEZ3=q*f4be{Ei*sp06R-*hW#i=A;Z5skr#F z$xZ+V4jx2&d_2~dmI@;|7>rBAclvBN`=9GBM{sp?Lb?TKpQG3?C-ZgXyIB#Ot(NJW9XTT!eE!rE z_(9Hdc(dsMYRSz$swIw5YHBKadV0dKT9lI$F{2rreQfPTJajY<$JsrKktHSucQ0Bb zl=&nl)O@$%Y(9mld%S&)v;Tk2Mh+HB!7rq#fvcumeY-h-E&cpP=v=@ye(6}Iv(XLp z=?1kQpHya?sl7@f=ry6QQG55A2j+jf>?fRF{QK~)d#|0SE{7GNMaUpN+m@15`gBV7>rbJepa8X1hiAIx^G|hTZ##suZ~h7O z^(SDpS`kb8NS#iHvGH+?5;x6X`2w=P_c;Fkb~GSg~X7v{0k9%!Lbuj@l;b zjCe8fVL`gh78QsOhZTAcs%@r@>U~qfTznrfBvVTLr=Q|NM+aJHcYC$BH-fL#HsKk0 z{gF@nSIH%DqP}bz#d?EQ{2lG}M{&*Ti*)V$i#w2C`7%ChZKcc^;c~geRUG&dhu=xN zUT?G*sp*-4vR~Q*56vu>0vk0>;s_{8j2Jy6>{|JeSmDN5icvGgh^32T6#ORw>!@wQ z$3E=yx+G#KJ6SJ40f diff --git a/plugins/kolab_zpush/skins/classic/pointer-left.gif b/plugins/kolab_zpush/skins/classic/pointer-left.gif deleted file mode 100644 index b2de5850c614bd915ea75ba2403b421bd0a86ef2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 173 zcmV;e08;-)Nk%w1VGaNn0J9GO?(XjD>gvl7a z*6{G~%*@Q^=jZ?b|MK$kA^8LW000jFEC2ui01f~b000D9@X0w>yNuFxZp+%kdCCxu zmx4GN+Ncg&b?F+IV9AWNsqt)y%`&cX;tJ&6sSyz<>Cbs b#oaG*lQy^7J2qJz;?7O=F&32fFd_gu?W9~0 diff --git a/plugins/kolab_zpush/skins/classic/synchronize.png b/plugins/kolab_zpush/skins/classic/synchronize.png deleted file mode 100755 index ba5ebd1f4e20b67ceaf49c2c9160f37a03ccc805..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 836 zcmV-K1H1f*P)dz zNLw#yfl;S$OS+NxP!T0`q27a_KcR=-dJ5{Xm+WEf!J>k|BqE5#C}ImGwQ$$<(wdh& z>AIJ5zVFx9lo8J{@aWY%oIP6CAZKYHol)uRf|554m_YTnpAlGl zL9{5M4)qVt#u>|mLQVrh46q|<)MP|~t8EE-8crSv>~io{4+abW2A~lV0z>OM)mbBD zmWtqV;+?9YGlhpQQ@M9zi{*KRG_T5PQ_nvC9tMo_mT6XEIU5;J+S?d(HD_0f!1ES7 z7@Uj?4!kD>&?r8e&c1jan^9Cs!}3z0d7Izafj)-}rbYZR}EdihS6`M#Fcy= zaw{u#mV;BNWe3{Sqiq;{_M>cOP*8MhP1CLQf3t|C2tpM55ycYh7RL%}Yf+Bp#e!~C z2(WG^ZnlxC>igv!sW2GM8SslVJ(oFwkp~5*6E^0rT$Q zya}^2lB>ITrX0zGEa!lc90$HC4~#^g#%ZB2j3vzHhi;TO$n{?Wwx(^0>^QA_x~H^z zTf=2SxY0rCf|fl@rb7TXYr%pE(0=u@G diff --git a/plugins/kolab_zpush/skins/classic/templates/config.html b/plugins/kolab_zpush/skins/classic/templates/config.html deleted file mode 100644 index beb062f2..00000000 --- a/plugins/kolab_zpush/skins/classic/templates/config.html +++ /dev/null @@ -1,63 +0,0 @@ - - - -<roundcube:object name="pagetitle" /> - - - - - - - - - - -
- -
-
-
- -
-
- -
-
- -
- - -
-
- - - - - -
-
-
-
- -
- - - - - diff --git a/plugins/kolab_zpush/skins/larry/alarm-clock.png b/plugins/kolab_zpush/skins/larry/alarm-clock.png deleted file mode 100755 index 518bdc1166b8d585f27c33d2066f04a3f9834f9b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 841 zcmV-P1GfB$P)q$gGRCwBylFLsMQ543%&P-dTw1pyr zpeWF^lt4uUB1S+G0t-b$!lHykqkjT!T=>`+b>$k_un`CeF@(^N7=sXk3jsqYuM{j$ zpi)2{0j7^>X~%m9A!ywACEq;mIlpu6oI9%CCk%LvDuZKD2Ot~A@*c&|Z+{)Hq%Ski zn2~?QLlN1}Nl|2~)QQipmOCoaFBh66DU5;so&&YyZHQxYpJq#}@JQ5V7uVD^7`562 zY^+W#xrVz-rKtFdI|#}T^KaGHCJ8q4s&@sWqhmN%*vMP#mA5sIElRt*pB{K-M$p&W zgR-*g5YF2Q%%%1G*N+`*Trh$XnPD=gU1OM^_adqK4Xk!Ml65*vPfp@WQ4vgOR##!-7NBc1X0ccxTyuajl;va@Er&ZRRayBrOK9RjD5RLm z?RH~jdmDc%x3CIs=ZksLriHW4sTOnRu*JT-5Q_mUO~^`3RT2*m4^N+|j^c6r*xf~< zF*A1egKBFbAB&uMjs$>+nqd$-1K+8sL3MRC9yB(>U@$1w>-8uuFGqPDJZVjr&ba=;-P?-GNNJABlkV`2vLVE3=^mh`&Xh&P|SGm2FZbrKBh@ zy`Na0oD3~Fjc{Mzrozwy5jt(4f2+NHA;ISpW4{ZW6gjIni6w1ph`zSA19w^fT0zjrvit|kk{QX%%YjLUV>+TcL`lmza&Oa)$ZLj0?6m{E!a|0^2*Yj93;$}H!Ew$=qNsJYQpbp zYPN=5^Lf2e5rLe!co74`!|?k3Hx<@lUAAG{Fp$^l)mSYSeCw{e@X`767#JG*Vh|c* z9oA(Vww(gDuxV{*r4vx%{g zo}|?Z+bJNoTjLAP$nEjKJ~D!|q$I@Clg|(sx21)JFq_TjBli-mR_Jv)XxWBrXUu5a z<-#Ck;e!|K%C4n?fOD5k+(~RqD zYebhQp-}n>sATWnz59B4`?Ky?un>9q`3MXJaYMjL0CMP8?<2+|{q;wuf4QD9)CIVB z=gys3ot>Td!y8q&@_9#~a{U1?Rt^Y|mt{k4dFys7ws2VpW>FgVbM zu@MKxM;#brq2D7TgRl>Di!eyj997Tg`kp;|o^EPxrCAVD66b8$v>wAl4xFk#h2s0~ zCn~Yxx39kgjaJ4yZ3bjrd{VR-#@q0eh#nO4V_L8gF%Pk;UT4w|wfz^}*qt@(; z1|)CYx)qtkNv=s3CR~%ue%QBfA6i>mv48*m0G}I8M6-H~`!w)KQgXO_RO#PBZ`@Zo zYlq)@kCY5Bn9QPhcvgOqJ_XM4G2D0WGQ9TMIIdiJx8urZuh^!iznxrrF)P3D<+3f? z5ucnXXjRfkWKN>^L@D@v9%yMsvd&Kr&??cgX5O%2gS*c@M9N+mO-4wvhLmL$kOIF~ zCLk>)40ia96G_p4lB=q!e!;H2QB?!IfipzJDG`_wSOz|NfJc(Wj66#|#H6FCA7^I* zwUKLU^A;~XR`$$u$Vf>f44J^>;INN241V6ZP!hbj(b5f_*?1+m1Qm6#nYRnS^Y9~Q zH5ysG*F^mW7qegJ`Ju3TBp$a5AN=Fka_Vd#438R!Ef_34xapFECh)C?*}A@6UBVq10ja#3Ag zUPGxQwW}W*W$ZHLygZE-p{Q(kK(BCNMMX(*Z7o__T2Q=VMbUy)tBTY>#dUR4W2;ve zwUw9aBi5QL@Y~=Y8-txT;*C!}iMaH1cxm^hZt5}CB|Ad|kRwo(aUQ!J-=w``c4np+ z?4q50LSo|7YldD?EMk{Ox?~p@5$qhglDweYW@DS2Acj`;Qk8jO`e(UhG@Px#dYp1C z1jIXAKhZ?>@#DDG*N1tu=`7p0Q4Dfl7xn$LtSr1KRRcwkKkr=K1hj6=8f@FV8GZfz z;y%>poR|pT2Y0zqXQU17{yVz5@K$v-RxDp0-sN)}qfCYG12fV_%qU1QVn!ZPOif9_ z&$e&J`zKC_`;d%M`a{22mu-|NK-!RNEZ3=q*f4be{Ei*sp06R-*hW#i=A;Z5skr#F z$xZ+V4jx2&d_2~dmI@;|7>rBAclvBN`=9GBM{sp?Lb?TKpQG3?C-ZgXyIB#Ot(NJW9XTT!eE!rE z_(9Hdc(dsMYRSz$swIw5YHBKadV0dKT9lI$F{2rreQfPTJajY<$JsrKktHSucQ0Bb zl=&nl)O@$%Y(9mld%S&)v;Tk2Mh+HB!7rq#fvcumeY-h-E&cpP=v=@ye(6}Iv(XLp z=?1kQpHya?sl7@f=ry6QQG55A2j+jf>?fRF{QK~)d#|0SE{7GNMaUpN+m@15`gBV7>rbJepa8X1hiAIx^G|hTZ##suZ~h7O z^(SDpS`kb8NS#iHvGH+?5;x6X`2w=P_c;Fkb~GSg~X7v{0k9%!Lbuj@l;b zjCe8fVL`gh78QsOhZTAcs%@r@>U~qfTznrfBvVTLr=Q|NM+aJHcYC$BH-fL#HsKk0 z{gF@nSIH%DqP}bz#d?EQ{2lG}M{&*Ti*)V$i#w2C`7%ChZKcc^;c~geRUG&dhu=xN zUT?G*sp*-4vR~Q*56vu>0vk0>;s_{8j2Jy6>{|JeSmDN5icvGgh^32T6#ORw>!@wQ z$3E=yx+G#KJ6SJ40f diff --git a/plugins/kolab_zpush/skins/larry/pointer-left.png b/plugins/kolab_zpush/skins/larry/pointer-left.png deleted file mode 100644 index bfa7e86cefddf0248e057c924d14862d46a74f4e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1283 zcmeAS@N?(olHy`uVBq!ia0vp^JV4CP!3HERJk;|9Qj#UE5hcO-X(i=}MX3yqDfvmM z3ZA)%>8U}fi7AzZCsS=07?_nZLn2Bde0{8v^Ko2Tt~skz|cV7z)0WFNY~KZ%Gk)tz(4^Clz_GsrKDK}xwt{?0`hE?GD=Dctn~HE z%ggo3jrH=2()A53EiFN27#ZmTRp=I1=9MH?=;jqG!%T2VElw`VEGWs$&r<-In3$Ab zT4JjNbScCOxdm`z^NOLt1Pn0!io^naLp=kKmtYEgeeo;J&4sHjE(uCSxEHIz#UYgi zsro^w#rdU0$-sz9QwCX8VC7ttnpl!w6q28x0}I7~jQo=P;*9(P1?ON>1>eNv%sdbu ztlrnx$}_LHBrz{J)zigR321^|W@d_&iG`Voi>tAjv4yLlp`ojxlZ%sqtEsc8iK&^J zv6-PWOs`9Ra%paAUI|QZ3Q(`Jsimo*lbH!ly`ZF!TL84#CABECEH%ZgC_h&L>}9J= z+-|{b9#n4%7PmnCVT#i&`as9%gCZ6wqG3Y7)B|F|lP!<~Pxz^Mz|>v@Ox8xyCHEK@ z7|T6f978H@wM??jJ`^C*cK?dNT9(DGu6LXSR2jEEcy?)}_rmOh@)v;c!G)!=yUX74 zzqqzF;Kij!YL8ZVC<@rKtA5=4JSOME;;rI({4L*YpWCX3N39LBDDhf4$*bZ@>0Y5u z7lr@IW}N4D#^_1+x?Oz#y|Ba~MzKbOt2IYQ>~Mz3p&YZ@?;Q>$A97ZkytD84W2Y;x zzZ%T-+Zdy_omb_WWmD#?qaj+X#|sVQ`VHrwKe+kRRP!U#Tb9?@tzWr{>-ILonLZmL zbT;ginAyVh@ti@=Y!@ZPRi(RQ1rx({cw)B3=*3^Wbs=y2(Smu;JNN8gYcs)b`F*Ro zeiK>P-p_1*q8Bsw{5sEHKOHWvYSA)O@SJo%(|p>0`K+zCbRW)nv11)~T&9Igo6hM6 zOf}nivM*~t+!D8bKR+9@%4E-iGYJMSW`Fxtj=28v|NQq%Bhmy8&TADBu0#u&e}AW4yBlYbwD{tWC(jQq%V%U`kapM{qIE#-Ca75QboFyt=akR{ E01=_ejQ{`u diff --git a/plugins/kolab_zpush/skins/larry/synchronize.png b/plugins/kolab_zpush/skins/larry/synchronize.png deleted file mode 100755 index ba5ebd1f4e20b67ceaf49c2c9160f37a03ccc805..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 836 zcmV-K1H1f*P)dz zNLw#yfl;S$OS+NxP!T0`q27a_KcR=-dJ5{Xm+WEf!J>k|BqE5#C}ImGwQ$$<(wdh& z>AIJ5zVFx9lo8J{@aWY%oIP6CAZKYHol)uRf|554m_YTnpAlGl zL9{5M4)qVt#u>|mLQVrh46q|<)MP|~t8EE-8crSv>~io{4+abW2A~lV0z>OM)mbBD zmWtqV;+?9YGlhpQQ@M9zi{*KRG_T5PQ_nvC9tMo_mT6XEIU5;J+S?d(HD_0f!1ES7 z7@Uj?4!kD>&?r8e&c1jan^9Cs!}3z0d7Izafj)-}rbYZR}EdihS6`M#Fcy= zaw{u#mV;BNWe3{Sqiq;{_M>cOP*8MhP1CLQf3t|C2tpM55ycYh7RL%}Yf+Bp#e!~C z2(WG^ZnlxC>igv!sW2GM8SslVJ(oFwkp~5*6E^0rT$Q zya}^2lB>ITrX0zGEa!lc90$HC4~#^g#%ZB2j3vzHhi;TO$n{?Wwx(^0>^QA_x~H^z zTf=2SxY0rCf|fl@rb7TXYr%pE(0=u@G diff --git a/plugins/kolab_zpush/skins/larry/templates/config.html b/plugins/kolab_zpush/skins/larry/templates/config.html deleted file mode 100644 index 9671bc6a..00000000 --- a/plugins/kolab_zpush/skins/larry/templates/config.html +++ /dev/null @@ -1,71 +0,0 @@ - - - -<roundcube:object name="pagetitle" /> - - - - - - -
- - - -
- -
-

-
- -
-
- -
-
- -
-

- - -
-
- - - - - -
-
-
- - -
- -
- - - - - - - From 8b381b8596ff5f2f33732f12287c6c64655a6cc2 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Mon, 29 Feb 2016 10:46:53 +0100 Subject: [PATCH 010/483] Remove owncloud plugin (T1041) --- .tx/config | 5 - plugins/owncloud/LICENSE | 661 ------------------ plugins/owncloud/README | 78 --- plugins/owncloud/config.inc.php.dist | 8 - .../apps/kolab_auth/appinfo/app.php | 51 -- .../apps/kolab_auth/appinfo/info.xml | 13 - .../apps/kolab_auth/appinfo/version | 1 - .../themes/kolab/core/css/styles.css | 15 - .../themes/kolab/core/js/kolab.js | 58 -- .../kolab/core/templates/layout.user.php | 56 -- plugins/owncloud/localization/bg_BG.inc | 3 - plugins/owncloud/localization/ca_ES.inc | 3 - plugins/owncloud/localization/cs_CZ.inc | 3 - plugins/owncloud/localization/da_DK.inc | 3 - plugins/owncloud/localization/de_CH.inc | 3 - plugins/owncloud/localization/de_DE.inc | 3 - plugins/owncloud/localization/en_US.inc | 6 - plugins/owncloud/localization/es_AR.inc | 3 - plugins/owncloud/localization/es_ES.inc | 3 - plugins/owncloud/localization/et_EE.inc | 3 - plugins/owncloud/localization/fi_FI.inc | 3 - plugins/owncloud/localization/fr_FR.inc | 3 - plugins/owncloud/localization/he.inc | 2 - plugins/owncloud/localization/hr.inc | 2 - plugins/owncloud/localization/hu_HU.inc | 3 - plugins/owncloud/localization/it_IT.inc | 3 - plugins/owncloud/localization/ja_JP.inc | 3 - plugins/owncloud/localization/ku_IQ.inc | 2 - plugins/owncloud/localization/nl_NL.inc | 3 - plugins/owncloud/localization/pl.inc | 3 - plugins/owncloud/localization/pl_PL.inc | 3 - plugins/owncloud/localization/pt_BR.inc | 3 - plugins/owncloud/localization/ro.inc | 2 - plugins/owncloud/localization/ru_RU.inc | 3 - plugins/owncloud/localization/sk.inc | 2 - plugins/owncloud/localization/sl.inc | 3 - plugins/owncloud/localization/sv.inc | 2 - plugins/owncloud/localization/sv_SE.inc | 3 - plugins/owncloud/localization/th.inc | 3 - plugins/owncloud/localization/tr_TR.inc | 2 - plugins/owncloud/localization/uk.inc | 3 - plugins/owncloud/localization/vi.inc | 2 - plugins/owncloud/localization/vi_VN.inc | 2 - plugins/owncloud/localization/zh_CN.inc | 2 - plugins/owncloud/localization/zh_TW.inc | 2 - plugins/owncloud/owncloud.js | 94 --- plugins/owncloud/owncloud.php | 136 ---- plugins/owncloud/skins/classic/cloud.png | Bin 1068 -> 0 bytes plugins/owncloud/skins/classic/owncloud.css | 12 - .../skins/classic/templates/owncloud.html | 19 - plugins/owncloud/skins/larry/cloud.png | Bin 1844 -> 0 bytes plugins/owncloud/skins/larry/owncloud.css | 14 - .../skins/larry/templates/owncloud.html | 20 - 53 files changed, 1338 deletions(-) delete mode 100644 plugins/owncloud/LICENSE delete mode 100644 plugins/owncloud/README delete mode 100644 plugins/owncloud/config.inc.php.dist delete mode 100644 plugins/owncloud/copy_to_owncloud/apps/kolab_auth/appinfo/app.php delete mode 100644 plugins/owncloud/copy_to_owncloud/apps/kolab_auth/appinfo/info.xml delete mode 100644 plugins/owncloud/copy_to_owncloud/apps/kolab_auth/appinfo/version delete mode 100755 plugins/owncloud/copy_to_owncloud/themes/kolab/core/css/styles.css delete mode 100644 plugins/owncloud/copy_to_owncloud/themes/kolab/core/js/kolab.js delete mode 100644 plugins/owncloud/copy_to_owncloud/themes/kolab/core/templates/layout.user.php delete mode 100644 plugins/owncloud/localization/bg_BG.inc delete mode 100644 plugins/owncloud/localization/ca_ES.inc delete mode 100644 plugins/owncloud/localization/cs_CZ.inc delete mode 100644 plugins/owncloud/localization/da_DK.inc delete mode 100644 plugins/owncloud/localization/de_CH.inc delete mode 100644 plugins/owncloud/localization/de_DE.inc delete mode 100644 plugins/owncloud/localization/en_US.inc delete mode 100644 plugins/owncloud/localization/es_AR.inc delete mode 100644 plugins/owncloud/localization/es_ES.inc delete mode 100644 plugins/owncloud/localization/et_EE.inc delete mode 100644 plugins/owncloud/localization/fi_FI.inc delete mode 100644 plugins/owncloud/localization/fr_FR.inc delete mode 100644 plugins/owncloud/localization/he.inc delete mode 100644 plugins/owncloud/localization/hr.inc delete mode 100644 plugins/owncloud/localization/hu_HU.inc delete mode 100644 plugins/owncloud/localization/it_IT.inc delete mode 100644 plugins/owncloud/localization/ja_JP.inc delete mode 100644 plugins/owncloud/localization/ku_IQ.inc delete mode 100644 plugins/owncloud/localization/nl_NL.inc delete mode 100644 plugins/owncloud/localization/pl.inc delete mode 100644 plugins/owncloud/localization/pl_PL.inc delete mode 100644 plugins/owncloud/localization/pt_BR.inc delete mode 100644 plugins/owncloud/localization/ro.inc delete mode 100644 plugins/owncloud/localization/ru_RU.inc delete mode 100644 plugins/owncloud/localization/sk.inc delete mode 100644 plugins/owncloud/localization/sl.inc delete mode 100644 plugins/owncloud/localization/sv.inc delete mode 100644 plugins/owncloud/localization/sv_SE.inc delete mode 100644 plugins/owncloud/localization/th.inc delete mode 100644 plugins/owncloud/localization/tr_TR.inc delete mode 100644 plugins/owncloud/localization/uk.inc delete mode 100644 plugins/owncloud/localization/vi.inc delete mode 100644 plugins/owncloud/localization/vi_VN.inc delete mode 100644 plugins/owncloud/localization/zh_CN.inc delete mode 100644 plugins/owncloud/localization/zh_TW.inc delete mode 100644 plugins/owncloud/owncloud.js delete mode 100644 plugins/owncloud/owncloud.php delete mode 100644 plugins/owncloud/skins/classic/cloud.png delete mode 100644 plugins/owncloud/skins/classic/owncloud.css delete mode 100644 plugins/owncloud/skins/classic/templates/owncloud.html delete mode 100644 plugins/owncloud/skins/larry/cloud.png delete mode 100644 plugins/owncloud/skins/larry/owncloud.css delete mode 100644 plugins/owncloud/skins/larry/templates/owncloud.html diff --git a/.tx/config b/.tx/config index e82c8bb8..3ad12edd 100644 --- a/.tx/config +++ b/.tx/config @@ -96,11 +96,6 @@ file_filter = plugins/kolab_tags/localization/.inc source_file = plugins/kolab_tags/localization/en_US.inc source_lang = en_US -[kolab.owncloud] -file_filter = plugins/owncloud/localization/.inc -source_file = plugins/owncloud/localization/en_US.inc -source_lang = en_US - [kolab.tasklist] file_filter = plugins/tasklist/localization/.inc source_file = plugins/tasklist/localization/en_US.inc diff --git a/plugins/owncloud/LICENSE b/plugins/owncloud/LICENSE deleted file mode 100644 index dba13ed2..00000000 --- a/plugins/owncloud/LICENSE +++ /dev/null @@ -1,661 +0,0 @@ - GNU AFFERO GENERAL PUBLIC LICENSE - Version 3, 19 November 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU Affero General Public License is a free, copyleft license for -software and other kinds of works, specifically designed to ensure -cooperation with the community in the case of network server software. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -our General Public Licenses are intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - Developers that use our General Public Licenses protect your rights -with two steps: (1) assert copyright on the software, and (2) offer -you this License which gives you legal permission to copy, distribute -and/or modify the software. - - A secondary benefit of defending all users' freedom is that -improvements made in alternate versions of the program, if they -receive widespread use, become available for other developers to -incorporate. Many developers of free software are heartened and -encouraged by the resulting cooperation. However, in the case of -software used on network servers, this result may fail to come about. -The GNU General Public License permits making a modified version and -letting the public access it on a server without ever releasing its -source code to the public. - - The GNU Affero General Public License is designed specifically to -ensure that, in such cases, the modified source code becomes available -to the community. It requires the operator of a network server to -provide the source code of the modified version running there to the -users of that server. Therefore, public use of a modified version, on -a publicly accessible server, gives the public access to the source -code of the modified version. - - An older license, called the Affero General Public License and -published by Affero, was designed to accomplish similar goals. This is -a different license, not a version of the Affero GPL, but Affero has -released a new version of the Affero GPL which permits relicensing under -this license. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU Affero General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Remote Network Interaction; Use with the GNU General Public License. - - Notwithstanding any other provision of this License, if you modify the -Program, your modified version must prominently offer all users -interacting with it remotely through a computer network (if your version -supports such interaction) an opportunity to receive the Corresponding -Source of your version by providing access to the Corresponding Source -from a network server at no charge, through some standard or customary -means of facilitating copying of software. This Corresponding Source -shall include the Corresponding Source for any work covered by version 3 -of the GNU General Public License that is incorporated pursuant to the -following paragraph. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the work with which it is combined will remain governed by version -3 of the GNU General Public License. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU Affero General Public License from time to time. Such new versions -will be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU Affero General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU Affero General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU Affero General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If your software can interact with users remotely through a computer -network, you should also make sure that it provides a way for users to -get its source. For example, if your program is a web application, its -interface could display a "Source" link that leads users to an archive -of the code. There are many ways you could offer source, and different -solutions will be better for different programs; see section 13 for the -specific requirements. - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU AGPL, see -. diff --git a/plugins/owncloud/README b/plugins/owncloud/README deleted file mode 100644 index ecf66f3e..00000000 --- a/plugins/owncloud/README +++ /dev/null @@ -1,78 +0,0 @@ -Connecting Roundcube with ownCloud -================================== - -ATTENTION: this is just a proof-of-concept and should be be considered for -production use. - -This plugin integrates ownCloud into the Roundcube web UI using iframes. -Beside giving the user one single location to log-in and get access to all -his/her data, the integration should also allow one to pick files from -ownCloud when sending email messages and to store email attachments directly -to the cloud. - - -HOW IT WORKS ------------- - -The plugin embeds the web UI for ownCloud though an iframe into the Roundcube -user interface. When accessing ownCloud though Roundcube, the user's login -credentials (username + password) will be handed over to ownCloud in order to -automatically authenticate the current Roundcube user to ownCloud. This -exchange happens with HTTP requests directley between the ownCloud server and -the Roundcube server using signed URLs to prevent from interception. - -IMPRTANT: The automatic user authentication only works if both apps use the -same authentication backend. In the Kolab realm this is LDAP. You therefore -need to configure the LDAP user authentication backend in ownCloud. - -Once authenticated, the user sees the ownCloud file manager within Roundcube -and can do whether he/she is authorized to do. When composing a new email -message in Roundcube, an addition button appear to select files from the -ownCloud storage. (this feaure is not yet fully implemented). - -When terminating the session in Roundcube by pressing the Logout button, an -according message is sent to ownCloud to termiate the user's session there, too. - - -REQUIREMENTS ------------- - -* Roundcube version 0.9-beta or higher -* ownCloud version 4.5 or higher -* both apps running on the same host (due to browser's XSS protection) -* both IMAP and ownCloud authenticate users on the same backend - - -INSTALLATION ------------- - -Install this plugin in Roundcube and enable it. - -Rsync the contents of the copy_to_owncloud folder to the ownCloud installation. -This will add a new app named "kolab_auth" and a theme "kolab" to ownCloud. - - -Roundcube CONFIGURATION ------------------------ - -Copy the config.inc.php.dist file to config.inc.php in Roundcube's owncloud -plugin directory. Then edit the file and set the absolute URL where ownCloud -is accessible and define a secret string for the authentication credential -exchange between the swo systems. - - -onwCloud CONFIGURATION ----------------------- - -Add the following lines to the ownCloud config array: - - 'theme' => 'kolab', - 'kolaburl' => '', - 'kolabsecret' => '', - -The value for 'kolabsecret' has to match the 'owncloud_secret' string in -the Roundcube owncloud plugin configuration. - -Log-in to ownCloud as an administrator and enable the kolab_auth app. - - diff --git a/plugins/owncloud/config.inc.php.dist b/plugins/owncloud/config.inc.php.dist deleted file mode 100644 index 448c6647..00000000 --- a/plugins/owncloud/config.inc.php.dist +++ /dev/null @@ -1,8 +0,0 @@ -'; diff --git a/plugins/owncloud/copy_to_owncloud/apps/kolab_auth/appinfo/app.php b/plugins/owncloud/copy_to_owncloud/apps/kolab_auth/appinfo/app.php deleted file mode 100644 index 02e3e151..00000000 --- a/plugins/owncloud/copy_to_owncloud/apps/kolab_auth/appinfo/app.php +++ /dev/null @@ -1,51 +0,0 @@ - 'https:///', - 'kolabsecret' => '', - -*/ - - -// check for kolab auth token -if (!OC_User::isLoggedIn() && !empty($_GET['kolab_auth'])) { - OCP\Util::writeLog('kolab_auth', 'got kolab auth token', OCP\Util::INFO); - - // decode auth data from Roundcube - parse_str(oc_kolab_decode($_GET['kolab_auth']), $request); - - // send back as POST request with session cookie - $postdata = http_build_query($request, '', '&'); - - // add request signature using secret key - $postdata .= '&hmac=' . hash_hmac('sha256', $postdata, OC_Config::getValue('kolabsecret', '')); - - $context = stream_context_create(array( - 'http' => array( - 'method' => 'POST', - 'header'=> "Content-type: application/x-www-form-urlencoded\r\n" - . "Content-Length: " . strlen($postdata) . "\r\n" - . "Cookie: " . $request['cname'] . '=' . $request['session'] . "\r\n", - 'content' => $postdata, - ) - ) - ); - - $url = !empty($_SERVER['HTTP_REFERER']) ? dirname($_SERVER['HTTP_REFERER']) . '/' : OC_Config::getValue('kolaburl', ''); - $auth = @json_decode(file_get_contents($url . '?_action=owncloudsso', false, $context), true); - - // fake HTTP authentication with user credentials received from Roundcube - if ($auth['user'] && $auth['pass']) { - $_SERVER['PHP_AUTH_USER'] = $auth['user']; - $_SERVER['PHP_AUTH_PW'] = $auth['pass']; - } -} - -function oc_kolab_decode($str) -{ - // TODO: chose a more sophisticated encryption method - return base64_decode(str_pad(strrev($str), strlen($str) % 4, '=', STR_PAD_RIGHT)); -} - diff --git a/plugins/owncloud/copy_to_owncloud/apps/kolab_auth/appinfo/info.xml b/plugins/owncloud/copy_to_owncloud/apps/kolab_auth/appinfo/info.xml deleted file mode 100644 index aad9cea7..00000000 --- a/plugins/owncloud/copy_to_owncloud/apps/kolab_auth/appinfo/info.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - kolab_auth - Kolab user authentication - Allow to authenticate an existing Kolab web client session - AGPL - Thomas Bruederli - 4.9 - true - - - - diff --git a/plugins/owncloud/copy_to_owncloud/apps/kolab_auth/appinfo/version b/plugins/owncloud/copy_to_owncloud/apps/kolab_auth/appinfo/version deleted file mode 100644 index 6e8bf73a..00000000 --- a/plugins/owncloud/copy_to_owncloud/apps/kolab_auth/appinfo/version +++ /dev/null @@ -1 +0,0 @@ -0.1.0 diff --git a/plugins/owncloud/copy_to_owncloud/themes/kolab/core/css/styles.css b/plugins/owncloud/copy_to_owncloud/themes/kolab/core/css/styles.css deleted file mode 100755 index 290721bc..00000000 --- a/plugins/owncloud/copy_to_owncloud/themes/kolab/core/css/styles.css +++ /dev/null @@ -1,15 +0,0 @@ - -#content, -#controls, -#navigation { - top: 0px; -} - -#navigation #settings { - bottom: 0px; -} - -#leftcontent, .leftcontent, -#rightcontent, .rightcontent { - top: 2.9em; -} \ No newline at end of file diff --git a/plugins/owncloud/copy_to_owncloud/themes/kolab/core/js/kolab.js b/plugins/owncloud/copy_to_owncloud/themes/kolab/core/js/kolab.js deleted file mode 100644 index f5204567..00000000 --- a/plugins/owncloud/copy_to_owncloud/themes/kolab/core/js/kolab.js +++ /dev/null @@ -1,58 +0,0 @@ - -function kolab_connector() -{ - var remote; - - // public members - this.window = window; - - // export public methods - this.init = init; - this.init_picker = init_picker; - this.list_files = list_files; - - function init(rcube) - { - remote = rcube; - } - - function init_picker(rcube) - { - remote = rcube; - - if (window.FileActions) { - // reset already registered actions - // FileActions.actions.file = {}; - - FileActions.register('file','Pick', OC.PERMISSION_READ, '', function(filename){ - var dir = $('#dir').val(); - remote.file_picked(dir, filename); - }); - FileActions.setDefault('file', 'Pick'); - } - } - - function list_files() - { - var files = []; - $('#fileList tr').each(function(item){ - var row = $(item), - type = row.attrib('data-type'), - file = row.attrib('data-file'), - mime = row.attrib('data-mime'); - - if (type == 'file') { - files.push(file); - } - }); - - return files; - } -} - -$(document).ready(function(){ - // connect with Roundcube running in parent window - if (window.parent && parent.rcmail && parent.rcube_owncloud) { - parent.rcube_owncloud.connect(new kolab_connector()); - } -}); \ No newline at end of file diff --git a/plugins/owncloud/copy_to_owncloud/themes/kolab/core/templates/layout.user.php b/plugins/owncloud/copy_to_owncloud/themes/kolab/core/templates/layout.user.php deleted file mode 100644 index a588eacb..00000000 --- a/plugins/owncloud/copy_to_owncloud/themes/kolab/core/templates/layout.user.php +++ /dev/null @@ -1,56 +0,0 @@ - - - - <?php echo isset($_['application']) && !empty($_['application'])?$_['application'].' | ':'' ?>ownCloud <?php echo OC_User::getUser()?' ('.OC_User::getUser().') ':'' ?> - - - - - - - - - - - - $value) { - echo "$name='$value' "; - }; - echo '/>'; - ?> - - - - - - -
- -
- - diff --git a/plugins/owncloud/localization/bg_BG.inc b/plugins/owncloud/localization/bg_BG.inc deleted file mode 100644 index a42b9738..00000000 --- a/plugins/owncloud/localization/bg_BG.inc +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/plugins/owncloud/localization/ca_ES.inc b/plugins/owncloud/localization/ca_ES.inc deleted file mode 100644 index 1703a1de..00000000 --- a/plugins/owncloud/localization/ca_ES.inc +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/plugins/owncloud/localization/cs_CZ.inc b/plugins/owncloud/localization/cs_CZ.inc deleted file mode 100644 index a1f3187e..00000000 --- a/plugins/owncloud/localization/cs_CZ.inc +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/plugins/owncloud/localization/da_DK.inc b/plugins/owncloud/localization/da_DK.inc deleted file mode 100644 index 6b4d91c3..00000000 --- a/plugins/owncloud/localization/da_DK.inc +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/plugins/owncloud/localization/de_CH.inc b/plugins/owncloud/localization/de_CH.inc deleted file mode 100644 index 33dc4439..00000000 --- a/plugins/owncloud/localization/de_CH.inc +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/plugins/owncloud/localization/de_DE.inc b/plugins/owncloud/localization/de_DE.inc deleted file mode 100644 index 33dc4439..00000000 --- a/plugins/owncloud/localization/de_DE.inc +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/plugins/owncloud/localization/en_US.inc b/plugins/owncloud/localization/en_US.inc deleted file mode 100644 index f5cb8fb8..00000000 --- a/plugins/owncloud/localization/en_US.inc +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/plugins/owncloud/localization/es_AR.inc b/plugins/owncloud/localization/es_AR.inc deleted file mode 100644 index d8d87720..00000000 --- a/plugins/owncloud/localization/es_AR.inc +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/plugins/owncloud/localization/es_ES.inc b/plugins/owncloud/localization/es_ES.inc deleted file mode 100644 index 1703a1de..00000000 --- a/plugins/owncloud/localization/es_ES.inc +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/plugins/owncloud/localization/et_EE.inc b/plugins/owncloud/localization/et_EE.inc deleted file mode 100644 index 1703a1de..00000000 --- a/plugins/owncloud/localization/et_EE.inc +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/plugins/owncloud/localization/fi_FI.inc b/plugins/owncloud/localization/fi_FI.inc deleted file mode 100644 index 7ebf6df6..00000000 --- a/plugins/owncloud/localization/fi_FI.inc +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/plugins/owncloud/localization/fr_FR.inc b/plugins/owncloud/localization/fr_FR.inc deleted file mode 100644 index 81b61700..00000000 --- a/plugins/owncloud/localization/fr_FR.inc +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/plugins/owncloud/localization/he.inc b/plugins/owncloud/localization/he.inc deleted file mode 100644 index acb6c354..00000000 --- a/plugins/owncloud/localization/he.inc +++ /dev/null @@ -1,2 +0,0 @@ - diff --git a/plugins/owncloud/localization/hr.inc b/plugins/owncloud/localization/hr.inc deleted file mode 100644 index acb6c354..00000000 --- a/plugins/owncloud/localization/hr.inc +++ /dev/null @@ -1,2 +0,0 @@ - diff --git a/plugins/owncloud/localization/hu_HU.inc b/plugins/owncloud/localization/hu_HU.inc deleted file mode 100644 index c29ea9de..00000000 --- a/plugins/owncloud/localization/hu_HU.inc +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/plugins/owncloud/localization/it_IT.inc b/plugins/owncloud/localization/it_IT.inc deleted file mode 100644 index fba73792..00000000 --- a/plugins/owncloud/localization/it_IT.inc +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/plugins/owncloud/localization/ja_JP.inc b/plugins/owncloud/localization/ja_JP.inc deleted file mode 100644 index f4dc46e5..00000000 --- a/plugins/owncloud/localization/ja_JP.inc +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/plugins/owncloud/localization/ku_IQ.inc b/plugins/owncloud/localization/ku_IQ.inc deleted file mode 100644 index acb6c354..00000000 --- a/plugins/owncloud/localization/ku_IQ.inc +++ /dev/null @@ -1,2 +0,0 @@ - diff --git a/plugins/owncloud/localization/nl_NL.inc b/plugins/owncloud/localization/nl_NL.inc deleted file mode 100644 index 4e631c5e..00000000 --- a/plugins/owncloud/localization/nl_NL.inc +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/plugins/owncloud/localization/pl.inc b/plugins/owncloud/localization/pl.inc deleted file mode 100644 index 40090ac7..00000000 --- a/plugins/owncloud/localization/pl.inc +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/plugins/owncloud/localization/pl_PL.inc b/plugins/owncloud/localization/pl_PL.inc deleted file mode 100644 index 40090ac7..00000000 --- a/plugins/owncloud/localization/pl_PL.inc +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/plugins/owncloud/localization/pt_BR.inc b/plugins/owncloud/localization/pt_BR.inc deleted file mode 100644 index cae8c161..00000000 --- a/plugins/owncloud/localization/pt_BR.inc +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/plugins/owncloud/localization/ro.inc b/plugins/owncloud/localization/ro.inc deleted file mode 100644 index acb6c354..00000000 --- a/plugins/owncloud/localization/ro.inc +++ /dev/null @@ -1,2 +0,0 @@ - diff --git a/plugins/owncloud/localization/ru_RU.inc b/plugins/owncloud/localization/ru_RU.inc deleted file mode 100644 index 87b3e3df..00000000 --- a/plugins/owncloud/localization/ru_RU.inc +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/plugins/owncloud/localization/sk.inc b/plugins/owncloud/localization/sk.inc deleted file mode 100644 index acb6c354..00000000 --- a/plugins/owncloud/localization/sk.inc +++ /dev/null @@ -1,2 +0,0 @@ - diff --git a/plugins/owncloud/localization/sl.inc b/plugins/owncloud/localization/sl.inc deleted file mode 100644 index ba20ec1d..00000000 --- a/plugins/owncloud/localization/sl.inc +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/plugins/owncloud/localization/sv.inc b/plugins/owncloud/localization/sv.inc deleted file mode 100644 index acb6c354..00000000 --- a/plugins/owncloud/localization/sv.inc +++ /dev/null @@ -1,2 +0,0 @@ - diff --git a/plugins/owncloud/localization/sv_SE.inc b/plugins/owncloud/localization/sv_SE.inc deleted file mode 100644 index 6b4d91c3..00000000 --- a/plugins/owncloud/localization/sv_SE.inc +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/plugins/owncloud/localization/th.inc b/plugins/owncloud/localization/th.inc deleted file mode 100644 index 164c2ef8..00000000 --- a/plugins/owncloud/localization/th.inc +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/plugins/owncloud/localization/tr_TR.inc b/plugins/owncloud/localization/tr_TR.inc deleted file mode 100644 index acb6c354..00000000 --- a/plugins/owncloud/localization/tr_TR.inc +++ /dev/null @@ -1,2 +0,0 @@ - diff --git a/plugins/owncloud/localization/uk.inc b/plugins/owncloud/localization/uk.inc deleted file mode 100644 index ea12a2dd..00000000 --- a/plugins/owncloud/localization/uk.inc +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/plugins/owncloud/localization/vi.inc b/plugins/owncloud/localization/vi.inc deleted file mode 100644 index acb6c354..00000000 --- a/plugins/owncloud/localization/vi.inc +++ /dev/null @@ -1,2 +0,0 @@ - diff --git a/plugins/owncloud/localization/vi_VN.inc b/plugins/owncloud/localization/vi_VN.inc deleted file mode 100644 index acb6c354..00000000 --- a/plugins/owncloud/localization/vi_VN.inc +++ /dev/null @@ -1,2 +0,0 @@ - diff --git a/plugins/owncloud/localization/zh_CN.inc b/plugins/owncloud/localization/zh_CN.inc deleted file mode 100644 index acb6c354..00000000 --- a/plugins/owncloud/localization/zh_CN.inc +++ /dev/null @@ -1,2 +0,0 @@ - diff --git a/plugins/owncloud/localization/zh_TW.inc b/plugins/owncloud/localization/zh_TW.inc deleted file mode 100644 index acb6c354..00000000 --- a/plugins/owncloud/localization/zh_TW.inc +++ /dev/null @@ -1,2 +0,0 @@ - diff --git a/plugins/owncloud/owncloud.js b/plugins/owncloud/owncloud.js deleted file mode 100644 index f52e25c1..00000000 --- a/plugins/owncloud/owncloud.js +++ /dev/null @@ -1,94 +0,0 @@ -/** - * - */ -function rcube_owncloud() -{ - this.cloud; - this.dialog; -} - -// singleton getter -rcube_owncloud.instance = function() -{ - if (!rcube_owncloud._instance) { - rcube_owncloud._instance = new rcube_owncloud; - } - - return rcube_owncloud._instance; -}; - -// remote connector -rcube_owncloud.connect = function(client) -{ - rcube_owncloud.instance().connect(client); -}; - -rcube_owncloud.prototype = { - -// callback from the ownCloud connector script loaded in the iframe -connect: function(client) -{ - var me = this; - this.cloud = client; - - if (this.dialog) - client.init_picker(this); -}, - -// callback function from FileAction in ownCloud widget -file_picked: function(dir, file) -{ - alert('Picked: ' + dir + '/' + file + '\n\nTODO: get direct access URL and paste it to the email message body.'); - - if (this.dialog && this.dialog.is(':ui-dialog')) - this.dialog.dialog('close'); -}, - -// open a dialog showing the ownCloud widget -open_dialog: function(html) -{ - // request iframe code from server - if (!this.dialog && !html) { - var me = this; - rcmail.addEventListener('plugin.owncloudembed', function(html){ me.open_dialog(html); }); - rcmail.http_request('owncloud/embed'); - return; - } - // create jQuery dialog with iframe - else if (html) { - var height = $(window).height() * 0.8; - this.dialog = $('
') - .addClass('owncloudembed') - .css({ width:'100%', height:height+'px' }) - .appendTo(document.body) - .html(html); - - this.dialog.dialog({ - modal: true, - autoOpen: false, - title: 'Select a file from the cloud', - width: '80%', - height: height - }); - } - - // open the dialog - if (this.dialog && this.dialog.is(':ui-dialog')) - this.dialog.dialog('open'); -} - -}; - -// Roundcube startup -window.rcmail && rcmail.addEventListener('init', function(){ - // add a button for ownCloud file selection to compose screen - if (rcmail.env.action == 'compose') { - $('') - .addClass('button owncloudcompose') - .html('ownCloud') - .click(function(){ rcube_owncloud.instance().open_dialog(); return false; }) - .insertAfter('#compose-attachments input.button') - .before(' '); - } -}) - diff --git a/plugins/owncloud/owncloud.php b/plugins/owncloud/owncloud.php deleted file mode 100644 index d8cbbf28..00000000 --- a/plugins/owncloud/owncloud.php +++ /dev/null @@ -1,136 +0,0 @@ - - * @author Thomas Bruederli - * @licence GNU AGPL - * - * Configuration (see config.inc.php.dist) - * - */ - -class owncloud extends rcube_plugin -{ - // all task excluding 'login' - public $task = '?(?!login).*'; - // skip frames - public $noframe = true; - - function init() - { - // requires kolab_auth plugin - if (empty($_SESSION['kolab_uid'])) { - // temporary: - $_SESSION['kolab_uid'] = $_SESSION['username']; - // return; - } - - $rcmail = rcube::get_instance(); - - $this->add_texts('localization/', false); - - // register task - $this->register_task('owncloud'); - - // register actions - $this->register_action('index', array($this, 'action')); - $this->register_action('embed', array($this, 'embed')); - $this->add_hook('session_destroy', array($this, 'logout')); - - // handler for sso requests sent by the owncloud kolab_auth app - if ($rcmail->action == 'owncloudsso' && !empty($_POST['token'])) { - $this->add_hook('startup', array($this, 'sso_request')); - } - - // add taskbar button - $this->add_button(array( - 'command' => 'owncloud', - 'class' => 'button-owncloud', - 'classsel' => 'button-owncloud button-selected', - 'innerclass' => 'button-inner', - 'label' => 'owncloud.owncloud', - ), 'taskbar'); - - // add style for taskbar button (must be here) and Help UI - $this->include_stylesheet($this->local_skin_path()."/owncloud.css"); - - if ($rcmail->task == 'owncloud' || $rcmail->action == 'compose') { - $this->include_script('owncloud.js'); - } - } - - function action() - { - $rcmail = rcube::get_instance(); - - $rcmail->output->add_handlers(array('owncloudframe' => array($this, 'frame'))); - $rcmail->output->set_pagetitle($this->gettext('owncloud')); - $rcmail->output->send('owncloud.owncloud'); - } - - function embed() - { - $rcmail = rcmail::get_instance(); - $rcmail->output->command('plugin.owncloudembed', $this->frame()); - $rcmail->output->send(); - } - - function frame() - { - $rcmail = rcube::get_instance(); - $this->load_config(); - - // generate SSO auth token - if (empty($_SESSION['owncloudauth'])) - $_SESSION['owncloudauth'] = md5('ocsso' . $_SESSION['user_id'] . microtime() . $rcmail->config->get('des_key')); - - $src = $rcmail->config->get('owncloud_url'); - $src .= '?kolab_auth=' . strrev(rtrim(base64_encode(http_build_query(array( - 'session' => session_id(), - 'cname' => session_name(), - 'token' => $_SESSION['owncloudauth'], - ))), '=')); - - return html::tag('iframe', array('id' => 'owncloudframe', 'src' => $src, - 'width' => "100%", 'height' => "100%", 'frameborder' => 0)); - } - - function logout() - { - $rcmail = rcube::get_instance(); - $this->load_config(); - - // send logout request to owncloud - $logout_url = $rcmail->config->get('owncloud_url') . '?logout=true'; - $rcmail->output->add_script("new Image().src = '$logout_url';", 'foot'); - } - - function sso_request() - { - $response = array(); - $sign_valid = false; - - $rcmail = rcube::get_instance(); - $this->load_config(); - - // check signature - if ($hmac = $_POST['hmac']) { - unset($_POST['hmac']); - $postdata = http_build_query($_POST, '', '&'); - $sign_valid = ($hmac == hash_hmac('sha256', $postdata, $rcmail->config->get('owncloud_secret', ''))); - } - - // if ownCloud sent a valid auth request, return plain username and password - if ($sign_valid && !empty($_POST['token']) && $_POST['token'] == $_SESSION['owncloudauth']) { - $user = $_SESSION['kolab_uid']; // requires kolab_auth plugin - $pass = $rcmail->decrypt($_SESSION['password']); - $response = array('user' => $user, 'pass' => $pass); - } - - echo json_encode($response); - exit; - } - -} diff --git a/plugins/owncloud/skins/classic/cloud.png b/plugins/owncloud/skins/classic/cloud.png deleted file mode 100644 index 15a288f79ba14ca1849b1f6e1794cdc7ec289f44..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1068 zcmV+{1k?M8P)LIHJFHnTWESEO)}Q2lrG@4D-0n1ZK9z^+=ez+zw2Y9m}1- z)lOg|SGvLI_#0?@qnRs)9ewBQ>eMh#JpAy=cgqLTU<>G&cg z0!+q2szw?LbiH9fuH3OBagPG0M+A&cBHgusA2eE1T#X9Yy^`v};HY@(rS~36?~!^dBIpaZ@<2NaCIp~ zK1|xT^L~VCbaPKP%fZE%2*JQGTyeZDA~OH=p*@Yhz_5@!^ZCU+wW;dfOsRQ^KFx!X z1tsfw-S-}mH9|OO&P|KREC^ - - -<roundcube:object name="pagetitle" /> - - - - - - - - - -
- -
- - - diff --git a/plugins/owncloud/skins/larry/cloud.png b/plugins/owncloud/skins/larry/cloud.png deleted file mode 100644 index 7ad3cd96da1b921eea699704547911ffb7f9176a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1844 zcmV-42g~@0P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L02{9W02{9XUK)`c00007bV*G`2iyY) z4l5-b)*$Zy00y*4L_t(Y$K94|R8-d$$A9O}3<0gl3>7qzDzK=_D_V)sRmydvlm)4ZA^}ZBmkeSjBJ%KsqLG0C zL1*sW{cxEP2u?@80`d=-#yU2Y_dP_Tqt@kMfO!hl(U~&AZp( z?gt;9kis3Ay8ERN_+w8^U6%U7%Zp!_`C6@rh-sQ)nx=?|6cip2V34ls_E%=l*6!$# zxoHdPL_})OU63=CRbm)M2;49X5fS;#>x-`pOMZL;5dQGaJzweoI}l!4RxUSh-jssE z!{TdhmOvmNA|fpn@3k>N811J_ly&{OSbS{HDY8hL{1Z zsi}zp{qMsc2(V+<2Q)S|(d28!eex8=M~*S}2V)qTIEWG78S_khT>rPC`wR%Qv^1KX zAgxmehXJHcn|V5@v4}|4#x1h%K%oT3g0hH+yt96DsQ==u`P%>{PoAuGjSQTw$nOJN zs%p-40{KVoUg>yPuno?yUb|j4ZP_k4xqD^F^3}J1Cp$(O&rh4*?Dq%c*oiV}ZEX#G zz>?#oa=oFU6Wfkvnx-5*c0$_P+Cpm@yk3bJl)NS{FHhs8nR82nOg=e!Tv~42u$Y^s z>_1r8iDg${!!V?(rdH0Jt?uMm!J%RS&{9%ThKdMEDZZHykGt#?N-55rzrg8=$}i3f zr7%swZnJgzeo4cI@Y|&;H)uV2gloZtkuE1~H*c}|FFSGA9gG~F#FhH1D5dBcYDy^p z&YinJzkB;4A_Pg^6&}XI^w->4soU)fZWa+9dT=zoBVEMw>&wUy_j912CDY;rogyLkqy3@xiZf?r`5#u99kmYHbHA7u=vo>Y zn|$)d;-w|G)ADQmezYRvPxU#u`}}L)+0fW;=p&hH-(Kg-$=h#y_lG~;323wC%>Q`H zwmmoU_8tiQ@$L2Aqz4{*7hu%bC*S|;`ybvqRCrj{|9MC2@DU?3L?qlaP5$3);w)HK z4%yX^zU|AG@c%k9E){yYAS@hx&N7VlNVF;NRMo)LIEYPz%n{Vf7SC~C0l}?mVq~E0%p=!oDLOYVE}z1om_L=1NF6#zZK?h zg2G+k>H}>p-4=?j9x1Y`i8&-Nan?fCm0u#ex{dU0`9K_7sz@jjSaATHQ7|Rl+Btq& zH?s6{fv*AkJ|XakQjANTL@$@0u<&Q~Im@0g1QSp;h3ToPu`x9+n%Kn8NPa?ibh?ll zZ!vGU(nXf1=7GBa_B3E__%)1r`~y8LW05y$!cWblu|qn@q72abL^{oDj(ZZRuRTWo zRxgR8g_wcptMj;a@jUq3U`4*QM=usU6C%OaVEwOr*nBhA)svc<8ZD-OED<(vC}=hy zU_wtjR901!5Fbb7;e!E|K7BvS_m}f&31m+1aK&x(;K@iMA@4eX3@DTU+kbzt#(f;F zU8L27rT}<~@;QIDg1Y*8Do>Tc(Oet~5=K8HtoheTW(*5w^8zipK%l=4S#DrTI(o5y zQZ{zfLCV~Xda;-Ti$wHd5#G!ZHk}r|SY%+4NJ9I#UM#}mzu)dpNq<={78pNGkzIW| zDh3vbR6H6PkFQ~@eV?q0kH9oRb13kXm!S{&7KT_c9&G($N#)7gDfDzIBKyLpC1=T; zo&uEfYL+Or*23<$N)mp#>Ve9uUIu*Ki9gBV_f_ZXE@M+r)e6*{{KDF!0RaJf2d_-9MSmmK&Z}9^YlQc3 zwI_#)3bWXIit;TP29M@~#VS-=LJ1NCyj;zF;g%PMfCdtw0jp#LFS2O&AqFQ;1z^PZ*<=<~l3nd*N$yDo iCr{Rk#r~Dwj{gCN3@RvrqjXyU0000 - - -<roundcube:object name="pagetitle" /> - - - - - - - - -
- -
- - - - - From 95af797971cd18f25a3a9dda306f05baebc82045 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Mon, 29 Feb 2016 11:38:46 +0100 Subject: [PATCH 011/483] Update localization --- plugins/kolab_auth/localization/es_ES.inc | 9 --------- plugins/kolab_auth/localization/pl_PL.inc | 9 --------- plugins/kolab_folders/localization/sv_SE.inc | 2 -- plugins/libcalendaring/localization/bg_BG.inc | 3 --- plugins/libcalendaring/localization/sk_SK.inc | 9 --------- plugins/libkolab/localization/ca_ES.inc | 4 ---- plugins/libkolab/localization/cs_CZ.inc | 4 ---- plugins/libkolab/localization/da_DK.inc | 4 ---- plugins/libkolab/localization/de_CH.inc | 6 ------ plugins/libkolab/localization/de_DE.inc | 4 ---- plugins/libkolab/localization/es_AR.inc | 4 ---- plugins/libkolab/localization/es_ES.inc | 4 ---- plugins/libkolab/localization/fi_FI.inc | 4 ---- plugins/libkolab/localization/fr_FR.inc | 4 ---- plugins/libkolab/localization/it_IT.inc | 4 ---- plugins/libkolab/localization/nl_NL.inc | 3 --- plugins/libkolab/localization/pl_PL.inc | 3 --- plugins/libkolab/localization/sl_SI.inc | 3 --- plugins/libkolab/localization/sv_SE.inc | 3 --- plugins/libkolab/localization/th_TH.inc | 3 --- plugins/libkolab/localization/uk_UA.inc | 3 --- 21 files changed, 92 deletions(-) delete mode 100644 plugins/kolab_auth/localization/es_ES.inc delete mode 100644 plugins/kolab_auth/localization/pl_PL.inc delete mode 100644 plugins/libcalendaring/localization/sk_SK.inc diff --git a/plugins/kolab_auth/localization/es_ES.inc b/plugins/kolab_auth/localization/es_ES.inc deleted file mode 100644 index ed203e6b..00000000 --- a/plugins/kolab_auth/localization/es_ES.inc +++ /dev/null @@ -1,9 +0,0 @@ - diff --git a/plugins/kolab_auth/localization/pl_PL.inc b/plugins/kolab_auth/localization/pl_PL.inc deleted file mode 100644 index ed203e6b..00000000 --- a/plugins/kolab_auth/localization/pl_PL.inc +++ /dev/null @@ -1,9 +0,0 @@ - diff --git a/plugins/kolab_folders/localization/sv_SE.inc b/plugins/kolab_folders/localization/sv_SE.inc index 6272ee17..ca563050 100644 --- a/plugins/kolab_folders/localization/sv_SE.inc +++ b/plugins/kolab_folders/localization/sv_SE.inc @@ -6,7 +6,6 @@ * * For translation see https://www.transifex.com/projects/p/kolab/resource/kolab_folders/ */ - $labels['folderctype'] = 'Innehållstyp'; $labels['foldertypemail'] = 'E-post'; $labels['foldertypeevent'] = 'Kalender'; @@ -26,5 +25,4 @@ $labels['wastebasket'] = 'Papperskorg'; $labels['junkemail'] = 'Skräp'; $labels['confidential'] = 'Konfidentiellt'; $messages['defaultfolderexists'] = 'Det finns redan en standardmapp för specificerad typ'; - ?> diff --git a/plugins/libcalendaring/localization/bg_BG.inc b/plugins/libcalendaring/localization/bg_BG.inc index 52501513..13764abb 100644 --- a/plugins/libcalendaring/localization/bg_BG.inc +++ b/plugins/libcalendaring/localization/bg_BG.inc @@ -1,5 +1,4 @@ Date: Mon, 29 Feb 2016 12:02:54 +0100 Subject: [PATCH 012/483] Fix bug where a note attached to an email was displayed as 0 in size --- plugins/kolab_notes/kolab_notes.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/kolab_notes/kolab_notes.php b/plugins/kolab_notes/kolab_notes.php index 7faee11c..feeb6b38 100644 --- a/plugins/kolab_notes/kolab_notes.php +++ b/plugins/kolab_notes/kolab_notes.php @@ -1152,10 +1152,12 @@ class kolab_notes extends rcube_plugin foreach ($uids as $uid) { if ($note = $this->get_note(array('uid' => $uid, 'list' => $list))) { + $data = $this->note2message($note); $args['attachments'][] = array( 'name' => abbreviate_string($note['title'], 50, ''), 'mimetype' => 'message/rfc822', - 'data' => $this->note2message($note), + 'data' => $data, + 'size' => strlen($data), ); if (empty($args['param']['subject'])) { From 05b1c5c8b9add26688e10fe36e9621381f2b8508 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 1 Mar 2016 10:56:18 +0100 Subject: [PATCH 013/483] Fix bug where tags (in the tags cloud) were not selected in search mode (#4792) I.e. on message view page and when returning from it in mail UI --- plugins/kolab_tags/lib/kolab_tags_engine.php | 25 ++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/plugins/kolab_tags/lib/kolab_tags_engine.php b/plugins/kolab_tags/lib/kolab_tags_engine.php index 4e15069b..9a2f5194 100644 --- a/plugins/kolab_tags/lib/kolab_tags_engine.php +++ b/plugins/kolab_tags/lib/kolab_tags_engine.php @@ -73,6 +73,11 @@ class kolab_tags_engine // load miniColors jqueryui::miniColors(); + + // Modify search filter (and set selected tags) + if ($this->rc->action == 'show' || !$this->rc->action) { + $this->search_filter_mods(); + } } /** @@ -443,6 +448,26 @@ class kolab_tags_engine return $args; } + /** + * Get selected tags when in search-mode + */ + protected function search_filter_mods() + { + if (!empty($_REQUEST['_search']) && !empty($_SESSION['search']) + && $_SESSION['search_request'] == $_REQUEST['_search'] + && ($filter = $_SESSION['search_filter']) + ) { + if (preg_match('/^(kolab_tags_[0-9]{10,}:([^:]+):)/', $filter, $m)) { + $search_tags = explode(',', $m[2]); + $search_filter = substr($filter, strlen($m[1])); + + // send current search properties to the browser + $this->rc->output->set_env('search_filter_selected', $search_filter); + $this->rc->output->set_env('selected_tags', $search_tags); + } + } + } + /** * "Convert" tag object to simple array for use in javascript */ From 7c3b2d646fa2a194b2bcf19272b8b0d83db9642f Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 1 Mar 2016 11:04:22 +0100 Subject: [PATCH 014/483] Fix weird javascript error when setting editor content (#4781) --- plugins/kolab_notes/notes.js | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/kolab_notes/notes.js b/plugins/kolab_notes/notes.js index 3f6a8ab3..f4a57cc4 100644 --- a/plugins/kolab_notes/notes.js +++ b/plugins/kolab_notes/notes.js @@ -954,6 +954,7 @@ function rcube_kolab_notes_ui(settings) if (!readonly && editor) { gui_object('notesdetailview', container).hide(); gui_object('noteseditform', container).show(); + editor.setContent(''); // #4781 editor.setContent(html); node = editor.getContentAreaContainer().childNodes[0]; if (node) node.tabIndex = content.get(0).tabIndex; From 65c49e78c79e6040c2899583998812e1259d51aa Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 1 Mar 2016 11:45:25 +0100 Subject: [PATCH 015/483] Fix bug where counters in tags cloud were not updated after task delete (#3482) --- plugins/tasklist/tasklist.js | 1 + plugins/tasklist/tasklist.php | 3 +++ 2 files changed, 4 insertions(+) diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js index 4d1ac570..3c8f2a32 100644 --- a/plugins/tasklist/tasklist.js +++ b/plugins/tasklist/tasklist.js @@ -276,6 +276,7 @@ function rcube_tasklist_ui(settings) rcmail.addEventListener('plugin.update_tasklist', update_list); rcmail.addEventListener('plugin.destroy_tasklist', destroy_list); rcmail.addEventListener('plugin.unlock_saving', unlock_saving); + rcmail.addEventListener('plugin.refresh_tagcloud', function() { update_tagcloud(); }); rcmail.addEventListener('requestrefresh', before_refresh); rcmail.addEventListener('plugin.reload_data', function(){ list_tasks(null, true); diff --git a/plugins/tasklist/tasklist.php b/plugins/tasklist/tasklist.php index 7c45a3f6..b8cd6681 100644 --- a/plugins/tasklist/tasklist.php +++ b/plugins/tasklist/tasklist.php @@ -562,6 +562,9 @@ class tasklist extends rcube_plugin } $this->rc->output->command('plugin.update_task', $refresh); } + else if ($success && ($action == 'delete' || $action == 'undelete')) { + $this->rc->output->command('plugin.refresh_tagcloud'); + } } /** From 494785384db670cf543ff6bbee514c3a4ef294db Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 1 Mar 2016 15:30:04 +0100 Subject: [PATCH 016/483] Update Horde_Date_Recurrence with fixes from upstream --- plugins/libcalendaring/lib/Horde_Date.php | 4 +- .../lib/Horde_Date_Recurrence.php | 118 ++++++++++++------ 2 files changed, 80 insertions(+), 42 deletions(-) diff --git a/plugins/libcalendaring/lib/Horde_Date.php b/plugins/libcalendaring/lib/Horde_Date.php index 9197f846..5265acd5 100644 --- a/plugins/libcalendaring/lib/Horde_Date.php +++ b/plugins/libcalendaring/lib/Horde_Date.php @@ -393,8 +393,8 @@ class Horde_Date */ public static function fromDays($days) { - if (function_exists('JDToGregorian')) { - list($month, $day, $year) = explode('/', JDToGregorian($days)); + if (function_exists('jdtogregorian')) { + list($month, $day, $year) = explode('/', jdtogregorian($days)); } else { $days = intval($days); diff --git a/plugins/libcalendaring/lib/Horde_Date_Recurrence.php b/plugins/libcalendaring/lib/Horde_Date_Recurrence.php index 19e372c2..99a7016c 100644 --- a/plugins/libcalendaring/lib/Horde_Date_Recurrence.php +++ b/plugins/libcalendaring/lib/Horde_Date_Recurrence.php @@ -1,26 +1,27 @@ 1 ? $plur : $sing); } + function t($arg) { return $arg; } + function ngettext($sing, $plur, $num) { return ($num > 1 ? $plur : $sing); } } /** * This file contains the Horde_Date_Recurrence class and according constants. * - * Copyright 2007-2012 Horde LLC (http://www.horde.org/) + * Copyright 2007-2015 Horde LLC (http://www.horde.org/) * * See the enclosed file COPYING for license information (LGPL). If you * did not receive this file, see http://www.horde.org/licenses/lgpl21. @@ -285,14 +286,19 @@ class Horde_Date_Recurrence public function getRecurName() { switch ($this->getRecurType()) { - case self::RECUR_NONE: return Horde_Date_Translation::t("No recurrence"); - case self::RECUR_DAILY: return Horde_Date_Translation::t("Daily"); - case self::RECUR_WEEKLY: return Horde_Date_Translation::t("Weekly"); + case self::RECUR_NONE: + return Horde_Date_Translation::t("No recurrence"); + case self::RECUR_DAILY: + return Horde_Date_Translation::t("Daily"); + case self::RECUR_WEEKLY: + return Horde_Date_Translation::t("Weekly"); case self::RECUR_MONTHLY_DATE: - case self::RECUR_MONTHLY_WEEKDAY: return Horde_Date_Translation::t("Monthly"); + case self::RECUR_MONTHLY_WEEKDAY: + return Horde_Date_Translation::t("Monthly"); case self::RECUR_YEARLY_DATE: case self::RECUR_YEARLY_DAY: - case self::RECUR_YEARLY_WEEKDAY: return Horde_Date_Translation::t("Yearly"); + case self::RECUR_YEARLY_WEEKDAY: + return Horde_Date_Translation::t("Yearly"); } } @@ -453,6 +459,7 @@ class Horde_Date_Recurrence $next->compareDateTime($after) >= 0) { return $next; } + break; case self::RECUR_WEEKLY: @@ -482,7 +489,6 @@ class Horde_Date_Recurrence $after_week = Horde_Date_Utils::firstDayOfWeek($week, $theYear); $after_week->timezone = $this->start->timezone; $after_week_end = clone $after_week; - $after_week_end->mday += 7; $diff = $start_week->diff($after_week); $interval = $this->recurInterval * 7; @@ -650,6 +656,10 @@ class Horde_Date_Recurrence $next = clone $estart; $next->setNthWeekday($weekday, $nth); + if ($next->month != $estart->month) { + // We're already in the next month. + continue; + } if ($next->compareDateTime($after) < 0) { // We haven't made it past $after yet, try again. continue; @@ -875,7 +885,10 @@ class Horde_Date_Recurrence */ public function addException($year, $month, $mday) { - $this->exceptions[] = sprintf('%04d%02d%02d', $year, $month, $mday); + $key = sprintf('%04d%02d%02d', $year, $month, $mday); + if (array_search($key, $this->exceptions) === false) { + $this->exceptions[] = sprintf('%04d%02d%02d', $year, $month, $mday); + } } /** @@ -1053,13 +1066,15 @@ class Horde_Date_Recurrence if (strpos($remainder, '#') === 0) { $this->setRecurCount(substr($remainder, 1)); } else { - list($year, $month, $mday) = sscanf($remainder, '%04d%02d%02d'); + list($year, $month, $mday, $hour, $min, $sec, $tz) = + sscanf($remainder, '%04d%02d%02dT%02d%02d%02d%s'); $this->setRecurEnd(new Horde_Date(array('year' => $year, 'month' => $month, 'mday' => $mday, - 'hour' => 23, - 'min' => 59, - 'sec' => 59))); + 'hour' => $hour, + 'min' => $min, + 'sec' => $sec), + $tz == 'Z' ? 'UTC' : $this->start->timezone)); } } } @@ -1225,15 +1240,23 @@ class Horde_Date_Recurrence break; } + // MUST take into account the time portion if it is present. + // See Bug: 12869 and Bug: 2813 if (isset($rdata['UNTIL'])) { - list($year, $month, $mday) = sscanf($rdata['UNTIL'], - '%04d%02d%02d'); - $this->setRecurEnd(new Horde_Date(array('year' => $year, - 'month' => $month, - 'mday' => $mday, - 'hour' => 23, - 'min' => 59, - 'sec' => 59))); + if (preg_match('/^(\d{4})-?(\d{2})-?(\d{2})T? ?(\d{2}):?(\d{2}):?(\d{2})(?:\.\d+)?(Z?)$/', $rdata['UNTIL'], $parts)) { + $until = new Horde_Date($rdata['UNTIL'], 'UTC'); + $until->setTimezone($this->start->timezone); + } else { + list($year, $month, $mday) = sscanf($rdata['UNTIL'], + '%04d%02d%02d'); + $until = new Horde_Date( + array('year' => $year, + 'month' => $month, + 'mday' => $mday + 1), + $this->start->timezone + ); + } + $this->setRecurEnd($until); } if (isset($rdata['COUNT'])) { $this->setRecurCount($rdata['COUNT']); @@ -1266,16 +1289,18 @@ class Horde_Date_Recurrence break; case self::RECUR_WEEKLY: - $rrule = 'FREQ=WEEKLY;INTERVAL=' . $this->recurInterval . ';BYDAY='; + $rrule = 'FREQ=WEEKLY;INTERVAL=' . $this->recurInterval; $vcaldays = array('SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'); for ($i = $flag = 0; $i <= 7; ++$i) { if ($this->recurOnDay(pow(2, $i))) { - if ($flag) { + if ($flag == 0) { + $rrule .= ';BYDAY='; + $flag = 1; + } else { $rrule .= ','; } $rrule .= $vcaldays[$i]; - $flag = true; } } break; @@ -1341,13 +1366,13 @@ class Horde_Date_Recurrence } /** - * Parses the recurrence data from a hash. + * Parses the recurrence data from a Kolab hash. * * @param array $hash The hash to convert. * * @return boolean True if the hash seemed valid, false otherwise. */ - public function fromHash($hash) + public function fromKolab($hash) { $this->reset(); @@ -1426,7 +1451,7 @@ class Horde_Date_Recurrence break; case 'yearday': - if (!isset($hash['month'])) { + if (!isset($hash['daynumber'])) { $this->setRecurType(self::RECUR_NONE); return false; } @@ -1537,23 +1562,31 @@ class Horde_Date_Recurrence } // Exceptions. - if (isset($hash['exceptions'])) { - $this->exceptions = $hash['exceptions']; + if (isset($hash['exclusion'])) { + foreach ($hash['exclusion'] as $exception) { + if ($exception instanceof DateTime) { + $this->exceptions[] = $exception->format('Ymd'); + } + } } - if (isset($hash['completions'])) { - $this->completions = $hash['completions']; + if (isset($hash['complete'])) { + foreach ($hash['complete'] as $completion) { + if ($exception instanceof DateTime) { + $this->completions[] = $completion->format('Ymd'); + } + } } return true; } /** - * Export this object into a hash. + * Export this object into a Kolab hash. * * @return array The recurrence hash. */ - public function toHash() + public function toKolab() { if ($this->getRecurType() == self::RECUR_NONE) { return array(); @@ -1651,15 +1684,20 @@ class Horde_Date_Recurrence } elseif ($this->hasRecurEnd()) { $date = $this->getRecurEnd(); $hash['range-type'] = 'date'; - $hash['range'] = $date->datestamp(); + $hash['range'] = $date->toDateTime(); } else { $hash['range-type'] = 'none'; $hash['range'] = ''; } // Recurrence exceptions - $hash['exceptions'] = $this->exceptions; - $hash['completions'] = $this->completions; + $hash['exclusion'] = $hash['complete'] = array(); + foreach ($this->exceptions as $exception) { + $hash['exclusion'][] = new DateTime($exception); + } + foreach ($this->completions as $completionexception) { + $hash['complete'][] = new DateTime($completionexception); + } return $hash; } From 429990b3cd8c05db784788b3dc14e67e3e9b3af5 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 3 Mar 2016 15:20:37 +0100 Subject: [PATCH 017/483] Fix weekly recurrences broken with last "upstream merge" of Horde_date_Recurrence --- plugins/libcalendaring/lib/Horde_Date_Recurrence.php | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/libcalendaring/lib/Horde_Date_Recurrence.php b/plugins/libcalendaring/lib/Horde_Date_Recurrence.php index 99a7016c..9247257f 100644 --- a/plugins/libcalendaring/lib/Horde_Date_Recurrence.php +++ b/plugins/libcalendaring/lib/Horde_Date_Recurrence.php @@ -489,6 +489,7 @@ class Horde_Date_Recurrence $after_week = Horde_Date_Utils::firstDayOfWeek($week, $theYear); $after_week->timezone = $this->start->timezone; $after_week_end = clone $after_week; + $after_week_end->mday += 7; $diff = $start_week->diff($after_week); $interval = $this->recurInterval * 7; From ca977f5caeef03a143114c02b9777a4b5ed5a760 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 4 Mar 2016 11:06:33 +0100 Subject: [PATCH 018/483] Fix matching recurring tasks with today/tomorrow/later filter (#4205) Differential Revision: https://git.kolab.org/D95 --- plugins/tasklist/tasklist.php | 65 ++++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 8 deletions(-) diff --git a/plugins/tasklist/tasklist.php b/plugins/tasklist/tasklist.php index b8cd6681..c4305abb 100644 --- a/plugins/tasklist/tasklist.php +++ b/plugins/tasklist/tasklist.php @@ -1352,14 +1352,63 @@ class tasklist extends rcube_plugin else if ($rec['date'] < $today) $mask |= self::FILTER_MASK_OVERDUE; - if ($duedate <= $today || ($rec['startdate'] && $start <= $today)) - $mask |= self::FILTER_MASK_TODAY; - if ($duedate <= $tomorrow || ($rec['startdate'] && $start <= $tomorrow)) - $mask |= self::FILTER_MASK_TOMORROW; - if (($start > $tomorrow && $start <= $weeklimit) || ($duedate > $tomorrow && $duedate <= $weeklimit)) - $mask |= self::FILTER_MASK_WEEK; - else if ($start > $weeklimit || ($rec['date'] && $duedate > $weeklimit)) - $mask |= self::FILTER_MASK_LATER; + if (empty($rec['recurrence']) || $duedate < $today || $start > $weeklimit) { + if ($duedate <= $today || ($rec['startdate'] && $start <= $today)) + $mask |= self::FILTER_MASK_TODAY; + if ($duedate <= $tomorrow || ($rec['startdate'] && $start <= $tomorrow)) + $mask |= self::FILTER_MASK_TOMORROW; + if (($start > $tomorrow && $start <= $weeklimit) || ($duedate > $tomorrow && $duedate <= $weeklimit)) + $mask |= self::FILTER_MASK_WEEK; + else if ($start > $weeklimit || $duedate > $weeklimit) + $mask |= self::FILTER_MASK_LATER; + } + else if ($rec['startdate'] || $rec['date']) { + $date = new DateTime($rec['startdate'] ?: $rec['date'], $this->timezone); + + // set safe recurrence start + while ($date->format('Y-m-d') >= $today) { + switch ($rec['recurrence']['FREQ']) { + case 'DAILY': + $date = clone $today_date; + $date->sub(new DateInterval('P1D')); + break; + case 'WEEKLY': $date->sub(new DateInterval('P7D')); break; + case 'MONTHLY': $date->sub(new DateInterval('P1M')); break; + case 'YEARLY': $date->sub(new DateInterval('P1Y')); break; + default; break 2; + } + } + + $date->_dateonly = true; + + $engine = libcalendaring::get_recurrence(); + $engine->init($rec['recurrence'], $date); + + // check task occurrences (stop next week) + // FIXME: is there a faster way of doing this? + while ($date = $engine->next()) { + $date = $date->format('Y-m-d'); + + // break iteration asap + if ($date > $duedate || ($mask & self::FILTER_MASK_LATER)) { + break; + } + + if ($date == $today) { + $mask |= self::FILTER_MASK_TODAY; + } + else if ($date == $tomorrow) { + $mask |= self::FILTER_MASK_TOMORROW; + } + else if ($date > $tomorrow && $date <= $weeklimit) { + $mask |= self::FILTER_MASK_WEEK; + } + else if ($date > $weeklimit) { + $mask |= self::FILTER_MASK_LATER; + break; + } + } + } // add masks for assigned tasks if ($this->is_organizer($rec) && !empty($rec['attendees']) && $this->is_attendee($rec) === false) From ff10091b6c8bb0665835ac88635d010b09fd0d09 Mon Sep 17 00:00:00 2001 From: "Jeroen van Meeuwen (Kolab Systems)" Date: Sun, 6 Mar 2016 09:02:54 +0100 Subject: [PATCH 019/483] Add .arcconfig --- .arcconfig | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .arcconfig diff --git a/.arcconfig b/.arcconfig new file mode 100644 index 00000000..b9fd1a34 --- /dev/null +++ b/.arcconfig @@ -0,0 +1,3 @@ +{ + "phabricator.uri": "https://git.kolab.org" +} From 9daf32495a7a57ab5df70dc8acd513db661ed3d9 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Mon, 7 Mar 2016 17:54:01 +0100 Subject: [PATCH 020/483] Fix regression in get_object() (T1098) Also remove the second argument as it was useless. --- plugins/libkolab/lib/kolab_storage.php | 2 +- plugins/libkolab/lib/kolab_storage_cache.php | 27 +++++++++++++ plugins/libkolab/lib/kolab_storage_folder.php | 40 ++----------------- 3 files changed, 32 insertions(+), 37 deletions(-) diff --git a/plugins/libkolab/lib/kolab_storage.php b/plugins/libkolab/lib/kolab_storage.php index db7e0ba3..53687cdb 100644 --- a/plugins/libkolab/lib/kolab_storage.php +++ b/plugins/libkolab/lib/kolab_storage.php @@ -221,7 +221,7 @@ class kolab_storage else $folder->set_folder($foldername, $type, $folderdata[$foldername]); - if ($object = $folder->get_object($uid, '*')) + if ($object = $folder->get_object($uid)) return $object; } diff --git a/plugins/libkolab/lib/kolab_storage_cache.php b/plugins/libkolab/lib/kolab_storage_cache.php index 41984702..5f8d3fe5 100644 --- a/plugins/libkolab/lib/kolab_storage_cache.php +++ b/plugins/libkolab/lib/kolab_storage_cache.php @@ -323,6 +323,33 @@ class kolab_storage_cache return $this->objects[$msguid]; } + /** + * Getter for a single Kolab object identified by its UID + * + * @param string $uid Object UID + * + * @return array The Kolab object represented as hash array + */ + public function get_by_uid($uid) + { + $old_order_by = $this->order_by; + $old_limit = $this->limit; + + // set order to make sure we get most recent object version + // set limit to skip count query + $this->order_by = '`msguid` DESC'; + $this->limit = array(1, 0); + + $list = $this->select(array(array('uid', '=', $uid))); + + // set the order/limit back to defined value + $this->order_by = $old_order_by; + $this->limit = $old_limit; + + if (!empty($list) && !empty($list[0])) { + return $list[0]; + } + } /** * Insert/Update a cache entry diff --git a/plugins/libkolab/lib/kolab_storage_folder.php b/plugins/libkolab/lib/kolab_storage_folder.php index 00d02efc..ce121ba7 100644 --- a/plugins/libkolab/lib/kolab_storage_folder.php +++ b/plugins/libkolab/lib/kolab_storage_folder.php @@ -348,12 +348,6 @@ class kolab_storage_folder extends kolab_storage_folder_api if ($length !== null) { $this->cache->set_limit($length, $offset); } - - $this->order_and_limit = array( - 'cols' => $sortcols, - 'limit' => $length, - 'offset' => $offset, - ); } /** @@ -383,48 +377,22 @@ class kolab_storage_folder extends kolab_storage_folder_api } /** - * Getter for a single Kolab object, identified by its UID + * Getter for a single Kolab object identified by its UID * - * @param string $uid Object UID - * @param string $type Object type (e.g. contact, event, todo, journal, note, configuration) - * Defaults to folder type + * @param string $uid Object UID * * @return array The Kolab object represented as hash array */ - public function get_object($uid, $type = null) + public function get_object($uid) { if (!$this->valid || !$uid) { return false; } - $query = array(array('uid', '=', $uid)); - - if ($type) { - $query[] = array('type', '=', $type); - } - // synchronize caches $this->cache->synchronize(); - // we don't use cache->get() here because we don't have msguid - // yet, using select() is faster - - // set order to make sure we get most recent object version - // set limit to skip count query - $this->cache->set_order_by('msguid DESC'); - $this->cache->set_limit(1); - - $list = $this->cache->select($this->_prepare_query($query)); - - // set the order/limit back to defined value - $this->cache->set_order_by($this->order_and_limit['order']); - $this->cache->set_limit($this->order_and_limit['limit'], $this->order_and_limit['offset']); - - if (!empty($list) && !empty($list[0])) { - return $list[0]; - } - - return false; + return $this->cache->get_by_uid($uid); } From 14fae65553b0d264ecb21cf55c1134a05f47b5cb Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 8 Mar 2016 10:41:52 +0100 Subject: [PATCH 021/483] Fix some not localized error messages --- plugins/calendar/calendar.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php index e416ea7d..5f2e3c62 100644 --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -1505,11 +1505,11 @@ class calendar extends rcube_plugin } else { if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) { - $msg = $this->gettext(array('name' => 'filesizeerror', 'vars' => array( + $msg = $this->rc->gettext(array('name' => 'filesizeerror', 'vars' => array( 'size' => $this->rc->show_bytes(parse_bytes(ini_get('upload_max_filesize')))))); } else { - $msg = $this->gettext('fileuploaderror'); + $msg = $this->rc->gettext('fileuploaderror'); } $this->rc->output->command('plugin.import_error', array('message' => $msg)); From 9d5dd5bf16067090d63971fd90cb29c09a5cc7b3 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 9 Mar 2016 11:46:26 +0100 Subject: [PATCH 022/483] Unified get_objects/count/get_uids/select methods argument handling (#5209) --- .../lib/rcube_kolab_contacts.php | 2 +- plugins/libkolab/lib/kolab_storage.php | 4 +- plugins/libkolab/lib/kolab_storage_folder.php | 50 ++++++------------- 3 files changed, 17 insertions(+), 39 deletions(-) diff --git a/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php b/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php index b53f8f5f..6c32448d 100644 --- a/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php +++ b/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php @@ -1123,7 +1123,7 @@ class rcube_kolab_contacts extends rcube_addressbook { if (!isset($this->distlists)) { $this->distlists = $this->groupmembers = array(); - foreach ($this->storagefolder->get_objects('distribution-list') as $record) { + foreach ($this->storagefolder->select('distribution-list') as $record) { $record['ID'] = $this->uid2id($record['uid']); foreach ((array)$record['member'] as $i => $member) { $mid = $this->uid2id($member['uid'] ? $member['uid'] : 'mailto:' . $member['email']); diff --git a/plugins/libkolab/lib/kolab_storage.php b/plugins/libkolab/lib/kolab_storage.php index 53687cdb..2f8493ef 100644 --- a/plugins/libkolab/lib/kolab_storage.php +++ b/plugins/libkolab/lib/kolab_storage.php @@ -232,7 +232,7 @@ class kolab_storage * Execute cross-folder searches with the given query. * * @param array Pseudo-SQL query as list of filter parameter triplets - * @param string Object type (contact,event,task,journal,file,note,configuration) + * @param string Folder type (contact,event,task,journal,file,note,configuration) * @param int Expected number of records or limit (for performance reasons) * * @return array List of Kolab data objects (each represented as hash array) @@ -251,7 +251,7 @@ class kolab_storage $folder->set_order_and_limit(null, $limit); } - foreach ($folder->select($query, '*') as $object) { + foreach ($folder->select($query) as $object) { $result[] = $object; } } diff --git a/plugins/libkolab/lib/kolab_storage_folder.php b/plugins/libkolab/lib/kolab_storage_folder.php index ce121ba7..6f338ba7 100644 --- a/plugins/libkolab/lib/kolab_storage_folder.php +++ b/plugins/libkolab/lib/kolab_storage_folder.php @@ -37,7 +37,6 @@ class kolab_storage_folder extends kolab_storage_folder_api public $valid = false; protected $error = 0; - protected $resource_uri; @@ -54,7 +53,6 @@ class kolab_storage_folder extends kolab_storage_folder_api $this->set_folder($name, $type, $type_annotation); } - /** * Set the IMAP folder this instance connects to * @@ -249,8 +247,9 @@ class kolab_storage_folder extends kolab_storage_folder_api /** * Get number of objects stored in this folder * - * @param mixed Pseudo-SQL query as list of filter parameter triplets + * @param mixed Pseudo-SQL query as list of filter parameter triplets * or string with object type (e.g. contact, event, todo, journal, note, configuration) + * * @return integer The number of objects of the given type * @see self::select() */ @@ -266,34 +265,26 @@ class kolab_storage_folder extends kolab_storage_folder_api return $this->cache->count($this->_prepare_query($query)); } - /** - * List all Kolab objects of the given type + * List Kolab objects matching the given query * - * @param string $type Object type (e.g. contact, event, todo, journal, note, configuration) - * @return array List of Kolab data objects (each represented as hash array) + * @param mixed Pseudo-SQL query as list of filter parameter triplets + * or string with object type (e.g. contact, event, todo, journal, note, configuration) + * + * @return array List of Kolab data objects (each represented as hash array) + * @deprecated Use select() */ - public function get_objects($type = null) + public function get_objects($query = array()) { - if (!$type) $type = $this->type; - - if (!$this->valid) { - return array(); - } - - // synchronize caches - $this->cache->synchronize(); - - // fetch objects from cache - return $this->cache->select($this->_prepare_query($type)); + return $this->select($query); } - /** - * Select *some* Kolab objects matching the given query + * Select Kolab objects matching the given query + * + * @param mixed Pseudo-SQL query as list of filter parameter triplets + * or string with object type (e.g. contact, event, todo, journal, note, configuration) * - * @param array Pseudo-SQL query as list of filter parameter triplets - * triplet: array('', '', '') * @return array List of Kolab data objects (each represented as hash array) */ public function select($query = array()) @@ -302,11 +293,6 @@ class kolab_storage_folder extends kolab_storage_folder_api return array(); } - // check query argument - if (empty($query)) { - return $this->get_objects(); - } - // synchronize caches $this->cache->synchronize(); @@ -314,7 +300,6 @@ class kolab_storage_folder extends kolab_storage_folder_api return $this->cache->select($this->_prepare_query($query)); } - /** * Getter for object UIDs only * @@ -395,7 +380,6 @@ class kolab_storage_folder extends kolab_storage_folder_api return $this->cache->get_by_uid($uid); } - /** * Fetch a Kolab object attachment which is stored in a separate part * of the mail MIME message that represents the Kolab record. @@ -443,7 +427,6 @@ class kolab_storage_folder extends kolab_storage_folder_api return null; } - /** * Fetch the mime message from the storage server and extract * the Kolab groupware object from it @@ -831,7 +814,6 @@ class kolab_storage_folder extends kolab_storage_folder_api return $success; } - /** * */ @@ -849,7 +831,6 @@ class kolab_storage_folder extends kolab_storage_folder_api return $result; } - /** * Restore a previously deleted object * @@ -875,7 +856,6 @@ class kolab_storage_folder extends kolab_storage_folder_api return false; } - /** * Move a Kolab object message to another IMAP folder * @@ -914,7 +894,6 @@ class kolab_storage_folder extends kolab_storage_folder_api return false; } - /** * Creates source of the configuration object message * @@ -1095,7 +1074,6 @@ class kolab_storage_folder extends kolab_storage_folder_api return $message; } - /** * Triggers any required updates after changes within the * folder. This is currently only required for handling free/busy From b02359a6ce476f005ed3fd9cb184f208e3fe1ef4 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 9 Mar 2016 12:49:14 +0100 Subject: [PATCH 023/483] Fix 'complete' field value formatting with locales that don't use a dot as decimal point separator (#5258) --- .../tasklist/drivers/database/tasklist_database_driver.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/tasklist/drivers/database/tasklist_database_driver.php b/plugins/tasklist/drivers/database/tasklist_database_driver.php index d5d97041..9dac99eb 100644 --- a/plugins/tasklist/drivers/database/tasklist_database_driver.php +++ b/plugins/tasklist/drivers/database/tasklist_database_driver.php @@ -558,6 +558,9 @@ class tasklist_database_driver extends tasklist_driver if (is_array($prop['recurrence'])) { $prop['recurrence'] = $this->serialize_recurrence($prop['recurrence']); } + if (array_key_exists('complete', $prop)) { + $prop['complete'] = number_format($prop['complete'], 2, '.', ''); + } foreach (array('parent_id', 'date', 'time', 'startdate', 'starttime', 'alarms', 'recurrence', 'status') as $col) { if (empty($prop[$col])) @@ -611,6 +614,9 @@ class tasklist_database_driver extends tasklist_driver if (is_array($prop['recurrence'])) { $prop['recurrence'] = $this->serialize_recurrence($prop['recurrence']); } + if (array_key_exists('complete', $prop)) { + $prop['complete'] = number_format($prop['complete'], 2, '.', ''); + } $sql_set = array(); foreach (array('title', 'description', 'flagged', 'complete') as $col) { From 3ec61b99be260e6bf33338177fa7b14fb404cd9f Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 9 Mar 2016 16:28:52 +0100 Subject: [PATCH 024/483] Fix error when opening an attachment from an itip message (#5343) Also make sure attachment bodies are excluded from the event data in JSON format --- plugins/calendar/calendar.php | 25 +++++++++++++++++++++++-- plugins/calendar/calendar_ui.js | 3 +++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php index 5f2e3c62..d490b4d4 100644 --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -1742,6 +1742,12 @@ class calendar extends rcube_plugin foreach ((array)$event['attachments'] as $k => $attachment) { $event['attachments'][$k]['classname'] = rcube_utils::file2class($attachment['mimetype'], $attachment['name']); + + unset($event['attachments'][$k]['data'], $event['attachments'][$k]['content']); + + if (!$attachment['id']) { + $event['attachments'][$k]['id'] = $k; + } } // convert link URIs references into structs @@ -1892,7 +1898,19 @@ class calendar extends rcube_plugin $rev = rcube_utils::get_input_value('_rev', rcube_utils::INPUT_GPC); $event = array('id' => $event_id, 'calendar' => $calendar, 'rev' => $rev); - $attachment = $this->driver->get_attachment($id, $event); + + if ($calendar == '--invitation--itip') { + $uid = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_GPC); + $part = rcube_utils::get_input_value('_part', rcube_utils::INPUT_GPC); + $mbox = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_GPC); + + $event = $this->lib->mail_get_itip_object($mbox, $uid, $part, 'event'); + $attachment = $event['attachments'][$id]; + $attachment['body'] = &$attachment['data']; + } + else { + $attachment = $this->driver->get_attachment($id, $event); + } // show part page if (!empty($_GET['_frame'])) { @@ -1903,7 +1921,10 @@ class calendar extends rcube_plugin } // deliver attachment content else if ($attachment) { - $attachment['body'] = $this->driver->get_attachment_body($id, $event); + if ($calendar != '--invitation--itip') { + $attachment['body'] = $this->driver->get_attachment_body($id, $event); + } + $this->lib->attachment_get($attachment); } diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js index bb7279d7..9f8601cc 100644 --- a/plugins/calendar/calendar_ui.js +++ b/plugins/calendar/calendar_ui.js @@ -335,6 +335,9 @@ function rcube_calendar_ui(settings) if (event.rev) query._rev = event.rev; + if (event.calendar == "--invitation--itip") + $.extend(query, {_uid: event._uid, _part: event._part, _mbox: event._mbox}); + // open attachment in frame if it's of a supported mimetype if (id && att.mimetype && $.inArray(att.mimetype, settings.mimetypes)>=0) { if (rcmail.open_window(rcmail.url('get-attachment', query), true, true)) { From 7a26dc8d9f8cdecdb2f340cbdbb45a6646639edc Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 9 Mar 2016 17:07:52 +0100 Subject: [PATCH 025/483] Fix PHP Warning: Invalid UTF-8 sequence in argument for json_encode() (#4336) Use rcube_output::json_serialize() instead of json_encode() --- plugins/calendar/calendar.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php index d490b4d4..d9499671 100644 --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -1721,7 +1721,7 @@ class calendar extends rcube_plugin foreach ($events as $event) { $json[] = $this->_client_event($event, $addcss); } - return json_encode($json); + return rcube_output::json_serialize($json); } /** @@ -2243,7 +2243,7 @@ class calendar extends rcube_plugin // let this information be cached for 5min $this->rc->output->future_expire_header(300); - echo json_encode(array( + echo rcube_output::json_serialize(array( 'email' => $email, 'start' => $dts->format('c'), 'end' => $dte->format('c'), @@ -2311,12 +2311,12 @@ class calendar extends rcube_plugin $this->ui->calendar_list(); # set env['calendars'] echo $this->api->output->parse('calendar.eventedit', false, false); echo html::tag('script', array('type' => 'text/javascript'), - "rcmail.set_env('calendars', " . json_encode($this->api->output->env['calendars']) . ");\n". + "rcmail.set_env('calendars', " . rcube_output::json_serialize($this->api->output->env['calendars']) . ");\n". "rcmail.set_env('deleteicon', '" . $this->api->output->env['deleteicon'] . "');\n". "rcmail.set_env('cancelicon', '" . $this->api->output->env['cancelicon'] . "');\n". "rcmail.set_env('loadingicon', '" . $this->api->output->env['loadingicon'] . "');\n". "rcmail.gui_object('attachmentlist', '" . $this->ui->attachmentlist_id . "');\n". - "rcmail.add_label(" . json_encode($texts) . ");\n" + "rcmail.add_label(" . rcube_output::json_serialize($texts) . ");\n" ); exit; } From 7634368e8931992c9ecba4e1acebfb908a60b8e4 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 10 Mar 2016 16:43:10 +0100 Subject: [PATCH 026/483] Prevent from fatal errors on events without end date (#5307) --- plugins/libkolab/lib/kolab_date_recurrence.php | 7 +++++-- plugins/libkolab/lib/kolab_format_event.php | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/plugins/libkolab/lib/kolab_date_recurrence.php b/plugins/libkolab/lib/kolab_date_recurrence.php index f2483c96..4f8ae61a 100644 --- a/plugins/libkolab/lib/kolab_date_recurrence.php +++ b/plugins/libkolab/lib/kolab_date_recurrence.php @@ -49,8 +49,11 @@ class kolab_date_recurrence if (is_object($data['start']) && is_object($data['end'])) $this->duration = $data['start']->diff($data['end']); - else - $this->duration = new DateInterval('PT' . ($data['end'] - $data['start']) . 'S'); + else { + // Prevent from errors when end date is not set (#5307) RFC5545 3.6.1 + $seconds = !empty($data['end']) ? ($data['end'] - $data['start']) : 0; + $this->duration = new DateInterval('PT' . $seconds . 'S'); + } } /** diff --git a/plugins/libkolab/lib/kolab_format_event.php b/plugins/libkolab/lib/kolab_format_event.php index a40f1798..83cee2d3 100644 --- a/plugins/libkolab/lib/kolab_format_event.php +++ b/plugins/libkolab/lib/kolab_format_event.php @@ -191,6 +191,10 @@ class kolab_format_event extends kolab_format_xcal $object['end'] = clone $object['start']; $object['end']->add($interval); } + // make sure end date is specified (#5307) RFC5545 3.6.1 + else if (!$object['end'] && $object['start']) { + $object['end'] = clone $object['start']; + } // organizer is part of the attendees list in Roundcube if ($object['organizer']) { From 2afb5714c54994391a3f0ffb3e04e6604ccfb994 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 2 Mar 2016 16:45:30 +0100 Subject: [PATCH 027/483] Fix duplicated events in other users calendar if its subfolders are active (#5340) Differential Revision: https://git.kolab.org/D94 --- plugins/calendar/calendar.php | 16 +++++++ plugins/calendar/calendar_ui.js | 10 ++++ .../drivers/kolab/kolab_user_calendar.php | 6 +-- plugins/libkolab/lib/kolab_storage.php | 48 ++++++++++++------- 4 files changed, 61 insertions(+), 19 deletions(-) diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php index d9499671..79bc7002 100644 --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -788,6 +788,22 @@ class calendar extends rcube_plugin case "subscribe": if (!$this->driver->subscribe_calendar($cal)) $this->rc->output->show_message($this->gettext('errorsaving'), 'error'); + else { + $calendars = $this->driver->list_calendars(); + $calendar = $calendars[$cal['id']]; + + // find parent folder and check if it's a "user calendar" + // if it's also activated we need to refresh it (#5340) + while ($calendar['parent']) { + if (isset($calendars[$calendar['parent']])) + $calendar = $calendars[$calendar['parent']]; + else + break; + } + + if ($calendar['id'] != $cal['id'] && $calendar['active'] && $calendar['group'] == "other user") + $this->rc->output->command('plugin.refresh_source', $calendar['id']); + } return; case "search": $results = array(); diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js index 9f8601cc..7630ccd7 100644 --- a/plugins/calendar/calendar_ui.js +++ b/plugins/calendar/calendar_ui.js @@ -2961,6 +2961,15 @@ function rcube_calendar_ui(settings) return false; }; + this.calendar_refresh_source = function(id) + { + // got race-conditions fc.currentFetchID when using refetchEvents, + // so we remove and add the source instead + // fc.fullCalendar('refetchEvents', me.calendars[id]); + fc.fullCalendar('removeEventSource', me.calendars[id]); + fc.fullCalendar('addEventSource', me.calendars[id]); + }; + this.calendar_destroy_source = function(id) { var delete_ids = []; @@ -4238,6 +4247,7 @@ window.rcmail && rcmail.addEventListener('init', function(evt) { rcmail.register_command('add-resource', function(){ cal.add_resource2event(); }, false); // register callback commands + rcmail.addEventListener('plugin.refresh_source', function(data) { cal.calendar_refresh_source(data); }); rcmail.addEventListener('plugin.destroy_source', function(p){ cal.calendar_destroy_source(p.id); }); rcmail.addEventListener('plugin.unlock_saving', function(p){ cal.unlock_saving(); }); rcmail.addEventListener('plugin.refresh_calendar', function(p){ cal.refresh(p); }); diff --git a/plugins/calendar/drivers/kolab/kolab_user_calendar.php b/plugins/calendar/drivers/kolab/kolab_user_calendar.php index 84f159bf..07017c4e 100644 --- a/plugins/calendar/drivers/kolab/kolab_user_calendar.php +++ b/plugins/calendar/drivers/kolab/kolab_user_calendar.php @@ -223,11 +223,11 @@ class kolab_user_calendar extends kolab_calendar } } - // aggregate all calendar folders the user shares (but are not subscribed) - foreach (kolab_storage::list_user_folders($this->userdata, 'event', false) as $foldername) { + // aggregate all calendar folders the user shares (but are not activated) + foreach (kolab_storage::list_user_folders($this->userdata, 'event', 2) as $foldername) { $cal = new kolab_calendar($foldername, $this->cal); foreach ($cal->list_events($start, $end, $search, 1) as $event) { - $this->events[$event['id']] = $event; + $this->events[$event['id'] ?: $event['uid']] = $event; $this->timeindex[$this->time_key($event)] = $event['id']; } } diff --git a/plugins/libkolab/lib/kolab_storage.php b/plugins/libkolab/lib/kolab_storage.php index 2f8493ef..88da8da0 100644 --- a/plugins/libkolab/lib/kolab_storage.php +++ b/plugins/libkolab/lib/kolab_storage.php @@ -825,11 +825,7 @@ class kolab_storage if (!$filter) { // Get ALL folders list, standard way if ($subscribed) { - $folders = self::$imap->list_folders_subscribed($root, $mbox); - // add temporarily subscribed folders - if (self::$with_tempsubs && is_array($_SESSION['kolab_subscribed_folders'])) { - $folders = array_unique(array_merge($folders, $_SESSION['kolab_subscribed_folders'])); - } + $folders = self::_imap_list_subscribed($root, $mbox); } else { $folders = self::_imap_list_folders($root, $mbox); @@ -866,12 +862,7 @@ class kolab_storage // Get folders list if ($subscribed) { - $folders = self::$imap->list_folders_subscribed($root, $mbox); - - // add temporarily subscribed folders - if (self::$with_tempsubs && is_array($_SESSION['kolab_subscribed_folders'])) { - $folders = array_unique(array_merge($folders, $_SESSION['kolab_subscribed_folders'])); - } + $folders = self::_imap_list_subscribed($root, $mbox); } else { $folders = self::_imap_list_folders($root, $mbox); @@ -934,6 +925,21 @@ class kolab_storage return $folders; } + /** + * Wrapper for rcube_imap::list_folders_subscribed() + * with support for temporarily subscribed folders + */ + protected static function _imap_list_subscribed($root, $mbox) + { + $folders = self::$imap->list_folders_subscribed($root, $mbox); + + // add temporarily subscribed folders + if (self::$with_tempsubs && is_array($_SESSION['kolab_subscribed_folders'])) { + $folders = array_unique(array_merge($folders, $_SESSION['kolab_subscribed_folders'])); + } + + return $folders; + } /** * Search for shared or otherwise not listed groupware folders the user has access @@ -1538,12 +1544,12 @@ class kolab_storage * * @param array User entry from LDAP * @param string Data type to list folders for (contact,event,task,journal,file,note,mail,configuration) - * @param boolean Return subscribed folders only (null to use configured subscription mode) + * @param int 1 - subscribed folders only, 0 - all folders, 2 - all non-active * @param array Will be filled with folder-types data * * @return array List of folders */ - public static function list_user_folders($user, $type, $subscribed = null, &$folderdata = array()) + public static function list_user_folders($user, $type, $subscribed = 0, &$folderdata = array()) { self::setup(); @@ -1554,9 +1560,19 @@ class kolab_storage if (!empty($user[$user_attrib])) { list($mbox) = explode('@', $user[$user_attrib]); - $delimiter = self::$imap->get_hierarchy_delimiter(); - $other_ns = self::namespace_root('other'); - $folders = self::list_folders($other_ns . $mbox . $delimiter, '*', $type, $subscribed, $folderdata); + $delimiter = self::$imap->get_hierarchy_delimiter(); + $other_ns = self::namespace_root('other'); + $prefix = $other_ns . $mbox . $delimiter; + $subscribed = (int) $subscribed; + $subs = $subscribed < 2 ? (bool) $subscribed : false; + $folders = self::list_folders($prefix, '*', $type, $subs, $folderdata); + + if ($subscribed === 2 && !empty($folders)) { + $active = self::get_states(); + if (!empty($active)) { + $folders = array_diff($folders, $active); + } + } } return $folders; From 4fe52716e3ad14efb8e247bed67abbb571d771ac Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Sun, 13 Mar 2016 13:01:03 +0100 Subject: [PATCH 028/483] Performance: don't load calendar css/js files on logon page (#4033) --- plugins/calendar/calendar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php index 79bc7002..b6c5e234 100644 --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -33,7 +33,7 @@ class calendar extends rcube_plugin const SESSION_KEY = 'calendar_temp'; - public $task = '?(?!logout).*'; + public $task = '?(?!login|logout).*'; public $rc; public $lib; public $resources_dir; From ed93508c8a162c727fb12c748d357ec3d4329343 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 15 Mar 2016 10:02:18 +0100 Subject: [PATCH 029/483] WAP Client plugin (T1124) --- plugins/wap_client/composer.json | 26 ++ plugins/wap_client/config.inc.php.dist | 49 ++++ plugins/wap_client/localization/en_US.inc | 14 + plugins/wap_client/wap_client.php | 340 ++++++++++++++++++++++ 4 files changed, 429 insertions(+) create mode 100644 plugins/wap_client/composer.json create mode 100644 plugins/wap_client/config.inc.php.dist create mode 100644 plugins/wap_client/localization/en_US.inc create mode 100644 plugins/wap_client/wap_client.php diff --git a/plugins/wap_client/composer.json b/plugins/wap_client/composer.json new file mode 100644 index 00000000..a626fbde --- /dev/null +++ b/plugins/wap_client/composer.json @@ -0,0 +1,26 @@ +{ + "name": "kolab/wap_client", + "type": "roundcube-plugin", + "description": "Kolab Web Admin Client", + "homepage": "https://git.kolab.org/diffusion/RPK/", + "license": "AGPLv3", + "version": "0.1.0", + "authors": [ + { + "name": "Aleksander Machniak", + "email": "machniak@kolabsys.com", + "role": "Lead" + } + ], + "repositories": [ + { + "type": "composer", + "url": "http://plugins.roundcube.net" + } + ], + "require": { + "php": ">=5.3.0", + "roundcube/plugin-installer": ">=0.1.3", + "kolab/libkolab": ">=3.2.8" + } +} diff --git a/plugins/wap_client/config.inc.php.dist b/plugins/wap_client/config.inc.php.dist new file mode 100644 index 00000000..7ce30c1e --- /dev/null +++ b/plugins/wap_client/config.inc.php.dist @@ -0,0 +1,49 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +// Kolab WAP API URL +$config['wap_client_uri'] = '/kolab-webadmin/api'; + +// Log conversation with WAP +$config['wap_client_debug'] = false; + +// Domain root DN +$config['wap_client_root_dn'] = 'dc=example,dc=org'; + +// Domain base DN +$config['wap_client_base_dn'] = 'dc=example,dc=org'; + +// Accounts definition +$config['wap_client_accounts'] = array(); +/* +$config['wap_client_accounts'] = array( + 'Lite' => array( + 'description' => 'Mail account with 2GB quota', + 'nsroledn' => array('cn=imap-user,$base_dn', 'cn=active-user,$root_dn'), + 'mailquota' => 2097152, + ), + 'Professional' => array( + 'description' => 'Professional groupware account with 10GB quota', + 'nsroledn' => array('cn=activesync-user,$base_dn', 'cn=kolab-user,$base_dn', 'cn=active-user,$root_dn'), + 'mailquota' => 10485760, + ), +); +*/ diff --git a/plugins/wap_client/localization/en_US.inc b/plugins/wap_client/localization/en_US.inc new file mode 100644 index 00000000..5400e2ae --- /dev/null +++ b/plugins/wap_client/localization/en_US.inc @@ -0,0 +1,14 @@ + + * + * Copyright (C) 2016, Kolab Systems AG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +class wap_client extends rcube_plugin +{ + public $task = 'settings'; + public $noajax = true; + + protected $rc; + protected $wap; + protected $userinfo; + protected $token; + + + /** + * Initializes the plugin + */ + function init() + { + $this->rc = rcmail::get_instance(); + + $this->add_hook('preferences_list', array($this, 'prefs_table')); + $this->add_hook('preferences_save', array($this, 'save_prefs')); + } + + /** + * Hook to inject plugin-specific user settings + */ + public function prefs_table($args) + { + global $CURR_SECTION; + + if ($args['section'] != 'server') { + return; + } + + $this->load_config(); + + $accounts = (array) $this->rc->config->get('wap_client_accounts'); + + if (empty($accounts)) { + return; + } + + $this->add_texts('localization'); + + if ($CURR_SECTION) { + $account_type = $this->get_account_type(); + $_SESSION['wap_client_account_type'] = $account_type; + } + + $input = new html_radiobutton(array('name' => '_account_type', 'style' => 'display:block; float:left')); + $content = ''; + + foreach ($accounts as $idx => $def) { + $id = 'account_type_' . strtolower(asciiwords($idx, true)); + $name = $idx; + $name = $this->rc->text_exists('wap_client.account.' . $name) ? $this->gettext('account.' . $name) : $name; + $desc = $this->rc->text_exists('wap_client.accountdesc.' . $name) ? $this->gettext('accountdesc.' . $name) : $def['description']; + + $name = html::span(array('style' => 'font-weight: bold'), rcube::Q($name)); + if ($desc) { + $name .= html::br() . html::span(null, rcube::Q($desc)); + } + + $label_style = 'display:block; margin: 5px 0; padding-left: 30px'; + $content .= $input->show($account_type, array('value' => $idx, 'id' => $id)) + . html::label(array('for' => $id, 'style' => $label_style), $name); + } + + $conf = array( + 'account' => array( + 'name' => rcube::Q($this->gettext('accountoptions')), + 'options' => array( + 'account_type' => array( + 'title' => $this->gettext('accounttype'), + 'content' => $content, + ) + ) + ) + ); + + $args['blocks'] = array_merge($conf, $args['blocks']); + + return $args; + } + + /** + * Hook to save plugin-specific user settings + */ + public function save_prefs($args) + { + if ($args['section'] != 'server') { + return; + } + + $account_type = rcube_utils::get_input_value('_account_type', rcube_utils::INPUT_POST); + + if (!$account_type || $account_type == $_SESSION['wap_client_account_type']) { + return; + } + + $this->add_texts('localization'); + + $this->set_account_type($account_type); + } + + /** + * Get current account type (from WAP) + */ + protected function get_account_type() + { + $this->init_wap(); + + if (empty($this->userinfo)) { + $this->rc->output->show_message($this->gettext('failedtypedetection'), 'warning'); + return; + } + + $roles = (array) $this->userinfo['nsroledn']; + $accounts = (array) $this->rc->config->get('wap_client_accounts'); + $root_dn = $this->rc->config->get('wap_client_root_dn'); + $base_dn = $this->rc->config->get('wap_client_base_dn'); + + foreach ($accounts as $name => $account) { + foreach ((array) $account['nsroledn'] as $role) { + $value = str_replace('$base_dn', $base_dn, $value); + $value = str_replace('$root_dn', $root_dn, $value); + + if (!in_array($value, $roles)) { + continue 2; + } + } + + return $name; + } + } + + /** + * Set account type (in WAP) + */ + protected function set_account_type($type) + { + if (!$this->init_wap()) { + return false; + } + + $query = $this->userinfo; + $accounts = (array) $this->rc->config->get('wap_client_accounts'); + $root_dn = $this->rc->config->get('wap_client_root_dn'); + $base_dn = $this->rc->config->get('wap_client_base_dn'); + $account = $accounts[$type]; + + if (empty($account)) { + $this->rc->output->show_message($this->gettext('failedtypeupdate'), 'warning'); + return; + } + + unset($account['description']); + + foreach ($account as $attr => $value) { + switch ($attr) { + case 'nsroledn': + $value = array(); + foreach ((array) $account['nsroledn'] as $role) { + $role = str_replace('$base_dn', $base_dn, $role); + $role = str_replace('$root_dn', $root_dn, $role); + $value[] = $role; + } + + default: + $query[$attr] = $value; + } + } + + $response = $this->post('user.edit', $query); + + if (!$response || $response['status'] != 'OK') { + $this->rc->output->show_message($this->gettext('failedtypeupdate'), 'warning'); + return; + } + + $this->userinfo = $query; + } + + /** + * Initialize WAP connection and user session + */ + protected function init_wap() + { + if ($this->wap) { + return $this->wap; + } + + $this->load_config(); + $this->require_plugin('libkolab'); + + $uri = $this->rc->config->get('wap_client_uri'); + $user = $this->rc->get_user_name(); + $pass = $this->rc->decrypt($_SESSION['password']); + + if (!$uri) { + rcube::raise_error("wap_client_uri is not set", true, false); + return; + } + + // get HTTP_Request2 object + $this->uri = rcube_utils::resolve_url($uri); + $this->wap = libkolab::http_request($this->uri); + + $query = array( + 'username' => $user, + 'password' => $pass, + // 'domain' => $domain, + 'info' => true, + ); + + // authenticate the user + $response = $this->post('system.authenticate', $query); + + if ($response) { + $this->userinfo = $response['result']['info']; + $this->token = $response['result']['session_token']; + } + + return $this->wap; + } + + /** + * API's POST request. + * + * @param string $action Action name + * @param array $post POST arguments + * + * @return kolab_client_api_result Response + */ + protected function post($action, $post = array()) + { + $url = $this->build_url($action); + + if ($this->rc->config->get('wap_client_debug')) { + $this->rc->write_log('wap', "Calling API POST: $url\n" . @json_encode($post)); + } + + if ($this->token) { + $this->wap->setHeader('X-Session-Token', $this->token); + } + + $this->wap->setMethod(HTTP_Request2::METHOD_POST); + $this->wap->setBody(@json_encode($post)); + + return $this->get_response($url); + } + + /** + * Build Net_URL2 object for the request + * + * @param string $action Action GET parameter + * @param array $args GET parameters (hash array: name => value) + * + * @return Net_URL2 URL object + */ + private function build_url($action, $args = array()) + { + $url = rtrim($this->uri, '/'); + + if ($action) { + $url .= '/' . urlencode($action); + } + + $url = new Net_URL2($url); + + if (!empty($args)) { + $url->setQueryVariables($args); + } + + return $url; + } + + /** + * HTTP Response handler. + * + * @param Net_URL2 $url URL object + * + * @return array Response data + */ + protected function get_response($url) + { + try { + $this->wap->setUrl($url); + $response = $this->wap->send(); + } + catch (Exception $e) { + rcube::raise_error($e, true, false); + return; + } + + try { + $body = $response->getBody(); + } + catch (Exception $e) { + rcube::raise_error($e, true, false); + return; + } + + if ($this->rc->config->get('wap_client_debug')) { + $this->rc->write_log('wap', "Response:\n$body"); + } + + $body = @json_decode($body, true); + + if (!is_array($body)) { + rcube::raise_error("Failed to decode WAP response", true, false); + return; + } + + return $body; + } +} From fc93828311d1e2bdbc7d442ed5c94a32d9bf1eb1 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 17 Mar 2016 16:33:18 +0100 Subject: [PATCH 030/483] Fix merging attachments list on event/task update from iTip (#5342) Reviewers: #roundcube_kolab_plugins_developers, vanmeeuwen Reviewed By: #roundcube_kolab_plugins_developers, vanmeeuwen Subscribers: vanmeeuwen Projects: #roundcube_kolab_plugins Differential Revision: https://git.kolab.org/D96 --- plugins/calendar/calendar.php | 7 ++ .../calendar/drivers/kolab/kolab_driver.php | 41 +----------- plugins/libkolab/lib/kolab_format.php | 64 +++++++++++++++++++ .../drivers/kolab/tasklist_kolab_driver.php | 42 +----------- plugins/tasklist/tasklist.php | 9 +++ 5 files changed, 83 insertions(+), 80 deletions(-) diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php index b6c5e234..d46b7229 100644 --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -2996,6 +2996,13 @@ class calendar extends rcube_plugin // set status=CANCELLED on CANCEL messages if ($event['_method'] == 'CANCEL') $event['status'] = 'CANCELLED'; + + // update attachments list, allow attachments update only on REQUEST (#5342) + if ($event['_method'] == 'REQUEST') + $event['deleted_attachments'] = true; + else + unset($event['attachments']); + // show me as free when declined (#1670) if ($status == 'declined' || $event['status'] == 'CANCELLED' || $event_attendee['role'] == 'NON-PARTICIPANT') $event['free_busy'] = 'free'; diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php index 0eafc3bd..4b7861b7 100644 --- a/plugins/calendar/drivers/kolab/kolab_driver.php +++ b/plugins/calendar/drivers/kolab/kolab_driver.php @@ -1972,46 +1972,7 @@ class kolab_driver extends calendar_driver */ public static function from_rcube_event($event, $old = array()) { - // in kolab_storage attachments are indexed by content-id - if (is_array($event['attachments']) || !empty($event['deleted_attachments'])) { - $event['_attachments'] = array(); - - foreach ($event['attachments'] as $attachment) { - $key = null; - // Roundcube ID has nothing to do with the storage ID, remove it - if ($attachment['content'] || $attachment['path']) { - unset($attachment['id']); - } - else { - foreach ((array)$old['_attachments'] as $cid => $oldatt) { - if ($attachment['id'] == $oldatt['id']) - $key = $cid; - } - } - - // flagged for deletion => set to false - if ($attachment['_deleted'] || in_array($attachment['id'], (array)$event['deleted_attachments'])) { - $event['_attachments'][$key] = false; - } - // replace existing entry - else if ($key) { - $event['_attachments'][$key] = $attachment; - } - // append as new attachment - else { - $event['_attachments'][] = $attachment; - } - } - - $event['_attachments'] = array_merge((array)$old['_attachments'], $event['_attachments']); - - // attachments flagged for deletion => set to false - foreach ($event['_attachments'] as $key => $attachment) { - if ($attachment['_deleted'] || in_array($attachment['id'], (array)$event['deleted_attachments'])) { - $event['_attachments'][$key] = false; - } - } - } + kolab_format::merge_attachments($event, $old); return $event; } diff --git a/plugins/libkolab/lib/kolab_format.php b/plugins/libkolab/lib/kolab_format.php index 5041dd38..27b8956e 100644 --- a/plugins/libkolab/lib/kolab_format.php +++ b/plugins/libkolab/lib/kolab_format.php @@ -704,4 +704,68 @@ abstract class kolab_format $this->obj->setAttachments($vattach); } } + + /** + * Unified way of updating/deleting attachments of edited object + * + * @param array $object Kolab object data + * @param array $old Old version of Kolab object + */ + public static function merge_attachments(&$object, $old) + { + $object['_attachments'] = (array) $old['_attachments']; + + // delete existing attachment(s) + if (!empty($object['deleted_attachments'])) { + foreach ($object['_attachments'] as $idx => $att) { + if ($object['deleted_attachments'] === true || in_array($att['id'], $object['deleted_attachments'])) { + $object['_attachments'][$idx] = false; + } + } + } + + // in kolab_storage attachments are indexed by content-id + foreach ((array) $object['attachments'] as $attachment) { + $key = null; + + // Roundcube ID has nothing to do with the storage ID, remove it + // for uploaded/new attachments + // FIXME: Roundcube uses 'data', kolab_format uses 'content' + if ($attachment['content'] || $attachment['path'] || $attachment['data']) { + unset($attachment['id']); + } + + if ($attachment['id']) { + foreach ((array) $object['_attachments'] as $cid => $att) { + if ($att && $attachment['id'] == $att['id']) { + $key = $cid; + } + } + } + else { + // find attachment by name, so we can update it if exists + // and make sure there are no duplicates + foreach ((array) $object['_attachments'] as $cid => $att) { + if ($att && $attachment['name'] == $att['name']) { + $key = $cid; + } + } + } + + if ($key && $attachment['_deleted']) { + $object['_attachments'][$key] = false; + } + // replace existing entry + else if ($key) { + $object['_attachments'][$key] = $attachment; + } + // append as new attachment + else { + $object['_attachments'][] = $attachment; + } + } + + unset($object['attachments']); + unset($object['deleted_attachments']); + } } diff --git a/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php b/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php index fe1af558..6f62819b 100644 --- a/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php +++ b/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php @@ -1308,46 +1308,8 @@ class tasklist_kolab_driver extends tasklist_driver $object['recurrence'] = $old['recurrence']; } - // delete existing attachment(s) - if (!empty($task['deleted_attachments'])) { - foreach ($task['deleted_attachments'] as $attachment) { - if (is_array($object['_attachments'])) { - foreach ($object['_attachments'] as $idx => $att) { - if ($att['id'] == $attachment) - $object['_attachments'][$idx] = false; - } - } - } - unset($task['deleted_attachments']); - } - - // in kolab_storage attachments are indexed by content-id - if (is_array($task['attachments'])) { - foreach ($task['attachments'] as $idx => $attachment) { - $key = null; - // Roundcube ID has nothing to do with the storage ID, remove it - if ($attachment['content'] || $attachment['path']) { - unset($attachment['id']); - } - else { - foreach ((array)$old['_attachments'] as $cid => $oldatt) { - if ($oldatt && $attachment['id'] == $oldatt['id']) - $key = $cid; - } - } - - // replace existing entry - if ($key) { - $object['_attachments'][$key] = $attachment; - } - // append as new attachment - else { - $object['_attachments'][] = $attachment; - } - } - - unset($object['attachments']); - } + unset($task['attachments']); + kolab_format::merge_attachments($object, $old); // allow sequence increments if I'm the organizer if ($this->plugin->is_organizer($object) && empty($object['_method'])) { diff --git a/plugins/tasklist/tasklist.php b/plugins/tasklist/tasklist.php index c4305abb..16518a57 100644 --- a/plugins/tasklist/tasklist.php +++ b/plugins/tasklist/tasklist.php @@ -2088,6 +2088,15 @@ class tasklist extends rcube_plugin if ($task['_method'] == 'CANCEL') { $task['status'] = 'CANCELLED'; } + + // update attachments list, allow attachments update only on REQUEST (#5342) + if ($task['_method'] == 'REQUEST') { + $task['deleted_attachments'] = true; + } + else { + unset($task['attachments']); + } + // show me as free when declined (#1670) if ($status == 'declined' || $task['status'] == 'CANCELLED') { $task['free_busy'] = 'free'; From 5d0b087559ca23b6fd149e67e6483a98e18ad8a0 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 22 Mar 2016 11:42:42 +0100 Subject: [PATCH 031/483] Use "cancelled" (with double l) consistently --- plugins/calendar/localization/en_US.inc | 2 +- plugins/tasklist/localization/en_US.inc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/calendar/localization/en_US.inc b/plugins/calendar/localization/en_US.inc index 2a5d2359..43d07922 100644 --- a/plugins/calendar/localization/en_US.inc +++ b/plugins/calendar/localization/en_US.inc @@ -182,7 +182,7 @@ $labels['invitationattendlinks'] = "In case your email client doesn't support iT $labels['eventupdatesubject'] = '"$title" has been updated'; $labels['eventupdatesubjectempty'] = 'An event that concerns you has been updated'; $labels['eventupdatemailbody'] = "*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees\n\nPlease find attached an iCalendar file with the updated event details which you can import to your calendar application."; -$labels['eventcancelsubject'] = '"$title" has been canceled'; +$labels['eventcancelsubject'] = '"$title" has been cancelled'; $labels['eventcancelmailbody'] = "*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees\n\nThe event has been cancelled by \$organizer.\n\nPlease find attached an iCalendar file with the updated event details."; // invitation handling (overrides labels from libcalendaring) diff --git a/plugins/tasklist/localization/en_US.inc b/plugins/tasklist/localization/en_US.inc index 2b2db862..7890683b 100644 --- a/plugins/tasklist/localization/en_US.inc +++ b/plugins/tasklist/localization/en_US.inc @@ -148,7 +148,7 @@ $labels['invitationmailbody'] = "*\$title*\n\nDue: \$date\n\nAssignees: \$attend $labels['itipupdatesubject'] = '"$title" has been updated'; $labels['itipupdatesubjectempty'] = 'A task that concerns you has been updated'; $labels['itipupdatemailbody'] = "*\$title*\n\nDue: \$date\n\nAssignees: \$attendees\n\nPlease find attached an iCalendar file with the updated task details which you can import to your tasks application."; -$labels['itipcancelsubject'] = '"$title" has been canceled'; +$labels['itipcancelsubject'] = '"$title" has been cancelled'; $labels['itipcancelmailbody'] = "*\$title*\n\nDue: \$date\n\nAssignees: \$attendees\n\nThe task has been cancelled by \$organizer.\n\nPlease find attached an iCalendar file with the updated task details."; $labels['saveintasklist'] = 'save in '; From a673d3c8f59e3970195068b4959501af0580c33c Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 23 Mar 2016 11:44:46 +0100 Subject: [PATCH 032/483] Fix indentation --- plugins/libkolab/lib/kolab_storage.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/plugins/libkolab/lib/kolab_storage.php b/plugins/libkolab/lib/kolab_storage.php index 88da8da0..318dce87 100644 --- a/plugins/libkolab/lib/kolab_storage.php +++ b/plugins/libkolab/lib/kolab_storage.php @@ -528,16 +528,17 @@ class kolab_storage ) { self::$last_error = 'No permission to create folder'; return false; - } + } } // update the folder name if (strlen($oldfolder)) { if ($oldfolder != $folder) { $result = self::folder_rename($oldfolder, $folder); - } - else - $result = true; + } + else { + $result = true; + } } // create new folder else { From 0b17f376a81c9d3efb8189fe89c53ea5c3031a77 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 24 Mar 2016 14:05:36 +0100 Subject: [PATCH 033/483] Fix duplicated DTSTAMP entry in generated iTip (T1148) Incopatibility with Sabre-VObject >= 3.3 --- plugins/libcalendaring/libvcalendar.php | 2 +- plugins/libcalendaring/tests/libvcalendar.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/libcalendaring/libvcalendar.php b/plugins/libcalendaring/libvcalendar.php index 938ccd07..65afde3c 100644 --- a/plugins/libcalendaring/libvcalendar.php +++ b/plugins/libcalendaring/libvcalendar.php @@ -997,7 +997,7 @@ class libvcalendar implements Iterator // set DTSTAMP according to RFC 5545, 3.8.7.2. $dtstamp = !empty($event['changed']) && !empty($this->method) ? $event['changed'] : new DateTime('now', new \DateTimeZone('UTC')); - $ve->add($this->datetime_prop($cal, 'DTSTAMP', $dtstamp, true)); + $ve->DTSTAMP = $this->datetime_prop($cal, 'DTSTAMP', $dtstamp, true); // all-day events end the next day if ($event['allday'] && !empty($event['end'])) { diff --git a/plugins/libcalendaring/tests/libvcalendar.php b/plugins/libcalendaring/tests/libvcalendar.php index d8794acf..7fca464e 100644 --- a/plugins/libcalendaring/tests/libvcalendar.php +++ b/plugins/libcalendaring/tests/libvcalendar.php @@ -394,6 +394,7 @@ class libvcalendar_test extends PHPUnit_Framework_TestCase $this->assertContains('END:VTIMEZONE', $ics, "VTIMEZONE encapsulation END"); $this->assertContains('BEGIN:VEVENT', $ics, "VEVENT encapsulation BEGIN"); + $this->assertSame(2, substr_count($ics, 'DTSTAMP'), "Duplicate DTSTAMP (T1148)"); $this->assertContains('UID:ac6b0aee-2519-4e5c-9a25-48c57064c9f0', $ics, "Event UID"); $this->assertContains('SEQUENCE:' . $event['sequence'], $ics, "Export Sequence number"); $this->assertContains('CLASS:CONFIDENTIAL', $ics, "Sensitivity => Class"); From 801d3588242e88dd56546cd426ba357fcdfcdc18 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 24 Mar 2016 16:07:02 +0100 Subject: [PATCH 034/483] Fix some linting errors --- plugins/calendar/calendar_ui.js | 2 +- plugins/kolab_files/kolab_files.js | 2 +- plugins/kolab_notes/notes.js | 4 ++-- plugins/tasklist/tasklist.js | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js index 7630ccd7..6232b7e4 100644 --- a/plugins/calendar/calendar_ui.js +++ b/plugins/calendar/calendar_ui.js @@ -38,7 +38,7 @@ function rcube_calendar_ui(settings) this.selected_event = null; this.selected_calendar = null; this.search_request = null; - this.saving_lock; + this.saving_lock = null; this.calendars = {}; this.quickview_sources = []; diff --git a/plugins/kolab_files/kolab_files.js b/plugins/kolab_files/kolab_files.js index 39b931a8..620a7ed2 100644 --- a/plugins/kolab_files/kolab_files.js +++ b/plugins/kolab_files/kolab_files.js @@ -2177,7 +2177,7 @@ function kolab_files_ui() { return $('
').attr({ title: rcmail.gettext('kolab_files.listpermanent'), - class: 'subscription' + (subscribed ? ' subscribed' : ''), + 'class': 'subscription' + (subscribed ? ' subscribed' : ''), 'aria-checked': subscribed, role: 'checkbox' }); diff --git a/plugins/kolab_notes/notes.js b/plugins/kolab_notes/notes.js index f4a57cc4..be9097d7 100644 --- a/plugins/kolab_notes/notes.js +++ b/plugins/kolab_notes/notes.js @@ -122,7 +122,7 @@ function rcube_kolab_notes_ui(settings) rcmail.enable_command('createnote', has_permission(me.notebooks[id], 'i')); rcmail.enable_command('list-edit', has_permission(me.notebooks[id], 'a')); rcmail.enable_command('list-delete', has_permission(me.notebooks[id], 'xa')); - rcmail.enable_command('list-remove', !me.notebooks[id].default); + rcmail.enable_command('list-remove', !me.notebooks[id]['default']); fetch_notes(id); // sets me.selected_list }, function(){ @@ -142,7 +142,7 @@ function rcube_kolab_notes_ui(settings) } }); notebookslist.addEventListener('remove', function(p) { - if (me.notebooks[p.id] && !me.notebooks[p.id].default) { + if (me.notebooks[p.id] && !me.notebooks[p.id]['default']) { list_remove(p.id); } }); diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js index 3c8f2a32..b39d9686 100644 --- a/plugins/tasklist/tasklist.js +++ b/plugins/tasklist/tasklist.js @@ -106,8 +106,8 @@ function rcube_tasklist_ui(settings) /* public members */ this.tasklists = rcmail.env.tasklists; - this.selected_task; - this.selected_list; + this.selected_task = null; + this.selected_list = null; /* public methods */ this.init = init; @@ -150,7 +150,7 @@ function rcube_tasklist_ui(settings) me.selected_list = settings.selected_list; $(rcmail.gui_objects.tasklistslist).find("input[value='"+settings.selected_list+"']").prop('checked', true); } - if (me.tasklists[id].editable && (!me.selected_list || me.tasklists[id].default || (me.tasklists[id].active && !me.tasklists[me.selected_list].active))) { + if (me.tasklists[id].editable && (!me.selected_list || me.tasklists[id]['default'] || (me.tasklists[id].active && !me.tasklists[me.selected_list].active))) { me.selected_list = id; } } From f866ceb84c0f8fd72d15a203afb3337c54eecec4 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 29 Mar 2016 13:18:30 +0200 Subject: [PATCH 035/483] Fix regression where calendar feeds required normal Roundcube login instead of HTTP auth (T1151) With proper performance fix 4fe52716e3a (#4033) --- plugins/calendar/calendar.php | 45 ++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php index d46b7229..c7dbba4a 100644 --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -33,7 +33,7 @@ class calendar extends rcube_plugin const SESSION_KEY = 'calendar_temp'; - public $task = '?(?!login|logout).*'; + public $task = '?(?!logout).*'; public $rc; public $lib; public $resources_dir; @@ -68,27 +68,13 @@ class calendar extends rcube_plugin */ function init() { - $this->require_plugin('libcalendaring'); - $this->rc = rcube::get_instance(); - $this->lib = libcalendaring::get_instance(); $this->register_task('calendar', 'calendar'); // load calendar configuration $this->load_config(); - // load localizations - $this->add_texts('localization/', $this->rc->task == 'calendar' && (!$this->rc->action || $this->rc->action == 'print')); - - $this->timezone = $this->lib->timezone; - $this->gmt_offset = $this->lib->gmt_offset; - $this->dst_active = $this->lib->dst_active; - $this->timezone_offset = $this->gmt_offset / 3600 - $this->dst_active; - - require($this->home . '/lib/calendar_ui.php'); - $this->ui = new calendar_ui($this); - // catch iTIP confirmation requests that don're require a valid session if ($this->rc->action == 'attend' && !empty($_REQUEST['_t'])) { $this->add_hook('startup', array($this, 'itip_attend_response')); @@ -96,7 +82,7 @@ class calendar extends rcube_plugin else if ($this->rc->action == 'feed' && !empty($_REQUEST['_cal'])) { $this->add_hook('startup', array($this, 'ical_feed_export')); } - else { + else if ($this->rc->task != 'login') { // default startup routine $this->add_hook('startup', array($this, 'startup')); } @@ -104,6 +90,26 @@ class calendar extends rcube_plugin $this->add_hook('user_delete', array($this, 'user_delete')); } + /** + * Setup basic plugin environment and UI + */ + protected function setup() + { + $this->require_plugin('libcalendaring'); + + $this->lib = libcalendaring::get_instance(); + $this->timezone = $this->lib->timezone; + $this->gmt_offset = $this->lib->gmt_offset; + $this->dst_active = $this->lib->dst_active; + $this->timezone_offset = $this->gmt_offset / 3600 - $this->dst_active; + + // load localizations + $this->add_texts('localization/', $this->rc->task == 'calendar' && (!$this->rc->action || $this->rc->action == 'print')); + + require($this->home . '/lib/calendar_ui.php'); + $this->ui = new calendar_ui($this); + } + /** * Startup hook */ @@ -113,6 +119,8 @@ class calendar extends rcube_plugin if ($this->rc->config->get('calendar_disabled', false) || !$this->rc->config->get('calendar_enabled', true)) return; + $this->setup(); + // load Calendar user interface if (!$this->rc->output->ajax_call && (!$this->rc->output->env['framed'] || $args['action'] == 'preview')) { $this->ui->init(); @@ -1667,6 +1675,7 @@ class calendar extends rcube_plugin // sanity check user if ($this->rc->user->get_username() == $user) { + $this->setup(); $this->load_driver(); $this->export_events(false); } @@ -1680,7 +1689,6 @@ class calendar extends rcube_plugin exit; } - /** * */ @@ -2598,6 +2606,8 @@ class calendar extends rcube_plugin */ public function itip_attend_response($p) { + $this->setup(); + if ($p['action'] == 'attend') { $this->ui->init(); @@ -3333,6 +3343,7 @@ class calendar extends rcube_plugin $table_itipinvitations = $db->table_name('itipinvitations', true); $db->query("DELETE FROM $table_itipinvitations WHERE `user_id` = ?", $args['user']->ID); + $this->setup(); $this->load_driver(); return $this->driver->user_delete($args); } From c159f75e484d1b202345170a32c505a521d57eb4 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 29 Mar 2016 13:37:38 +0200 Subject: [PATCH 036/483] Fix missing file size of attached ics file in mail compose --- plugins/calendar/calendar.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php index c7dbba4a..6dfac691 100644 --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -3300,7 +3300,12 @@ class calendar extends rcube_plugin $tmp_path = tempnam($this->rc->config->get('temp_dir'), 'rcmAttmntCal'); file_put_contents($tmp_path, $this->get_ical()->export(array($event), '', false, array($this->driver, 'get_attachment_body'))); - $args['attachments'][] = array('path' => $tmp_path, 'name' => $filename . '.ics', 'mimetype' => 'text/calendar'); + $args['attachments'][] = array( + 'path' => $tmp_path, + 'name' => $filename . '.ics', + 'mimetype' => 'text/calendar', + 'size' => filesize($tmp_path), + ); $args['param']['subject'] = $event['title']; } } From 88c74fa16707ab8d286e42dfe4d4d28bc984fceb Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 29 Mar 2016 13:40:45 +0200 Subject: [PATCH 037/483] Add missing UID in birthdays calendar exports/feed (T855) --- plugins/calendar/drivers/calendar_driver.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/calendar/drivers/calendar_driver.php b/plugins/calendar/drivers/calendar_driver.php index 32586666..b146343c 100644 --- a/plugins/calendar/drivers/calendar_driver.php +++ b/plugins/calendar/drivers/calendar_driver.php @@ -704,8 +704,10 @@ abstract class calendar_driver // birthday is within requested range if ($bday <= $end && $bday >= $start) { $age = $this_year - $birthyear; + $uid = rcube_ldap::dn_encode('bday:' . $source . ':' . $contact['ID'] . ':' . $this_year); $event = array( - 'id' => rcube_ldap::dn_encode('bday:' . $source . ':' . $contact['ID'] . ':' . $this_year), + 'id' => $uid, + 'uid' => $uid, 'calendar' => self::BIRTHDAY_CALENDAR_ID, 'title' => $event_title, 'description' => $rcmail->gettext(array('name' => 'birthdayage', 'vars' => array('age' => $age)), 'calendar'), @@ -765,10 +767,11 @@ abstract class calendar_driver $display_name = rcube_addressbook::compose_display_name($contact); $event_title = $rcmail->gettext(array('name' => 'birthdayeventtitle', 'vars' => array('name' => $display_name)), 'calendar'); + $uid = rcube_ldap::dn_encode('bday:' . $source . ':' . $contact['ID'] . ':' . $birthyear); $event = array( - 'id' => rcube_ldap::dn_encode('bday:' . $source . ':' . $contact['ID'] . ':' . $year), - 'uid' => rcube_ldap::dn_encode('bday:' . $source . ':' . $contact['ID'] . ':' . $birthyear), + 'id' => $uid, + 'uid' => $uid, 'calendar' => self::BIRTHDAY_CALENDAR_ID, 'title' => $event_title, 'description' => '', From 0c5b7541bcbb4e979b3c6b955177e06163ce0754 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 29 Mar 2016 16:18:00 +0200 Subject: [PATCH 038/483] Fix "Addressbook source not found!" error on birthdays calendar feed (T1152) --- plugins/kolab_addressbook/kolab_addressbook.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/kolab_addressbook/kolab_addressbook.php b/plugins/kolab_addressbook/kolab_addressbook.php index 3b1451d3..22c5ccbc 100644 --- a/plugins/kolab_addressbook/kolab_addressbook.php +++ b/plugins/kolab_addressbook/kolab_addressbook.php @@ -29,7 +29,7 @@ class kolab_addressbook extends rcube_plugin { - public $task = '?(?!login|logout).*'; + public $task = '?(?!logout).*'; private $sources; private $folders; @@ -97,7 +97,6 @@ class kolab_addressbook extends rcube_plugin $this->add_hook('folder_update', array($this, 'prefs_folder_update')); } - /** * Handler for the addressbooks_list hook. * From 96dd1b271358ea91c2b8a7b98d2e17c7a472461d Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 6 Apr 2016 07:31:19 -0400 Subject: [PATCH 039/483] Fix missing COMMENT in iTip delegation message from the event dialog (#5377) --- plugins/calendar/calendar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php index 6dfac691..8def8374 100644 --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -1015,6 +1015,7 @@ class calendar extends rcube_plugin $ev['attendees'] = $event['attendees']; $ev['free_busy'] = $event['free_busy']; $ev['_savemode'] = $event['_savemode']; + $ev['comment'] = $reply_comment; // send invitation to delegatee + add it as attendee if ($status == 'delegated' && $event['to']) { @@ -1051,7 +1052,6 @@ class calendar extends rcube_plugin if (!$noreply) { $itip = $this->load_itip(); $itip->set_sender_email($reply_sender); - $event['comment'] = $reply_comment; $event['thisandfuture'] = $event['_savemode'] == 'future'; if ($organizer && $itip->send_itip_message($event, 'REPLY', $organizer, 'itipsubject' . $status, 'itipmailbody' . $status)) $this->rc->output->command('display_message', $this->gettext(array('name' => 'sentresponseto', 'vars' => array('mailto' => $organizer['name'] ? $organizer['name'] : $organizer['email']))), 'confirmation'); From 37659bb303f9c5c63f0449ea0459110e7044c989 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 6 Apr 2016 17:31:53 +0200 Subject: [PATCH 040/483] Fix "ReferenceError: organizer is not defined" in some cases --- plugins/calendar/calendar_ui.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js index 6232b7e4..1d60e6dc 100644 --- a/plugins/calendar/calendar_ui.js +++ b/plugins/calendar/calendar_ui.js @@ -513,7 +513,7 @@ function rcube_calendar_ui(settings) return (j - k); }); - var data, mystatus = null, rsvp, line, morelink, html = '', overflow = ''; + var data, organizer, mystatus = null, rsvp, line, morelink, html = '', overflow = ''; for (var j=0; j < event.attendees.length; j++) { data = event.attendees[j]; if (data.email) { From 3e1346dc6c558d3114160adadd38751bf9c66707 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 7 Apr 2016 10:46:24 +0200 Subject: [PATCH 041/483] CS fixes --- .../libkolab/lib/kolab_date_recurrence.php | 43 +++++++++++-------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/plugins/libkolab/lib/kolab_date_recurrence.php b/plugins/libkolab/lib/kolab_date_recurrence.php index 4f8ae61a..64d9d0a5 100644 --- a/plugins/libkolab/lib/kolab_date_recurrence.php +++ b/plugins/libkolab/lib/kolab_date_recurrence.php @@ -9,7 +9,7 @@ * @version @package_version@ * @author Thomas Bruederli * - * Copyright (C) 2012, Kolab Systems AG + * Copyright (C) 2012-2016, Kolab Systems AG * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -36,7 +36,7 @@ class kolab_date_recurrence /** * Default constructor * - * @param array The Kolab object to operate on + * @param kolab_format_xcal The Kolab object to operate on */ function __construct($object) { @@ -44,11 +44,12 @@ class kolab_date_recurrence $this->object = $object; $this->engine = $object->to_libcal(); - $this->start = $this->next = $data['start']; - $this->cnext = kolab_format::get_datetime($this->next); + $this->start = $this->next = $data['start']; + $this->cnext = kolab_format::get_datetime($this->next); - if (is_object($data['start']) && is_object($data['end'])) + if (is_object($data['start']) && is_object($data['end'])) { $this->duration = $data['start']->diff($data['end']); + } else { // Prevent from errors when end date is not set (#5307) RFC5545 3.6.1 $seconds = !empty($data['end']) ? ($data['end'] - $data['start']) : 0; @@ -70,8 +71,9 @@ class kolab_date_recurrence if (($cnext = new cDateTime($this->engine->getNextOccurence($this->cnext))) && $cnext->isValid()) { $next = kolab_format::php_datetime($cnext); $time = $timestamp ? $next->format('U') : $next; + $this->cnext = $cnext; - $this->next = $next; + $this->next = $next; } } @@ -89,13 +91,13 @@ class kolab_date_recurrence $next_end = clone $next_start; $next_end->add($this->duration); - $next = $this->object->to_array(); + $next = $this->object->to_array(); $next['start'] = $next_start; - $next['end'] = $next_end; + $next['end'] = $next_end; - $recurrence_id_format = libkolab::recurrence_id_format($next); + $recurrence_id_format = libkolab::recurrence_id_format($next); $next['recurrence_date'] = clone $next_start; - $next['_instance'] = $next_start->format($recurrence_id_format); + $next['_instance'] = $next_start->format($recurrence_id_format); unset($next['_formatobj']); @@ -120,21 +122,24 @@ class kolab_date_recurrence } // let libkolab do the work - if ($this->engine && ($cend = $this->engine->getLastOccurrence()) && ($end_dt = kolab_format::php_datetime(new cDateTime($cend)))) { + if ($this->engine && ($cend = $this->engine->getLastOccurrence()) + && ($end_dt = kolab_format::php_datetime(new cDateTime($cend))) + ) { return $end_dt; } // determine a reasonable end date if none given if (!$event['recurrence']['COUNT'] && $event['end'] instanceof DateTime) { - switch ($event['recurrence']['FREQ']) { - case 'YEARLY': $intvl = 'P100Y'; break; - case 'MONTHLY': $intvl = 'P20Y'; break; - default: $intvl = 'P10Y'; break; - } + switch ($event['recurrence']['FREQ']) { + case 'YEARLY': $intvl = 'P100Y'; break; + case 'MONTHLY': $intvl = 'P20Y'; break; + default: $intvl = 'P10Y'; break; + } - $end_dt = clone $event['end']; - $end_dt->add(new DateInterval($intvl)); - return $end_dt; + $end_dt = clone $event['end']; + $end_dt->add(new DateInterval($intvl)); + + return $end_dt; } return false; From 9fb5775d82e83f0c507c6c7107369a167ea737ae Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 7 Apr 2016 10:47:45 +0200 Subject: [PATCH 042/483] Fix "PHP Fatal error: Call to undefined method kolab_format_task::to_libcal()" (T1176) --- plugins/libkolab/lib/kolab_format_event.php | 23 ------------------- plugins/libkolab/lib/kolab_format_xcal.php | 25 ++++++++++++++++++++- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/plugins/libkolab/lib/kolab_format_event.php b/plugins/libkolab/lib/kolab_format_event.php index 83cee2d3..32ae60b6 100644 --- a/plugins/libkolab/lib/kolab_format_event.php +++ b/plugins/libkolab/lib/kolab_format_event.php @@ -49,29 +49,6 @@ class kolab_format_event extends kolab_format_xcal $this->_scheduling_properties = self::$scheduling_properties; } - /** - * Clones into an instance of libcalendaring's extended EventCal class - * - * @return mixed EventCal object or false on failure - */ - public function to_libcal() - { - static $error_logged = false; - - if (class_exists('kolabcalendaring')) { - return new EventCal($this->obj); - } - else if (!$error_logged) { - $error_logged = true; - rcube::raise_error(array( - 'code' => 900, 'type' => 'php', - 'message' => "required kolabcalendaring module not found" - ), true); - } - - return false; - } - /** * Set event properties to the kolabformat object * diff --git a/plugins/libkolab/lib/kolab_format_xcal.php b/plugins/libkolab/lib/kolab_format_xcal.php index a9dd70c8..69a273a2 100644 --- a/plugins/libkolab/lib/kolab_format_xcal.php +++ b/plugins/libkolab/lib/kolab_format_xcal.php @@ -711,4 +711,27 @@ abstract class kolab_format_xcal extends kolab_format return $reschedule; } -} \ No newline at end of file + + /** + * Clones into an instance of libcalendaring's extended EventCal class + * + * @return mixed EventCal object or false on failure + */ + public function to_libcal() + { + static $error_logged = false; + + if (class_exists('kolabcalendaring')) { + return new EventCal($this->obj); + } + else if (!$error_logged) { + $error_logged = true; + rcube::raise_error(array( + 'code' => 900, + 'message' => "required kolabcalendaring module not found" + ), true); + } + + return false; + } +} From 241dcfb9b31d23bfb6bf17f771c2c6b2e263f8f1 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 7 Apr 2016 06:10:02 -0400 Subject: [PATCH 043/483] Don't allow birthday/invitation calendars to be set as the default calendar (T1177) --- plugins/calendar/calendar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php index 8def8374..46bc5b97 100644 --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -520,7 +520,7 @@ class calendar extends rcube_plugin // default calendar selection $field_id = 'rcmfd_default_calendar'; $select_cal = new html_select(array('name' => '_default_calendar', 'id' => $field_id, 'is_escaped' => true)); - foreach ((array)$this->driver->list_calendars(calendar_driver::FILTER_PERSONAL) as $id => $prop) { + foreach ((array)$this->driver->list_calendars(calendar_driver::FILTER_PERSONAL | calendar_driver::FILTER_ACTIVE) as $id => $prop) { $select_cal->add($prop['name'], strval($id)); if ($prop['default']) $default_calendar = $id; From 748e42abfb9a2830c4d6b28179d030a3ec7134b8 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 7 Apr 2016 08:18:23 -0400 Subject: [PATCH 044/483] Skip partstat filter when kolab_invitation_calendars=false (T1179) --- plugins/calendar/drivers/kolab/kolab_calendar.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php index 3e13020d..7fe299df 100644 --- a/plugins/calendar/drivers/kolab/kolab_calendar.php +++ b/plugins/calendar/drivers/kolab/kolab_calendar.php @@ -295,7 +295,9 @@ class kolab_calendar extends kolab_storage_folder_api } // set partstat filter to skip pending and declined invitations - if (empty($filter_query) && $this->get_namespace() != 'other') { + if (empty($filter_query) && $this->cal->rc->config->get('kolab_invitation_calendars') + && $this->get_namespace() != 'other' + ) { $partstat_exclude = array('NEEDS-ACTION','DECLINED'); } else { From 2165bfad2d261ab90cef8f93530c4ee43687e170 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 8 Apr 2016 09:19:55 +0200 Subject: [PATCH 045/483] Option kolab_invitation_calendars is false by default --- plugins/calendar/config.inc.php.dist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/calendar/config.inc.php.dist b/plugins/calendar/config.inc.php.dist index e9067766..83864edd 100644 --- a/plugins/calendar/config.inc.php.dist +++ b/plugins/calendar/config.inc.php.dist @@ -149,7 +149,7 @@ $config['calendar_itip_smtp_user'] = 'smtpauth'; $config['calendar_itip_smtp_pass'] = '123456'; // show virtual invitation calendars (Kolab driver only) -$config['kolab_invitation_calendars'] = true; +$config['kolab_invitation_calendars'] = false; // Base URL to build fully qualified URIs to access calendars via CALDAV // The following replacement variables are supported: From 3fd7fb5a5d656a28160b95cf7d718543dc49a54f Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 14 Apr 2016 11:41:44 +0200 Subject: [PATCH 046/483] Fix bug where reply wasn't send to organizer on task status update (#5341) Reviewers: #roundcube_kolab_plugins_developers, vanmeeuwen Reviewed By: #roundcube_kolab_plugins_developers, vanmeeuwen Subscribers: vanmeeuwen Projects: #roundcube_kolab_plugins Differential Revision: https://git.kolab.org/D99 --- plugins/tasklist/tasklist.js | 2 +- plugins/tasklist/tasklist.php | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js index b39d9686..979f45fa 100644 --- a/plugins/tasklist/tasklist.js +++ b/plugins/tasklist/tasklist.js @@ -2516,7 +2516,7 @@ function rcube_tasklist_ui(settings) } // tell server to send notifications - if ((data.attendees.length || (rec.id && rec.attendees.length)) && allow_invitations && (notify.checked || invite.checked || need_invitation)) { + if ((has_attendees(data) || (rec.id && has_attendees(rec))) && allow_invitations && (notify.checked || invite.checked || need_invitation)) { data._notify = settings.itip_notify; data._comment = comment.val(); } diff --git a/plugins/tasklist/tasklist.php b/plugins/tasklist/tasklist.php index 16518a57..051fb418 100644 --- a/plugins/tasklist/tasklist.php +++ b/plugins/tasklist/tasklist.php @@ -527,9 +527,12 @@ class tasklist extends rcube_plugin $this->rc->output->show_message('tasklist.errornotifying', 'error'); } } - else if ($success && $rec['_reportpartstat'] && $rec['_reportpartstat'] != 'NEEDS-ACTION') { + + if ($success && $rec['_reportpartstat'] && $rec['_reportpartstat'] != 'NEEDS-ACTION') { // get the full record after update - $task = $this->driver->get_task($rec); + if (!$task) { + $task = $this->driver->get_task($rec); + } // send iTip REPLY with the updated partstat if ($task['organizer'] && ($idx = $this->is_attendee($task)) !== false) { From 5060fb30b6f777dd91120428fa236d69c5534350 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 14 Apr 2016 11:41:50 +0200 Subject: [PATCH 047/483] Fix bug where delegatee would be lost on task/event update (#5058) Summary: Fixes #5058 Reviewers: #roundcube_kolab_plugins_developers, vanmeeuwen Reviewed By: #roundcube_kolab_plugins_developers, vanmeeuwen Differential Revision: https://git.kolab.org/D110 --- plugins/calendar/calendar.php | 12 +----- plugins/libcalendaring/libcalendaring.php | 48 +++++++++++++++++++++++ plugins/tasklist/tasklist.php | 12 +----- 3 files changed, 50 insertions(+), 22 deletions(-) diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php index 46bc5b97..3744a69d 100644 --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -2990,17 +2990,7 @@ class calendar extends rcube_plugin // preserve my participant status for regular updates if (empty($status)) { - $emails = $this->get_user_emails(); - foreach ($event['attendees'] as $i => $attendee) { - if ($attendee['email'] && in_array(strtolower($attendee['email']), $emails)) { - foreach ($existing['attendees'] as $j => $_attendee) { - if ($attendee['email'] == $_attendee['email']) { - $event['attendees'][$i] = $existing['attendees'][$j]; - break; - } - } - } - } + $this->lib->merge_attendees($event, $existing); } // set status=CANCELLED on CANCEL messages diff --git a/plugins/libcalendaring/libcalendaring.php b/plugins/libcalendaring/libcalendaring.php index a3fc7679..3719ca8d 100644 --- a/plugins/libcalendaring/libcalendaring.php +++ b/plugins/libcalendaring/libcalendaring.php @@ -1551,6 +1551,54 @@ class libcalendaring extends rcube_plugin $this->rc->output->command('plugin.expand_attendee_callback', $result); } + /** + * Merge attendees of the old and new event version + * with keeping current user and his delegatees status + * + * @param array &$new New object data + * @param array $old Old object data + */ + public function merge_attendees(&$new, $old) + { + $emails = $this->get_user_emails(); + $delegates = array(); + $attendees = array(); + + // keep attendee status of the current user + foreach ((array) $new['attendees'] as $i => $attendee) { + if (empty($attendee['email'])) { + continue; + } + + $attendees[] = $email = strtolower($attendee['email']); + + if (in_array($email, $emails)) { + foreach ($old['attendees'] as $_attendee) { + if ($attendee['email'] == $_attendee['email']) { + $new['attendees'][$i] = $_attendee; + if ($_attendee['status'] == 'DELEGATED' && ($email = $_attendee['delegated-to'])) { + $delegates[] = strtolower($email); + } + + break; + } + } + } + } + + // make sure delegated attendee is not lost + foreach ($delegates as $delegatee) { + if (!in_array($delegatee, $attendees)) { + foreach ((array) $old['attendees'] as $attendee) { + if ($attendee['email'] && ($email = strtolower($attendee['email'])) && $email == $delegatee) { + $new['attendees'][] = $attendee; + break; + } + } + } + } + } + /********* Static utility functions *********/ diff --git a/plugins/tasklist/tasklist.php b/plugins/tasklist/tasklist.php index 051fb418..05baefbd 100644 --- a/plugins/tasklist/tasklist.php +++ b/plugins/tasklist/tasklist.php @@ -2074,17 +2074,7 @@ class tasklist extends rcube_plugin // preserve my participant status for regular updates if (empty($status)) { - $emails = $this->lib->get_user_emails(); - foreach ($task['attendees'] as $i => $attendee) { - if ($attendee['email'] && in_array(strtolower($attendee['email']), $emails)) { - foreach ($existing['attendees'] as $j => $_attendee) { - if ($attendee['email'] == $_attendee['email']) { - $task['attendees'][$i] = $existing['attendees'][$j]; - break; - } - } - } - } + $this->lib->merge_attendees($task, $existing); } // set status=CANCELLED on CANCEL messages From ba78c23364f39d3765fdf20337ea0369b66cbeaf Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 14 Apr 2016 11:41:55 +0200 Subject: [PATCH 048/483] Fix wrong/missing delegated ATTENDEE in event iTip REPLY (#5365) Summary: Fixes #5365 and unifies COMMENT handling code Reviewers: #roundcube_kolab_plugins_developers, vanmeeuwen Reviewed By: #roundcube_kolab_plugins_developers, vanmeeuwen Differential Revision: https://git.kolab.org/D111 --- plugins/calendar/calendar.php | 20 ++++++++++---------- plugins/tasklist/tasklist.php | 14 +++++++++----- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php index 3744a69d..4156216f 100644 --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -2821,17 +2821,17 @@ class calendar extends rcube_plugin $noreply = $noreply || $status == 'needs-action' || $itip_sending === 0; $instance = rcube_utils::get_input_value('_instance', rcube_utils::INPUT_POST); $savemode = rcube_utils::get_input_value('_savemode', rcube_utils::INPUT_POST); + $comment = rcube_utils::get_input_value('_comment', rcube_utils::INPUT_POST); $error_msg = $this->gettext('errorimportingevent'); - $success = false; - $delegate = null; + $success = false; if ($status == 'delegated') { $delegates = rcube_mime::decode_address_list(rcube_utils::get_input_value('_to', rcube_utils::INPUT_POST, true), 1, false); $delegate = reset($delegates); if (empty($delegate) || empty($delegate['mailto'])) { - $this->rc->output->command('display_message', $this->gettext('libcalendaring.delegateinvalidaddress'), 'error'); + $this->rc->output->command('display_message', $this->rc->gettext('libcalendaring.delegateinvalidaddress'), 'error'); return; } } @@ -2840,19 +2840,20 @@ class calendar extends rcube_plugin if ($event = $this->lib->mail_get_itip_object($mbox, $uid, $mime_id, 'event')) { // forward iTip request to delegatee if ($delegate) { - $rsvpme = (bool) intval(rcube_utils::get_input_value('_rsvp', rcube_utils::INPUT_POST)); - $comment = rcube_utils::get_input_value('_comment', rcube_utils::INPUT_POST); + $rsvpme = rcube_utils::get_input_value('_rsvp', rcube_utils::INPUT_POST); + $itip = $this->load_itip(); - $d_event = $comment ? array_merge($event, array('comment' => $comment)) : $event; - $itip = $this->load_itip(); + $event['comment'] = $comment; - if ($itip->delegate_to($d_event, $delegate, $rsvpme)) { + if ($itip->delegate_to($event, $delegate, !empty($rsvpme))) { $this->rc->output->show_message('calendar.itipsendsuccess', 'confirmation'); } else { $this->rc->output->command('display_message', $this->gettext('itipresponseerror'), 'error'); } + unset($event['comment']); + // the delegator is set to non-participant, thus save as non-blocking $event['free_busy'] = 'free'; } @@ -3088,7 +3089,7 @@ class calendar extends rcube_plugin // send iTip reply if ($event['_method'] == 'REQUEST' && $organizer && !$noreply && !in_array(strtolower($organizer['email']), $emails) && !$error_msg) { - $event['comment'] = rcube_utils::get_input_value('_comment', rcube_utils::INPUT_POST); + $event['comment'] = $comment; $itip = $this->load_itip(); $itip->set_sender_email($reply_sender); if ($itip->send_itip_message($event, 'REPLY', $organizer, 'itipsubject' . $status, 'itipmailbody' . $status)) @@ -3100,7 +3101,6 @@ class calendar extends rcube_plugin $this->rc->output->send(); } - /** * Handler for calendar/itip-remove requests */ diff --git a/plugins/tasklist/tasklist.php b/plugins/tasklist/tasklist.php index 05baefbd..8d31c7a2 100644 --- a/plugins/tasklist/tasklist.php +++ b/plugins/tasklist/tasklist.php @@ -1914,12 +1914,12 @@ class tasklist extends rcube_plugin $mbox = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST); $mime_id = rcube_utils::get_input_value('_part', rcube_utils::INPUT_POST); $status = rcube_utils::get_input_value('_status', rcube_utils::INPUT_POST); + $comment = rcube_utils::get_input_value('_comment', rcube_utils::INPUT_POST); $delete = intval(rcube_utils::get_input_value('_del', rcube_utils::INPUT_POST)); $noreply = intval(rcube_utils::get_input_value('_noreply', rcube_utils::INPUT_POST)) || $status == 'needs-action'; $error_msg = $this->gettext('errorimportingtask'); $success = false; - $delegate = null; if ($status == 'delegated') { $delegates = rcube_mime::decode_address_list(rcube_utils::get_input_value('_to', rcube_utils::INPUT_POST, true), 1, false); @@ -1937,15 +1937,19 @@ class tasklist extends rcube_plugin // forward iTip request to delegatee if ($delegate) { - $rsvpme = intval(rcube_utils::get_input_value('_rsvp', rcube_utils::INPUT_POST)); + $rsvpme = rcube_utils::get_input_value('_rsvp', rcube_utils::INPUT_POST); + $itip = $this->load_itip(); - $itip = $this->load_itip(); - if ($itip->delegate_to($task, $delegate, $rsvpme ? true : false)) { + $task['comment'] = $comment; + + if ($itip->delegate_to($task, $delegate, !empty($rsvpme))) { $this->rc->output->show_message('tasklist.itipsendsuccess', 'confirmation'); } else { $this->rc->output->command('display_message', $this->gettext('itipresponseerror'), 'error'); } + + unset($task['comment']); } // find writeable list to store the task @@ -2142,7 +2146,7 @@ class tasklist extends rcube_plugin // send iTip reply if ($task['_method'] == 'REQUEST' && $organizer && !$noreply && !in_array(strtolower($organizer['email']), $emails) && !$error_msg) { - $task['comment'] = rcube_utils::get_input_value('_comment', rcube_utils::INPUT_POST); + $task['comment'] = $comment; $itip = $this->load_itip(); $itip->set_sender_email($reply_sender); From 9d883ed07d858d64587262b18b6342b4b9cfd881 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 14 Apr 2016 11:42:00 +0200 Subject: [PATCH 049/483] Properly set skip_deleted and threading on Kolab storage IMAP operations (T1145) Summary: Fixes race-conditions between Kolab folders and Roundcube core where skip_deleted/threading could be set for operations outside of Kolab plugins, causing e.g. inability to see \Deleted messages. Fixes T1145. Reviewers: #roundcube_kolab_plugins_developers, vanmeeuwen Reviewed By: #roundcube_kolab_plugins_developers, vanmeeuwen Maniphest Tasks: T1145 Differential Revision: https://git.kolab.org/D112 --- plugins/libkolab/lib/kolab_storage.php | 6 +-- plugins/libkolab/lib/kolab_storage_cache.php | 48 +++++++++++++++---- plugins/libkolab/lib/kolab_storage_folder.php | 25 +++++----- 3 files changed, 51 insertions(+), 28 deletions(-) diff --git a/plugins/libkolab/lib/kolab_storage.php b/plugins/libkolab/lib/kolab_storage.php index 318dce87..55f5f585 100644 --- a/plugins/libkolab/lib/kolab_storage.php +++ b/plugins/libkolab/lib/kolab_storage.php @@ -86,11 +86,7 @@ class kolab_storage (self::$imap->get_capability('METADATA') || self::$imap->get_capability('ANNOTATEMORE') || self::$imap->get_capability('ANNOTATEMORE2')); if (self::$ready) { - // set imap options - self::$imap->set_options(array( - 'skip_deleted' => true, - 'threading' => false, - )); + // do nothing } else if (!class_exists('kolabformat')) { rcube::raise_error(array( diff --git a/plugins/libkolab/lib/kolab_storage_cache.php b/plugins/libkolab/lib/kolab_storage_cache.php index 5f8d3fe5..3a73d982 100644 --- a/plugins/libkolab/lib/kolab_storage_cache.php +++ b/plugins/libkolab/lib/kolab_storage_cache.php @@ -186,7 +186,9 @@ class kolab_storage_cache if (!$this->ready) { // kolab cache is disabled, synchronize IMAP mailbox cache only + $this->imap_mode(true); $this->imap->folder_sync($this->folder->name); + $this->imap_mode(false); } else { // read cached folder metadata @@ -204,7 +206,7 @@ class kolab_storage_cache $this->_sync_lock(); // disable messages cache if configured to do so - $this->bypass(true); + $this->imap_mode(true); // synchronize IMAP mailbox cache $this->imap->folder_sync($this->folder->name); @@ -212,6 +214,8 @@ class kolab_storage_cache // compare IMAP index with object cache index $imap_index = $this->imap->index($this->folder->name, null, null, true, true); + $this->imap_mode(false); + // determine objects to fetch or to invalidate if (!$imap_index->is_error()) { $imap_index = $imap_index->get(); @@ -261,8 +265,6 @@ class kolab_storage_cache } } - $this->bypass(false); - // remove lock $this->_sync_unlock(); } @@ -603,6 +605,8 @@ class kolab_storage_cache else { $filter = $this->_query2assoc($query); + $this->imap_mode(true); + if ($filter['type']) { $search = 'UNDELETED HEADER X-Kolab-Type ' . kolab_format::KTYPE_PREFIX . $filter['type']; $index = $this->imap->search_once($this->folder->name, $search); @@ -611,6 +615,8 @@ class kolab_storage_cache $index = $this->imap->index($this->folder->name, null, null, true, true); } + $this->imap_mode(false); + if ($index->is_error()) { $this->check_error(); if ($uids) { @@ -670,6 +676,8 @@ class kolab_storage_cache else { $filter = $this->_query2assoc($query); + $this->imap_mode(true); + if ($filter['type']) { $search = 'UNDELETED HEADER X-Kolab-Type ' . kolab_format::KTYPE_PREFIX . $filter['type']; $index = $this->imap->search_once($this->folder->name, $search); @@ -678,6 +686,8 @@ class kolab_storage_cache $index = $this->imap->index($this->folder->name, null, null, true, true); } + $this->imap_mode(false); + if ($index->is_error()) { $this->check_error(); return null; @@ -1136,13 +1146,31 @@ class kolab_storage_cache } /** - * Bypass Roundcube messages cache. - * Roundcube cache duplicates information already stored in kolab_cache. + * Set Roundcube storage options and bypass messages cache. * - * @param bool $disable True disables, False enables messages cache + * We use skip_deleted and threading settings specific to Kolab, + * we have to change these global settings only temporarily. + * Roundcube cache duplicates information already stored in kolab_cache, + * that's why we can disable it for better performance. + * + * @param bool $force True to start Kolab mode, False to stop it. */ - public function bypass($disable = false) + public function imap_mode($force = false) { + // remember current IMAP settings + if ($force) { + $this->imap_options = array( + 'skip_deleted' => $this->imap->get_option('skip_deleted'), + 'threading' => $this->imap->get_threading(), + ); + } + + // re-set IMAP settings + $this->imap->set_threading($force ? false : $this->imap_options['threading']); + $this->imap->set_options(array( + 'skip_deleted' => $force ? true : $this->imap_options['skip_deleted'], + )); + // if kolab cache is disabled do nothing if (!$this->enabled) { return; @@ -1158,7 +1186,7 @@ class kolab_storage_cache if ($messages_cache) { // handle recurrent (multilevel) bypass() calls - if ($disable) { + if ($force) { $this->cache_bypassed += 1; if ($this->cache_bypassed > 1) { return; @@ -1174,7 +1202,7 @@ class kolab_storage_cache switch ($cache_bypass) { case 2: // Disable messages cache completely - $this->imap->set_messages_caching(!$disable); + $this->imap->set_messages_caching(!$force); break; case 1: @@ -1182,7 +1210,7 @@ class kolab_storage_cache // Default mode is both (MODE_INDEX | MODE_MESSAGE) $mode = rcube_imap_cache::MODE_INDEX; - if (!$disable) { + if (!$force) { $mode |= rcube_imap_cache::MODE_MESSAGE; } diff --git a/plugins/libkolab/lib/kolab_storage_folder.php b/plugins/libkolab/lib/kolab_storage_folder.php index 6f338ba7..4018b7df 100644 --- a/plugins/libkolab/lib/kolab_storage_folder.php +++ b/plugins/libkolab/lib/kolab_storage_folder.php @@ -49,7 +49,6 @@ class kolab_storage_folder extends kolab_storage_folder_api function __construct($name, $type = null, $type_annotation = null) { parent::__construct($name); - $this->imap->set_options(array('skip_deleted' => true)); $this->set_folder($name, $type, $type_annotation); } @@ -448,9 +447,9 @@ class kolab_storage_folder extends kolab_storage_folder_api $this->imap->set_folder($folder); - $this->cache->bypass(true); + $this->cache->imap_mode(true); $message = new rcube_message($msguid); - $this->cache->bypass(false); + $this->cache->imap_mode(false); // Message doesn't exist? if (empty($message->headers)) { @@ -698,9 +697,9 @@ class kolab_storage_folder extends kolab_storage_folder_api if ($old_uid) { // delete old message - $this->cache->bypass(true); + $this->cache->imap_mode(true); $this->imap->delete_message($old_uid, $object['_mailbox']); - $this->cache->bypass(false); + $this->cache->imap_mode(false); } // insert/update message in cache @@ -796,7 +795,7 @@ class kolab_storage_folder extends kolab_storage_folder_api $msguid = is_array($object) ? $object['_msguid'] : $this->cache->uid2msguid($object); $success = false; - $this->cache->bypass(true); + $this->cache->imap_mode(true); if ($msguid && $expunge) { $success = $this->imap->delete_message($msguid, $this->name); @@ -805,7 +804,7 @@ class kolab_storage_folder extends kolab_storage_folder_api $success = $this->imap->set_flag($msguid, 'DELETED', $this->name); } - $this->cache->bypass(false); + $this->cache->imap_mode(false); if ($success) { $this->cache->set($msguid, false); @@ -824,9 +823,9 @@ class kolab_storage_folder extends kolab_storage_folder_api } $this->cache->purge(); - $this->cache->bypass(true); + $this->cache->imap_mode(true); $result = $this->imap->clear_folder($this->name); - $this->cache->bypass(false); + $this->cache->imap_mode(false); return $result; } @@ -844,9 +843,9 @@ class kolab_storage_folder extends kolab_storage_folder_api } if ($msguid = $this->cache->uid2msguid($uid, true)) { - $this->cache->bypass(true); + $this->cache->imap_mode(true); $result = $this->imap->set_flag($msguid, 'UNDELETED', $this->name); - $this->cache->bypass(false); + $this->cache->imap_mode(false); if ($result) { return $msguid; @@ -873,9 +872,9 @@ class kolab_storage_folder extends kolab_storage_folder_api $target_folder = kolab_storage::get_folder($target_folder); if ($msguid = $this->cache->uid2msguid($uid)) { - $this->cache->bypass(true); + $this->cache->imap_mode(true); $result = $this->imap->move_message($msguid, $target_folder->name, $this->name); - $this->cache->bypass(false); + $this->cache->imap_mode(false); if ($result) { $new_uid = ($copyuid = $this->imap->conn->data['COPYUID']) ? $copyuid[1] : null; From a7f5885d8ded79f86d2c0a5468afc7ad1fb97ddd Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 14 Apr 2016 11:42:04 +0200 Subject: [PATCH 050/483] Fix so birthdays are exported with correct year and recurrence definition (T855) Summary: Refactored birthdays handling code. The "all birthdays" request is detected and for this case year is not modified and recurrence rule is added. Reviewers: #roundcube_kolab_plugins_developers, vanmeeuwen Reviewed By: #roundcube_kolab_plugins_developers, vanmeeuwen Differential Revision: https://git.kolab.org/D117 --- plugins/calendar/drivers/calendar_driver.php | 218 ++++++++++--------- 1 file changed, 112 insertions(+), 106 deletions(-) diff --git a/plugins/calendar/drivers/calendar_driver.php b/plugins/calendar/drivers/calendar_driver.php index b146343c..d2703488 100644 --- a/plugins/calendar/drivers/calendar_driver.php +++ b/plugins/calendar/drivers/calendar_driver.php @@ -632,9 +632,9 @@ abstract class calendar_driver $cache = $rcmail->get_cache('calendar.birthdays', 'db', 3600); $cache->expunge(); - $alarm_type = $rcmail->config->get('calendar_birthdays_alarm_type', ''); + $alarm_type = $rcmail->config->get('calendar_birthdays_alarm_type', ''); $alarm_offset = $rcmail->config->get('calendar_birthdays_alarm_offset', '-1D'); - $alarms = $alarm_type ? $alarm_offset . ':' . $alarm_type : null; + $alarms = $alarm_type ? $alarm_offset . ':' . $alarm_type : null; // let the user select the address books to consider in prefs $selected_sources = $rcmail->config->get('calendar_birthday_adressbooks'); @@ -655,72 +655,56 @@ abstract class calendar_driver // iterate over (cached) contacts foreach (($cached ?: $abook->search('*', '', 2, true, true, array('birthday'))) as $contact) { - if (is_array($contact) && !empty($contact['birthday'])) { - try { - if (is_array($contact['birthday'])) - $contact['birthday'] = reset($contact['birthday']); + $event = self::parse_contact($contact, $source); - $bday = $contact['birthday'] instanceof DateTime ? $contact['birthday'] : - new DateTime($contact['birthday'], new DateTimezone('UTC')); - $birthyear = $bday->format('Y'); - } - catch (Exception $e) { - rcube::raise_error(array( - 'code' => 600, 'type' => 'php', - 'file' => __FILE__, 'line' => __LINE__, - 'message' => 'BIRTHDAY PARSE ERROR: ' . $e), - true, false); - continue; + if (empty($event)) { + continue; + } + + // add stripped record to cache + if (empty($cached)) { + $cache_records[] = array( + 'ID' => $contact['ID'], + 'name' => $event['_displayname'], + 'birthday' => $event['start']->format('Y-m-d'), + ); + } + + // filter by search term (only name is involved here) + if (!empty($search) && strpos(mb_strtolower($event['title']), $search) === false) { + continue; + } + + $bday = clone $event['start']; + $byear = $bday->format('Y'); + + // quick-and-dirty recurrence computation: just replace the year + $bday->setDate($year, $bday->format('n'), $bday->format('j')); + $bday->setTime(12, 0, 0); + $this_year = $year; + + // date range reaches over multiple years: use end year if not in range + if (($bday > $end || $bday < $start) && $year2 != $year) { + $bday->setDate($year2, $bday->format('n'), $bday->format('j')); + $this_year = $year2; + } + + // birthday is within requested range + if ($bday <= $end && $bday >= $start) { + unset($event['_displayname']); + $event['alarms'] = $alarms; + + // if this is not the first occurence modify event details + // but not when this is "all birthdays feed" request + if ($year2 - $year < 10 && ($age = ($this_year - $byear))) { + $event['description'] = $rcmail->gettext(array('name' => 'birthdayage', 'vars' => array('age' => $age)), 'calendar'); + $event['start'] = $bday; + $event['end'] = clone $bday; + unset($event['recurrence']); } - $display_name = rcube_addressbook::compose_display_name($contact); - $event_title = $rcmail->gettext(array('name' => 'birthdayeventtitle', 'vars' => array('name' => $display_name)), 'calendar'); - - // add stripped record to cache - if (empty($cached)) { - $cache_records[] = array( - 'ID' => $contact['ID'], - 'name' => $display_name, - 'birthday' => $bday->format('Y-m-d'), - ); - } - - // filter by search term (only name is involved here) - if (!empty($search) && strpos(mb_strtolower($event_title), $search) === false) { - continue; - } - - // quick-and-dirty recurrence computation: just replace the year - $bday->setDate($year, $bday->format('n'), $bday->format('j')); - $bday->setTime(12, 0, 0); - $this_year = $year; - - // date range reaches over multiple years: use end year if not in range - if (($bday > $end || $bday < $start) && $year2 != $year) { - $bday->setDate($year2, $bday->format('n'), $bday->format('j')); - $this_year = $year2; - } - - // birthday is within requested range - if ($bday <= $end && $bday >= $start) { - $age = $this_year - $birthyear; - $uid = rcube_ldap::dn_encode('bday:' . $source . ':' . $contact['ID'] . ':' . $this_year); - $event = array( - 'id' => $uid, - 'uid' => $uid, - 'calendar' => self::BIRTHDAY_CALENDAR_ID, - 'title' => $event_title, - 'description' => $rcmail->gettext(array('name' => 'birthdayage', 'vars' => array('age' => $age)), 'calendar'), - // Add more contact information to description block? - 'allday' => true, - 'start' => $bday, - 'alarms' => $alarms, - ); - $event['end'] = clone $bday; - $event['end']->add(new DateInterval('PT1H')); - - $events[] = $event; - } + // add the main instance + $events[] = $event; } } @@ -744,50 +728,72 @@ abstract class calendar_driver $rcmail = rcmail::get_instance(); if ($source && $contact_id && ($abook = $rcmail->get_address_book($source))) { - $contact = $abook->get_record($contact_id, true); - - if (is_array($contact) && !empty($contact['birthday'])) { - try { - if (is_array($contact['birthday'])) - $contact['birthday'] = reset($contact['birthday']); - - $bday = $contact['birthday'] instanceof DateTime ? $contact['birthday'] : - new DateTime($contact['birthday'], new DateTimezone('UTC')); - $birthyear = $bday->format('Y'); - } - catch (Exception $e) { - rcube::raise_error(array( - 'code' => 600, 'type' => 'php', - 'file' => __FILE__, 'line' => __LINE__, - 'message' => 'BIRTHDAY PARSE ERROR: ' . $e), - true, false); - - return null; - } - - $display_name = rcube_addressbook::compose_display_name($contact); - $event_title = $rcmail->gettext(array('name' => 'birthdayeventtitle', 'vars' => array('name' => $display_name)), 'calendar'); - $uid = rcube_ldap::dn_encode('bday:' . $source . ':' . $contact['ID'] . ':' . $birthyear); - - $event = array( - 'id' => $uid, - 'uid' => $uid, - 'calendar' => self::BIRTHDAY_CALENDAR_ID, - 'title' => $event_title, - 'description' => '', - 'allday' => true, - 'start' => $bday, - 'recurrence' => array('FREQ' => 'YEARLY', 'INTERVAL' => 1), - 'free_busy' => 'free', - ); - $event['end'] = clone $bday; - $event['end']->add(new DateInterval('PT1H')); - - return $event; + if ($contact = $abook->get_record($contact_id, true)) { + return self::parse_contact($contact, $source); } } + } - return null; + /** + * Parse contact and create an event for its birthday + * + * @param array $contact Contact data + * @param string $source Addressbook source ID + * + * @return array Birthday event data + */ + public static function parse_contact($contact, $source) + { + if (!is_array($contact)) { + return; + } + + if (is_array($contact['birthday'])) { + $contact['birthday'] = reset($contact['birthday']); + } + + if (empty($contact['birthday'])) { + return; + } + + try { + $bday = $contact['birthday']; + if (!$bday instanceof DateTime) { + $bday = new DateTime($bday, new DateTimezone('UTC')); + } + $bday->_dateonly = true; + } + catch (Exception $e) { + rcube::raise_error(array( + 'code' => 600, 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => 'BIRTHDAY PARSE ERROR: ' . $e->getMessage()), + true, false); + return; + } + + $rcmail = rcmail::get_instance(); + $birthyear = $bday->format('Y'); + $display_name = rcube_addressbook::compose_display_name($contact); + $label = array('name' => 'birthdayeventtitle', 'vars' => array('name' => $display_name)); + $event_title = $rcmail->gettext($label, 'calendar'); + $uid = rcube_ldap::dn_encode('bday:' . $source . ':' . $contact['ID'] . ':' . $birthyear); + + $event = array( + 'id' => $uid, + 'uid' => $uid, + 'calendar' => self::BIRTHDAY_CALENDAR_ID, + 'title' => $event_title, + 'description' => '', + 'allday' => true, + 'start' => $bday, + 'end' => clone $bday, + 'recurrence' => array('FREQ' => 'YEARLY', 'INTERVAL' => 1), + 'free_busy' => 'free', + '_displayname' => $display_name, + ); + + return $event; } /** From e32c44d99c12712aa7bf5a5eca26891fdfe2e847 Mon Sep 17 00:00:00 2001 From: Christoph Schwarzenberg Date: Sun, 17 Apr 2016 13:55:42 +0200 Subject: [PATCH 051/483] Add possibility to copy events Reviewers: #roundcube_kolab_plugins_developers Subscribers: bruederli Differential Revision: https://git.kolab.org/D128 --- plugins/calendar/calendar_ui.js | 13 +++++++++++++ .../calendar/skins/classic/templates/calendar.html | 1 + .../calendar/skins/larry/templates/calendar.html | 1 + 3 files changed, 15 insertions(+) diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js index 1d60e6dc..d7c50d29 100644 --- a/plugins/calendar/calendar_ui.js +++ b/plugins/calendar/calendar_ui.js @@ -3154,6 +3154,18 @@ function rcube_calendar_ui(settings) } }; + // display the edit dialog, request 'new' action and pass the selected event + this.event_copy = function(event) { + if (event && event.id) { + var copy = $.extend(true, {}, event); + delete copy.id; + delete copy._id; + delete copy.created; + delete copy.changed; + event_edit_dialog('new', copy); + } + }; + // show URL of the given calendar in a dialog box this.showurl = function(calendar) { @@ -4234,6 +4246,7 @@ window.rcmail && rcmail.addEventListener('init', function(evt) { rcmail.register_command('calendar-showurl', function(){ cal.showurl(cal.calendars[cal.selected_calendar]); }, false); rcmail.register_command('event-download', function(){ cal.event_download(cal.selected_event); }, true); rcmail.register_command('event-sendbymail', function(p, obj, e){ cal.event_sendbymail(cal.selected_event, e); }, true); + rcmail.register_command('event-copy', function(){ cal.event_copy(cal.selected_event); }, true); rcmail.register_command('event-history', function(p, obj, e){ cal.event_history_dialog(cal.selected_event); }, false); // search and export events diff --git a/plugins/calendar/skins/classic/templates/calendar.html b/plugins/calendar/skins/classic/templates/calendar.html index b955c714..d8d27acb 100644 --- a/plugins/calendar/skins/classic/templates/calendar.html +++ b/plugins/calendar/skins/classic/templates/calendar.html @@ -122,6 +122,7 @@
  • +
diff --git a/plugins/calendar/skins/larry/templates/calendar.html b/plugins/calendar/skins/larry/templates/calendar.html index 8dc779d5..64c84738 100644 --- a/plugins/calendar/skins/larry/templates/calendar.html +++ b/plugins/calendar/skins/larry/templates/calendar.html @@ -163,6 +163,7 @@
+
+ +
+ diff --git a/plugins/kolab_addressbook/skins/elastic/templates/search_addon.html b/plugins/kolab_addressbook/skins/elastic/templates/search_addon.html new file mode 100644 index 00000000..45d405cd --- /dev/null +++ b/plugins/kolab_addressbook/skins/elastic/templates/search_addon.html @@ -0,0 +1,32 @@ + + + diff --git a/plugins/kolab_addressbook/skins/larry/templates/search_addon.html b/plugins/kolab_addressbook/skins/larry/templates/search_addon.html new file mode 100644 index 00000000..fa9dc7db --- /dev/null +++ b/plugins/kolab_addressbook/skins/larry/templates/search_addon.html @@ -0,0 +1,43 @@ + diff --git a/plugins/libkolab/js/folderlist.js b/plugins/libkolab/js/folderlist.js index 64f8a35c..00dcc475 100644 --- a/plugins/libkolab/js/folderlist.js +++ b/plugins/libkolab/js/folderlist.js @@ -46,7 +46,8 @@ function kolab_folderlist(node, p) if (results.length) { // create treelist widget to present the search results if (!search_results_widget) { - var list_id = (me.container.attr('id') || p.id_prefix || '0') + var list_id = (me.container.attr('id') || p.id_prefix || '0'); + search_results_container = $('
') .html(p.search_title ? '

' + p.search_title + '

' : '') .insertAfter(me.container); @@ -57,6 +58,7 @@ function kolab_folderlist(node, p) id_decode: p.id_decode, selectable: false }); + // copy classes from main list search_results_widget.container.addClass(me.container.attr('class')).attr('aria-labelledby', 'st:' + list_id); @@ -66,6 +68,7 @@ function kolab_folderlist(node, p) .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); @@ -109,7 +112,7 @@ function kolab_folderlist(node, p) // set focus to cloned checkbox if (rcube_event.is_keyboard(e)) { - $(me.get_item(id, true)).find('input[type=checkbox]').first().focus(); + $(me.get_item(id, true)).find('input[type=checkbox]').first().focus(); } }) .on('click', function(e) { @@ -149,6 +152,9 @@ function kolab_folderlist(node, p) item.find('input[type=checkbox]').first().prop('disabled', true).prop('checked', true); item.find('a.subscribed, span.subscribed').hide(); } + + prop.li = item.parent().get(0); + me.triggerEvent('add-item', prop); } search_results_container.show(); diff --git a/plugins/libkolab/skins/elastic/libkolab.less b/plugins/libkolab/skins/elastic/libkolab.less index 0a475c17..78d1836c 100644 --- a/plugins/libkolab/skins/elastic/libkolab.less +++ b/plugins/libkolab/skins/elastic/libkolab.less @@ -169,6 +169,21 @@ // TODO: @fa-var-lock; } } + + &.virtual > div > a { + opacity: .5; + } + } + + input.flex-checkbox { + position: relative !important; + margin: 0 0 0 .25em !important; + + & + label { + position: relative !important; + margin: 0 .3em 0 -1em !important; + right: 0 !important; + } } } @@ -182,6 +197,19 @@ html.touch .listing { } } +.searchresults { + .boxtitle { + line-height: 2rem !important; + font-size: 80%; + padding: 0 .5rem; + margin: 0; + color: @color-popover-separator; + background-color: @color-popover-separator-background; + text-align: center; + border-bottom: 1px solid @color-layout-border; + } +} + #calendarcategories { .input-group:not(:last-child) { margin-bottom: .25rem; From 22f86457c79322ea31325add9eb3cac9558a7611 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 29 Nov 2017 13:14:48 +0100 Subject: [PATCH 264/483] Elastic: Kolab Tags support --- plugins/kolab_tags/kolab_tags.js | 116 +++++++++++------- .../skins/elastic/templates/ui.html | 67 ++++++++++ plugins/kolab_tags/skins/larry/style.css | 7 +- plugins/libkolab/skins/elastic/libkolab.less | 102 +++++++++++++++ 4 files changed, 244 insertions(+), 48 deletions(-) create mode 100644 plugins/kolab_tags/skins/elastic/templates/ui.html diff --git a/plugins/kolab_tags/kolab_tags.js b/plugins/kolab_tags/kolab_tags.js index 0f5fbb3e..0513187c 100644 --- a/plugins/kolab_tags/kolab_tags.js +++ b/plugins/kolab_tags/kolab_tags.js @@ -109,18 +109,19 @@ function load_tags() } }); + rcmail.triggerEvent('kolab-tags-update', {}); rcmail.enable_command('reset-tags', tagsfilter.length && clickable); } function add_tag_element(list, tag, clickable) { // @todo: .append('') - var element = $('
  • ').text(tag.name).data('tag', tag.uid).appendTo(list); + var element = $('
  • ').attr('class', tag_class_name(tag)) + .text(tag.name).data('tag', tag.uid).appendTo(list); if (clickable) { element.click(function(e) { - var item = $(this), - tagid = item.data('tag'); + var tagid = element.data('tag'); if (!tagid) return false; @@ -140,12 +141,12 @@ function add_tag_element(list, tag, clickable) // add tag to the filter if (index < 0) { - item.addClass('selected'); - tag_set_color(item, t); + element.addClass('selected'); + tag_set_color(element, t); tagsfilter.push(tagid); } else if (shift) { - item.removeClass('selected').css(reset_css); + element.removeClass('selected').css(reset_css); tagsfilter.splice(index, 1); } @@ -167,20 +168,22 @@ function manage_tags() { // display it as popup rcmail.tags_popup = rcmail.show_popup_dialog( - '
    ', + '
    ', rcmail.gettext('kolab_tags.tags'), [{ text: rcmail.gettext('save'), - 'class': 'mainaction', + 'class': 'mainaction save', click: function() { if (tag_form_save()) $(this).dialog('close'); } }, { text: rcmail.gettext('cancel'), + 'class': 'cancel', click: function() { $(this).dialog('close'); } }], { width: 400, modal: true, + classes: {'ui-dialog-content': 'formcontent'}, closeOnEscape: true, close: function(e, ui) { $(this).remove(); @@ -194,12 +197,15 @@ function manage_tags() var form = $('#tagsform'), select = $('select', form), buttons = [ - $('').val(rcmail.gettext('kolab_tags.add')) + $('