From a0bdebc9596af2dfb13bca100576457bf3af3a16 Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Thu, 20 Jun 2024 10:15:34 +0200 Subject: [PATCH 01/56] Create and initialize `rcmail` in external JS file We're relying on parsing order here, which means that app.js must(!) be loaded after its dependencies, but before all other scripts, and rcmail-init.js must be loaded and the end of the page. --- program/include/rcmail_output_html.php | 7 +------ program/js/app.js | 2 ++ program/js/rcmail-init.js | 1 + 3 files changed, 4 insertions(+), 6 deletions(-) create mode 100644 program/js/rcmail-init.js diff --git a/program/include/rcmail_output_html.php b/program/include/rcmail_output_html.php index 75e9fbf0e17..925e88c2127 100644 --- a/program/include/rcmail_output_html.php +++ b/program/include/rcmail_output_html.php @@ -141,16 +141,11 @@ protected function init($framed = false) */ EOF; // add common javascripts - $this->add_script($lic, 'head_top'); - $this->add_script('var ' . self::JS_OBJECT_NAME . ' = new rcube_webmail();', 'head_top'); - - // don't wait for page onload. Call init at the bottom of the page (delayed) - $this->add_script(self::JS_OBJECT_NAME . '.init();', 'docready'); - $this->scripts_path = 'program/js/'; $this->include_script('jquery.min.js'); $this->include_script('common.js'); $this->include_script('app.js'); + $this->include_script('rcmail-init.js', 'foot'); // register common UI objects $this->add_handlers([ diff --git a/program/js/app.js b/program/js/app.js index 21c907ae13a..8cb8f5197be 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -10725,3 +10725,5 @@ rcube_webmail.prototype.get_cookie = getCookie; rcube_webmail.prototype.addEventListener = rcube_event_engine.prototype.addEventListener; rcube_webmail.prototype.removeEventListener = rcube_event_engine.prototype.removeEventListener; rcube_webmail.prototype.triggerEvent = rcube_event_engine.prototype.triggerEvent; + +window.rcmail = new rcube_webmail(); diff --git a/program/js/rcmail-init.js b/program/js/rcmail-init.js new file mode 100644 index 00000000000..5e15508c5bf --- /dev/null +++ b/program/js/rcmail-init.js @@ -0,0 +1 @@ +document.addEventListener('DOMContentLoaded', function () { rcmail.init(); }); From c233a41ff8c5a5c03abf8a9ea132672d8f0bf8f7 Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Fri, 21 Jun 2024 12:39:00 +0200 Subject: [PATCH 02/56] Replace inline scripts by JSON-data with instructions We're now sending JSON-encoded data that instructs the interpreting side (app.js), which callbacks to call (or events to trigger) with the given arguments. Basically that means server code now calls $output->command() instead of add_script(). This way the change is not too radical but we still can get rid of inline Javascript code. --- program/actions/settings/index.php | 3 +- program/include/rcmail_action.php | 8 +- program/include/rcmail_output_html.php | 110 ++++-------------- program/js/app.js | 50 ++++++++ tests/Actions/Contacts/EditTest.php | 7 +- tests/Actions/Contacts/ImportTest.php | 2 +- tests/Actions/Settings/FolderCreateTest.php | 2 +- tests/Actions/Settings/FolderSaveTest.php | 6 +- tests/Actions/Settings/IdentityCreateTest.php | 2 +- tests/Actions/Settings/IdentityEditTest.php | 2 +- tests/Actions/Settings/PrefsEditTest.php | 2 +- tests/Actions/Settings/ResponseCreateTest.php | 2 +- tests/Actions/Settings/ResponseEditTest.php | 2 +- 13 files changed, 88 insertions(+), 110 deletions(-) diff --git a/program/actions/settings/index.php b/program/actions/settings/index.php index bcac1d5ebb5..e6f387baf06 100644 --- a/program/actions/settings/index.php +++ b/program/actions/settings/index.php @@ -371,8 +371,7 @@ public static function user_prefs($current = null) if ($current) { $product_name = $rcmail->config->get('product_name', 'Roundcube Webmail'); - $rcmail->output->add_script(sprintf("%s.check_protocol_handler('%s', '#mailtoprotohandler');", - rcmail_output::JS_OBJECT_NAME, rcube::JQ($product_name)), 'docready'); + $rcmail->output->command('check_protocol_handler', rcube::JQ($product_name), '#mailtoprotohandler'); } $blocks['browser']['options']['mailtoprotohandler'] = [ diff --git a/program/include/rcmail_action.php b/program/include/rcmail_action.php index 167ca6364e0..4a3b0b1587d 100644 --- a/program/include/rcmail_action.php +++ b/program/include/rcmail_action.php @@ -171,7 +171,7 @@ public static function quota_display($attrib) $quota = self::quota_content($attrib); $rcmail->output->add_gui_object('quotadisplay', $attrib['id']); - $rcmail->output->add_script('rcmail.set_quota(' . rcube_output::json_serialize($quota) . ');', 'docready'); + $rcmail->output->command('set_quota', rcube_output::json_serialize($quota)); return html::span($attrib, ' '); } @@ -431,10 +431,8 @@ public static function html_editor($mode = '', $editorId = null) } if (!empty($editorId)) { - $script = rcmail_output::JS_OBJECT_NAME . ".enable_command('toggle-editor', true);" - . rcmail_output::JS_OBJECT_NAME . ".editor_init(null, '{$editorId}');"; - - $rcmail->output->add_script($script, 'docready'); + $rcmail->output->command('enable_command', 'toggle-editor', true); + $rcmail->output->command('editor_init', null, $editorId); } $rcmail->output->include_script('tinymce/tinymce.min.js'); diff --git a/program/include/rcmail_output_html.php b/program/include/rcmail_output_html.php index 925e88c2127..dc9c5178c07 100644 --- a/program/include/rcmail_output_html.php +++ b/program/include/rcmail_output_html.php @@ -36,7 +36,6 @@ class rcmail_output_html extends rcmail_output protected $scripts_path = ''; protected $script_files = []; protected $css_files = []; - protected $scripts = []; protected $task; protected $meta_tags = []; protected $link_tags = ['shortcut icon' => '']; @@ -485,7 +484,7 @@ protected function find_file_path($file, $skin_paths) */ public function add_gui_object($obj, $id) { - $this->add_script(self::JS_OBJECT_NAME . ".gui_object('{$obj}', '{$id}');"); + $this->js_commands[] = ['gui_object', $obj, $id]; } /** @@ -589,7 +588,6 @@ public function reset($all = false) $this->framed = $framed || !empty($this->env['framed']); $this->js_labels = []; $this->js_commands = []; - $this->scripts = []; $this->header = ''; $this->footer = ''; $this->body = ''; @@ -683,23 +681,6 @@ public function write($template = '') $this->js_env['blankpage'] = $this->asset_url($this->js_env['blankpage'], true); } - $commands = $this->get_js_commands($framed); - - // if all js commands go to parent window we can ignore all - // script files and skip rcube_webmail initialization (#1489792) - // but not on error pages where skins may need jQuery, etc. - if ($framed && empty($this->js_env['server_error'])) { - $this->scripts = []; - $this->script_files = []; - $this->header = ''; - $this->footer = ''; - } - - // write all javascript commands - if (!empty($commands)) { - $this->add_script($commands, 'head_top'); - } - $this->page_headers(); // call super method @@ -848,7 +829,7 @@ public function parse($name = 'main', $exit = true, $write = true) /** * Return executable javascript code for all registered commands */ - protected function get_js_commands(&$framed = null) + protected function get_js_commands() { $out = ''; $parent_commands = 0; @@ -874,36 +855,20 @@ protected function get_js_commands(&$framed = null) $commands = array_merge($top_commands, $this->js_commands); - foreach ($commands as $args) { - $method = array_shift($args); - $parent = $this->framed || preg_match('/^parent\./', $method); - - foreach ($args as $i => $arg) { - $args[$i] = self::json_serialize($arg, $this->devel_mode); - } - - if ($parent) { - $parent_commands++; - $method = preg_replace('/^parent\./', '', $method); - $parent_prefix = 'if (window.parent && parent.' . self::JS_OBJECT_NAME . ') parent.'; - $method = $parent_prefix . self::JS_OBJECT_NAME . '.' . $method; - } else { - $method = self::JS_OBJECT_NAME . '.' . $method; + // If this is framed, prefix all commands (which are the first element + // of each sub-array) so they're called on the parent's `rcmail` + // instance. + if ($this->framed) { + // Using `for` because we modify the array's content and want to + // avoid the pitfalls of `foreach` and passing by reference. + for ($i = 0; $i < count($commands); $i++) { + if (strpos($commands[$i][0], 'parent.') !== 0) { + $commands[$i][0] = "parent.{$commands[$i][0]}"; + } } - - $out .= sprintf("%s(%s);\n", $method, implode(',', $args)); - } - - $framed = $parent_prefix && $parent_commands == count($commands); - - // make the output more compact if all commands go to parent window - if ($framed) { - $out = 'if (window.parent && parent.' . self::JS_OBJECT_NAME . ") {\n" - . str_replace($parent_prefix, "\tparent.", $out) - . "}\n"; } - return $out; + return self::json_serialize($commands, $this->devel_mode); } /** @@ -1796,16 +1761,15 @@ public function button($attrib) // register button in the system if (!empty($attrib['command'])) { - $this->add_script(sprintf( - "%s.register_button('%s', '%s', '%s', '%s', '%s', '%s');", - self::JS_OBJECT_NAME, + $this->js_commands[] = [ + 'register_button', $command, $attrib['id'], $attrib['type'], !empty($attrib['imageact']) ? $this->abs_url($attrib['imageact']) : (!empty($attrib['classact']) ? $attrib['classact'] : ''), !empty($attrib['imagesel']) ? $this->abs_url($attrib['imagesel']) : (!empty($attrib['classsel']) ? $attrib['classsel'] : ''), - !empty($attrib['imageover']) ? $this->abs_url($attrib['imageover']) : '' - )); + !empty($attrib['imageover']) ? $this->abs_url($attrib['imageover']) : '', + ]; // make valid href to specific buttons if (in_array($attrib['command'], rcmail::$main_tasks)) { @@ -1924,21 +1888,6 @@ public function include_script($file, $position = 'head', $add_path = true) } } - /** - * Add inline javascript code - * - * @param string $script JS code snippet - * @param string $position Target position [head|head_top|foot|docready] - */ - public function add_script($script, $position = 'head') - { - if (!isset($this->scripts[$position])) { - $this->scripts[$position] = rtrim($script); - } else { - $this->scripts[$position] .= "\n" . rtrim($script); - } - } - /** * Link an external css file * @@ -1988,15 +1937,6 @@ protected function _write($output = '') return $output . html::script($script); }; - $merge_scripts = static function ($output, $script) { - return $output . html::script([], $script); - }; - - // put docready commands into page footer - if (!empty($this->scripts['docready'])) { - $this->add_script("\$(function() {\n" . $this->scripts['docready'] . "\n});", 'foot'); - } - $page_header = ''; $page_footer = ''; $meta = ''; @@ -2037,26 +1977,20 @@ protected function _write($output = '') $page_header .= array_reduce((array) $this->script_files['head'], $merge_script_files); } - $head = $this->scripts['head_top'] ?? ''; - $head .= $this->scripts['head'] ?? ''; - - $page_header .= array_reduce((array) $head, $merge_scripts); $page_header .= $this->header . "\n"; if (!empty($this->script_files['head_bottom'])) { $page_header .= array_reduce((array) $this->script_files['head_bottom'], $merge_script_files); } + $page_footer .= html::div(['id' => 'js-data', 'style' => 'display: none', 'hidden' => true], $this->get_js_commands()); + if (!empty($this->script_files['foot'])) { $page_footer .= array_reduce((array) $this->script_files['foot'], $merge_script_files); } $page_footer .= $this->footer . "\n"; - if (!empty($this->scripts['foot'])) { - $page_footer .= array_reduce((array) $this->scripts['foot'], $merge_scripts); - } - // find page header if ($hpos = stripos($output, '')) { $page_header .= "\n"; @@ -2419,11 +2353,7 @@ protected function preloader($attrib): void return; } - $this->add_script('var images = ' . self::json_serialize($images, $this->devel_mode) . '; - for (var i=0; icommand('preload_images', $images); } /** diff --git a/program/js/app.js b/program/js/app.js index 8cb8f5197be..7e498cccda5 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -224,6 +224,9 @@ function rcube_webmail() { // initialize webmail client this.init = function () { var n; + + this.interpret_js_data(); + this.task = this.env.task; if (!this.env.blankpage) { @@ -800,6 +803,46 @@ function rcube_webmail() { this.start_keepalive(); }; + /** + * Handle "commands" passed in via #js-data. Through this, server code can + * trigger Javascript-methods to be called or events to be thrown (for + * plugin JS-code). + */ + this.interpret_js_data = function () { + // Do not use `.textContent`, and neither jQuery's `.text()` here, + // because both modify the actual string! + var raw = $('#js-data').html(); + if (!raw) { + return; + } + var data = JSON.parse(raw); + data.forEach((args) => { + if (!Array.isArray(args)) { + this.log("Unexpected data in '#js-data'! This is not an array: ", args); + } + var command = args.shift(); + if (command.startsWith('plugin.')) { + // Plugin code is reached via events. + this.triggerEvent(command, args); + return; + } + if (command.startsWith('parent.')) { + command = command.replace(/^parent\./, ''); + if (typeof parent.rcmail[command] !== 'function') { + this.log("'" + command + "' is not a callable function!"); + return; + } + parent.rcmail[command](...args); + } else { + if (typeof this[command] !== 'function') { + this.log("'" + command + "' is not a callable function!"); + return; + } + this[command](...args); + } + }); + }; + this.log = function (msg) { if (this.env.devel_mode && window.console && console.log) { console.log(msg); @@ -10652,6 +10695,13 @@ function rcube_webmail() { // setTimeout for Safari setTimeout('window.print()', 10); }; + + this.preload_images = function (urls) { + for (var i=0; iassertSame('Edit contact', $output->getProperty('pagetitle')); $this->assertSame($contact['contact_id'], $output->get_env('cid')); $this->assertTrue(stripos($result, '') === 0); - $this->assertTrue(strpos($result, "rcmail.gui_object('contactphoto', 'contactpic');") !== false); + $this->assertTrue(strpos($result, '["gui_object","contactphoto","contactpic"]') !== false); } /** @@ -96,10 +96,11 @@ public function test_photo_drop_area() $output = $this->initOutput(\rcmail_action::MODE_HTTP, 'contacts', 'edit'); $result = \rcmail_action_contacts_edit::photo_drop_area(['id' => 'test']); - $scripts = $output->getProperty('scripts'); + $commands = $output->getProperty('js_commands'); + $gui_object_commands = array_filter($commands, static function ($arr) { return $arr['0'] === 'gui_object'; }); $filedrop = $output->get_env('filedrop'); - $this->assertSame("rcmail.gui_object('filedrop', 'test');", trim($scripts['head'])); + $this->assertSame([['gui_object', 'filedrop', 'test']], $gui_object_commands); $this->assertSame('upload-photo', $filedrop['action']); $this->assertSame('_photo', $filedrop['fieldname']); $this->assertSame(1, $filedrop['single']); diff --git a/tests/Actions/Contacts/ImportTest.php b/tests/Actions/Contacts/ImportTest.php index a20d73f4212..fadfc2b556e 100644 --- a/tests/Actions/Contacts/ImportTest.php +++ b/tests/Actions/Contacts/ImportTest.php @@ -32,7 +32,7 @@ public function test_run_init() $this->assertSame('contactimport', $output->template); $this->assertSame('Import contacts', $output->getProperty('pagetitle')); $this->assertTrue(stripos($result, '') === 0); - $this->assertTrue(strpos($result, "rcmail.gui_object('importform', 'rcmImportForm');") !== false); + $this->assertTrue(strpos($result, '["gui_object","importform","rcmImportForm"]') !== false); } /** diff --git a/tests/Actions/Settings/FolderCreateTest.php b/tests/Actions/Settings/FolderCreateTest.php index 0a79ffa8f4d..a94b70d74d0 100644 --- a/tests/Actions/Settings/FolderCreateTest.php +++ b/tests/Actions/Settings/FolderCreateTest.php @@ -50,6 +50,6 @@ public function test_run() $this->assertSame('folderedit', $output->template); $this->assertSame('', $output->getProperty('pagetitle')); // TODO: It should have some title $this->assertTrue(stripos($result, '') === 0); - $this->assertTrue(strpos($result, "rcmail.gui_object('editform', 'form');") !== false); + $this->assertTrue(strpos($result, '["gui_object","editform","form"]') !== false); } } diff --git a/tests/Actions/Settings/FolderSaveTest.php b/tests/Actions/Settings/FolderSaveTest.php index 22b9425575f..0a4eabbf3ec 100644 --- a/tests/Actions/Settings/FolderSaveTest.php +++ b/tests/Actions/Settings/FolderSaveTest.php @@ -40,9 +40,9 @@ public function test_new_folder() $this->assertSame('iframe', $output->template); $this->assertTrue(stripos($result, '') === 0); - $this->assertTrue(strpos($result, 'display_message("Folder created successfully.","confirmation",0);') !== false); - $this->assertTrue(strpos($result, '.add_folder_row("NewTest"') !== false); - $this->assertTrue(strpos($result, '.subscription_select()') !== false); + $this->assertTrue(strpos($result, '["parent.display_message","Folder created successfully.","confirmation",0]') !== false); + $this->assertTrue(strpos($result, '["parent.add_folder_row","NewTest",') !== false); + $this->assertTrue(strpos($result, '["parent.subscription_select"]') !== false); } /** diff --git a/tests/Actions/Settings/IdentityCreateTest.php b/tests/Actions/Settings/IdentityCreateTest.php index 96f0b31a0a0..29d272d1832 100644 --- a/tests/Actions/Settings/IdentityCreateTest.php +++ b/tests/Actions/Settings/IdentityCreateTest.php @@ -28,6 +28,6 @@ public function test_run() $this->assertSame('identityedit', $output->template); $this->assertSame('Add identity', $output->getProperty('pagetitle')); $this->assertTrue(stripos($result, '') === 0); - $this->assertTrue(strpos($result, "rcmail.gui_object('editform', 'form')") !== false); + $this->assertTrue(strpos($result, '["gui_object","editform","form"]') !== false); } } diff --git a/tests/Actions/Settings/IdentityEditTest.php b/tests/Actions/Settings/IdentityEditTest.php index cd4d21f652d..ab5509c9f5c 100644 --- a/tests/Actions/Settings/IdentityEditTest.php +++ b/tests/Actions/Settings/IdentityEditTest.php @@ -37,7 +37,7 @@ public function test_run() $this->assertSame('Edit identity', $output->getProperty('pagetitle')); $this->assertSame($identity['identity_id'], $output->get_env('iid')); $this->assertTrue(stripos($result, '') === 0); - $this->assertTrue(strpos($result, "rcmail.gui_object('editform', 'form')") !== false); + $this->assertTrue(strpos($result, '["gui_object","editform","form"]') !== false); $this->assertTrue(strpos($result, 'test@example.com') !== false); // TODO: Test error handling diff --git a/tests/Actions/Settings/PrefsEditTest.php b/tests/Actions/Settings/PrefsEditTest.php index 8c6e496f489..3f1a706d096 100644 --- a/tests/Actions/Settings/PrefsEditTest.php +++ b/tests/Actions/Settings/PrefsEditTest.php @@ -30,6 +30,6 @@ public function test_run() $this->assertSame('settingsedit', $output->template); $this->assertSame('Preferences', $output->getProperty('pagetitle')); $this->assertTrue(stripos($result, '') === 0); - $this->assertTrue(strpos($result, "rcmail.gui_object('editform', 'form')") !== false); + $this->assertTrue(strpos($result, '["gui_object","editform","form"]') !== false); } } diff --git a/tests/Actions/Settings/ResponseCreateTest.php b/tests/Actions/Settings/ResponseCreateTest.php index 62919498eac..d39caa74416 100644 --- a/tests/Actions/Settings/ResponseCreateTest.php +++ b/tests/Actions/Settings/ResponseCreateTest.php @@ -31,6 +31,6 @@ public function test_run() $this->assertSame('Add response', $output->getProperty('pagetitle')); $this->assertFalse($output->get_env('readonly')); $this->assertTrue(stripos($result, '') === 0); - $this->assertTrue(strpos($result, "rcmail.gui_object('editform', 'form')") !== false); + $this->assertTrue(strpos($result, '["gui_object","editform","form"]') !== false); } } diff --git a/tests/Actions/Settings/ResponseEditTest.php b/tests/Actions/Settings/ResponseEditTest.php index 1ce083b2a92..953018f361a 100644 --- a/tests/Actions/Settings/ResponseEditTest.php +++ b/tests/Actions/Settings/ResponseEditTest.php @@ -43,7 +43,7 @@ public function test_run() $this->assertSame('Edit response', $output->getProperty('pagetitle')); $this->assertTrue($output->get_env('readonly')); $this->assertTrue(stripos($result, '') === 0); - $this->assertTrue(strpos($result, "rcmail.gui_object('editform', 'form')") !== false); + $this->assertTrue(strpos($result, '["gui_object","editform","form"]') !== false); $this->assertTrue(strpos($result, 'tinymce.min.js') !== false); $this->assertTrue(strpos($result, 'Static Response One') !== false); From 28edc311a8188ffd7945623f7a55e95d183db9ba Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Fri, 21 Jun 2024 16:04:09 +0200 Subject: [PATCH 03/56] Put footer scripts after footer data --- program/include/rcmail_output_html.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/program/include/rcmail_output_html.php b/program/include/rcmail_output_html.php index dc9c5178c07..a09b74c24eb 100644 --- a/program/include/rcmail_output_html.php +++ b/program/include/rcmail_output_html.php @@ -1985,12 +1985,15 @@ protected function _write($output = '') $page_footer .= html::div(['id' => 'js-data', 'style' => 'display: none', 'hidden' => true], $this->get_js_commands()); + $page_footer .= $this->footer . "\n"; + + // Make sure to add the 'foot' scripts after the footer data, because + // that data can contain e.g. relevant elements for popups that are + // triggered from `rcmail.init()`. if (!empty($this->script_files['foot'])) { $page_footer .= array_reduce((array) $this->script_files['foot'], $merge_script_files); } - $page_footer .= $this->footer . "\n"; - // find page header if ($hpos = stripos($output, '')) { $page_header .= "\n"; From 4d66faf837a127874824000f5d029f7ae7a50f01 Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Fri, 21 Jun 2024 14:41:10 +0200 Subject: [PATCH 04/56] De-inline elastic dark-mode switch --- skins/elastic/dark-mode.js | 8 ++++++++ skins/elastic/templates/includes/layout.html | 10 +--------- skins/elastic/watermark.html | 10 +--------- 3 files changed, 10 insertions(+), 18 deletions(-) create mode 100644 skins/elastic/dark-mode.js diff --git a/skins/elastic/dark-mode.js b/skins/elastic/dark-mode.js new file mode 100644 index 00000000000..20b911cd43e --- /dev/null +++ b/skins/elastic/dark-mode.js @@ -0,0 +1,8 @@ +try { + if (document.cookie.indexOf('colorMode=dark') > -1 + || (document.cookie.indexOf('colorMode=light') === -1 && window.matchMedia('(prefers-color-scheme: dark)').matches) + ) { + document.documentElement.className += ' dark-mode'; + } +} catch (e) {} + diff --git a/skins/elastic/templates/includes/layout.html b/skins/elastic/templates/includes/layout.html index 90a83741475..d4ed18fd7c8 100644 --- a/skins/elastic/templates/includes/layout.html +++ b/skins/elastic/templates/includes/layout.html @@ -29,15 +29,7 @@ - + action-"> diff --git a/skins/elastic/watermark.html b/skins/elastic/watermark.html index 4765caf3138..a56478f97a6 100644 --- a/skins/elastic/watermark.html +++ b/skins/elastic/watermark.html @@ -24,15 +24,7 @@ background-blend-mode: soft-light; } - + From aa144eba6d20ab2b59e0417b035376ddd32ad04c Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Fri, 21 Jun 2024 14:48:00 +0200 Subject: [PATCH 05/56] De-inline markasjunk --- plugins/markasjunk/drivers/jsevent.php | 33 +------------------------- plugins/markasjunk/markasjunk.js | 26 ++++++++++++++++++++ 2 files changed, 27 insertions(+), 32 deletions(-) diff --git a/plugins/markasjunk/drivers/jsevent.php b/plugins/markasjunk/drivers/jsevent.php index 56e0e340b53..b29955a8817 100644 --- a/plugins/markasjunk/drivers/jsevent.php +++ b/plugins/markasjunk/drivers/jsevent.php @@ -42,38 +42,7 @@ public function init() return; } - $js_addition_spam_folders = json_encode($this->addition_spam_folders); - $js_suspicious_folders = json_encode($this->suspicious_folders); - - $script = << -1) { - props.disp.spam = false; - props.disp.ham = true; - } - else if ($.inArray(rcmail.env.mailbox, suspicious_folders) > -1) { - props.disp.spam = true; - props.disp.ham = true; - - // from here it is also possible to alter the buttons themselves... - props.objs.spamobj.find('a > span').text('As possibly spam'); - } - else { - props.objs.spamobj.find('a > span').text(rcmail.get_label('markasjunk.markasjunk')); - } - - return props; - }); - EOL; - - $rcmail->output->add_script($script, 'docready'); + $rcmail->output->command('plugin.markasjunk-init', $this->addition_spam_folders, $this->suspicious_folders); } public function spam(&$uids, $mbox) diff --git a/plugins/markasjunk/markasjunk.js b/plugins/markasjunk/markasjunk.js index f295bbc12c7..9ffa751ea5b 100644 --- a/plugins/markasjunk/markasjunk.js +++ b/plugins/markasjunk/markasjunk.js @@ -145,4 +145,30 @@ if (window.rcmail) { return false; } }); + + rcmail.addEventListener('plugin.markasjunk-init', function (addition_spam_folders, suspicious_folders) { + rcmail.addEventListener('markasjunk-update', function (props) { + // ignore this special code when in a multifolder listing + if (rcmail.is_multifolder_listing()) { + return; + } + + if ($.inArray(rcmail.env.mailbox, addition_spam_folders) > -1) { + props.disp.spam = false; + props.disp.ham = true; + } + else if ($.inArray(rcmail.env.mailbox, suspicious_folders) > -1) { + props.disp.spam = true; + props.disp.ham = true; + + // from here it is also possible to alter the buttons themselves... + props.objs.spamobj.find('a > span').text('As possibly spam'); + } + else { + props.objs.spamobj.find('a > span').text(rcmail.get_label('markasjunk.markasjunk')); + } + + return props; + }); + }); } From 218e18f361e9230feb75313d6c13f95ee50f7e02 Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Fri, 21 Jun 2024 14:50:44 +0200 Subject: [PATCH 06/56] De-inline jquery-ui --- plugins/jqueryui/jqueryui.php | 3 ++- plugins/jqueryui/js/jqueryui-minicolors-init.js | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 plugins/jqueryui/js/jqueryui-minicolors-init.js diff --git a/plugins/jqueryui/jqueryui.php b/plugins/jqueryui/jqueryui.php index c971bd133f2..639b8eb3a36 100644 --- a/plugins/jqueryui/jqueryui.php +++ b/plugins/jqueryui/jqueryui.php @@ -120,7 +120,8 @@ public static function miniColors() $rcube->output->include_css('plugins/jqueryui/' . $css); $rcube->output->include_script($script, 'head', false); - $rcube->output->add_script('$.fn.miniColors = $.fn.minicolors; $("input.colors").minicolors(' . $config_str . ')', 'docready'); + $rcube->output->include_script('plugins/jqueryui/js/jqueryui-minicolors-init.js'); + $rcube->output->command('plugin.jqueryui-minicolors-init', $config_str); $rcube->output->set_env('minicolors_config', $config); } diff --git a/plugins/jqueryui/js/jqueryui-minicolors-init.js b/plugins/jqueryui/js/jqueryui-minicolors-init.js new file mode 100644 index 00000000000..fe591d26a6f --- /dev/null +++ b/plugins/jqueryui/js/jqueryui-minicolors-init.js @@ -0,0 +1,4 @@ +rcmail.addEventListener('jqueryui-minicolors-init', function (config) { + $.fn.miniColors = $.fn.minicolors; + $("input.colors").minicolors(config); +}); From c04dad5cff592d7177e11144023a16b163b3794d Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Fri, 21 Jun 2024 14:51:42 +0200 Subject: [PATCH 07/56] De-inline managesieve --- plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php b/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php index 15b468bac34..6393c4c3a0e 100644 --- a/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php +++ b/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php @@ -2757,8 +2757,7 @@ protected function print_tips() return; } - $script = rcmail_output::JS_OBJECT_NAME . '.managesieve_tip_register(' . json_encode($this->tips) . ');'; - $this->rc->output->add_script($script, 'docready'); + $this->rc->output->command('managesieve_tip_register', $this->tips); } protected function list_input($id, $name, $value, $size = null, $hidden = false, $attrib = []) From 66361e61274beae0388ccbcc4a997bb6e37a3f74 Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Fri, 21 Jun 2024 14:53:27 +0200 Subject: [PATCH 08/56] De-inline googiespell --- program/actions/mail/compose.php | 18 ++++-------------- program/js/googiespell_init.js | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 14 deletions(-) create mode 100644 program/js/googiespell_init.js diff --git a/program/actions/mail/compose.php b/program/actions/mail/compose.php index 900fc8161d1..a98bac3817e 100644 --- a/program/actions/mail/compose.php +++ b/program/actions/mail/compose.php @@ -513,18 +513,8 @@ public static function spellchecker_init() // include GoogieSpell $rcmail->output->include_script('googiespell.js'); - $rcmail->output->add_script(sprintf( - "var googie = new GoogieSpell('%s/images/googiespell/','%s&lang=', %s);\n" . - "googie.lang_chck_spell = \"%s\";\n" . - "googie.lang_rsm_edt = \"%s\";\n" . - "googie.lang_close = \"%s\";\n" . - "googie.lang_revert = \"%s\";\n" . - "googie.lang_no_error_found = \"%s\";\n" . - "googie.lang_learn_word = \"%s\";\n" . - "googie.setLanguages(%s);\n" . - "googie.setCurrentLanguage('%s');\n" . - "googie.setDecoration(false);\n" . - "googie.decorateTextarea(rcmail.env.composebody);\n", + $rcmail->output->include_script('googiespell_init.js'); + $rcmail->output->command('googiespell_init', $rcmail->output->asset_url($rcmail->output->get_skin_path()), $rcmail->url(['_task' => 'utils', '_action' => 'spell', '_remote' => 1]), !empty($dictionary) ? 'true' : 'false', @@ -534,9 +524,9 @@ public static function spellchecker_init() rcube::JQ(rcube::Q($rcmail->gettext('revertto'))), rcube::JQ(rcube::Q($rcmail->gettext('nospellerrors'))), rcube::JQ(rcube::Q($rcmail->gettext('addtodict'))), - rcube_output::json_serialize($spellcheck_langs), + $spellcheck_langs, $lang - ), 'foot'); + ); $rcmail->output->add_label('checking'); $rcmail->output->set_env('spell_langs', $spellcheck_langs); diff --git a/program/js/googiespell_init.js b/program/js/googiespell_init.js new file mode 100644 index 00000000000..556ea1e4047 --- /dev/null +++ b/program/js/googiespell_init.js @@ -0,0 +1,17 @@ +rcube_webmail.prototype.googiespell_init = function (asseturl, baseurl, use_dict, lang_chck_spell, lang_rsm_edt, lang_close, lang_revert, lang_no_error_found, lang_learn_word, languages, currentLanguage) { + window.googie = new GoogieSpell( + asseturl + '/images/googiespell/', + baseurl + '&lang=', + use_dict + ); + googie.lang_chck_spell = lang_chck_spell; + googie.lang_rsm_edt = lang_rsm_edt; + googie.lang_close = lang_close; + googie.lang_revert = lang_revert; + googie.lang_no_error_found = lang_no_error_found; + googie.lang_learn_word = lang_learn_word; + googie.setLanguages(languages); + googie.setCurrentLanguage(currentLanguage); + googie.setDecoration(false); + googie.decorateTextarea(rcmail.env.composebody); +}; From 6a0a98ed96af785c0889e5d102271b64ec886e7a Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Fri, 21 Jun 2024 14:59:13 +0200 Subject: [PATCH 09/56] De-inline compose.php Signed-off-by: Pablo Zmdl --- program/actions/mail/compose.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/program/actions/mail/compose.php b/program/actions/mail/compose.php index a98bac3817e..1ee97be255d 100644 --- a/program/actions/mail/compose.php +++ b/program/actions/mail/compose.php @@ -1242,8 +1242,7 @@ public static function write_forward_attachments() 'name' => 'msgsizeerrorfwd', 'vars' => ['num' => $size_errors, 'size' => $limit], ]); - $script = sprintf("%s.display_message('%s', 'error');", rcmail_output::JS_OBJECT_NAME, rcube::JQ($error)); - $rcmail->output->add_script($script, 'docready'); + $rcmail->output->command('display_message', rcube::JQ($error), 'error'); } } From bc2ce2364fd96d09f6be25eaf9c24b8c5e63e2d8 Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Fri, 21 Jun 2024 15:04:16 +0200 Subject: [PATCH 10/56] De-inline new_user_dialog --- plugins/new_user_dialog/new_user_dialog.js | 29 +++++++++++++++++++ plugins/new_user_dialog/new_user_dialog.php | 32 ++------------------- 2 files changed, 32 insertions(+), 29 deletions(-) create mode 100644 plugins/new_user_dialog/new_user_dialog.js diff --git a/plugins/new_user_dialog/new_user_dialog.js b/plugins/new_user_dialog/new_user_dialog.js new file mode 100644 index 00000000000..aeca0771c67 --- /dev/null +++ b/plugins/new_user_dialog/new_user_dialog.js @@ -0,0 +1,29 @@ +rcmail.addEventListener('plugin.new_user_dialog_open', function (title) { + var newuserdialog = rcmail.show_popup_dialog($('#newuserdialog'), title, [{ + text: rcmail.get_label('save'), + class: 'mainaction save', + click: function () { + var request = {}; + $.each($('form', this).serializeArray(), function () { + request[this.name] = this.value; + }); + + rcmail.http_post('plugin.newusersave', request, true); + return false; + }, + }], + { + resizable: false, + closeOnEscape: false, + width: 500, + open: function () { $('#newuserdialog').show(); $('#newuserdialog-name').focus(); }, + beforeClose: function () { + return false; + }, + } + ); + + rcmail.addEventListener('plugin.new_user_dialog_close', function (title) { + newuserdialog.dialog('destroy'); + }); +}); diff --git a/plugins/new_user_dialog/new_user_dialog.php b/plugins/new_user_dialog/new_user_dialog.php index 512ea617dc3..599bc6eb54f 100644 --- a/plugins/new_user_dialog/new_user_dialog.php +++ b/plugins/new_user_dialog/new_user_dialog.php @@ -104,34 +104,8 @@ public function render_page($p) )); $title = rcube::JQ($this->gettext('identitydialogtitle')); - $script = " -var newuserdialog = rcmail.show_popup_dialog($('#newuserdialog'), '{$title}', [{ - text: rcmail.get_label('save'), - 'class': 'mainaction save', - click: function() { - var request = {}; - $.each($('form', this).serializeArray(), function() { - request[this.name] = this.value; - }); - - rcmail.http_post('plugin.newusersave', request, true); - return false; - } - }], - { - resizable: false, - closeOnEscape: false, - width: 500, - open: function() { $('#newuserdialog').show(); $('#newuserdialog-name').focus(); }, - beforeClose: function() { - return false; - } - } -); -rcube_webmail.prototype.new_user_dialog_close = function() { newuserdialog.dialog('destroy'); }; -"; - // disable keyboard events for messages list (#1486726) - $rcmail->output->add_script($script, 'docready'); + $rcmail->output->command('plugin.new_user_dialog_open', $title); + $this->include_script('new_user_dialog.js'); } } @@ -187,7 +161,7 @@ public function save_data() // save prefs to not show dialog again $rcmail->user->save_prefs(['newuserdialog' => null]); // hide dialog - $rcmail->output->command('new_user_dialog_close'); + $rcmail->output->command('plugin.new_user_dialog_close'); $rcmail->output->show_message('successfullysaved', 'confirmation'); } else { // show error From 600e8647f2f6344aac50417836fe503c0d9f989f Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Tue, 9 Jul 2024 10:34:26 +0200 Subject: [PATCH 11/56] Initialize rcmail from app.js --- program/include/rcmail_output_html.php | 1 - program/js/app.js | 1 + program/js/rcmail-init.js | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 program/js/rcmail-init.js diff --git a/program/include/rcmail_output_html.php b/program/include/rcmail_output_html.php index a09b74c24eb..b8b2ea15013 100644 --- a/program/include/rcmail_output_html.php +++ b/program/include/rcmail_output_html.php @@ -144,7 +144,6 @@ protected function init($framed = false) $this->include_script('jquery.min.js'); $this->include_script('common.js'); $this->include_script('app.js'); - $this->include_script('rcmail-init.js', 'foot'); // register common UI objects $this->add_handlers([ diff --git a/program/js/app.js b/program/js/app.js index 7e498cccda5..b47b10f6db8 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -10777,3 +10777,4 @@ rcube_webmail.prototype.removeEventListener = rcube_event_engine.prototype.remov rcube_webmail.prototype.triggerEvent = rcube_event_engine.prototype.triggerEvent; window.rcmail = new rcube_webmail(); +document.addEventListener('DOMContentLoaded', function () { window.rcmail.init(); }); diff --git a/program/js/rcmail-init.js b/program/js/rcmail-init.js deleted file mode 100644 index 5e15508c5bf..00000000000 --- a/program/js/rcmail-init.js +++ /dev/null @@ -1 +0,0 @@ -document.addEventListener('DOMContentLoaded', function () { rcmail.init(); }); From a6a052c8e3aaf963439311d7ebefa39cc1faefbf Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Tue, 9 Jul 2024 15:09:39 +0200 Subject: [PATCH 12/56] Initialize googiespell in editor.js, not external file This looks like more than it is, because now most of the intialization code of rcube_text_editor is wrapped in an init-function, which enables us to call other functions. --- program/actions/mail/compose.php | 25 ++- program/js/app.js | 16 +- program/js/editor.js | 279 +++++++++++++++++-------------- program/js/googiespell_init.js | 17 -- 4 files changed, 173 insertions(+), 164 deletions(-) delete mode 100644 program/js/googiespell_init.js diff --git a/program/actions/mail/compose.php b/program/actions/mail/compose.php index 1ee97be255d..5d8393d8389 100644 --- a/program/actions/mail/compose.php +++ b/program/actions/mail/compose.php @@ -513,20 +513,17 @@ public static function spellchecker_init() // include GoogieSpell $rcmail->output->include_script('googiespell.js'); - $rcmail->output->include_script('googiespell_init.js'); - $rcmail->output->command('googiespell_init', - $rcmail->output->asset_url($rcmail->output->get_skin_path()), - $rcmail->url(['_task' => 'utils', '_action' => 'spell', '_remote' => 1]), - !empty($dictionary) ? 'true' : 'false', - rcube::JQ(rcube::Q($rcmail->gettext('checkspelling'))), - rcube::JQ(rcube::Q($rcmail->gettext('resumeediting'))), - rcube::JQ(rcube::Q($rcmail->gettext('close'))), - rcube::JQ(rcube::Q($rcmail->gettext('revertto'))), - rcube::JQ(rcube::Q($rcmail->gettext('nospellerrors'))), - rcube::JQ(rcube::Q($rcmail->gettext('addtodict'))), - $spellcheck_langs, - $lang - ); + $rcmail->output->set_env('googiespell_asset_url', $rcmail->output->asset_url($rcmail->output->get_skin_path())); + $rcmail->output->set_env('googiespell_base_url', $rcmail->url(['_task' => 'utils', '_action' => 'spell', '_remote' => 1])); + $rcmail->output->set_env('googiespell_use_dict', !empty($dictionary) ? 'true' : 'false'); + $rcmail->output->set_env('googiespell_lang_chck_spell', rcube::JQ(rcube::Q($rcmail->gettext('checkspelling')))); + $rcmail->output->set_env('googiespell_lang_rsm_edt', rcube::JQ(rcube::Q($rcmail->gettext('resumeediting')))); + $rcmail->output->set_env('googiespell_lang_close', rcube::JQ(rcube::Q($rcmail->gettext('close')))); + $rcmail->output->set_env('googiespell_lang_revert', rcube::JQ(rcube::Q($rcmail->gettext('revertto')))); + $rcmail->output->set_env('googiespell_lang_no_error_found', rcube::JQ(rcube::Q($rcmail->gettext('nospellerrors')))); + $rcmail->output->set_env('googiespell_lang_learn_word', rcube::JQ(rcube::Q($rcmail->gettext('addtodict')))); + $rcmail->output->set_env('googiespell_languages', $spellcheck_langs); + $rcmail->output->set_env('googiespell_currentLanguage', $lang); $rcmail->output->add_label('checking'); $rcmail->output->set_env('spell_langs', $spellcheck_langs); diff --git a/program/js/app.js b/program/js/app.js index b47b10f6db8..fe4ceb1b06e 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -417,19 +417,18 @@ function rcube_webmail() { // add more commands (not enabled) $.merge(this.env.compose_commands, ['add-recipient', 'firstpage', 'previouspage', 'nextpage', 'lastpage']); - if (window.googie) { - this.env.editor_config.spellchecker = googie; - this.env.editor_config.spellcheck_observer = function (s) { - ref.spellcheck_state(); - }; + this.env.editor_config.spellcheck_observer = function (s) { + ref.spellcheck_state(); + }; + + // initialize HTML editor + this.editor_init(null, this.env.composebody); + if (window.googie) { this.env.compose_commands.push('spellcheck'); this.enable_command('spellcheck', true); } - // initialize HTML editor - this.editor_init(null, this.env.composebody); - // init message compose form this.init_messageform(); } else if (this.env.action == 'bounce') { @@ -9388,6 +9387,7 @@ function rcube_webmail() { // initialize HTML editor this.editor_init = function (config, id) { this.editor = new rcube_text_editor(config || this.env.editor_config, id); + this.editor.init(); }; diff --git a/program/js/editor.js b/program/js/editor.js index a8058bd09cf..8592a4932ab 100644 --- a/program/js/editor.js +++ b/program/js/editor.js @@ -77,145 +77,152 @@ function rcube_text_editor(config, id) { deprecation_warnings: false, }; - // register spellchecker for plain text editor - this.spellcheck_observer = function () {}; - if (config.spellchecker) { - this.spellchecker = config.spellchecker; - if (config.spellcheck_observer) { - this.spellchecker.spelling_state_observer = this.spellcheck_observer = config.spellcheck_observer; - } - } - - // Note: must be registered only once (#1490311) - if (!tinymce.registered_request_token) { - tinymce.registered_request_token = true; - tinymce.util.XHR.on('beforeSend', function (e) { - // secure spellchecker requests with Roundcube token - e.xhr.setRequestHeader('X-Roundcube-Request', rcmail.env.request_token); - // A hacky way of setting spellchecker language (there's no API for this in Tiny) - if (e.settings && e.settings.data) { - e.settings.data = e.settings.data.replace(/^(method=[a-zA-Z]+&lang=)([^&]+)/, '$1' + rcmail.env.spell_lang); + this.init = function () { + this.googiespell_init(); + if (window.googie) { + config.spellchecker = window.googie; + } + + // register spellchecker for plain text editor + this.spellcheck_observer = function () {}; + if (config.spellchecker) { + this.spellchecker = config.spellchecker; + if (config.spellcheck_observer) { + this.spellchecker.spelling_state_observer = this.spellcheck_observer = config.spellcheck_observer; + } + } + + // Note: must be registered only once (#1490311) + if (!tinymce.registered_request_token) { + tinymce.registered_request_token = true; + tinymce.util.XHR.on('beforeSend', function (e) { + // secure spellchecker requests with Roundcube token + e.xhr.setRequestHeader('X-Roundcube-Request', rcmail.env.request_token); + // A hacky way of setting spellchecker language (there's no API for this in Tiny) + if (e.settings && e.settings.data) { + e.settings.data = e.settings.data.replace(/^(method=[a-zA-Z]+&lang=)([^&]+)/, '$1' + rcmail.env.spell_lang); + } + }); + } + + // minimal editor + if (config.mode == 'identity' || config.mode == 'response') { + conf.toolbar += ' | charmap hr link unlink image code $extra'; + $.extend(conf, { + plugins: 'autolink charmap code hr image link paste tabfocus', + file_picker_types: 'image', + }); + } + // full-featured editor + else { + conf.toolbar += ' | bullist numlist outdent indent ltr rtl blockquote' + + ' | link unlink table | $extra charmap image media | code searchreplace undo redo', + $.extend(conf, { + plugins: 'autolink charmap code directionality link lists image media nonbreaking' + + ' paste table tabfocus searchreplace spellchecker', + spellchecker_rpc_url: abs_url + '/?_task=utils&_action=spell_html&_remote=1', + spellchecker_language: rcmail.env.spell_lang, + }); + } + + // add TinyMCE plugins/buttons from Roundcube plugin + $.each(config.extra_plugins || [], function () { + if (conf.plugins.indexOf(this) < 0) { + conf.plugins = conf.plugins + ' ' + this; } }); - } - - // minimal editor - if (config.mode == 'identity' || config.mode == 'response') { - conf.toolbar += ' | charmap hr link unlink image code $extra'; - $.extend(conf, { - plugins: 'autolink charmap code hr image link paste tabfocus', - file_picker_types: 'image', - }); - } - // full-featured editor - else { - conf.toolbar += ' | bullist numlist outdent indent ltr rtl blockquote' - + ' | link unlink table | $extra charmap image media | code searchreplace undo redo', - $.extend(conf, { - plugins: 'autolink charmap code directionality link lists image media nonbreaking' - + ' paste table tabfocus searchreplace spellchecker', - spellchecker_rpc_url: abs_url + '/?_task=utils&_action=spell_html&_remote=1', - spellchecker_language: rcmail.env.spell_lang, + $.each(config.extra_buttons || [], function () { + if (conf.toolbar.indexOf(this) < 0) { + conf.toolbar = conf.toolbar.replace('$extra', '$extra ' + this); + } }); - } - - // add TinyMCE plugins/buttons from Roundcube plugin - $.each(config.extra_plugins || [], function () { - if (conf.plugins.indexOf(this) < 0) { - conf.plugins = conf.plugins + ' ' + this; - } - }); - $.each(config.extra_buttons || [], function () { - if (conf.toolbar.indexOf(this) < 0) { - conf.toolbar = conf.toolbar.replace('$extra', '$extra ' + this); - } - }); - - // disable TinyMCE plugins/buttons from Roundcube plugin - $.each(config.disabled_plugins || [], function () { - conf.plugins = conf.plugins.replace(this, ''); - }); - $.each(config.disabled_buttons || [], function () { - conf.toolbar = conf.toolbar.replace(this, ''); - }); - - // font list, convert to TinyMCE format - var fonts = []; - $.each(config.font_formats || [], function (key) { - fonts.push(key + '=' + this.replace(/"/g, '').toLowerCase()); - }); - conf.font_formats = fonts.join('; '); - if (fonts.length > 1) { - conf.toolbar = conf.toolbar.replace('$font', 'fontselect $font'); - } - - // font size list - conf.fontsize_formats = config.fontsize_formats.join(' '); - if (config.fontsize_formats.length > 1) { - conf.toolbar = conf.toolbar.replace('$font', 'fontsizeselect $font'); - } - - conf.toolbar = conf.toolbar.replace(/($extra|$font)/, '').replace(/\|\s+\|/g, '|'); - - // support external configuration settings e.g. from skin - if (window.rcmail_editor_settings) { - $.extend(conf, window.rcmail_editor_settings); - } - - conf.setup = function (ed) { - ed.on('init', function () { - ref.init_callback(ed); + + // disable TinyMCE plugins/buttons from Roundcube plugin + $.each(config.disabled_plugins || [], function () { + conf.plugins = conf.plugins.replace(this, ''); }); - // add handler for spellcheck button state update - ed.on('SpellcheckStart SpellcheckEnd', function (args) { - ref.spellcheck_active = args.type == 'spellcheckstart'; - ref.spellcheck_observer(); + $.each(config.disabled_buttons || [], function () { + conf.toolbar = conf.toolbar.replace(this, ''); }); - ed.on('keypress', function () { - rcmail.compose_type_activity++; + + // font list, convert to TinyMCE format + var fonts = []; + $.each(config.font_formats || [], function (key) { + fonts.push(key + '=' + this.replace(/"/g, '').toLowerCase()); }); - ed.on('PastePostProcess', function (e) { - $(e.node).find('img').each(function () { - var id = 'i' + Date.now(); + conf.font_formats = fonts.join('; '); + if (fonts.length > 1) { + conf.toolbar = conf.toolbar.replace('$font', 'fontselect $font'); + } - // No referrer for privacy - this.referrerPolicy = 'no-referrer'; + // font size list + conf.fontsize_formats = config.fontsize_formats.join(' '); + if (config.fontsize_formats.length > 1) { + conf.toolbar = conf.toolbar.replace('$font', 'fontsizeselect $font'); + } + + conf.toolbar = conf.toolbar.replace(/($extra|$font)/, '').replace(/\|\s+\|/g, '|'); - // Need an attribute to find the image element after paste - this.setAttribute('data-img-id', id); + // support external configuration settings e.g. from skin + if (window.rcmail_editor_settings) { + $.extend(conf, window.rcmail_editor_settings); + } - // Replace the 'src' with data: URI after the image has been loaded - // This way if we fetch the image again the browser will fetch it from cache - this.onload = function () { - this.onload = null; ref.replace_img_src(this.src, id); - }; + conf.setup = function (ed) { + ed.on('init', function () { + ref.init_callback(ed); }); - }); - // make links open on shift-click - ed.on('click', function (e) { - var link = $(e.target).closest('a'); - if (link.length && e.shiftKey) { - window.open(link.get(0).href, '_blank'); - return false; - } - }); - ed.on('focus blur', function (e) { - $(ed.getContainer()).toggleClass('focused'); - }); + // add handler for spellcheck button state update + ed.on('SpellcheckStart SpellcheckEnd', function (args) { + ref.spellcheck_active = args.type == 'spellcheckstart'; + ref.spellcheck_observer(); + }); + ed.on('keypress', function () { + rcmail.compose_type_activity++; + }); + ed.on('PastePostProcess', function (e) { + $(e.node).find('img').each(function () { + var id = 'i' + Date.now(); - if (conf.setup_callback) { - conf.setup_callback(ed); - } - }; + // No referrer for privacy + this.referrerPolicy = 'no-referrer'; + + // Need an attribute to find the image element after paste + this.setAttribute('data-img-id', id); + + // Replace the 'src' with data: URI after the image has been loaded + // This way if we fetch the image again the browser will fetch it from cache + this.onload = function () { + this.onload = null; ref.replace_img_src(this.src, id); + }; + }); + }); + // make links open on shift-click + ed.on('click', function (e) { + var link = $(e.target).closest('a'); + if (link.length && e.shiftKey) { + window.open(link.get(0).href, '_blank'); + return false; + } + }); + ed.on('focus blur', function (e) { + $(ed.getContainer()).toggleClass('focused'); + }); - rcmail.triggerEvent('editor-init', { config: conf, ref: ref, id: id }); + if (conf.setup_callback) { + conf.setup_callback(ed); + } + }; - // textarea identifier - this.id = id; - // reference to active editor (if in HTML mode) - this.editor = null; + rcmail.triggerEvent('editor-init', { config: conf, ref: ref, id: id }); - tinymce.init(conf); + // textarea identifier + this.id = id; + // reference to active editor (if in HTML mode) + this.editor = null; + + tinymce.init(conf); + }; // react to real individual tinyMCE editor init this.init_callback = function (editor) { @@ -947,4 +954,26 @@ function rcube_text_editor(config, id) { }); }); }; + + this.googiespell_init = function () { + // Don't initialize if it's already present or dependencies are not met. + if (window.googie || !window.GoogieSpell || !rcmail.env.googiespell_asset_url) { + return; + } + window.googie = new window.GoogieSpell( + rcmail.env.googiespell_asset_url + '/images/googiespell/', + rcmail.env.googiespell_base_url + '&lang=', + rcmail.env.googiespell_use_dict + ); + googie.lang_chck_spell = rcmail.env.googiespell_lang_chck_spell; + googie.lang_rsm_edt = rcmail.env.googiespell_lang_rsm_edt; + googie.lang_close = rcmail.env.googiespell_lang_close; + googie.lang_revert = rcmail.env.googiespell_lang_revert; + googie.lang_no_error_found = rcmail.env.googiespell_lang_no_error_found; + googie.lang_learn_word = rcmail.env.googiespell_lang_learn_word; + googie.setLanguages(rcmail.env.googiespell_languages); + googie.setCurrentLanguage(rcmail.env.googiespell_currentLanguage); + googie.setDecoration(false); + googie.decorateTextarea(rcmail.env.composebody); + }; } diff --git a/program/js/googiespell_init.js b/program/js/googiespell_init.js deleted file mode 100644 index 556ea1e4047..00000000000 --- a/program/js/googiespell_init.js +++ /dev/null @@ -1,17 +0,0 @@ -rcube_webmail.prototype.googiespell_init = function (asseturl, baseurl, use_dict, lang_chck_spell, lang_rsm_edt, lang_close, lang_revert, lang_no_error_found, lang_learn_word, languages, currentLanguage) { - window.googie = new GoogieSpell( - asseturl + '/images/googiespell/', - baseurl + '&lang=', - use_dict - ); - googie.lang_chck_spell = lang_chck_spell; - googie.lang_rsm_edt = lang_rsm_edt; - googie.lang_close = lang_close; - googie.lang_revert = lang_revert; - googie.lang_no_error_found = lang_no_error_found; - googie.lang_learn_word = lang_learn_word; - googie.setLanguages(languages); - googie.setCurrentLanguage(currentLanguage); - googie.setDecoration(false); - googie.decorateTextarea(rcmail.env.composebody); -}; From b70065f239c2ea88f3259b92beac155a7b8d8baf Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Tue, 9 Jul 2024 15:27:02 +0200 Subject: [PATCH 13/56] Remove redundant json-encoding --- program/actions/mail/compose.php | 2 +- program/actions/settings/index.php | 2 +- program/include/rcmail_action.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/program/actions/mail/compose.php b/program/actions/mail/compose.php index 5d8393d8389..75accb17df4 100644 --- a/program/actions/mail/compose.php +++ b/program/actions/mail/compose.php @@ -1239,7 +1239,7 @@ public static function write_forward_attachments() 'name' => 'msgsizeerrorfwd', 'vars' => ['num' => $size_errors, 'size' => $limit], ]); - $rcmail->output->command('display_message', rcube::JQ($error), 'error'); + $rcmail->output->command('display_message', $error, 'error'); } } diff --git a/program/actions/settings/index.php b/program/actions/settings/index.php index e6f387baf06..5525d386e1f 100644 --- a/program/actions/settings/index.php +++ b/program/actions/settings/index.php @@ -371,7 +371,7 @@ public static function user_prefs($current = null) if ($current) { $product_name = $rcmail->config->get('product_name', 'Roundcube Webmail'); - $rcmail->output->command('check_protocol_handler', rcube::JQ($product_name), '#mailtoprotohandler'); + $rcmail->output->command('check_protocol_handler', $product_name, '#mailtoprotohandler'); } $blocks['browser']['options']['mailtoprotohandler'] = [ diff --git a/program/include/rcmail_action.php b/program/include/rcmail_action.php index 4a3b0b1587d..003d0b47a86 100644 --- a/program/include/rcmail_action.php +++ b/program/include/rcmail_action.php @@ -171,7 +171,7 @@ public static function quota_display($attrib) $quota = self::quota_content($attrib); $rcmail->output->add_gui_object('quotadisplay', $attrib['id']); - $rcmail->output->command('set_quota', rcube_output::json_serialize($quota)); + $rcmail->output->command('set_quota', $quota); return html::span($attrib, ' '); } From 11ccc83177b03a79c2a861d02024e203ac94a779 Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Tue, 9 Jul 2024 18:04:40 +0200 Subject: [PATCH 14/56] Use data-attribute for js-data to avoid getting data encoded None of the methods to get the content of a DOM element (`.innerHTML`, `.innerText`, ...) return the content unchanged as it went over the wire. Using a data-attribute we can achieve that and need not to worry anymore about which solution will encode which value and thus break a feature. --- program/include/rcmail_output_html.php | 2 +- program/js/app.js | 2 +- tests/Actions/Contacts/EditTest.php | 2 +- tests/Actions/Contacts/ImportTest.php | 2 +- tests/Actions/Settings/FolderCreateTest.php | 2 +- tests/Actions/Settings/FolderSaveTest.php | 6 +++--- tests/Actions/Settings/IdentityCreateTest.php | 2 +- tests/Actions/Settings/IdentityEditTest.php | 2 +- tests/Actions/Settings/PrefsEditTest.php | 2 +- tests/Actions/Settings/ResponseCreateTest.php | 2 +- tests/Actions/Settings/ResponseEditTest.php | 2 +- 11 files changed, 13 insertions(+), 13 deletions(-) diff --git a/program/include/rcmail_output_html.php b/program/include/rcmail_output_html.php index b8b2ea15013..a4f121e6d44 100644 --- a/program/include/rcmail_output_html.php +++ b/program/include/rcmail_output_html.php @@ -1982,7 +1982,7 @@ protected function _write($output = '') $page_header .= array_reduce((array) $this->script_files['head_bottom'], $merge_script_files); } - $page_footer .= html::div(['id' => 'js-data', 'style' => 'display: none', 'hidden' => true], $this->get_js_commands()); + $page_footer .= html::div(['id' => 'js-data', 'style' => 'display: none', 'hidden' => true, 'data-js' => $this->get_js_commands()], ''); $page_footer .= $this->footer . "\n"; diff --git a/program/js/app.js b/program/js/app.js index fe4ceb1b06e..0db27a69da1 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -810,7 +810,7 @@ function rcube_webmail() { this.interpret_js_data = function () { // Do not use `.textContent`, and neither jQuery's `.text()` here, // because both modify the actual string! - var raw = $('#js-data').html(); + var raw = document.getElementById('js-data').dataset.js; if (!raw) { return; } diff --git a/tests/Actions/Contacts/EditTest.php b/tests/Actions/Contacts/EditTest.php index f65981fa640..a7b08e351f1 100644 --- a/tests/Actions/Contacts/EditTest.php +++ b/tests/Actions/Contacts/EditTest.php @@ -40,7 +40,7 @@ public function test_run_edit_mode() $this->assertSame('Edit contact', $output->getProperty('pagetitle')); $this->assertSame($contact['contact_id'], $output->get_env('cid')); $this->assertTrue(stripos($result, '') === 0); - $this->assertTrue(strpos($result, '["gui_object","contactphoto","contactpic"]') !== false); + $this->assertTrue(strpos($result, htmlentities('["gui_object","contactphoto","contactpic"]')) !== false); } /** diff --git a/tests/Actions/Contacts/ImportTest.php b/tests/Actions/Contacts/ImportTest.php index fadfc2b556e..c9a1f5cb4b2 100644 --- a/tests/Actions/Contacts/ImportTest.php +++ b/tests/Actions/Contacts/ImportTest.php @@ -32,7 +32,7 @@ public function test_run_init() $this->assertSame('contactimport', $output->template); $this->assertSame('Import contacts', $output->getProperty('pagetitle')); $this->assertTrue(stripos($result, '') === 0); - $this->assertTrue(strpos($result, '["gui_object","importform","rcmImportForm"]') !== false); + $this->assertTrue(strpos($result, htmlentities('["gui_object","importform","rcmImportForm"]')) !== false); } /** diff --git a/tests/Actions/Settings/FolderCreateTest.php b/tests/Actions/Settings/FolderCreateTest.php index a94b70d74d0..5c196d76fb5 100644 --- a/tests/Actions/Settings/FolderCreateTest.php +++ b/tests/Actions/Settings/FolderCreateTest.php @@ -50,6 +50,6 @@ public function test_run() $this->assertSame('folderedit', $output->template); $this->assertSame('', $output->getProperty('pagetitle')); // TODO: It should have some title $this->assertTrue(stripos($result, '') === 0); - $this->assertTrue(strpos($result, '["gui_object","editform","form"]') !== false); + $this->assertTrue(strpos($result, htmlentities('["gui_object","editform","form"]')) !== false); } } diff --git a/tests/Actions/Settings/FolderSaveTest.php b/tests/Actions/Settings/FolderSaveTest.php index 0a4eabbf3ec..ddb49df1ac4 100644 --- a/tests/Actions/Settings/FolderSaveTest.php +++ b/tests/Actions/Settings/FolderSaveTest.php @@ -40,9 +40,9 @@ public function test_new_folder() $this->assertSame('iframe', $output->template); $this->assertTrue(stripos($result, '') === 0); - $this->assertTrue(strpos($result, '["parent.display_message","Folder created successfully.","confirmation",0]') !== false); - $this->assertTrue(strpos($result, '["parent.add_folder_row","NewTest",') !== false); - $this->assertTrue(strpos($result, '["parent.subscription_select"]') !== false); + $this->assertTrue(strpos($result, htmlentities('["parent.display_message","Folder created successfully.","confirmation",0]')) !== false); + $this->assertTrue(strpos($result, htmlentities('["parent.add_folder_row","NewTest",')) !== false); + $this->assertTrue(strpos($result, htmlentities('["parent.subscription_select"]')) !== false); } /** diff --git a/tests/Actions/Settings/IdentityCreateTest.php b/tests/Actions/Settings/IdentityCreateTest.php index 29d272d1832..26518b2ff77 100644 --- a/tests/Actions/Settings/IdentityCreateTest.php +++ b/tests/Actions/Settings/IdentityCreateTest.php @@ -28,6 +28,6 @@ public function test_run() $this->assertSame('identityedit', $output->template); $this->assertSame('Add identity', $output->getProperty('pagetitle')); $this->assertTrue(stripos($result, '') === 0); - $this->assertTrue(strpos($result, '["gui_object","editform","form"]') !== false); + $this->assertTrue(strpos($result, htmlentities('["gui_object","editform","form"]')) !== false); } } diff --git a/tests/Actions/Settings/IdentityEditTest.php b/tests/Actions/Settings/IdentityEditTest.php index ab5509c9f5c..805a98df8a9 100644 --- a/tests/Actions/Settings/IdentityEditTest.php +++ b/tests/Actions/Settings/IdentityEditTest.php @@ -37,7 +37,7 @@ public function test_run() $this->assertSame('Edit identity', $output->getProperty('pagetitle')); $this->assertSame($identity['identity_id'], $output->get_env('iid')); $this->assertTrue(stripos($result, '') === 0); - $this->assertTrue(strpos($result, '["gui_object","editform","form"]') !== false); + $this->assertTrue(strpos($result, htmlentities('["gui_object","editform","form"]')) !== false); $this->assertTrue(strpos($result, 'test@example.com') !== false); // TODO: Test error handling diff --git a/tests/Actions/Settings/PrefsEditTest.php b/tests/Actions/Settings/PrefsEditTest.php index 3f1a706d096..71339816f25 100644 --- a/tests/Actions/Settings/PrefsEditTest.php +++ b/tests/Actions/Settings/PrefsEditTest.php @@ -30,6 +30,6 @@ public function test_run() $this->assertSame('settingsedit', $output->template); $this->assertSame('Preferences', $output->getProperty('pagetitle')); $this->assertTrue(stripos($result, '') === 0); - $this->assertTrue(strpos($result, '["gui_object","editform","form"]') !== false); + $this->assertTrue(strpos($result, htmlentities('["gui_object","editform","form"]')) !== false); } } diff --git a/tests/Actions/Settings/ResponseCreateTest.php b/tests/Actions/Settings/ResponseCreateTest.php index d39caa74416..0f25868e78f 100644 --- a/tests/Actions/Settings/ResponseCreateTest.php +++ b/tests/Actions/Settings/ResponseCreateTest.php @@ -31,6 +31,6 @@ public function test_run() $this->assertSame('Add response', $output->getProperty('pagetitle')); $this->assertFalse($output->get_env('readonly')); $this->assertTrue(stripos($result, '') === 0); - $this->assertTrue(strpos($result, '["gui_object","editform","form"]') !== false); + $this->assertTrue(strpos($result, htmlentities('["gui_object","editform","form"]')) !== false); } } diff --git a/tests/Actions/Settings/ResponseEditTest.php b/tests/Actions/Settings/ResponseEditTest.php index 953018f361a..2dcab5c7f91 100644 --- a/tests/Actions/Settings/ResponseEditTest.php +++ b/tests/Actions/Settings/ResponseEditTest.php @@ -43,7 +43,7 @@ public function test_run() $this->assertSame('Edit response', $output->getProperty('pagetitle')); $this->assertTrue($output->get_env('readonly')); $this->assertTrue(stripos($result, '') === 0); - $this->assertTrue(strpos($result, '["gui_object","editform","form"]') !== false); + $this->assertTrue(strpos($result, htmlentities('["gui_object","editform","form"]')) !== false); $this->assertTrue(strpos($result, 'tinymce.min.js') !== false); $this->assertTrue(strpos($result, 'Static Response One') !== false); From 125f992c2838cee49bae38947318ebdcb5574639 Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Wed, 10 Jul 2024 11:13:23 +0200 Subject: [PATCH 15/56] Clean up spellchecker initialization We don't need the global variable, so we got rid of it. Also since we're passing the config details encoded anyway, we can also use an array instead of a list of key-value pairs, and we can skip the quoting, too. --- program/actions/mail/compose.php | 24 +++++++++++--------- program/js/app.js | 4 ++-- program/js/editor.js | 38 ++++++++++++++++---------------- 3 files changed, 34 insertions(+), 32 deletions(-) diff --git a/program/actions/mail/compose.php b/program/actions/mail/compose.php index 75accb17df4..29d2e94df59 100644 --- a/program/actions/mail/compose.php +++ b/program/actions/mail/compose.php @@ -513,17 +513,19 @@ public static function spellchecker_init() // include GoogieSpell $rcmail->output->include_script('googiespell.js'); - $rcmail->output->set_env('googiespell_asset_url', $rcmail->output->asset_url($rcmail->output->get_skin_path())); - $rcmail->output->set_env('googiespell_base_url', $rcmail->url(['_task' => 'utils', '_action' => 'spell', '_remote' => 1])); - $rcmail->output->set_env('googiespell_use_dict', !empty($dictionary) ? 'true' : 'false'); - $rcmail->output->set_env('googiespell_lang_chck_spell', rcube::JQ(rcube::Q($rcmail->gettext('checkspelling')))); - $rcmail->output->set_env('googiespell_lang_rsm_edt', rcube::JQ(rcube::Q($rcmail->gettext('resumeediting')))); - $rcmail->output->set_env('googiespell_lang_close', rcube::JQ(rcube::Q($rcmail->gettext('close')))); - $rcmail->output->set_env('googiespell_lang_revert', rcube::JQ(rcube::Q($rcmail->gettext('revertto')))); - $rcmail->output->set_env('googiespell_lang_no_error_found', rcube::JQ(rcube::Q($rcmail->gettext('nospellerrors')))); - $rcmail->output->set_env('googiespell_lang_learn_word', rcube::JQ(rcube::Q($rcmail->gettext('addtodict')))); - $rcmail->output->set_env('googiespell_languages', $spellcheck_langs); - $rcmail->output->set_env('googiespell_currentLanguage', $lang); + $rcmail->output->set_env('googiespell_conf', [ + 'asset_url' => $rcmail->output->asset_url($rcmail->output->get_skin_path()), + 'base_url' => $rcmail->url(['_task' => 'utils', '_action' => 'spell', '_remote' => 1]), + 'use_dict' => !empty($dictionary) ? 'true' : 'false', + 'lang_chck_spell' => $rcmail->gettext('checkspelling'), + 'lang_rsm_edt' => $rcmail->gettext('resumeediting'), + 'lang_close' => $rcmail->gettext('close'), + 'lang_revert' => $rcmail->gettext('revertto'), + 'lang_no_error_found' => $rcmail->gettext('nospellerrors'), + 'lang_learn_word' => $rcmail->gettext('addtodict'), + 'languages' => $spellcheck_langs, + 'currentLanguage' => $lang, + ]); $rcmail->output->add_label('checking'); $rcmail->output->set_env('spell_langs', $spellcheck_langs); diff --git a/program/js/app.js b/program/js/app.js index 0db27a69da1..960df12f4e4 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -424,7 +424,7 @@ function rcube_webmail() { // initialize HTML editor this.editor_init(null, this.env.composebody); - if (window.googie) { + if (this.editor.spellchecker) { this.env.compose_commands.push('spellcheck'); this.enable_command('spellcheck', true); } @@ -4176,7 +4176,7 @@ function rcube_webmail() { // re-enable commands that operate on the compose body ref.enable_command('toggle-editor', 'insert-response', true); - ref.enable_command('spellcheck', !!window.googie); + ref.enable_command('spellcheck', !!ref.editor.spellchecker); ref.enable_command('insert-sig', !!(ref.env.signatures && ref.env.identity && ref.env.signatures[ref.env.identity])); ref.triggerEvent('compose-encrypted', { active: false }); diff --git a/program/js/editor.js b/program/js/editor.js index 8592a4932ab..c9171731f7d 100644 --- a/program/js/editor.js +++ b/program/js/editor.js @@ -78,15 +78,12 @@ function rcube_text_editor(config, id) { }; this.init = function () { - this.googiespell_init(); - if (window.googie) { - config.spellchecker = window.googie; - } + var googie = this.googiespell_init(); // register spellchecker for plain text editor this.spellcheck_observer = function () {}; - if (config.spellchecker) { - this.spellchecker = config.spellchecker; + if (googie) { + this.spellchecker = googie; if (config.spellcheck_observer) { this.spellchecker.spelling_state_observer = this.spellcheck_observer = config.spellcheck_observer; } @@ -957,23 +954,26 @@ function rcube_text_editor(config, id) { this.googiespell_init = function () { // Don't initialize if it's already present or dependencies are not met. - if (window.googie || !window.GoogieSpell || !rcmail.env.googiespell_asset_url) { + if (!window.GoogieSpell || !rcmail.env.googiespell_conf) { return; } - window.googie = new window.GoogieSpell( - rcmail.env.googiespell_asset_url + '/images/googiespell/', - rcmail.env.googiespell_base_url + '&lang=', - rcmail.env.googiespell_use_dict + var conf = rcmail.env.googiespell_conf; + var googie = new window.GoogieSpell( + conf.asset_url + '/images/googiespell/', + conf.base_url + '&lang=', + conf.use_dict ); - googie.lang_chck_spell = rcmail.env.googiespell_lang_chck_spell; - googie.lang_rsm_edt = rcmail.env.googiespell_lang_rsm_edt; - googie.lang_close = rcmail.env.googiespell_lang_close; - googie.lang_revert = rcmail.env.googiespell_lang_revert; - googie.lang_no_error_found = rcmail.env.googiespell_lang_no_error_found; - googie.lang_learn_word = rcmail.env.googiespell_lang_learn_word; - googie.setLanguages(rcmail.env.googiespell_languages); - googie.setCurrentLanguage(rcmail.env.googiespell_currentLanguage); + googie.lang_chck_spell = conf.lang_chck_spell; + googie.lang_rsm_edt = conf.lang_rsm_edt; + googie.lang_close = conf.lang_close; + googie.lang_revert = conf.lang_revert; + googie.lang_no_error_found = conf.lang_no_error_found; + googie.lang_learn_word = conf.lang_learn_word; + googie.setLanguages(conf.languages); + googie.setCurrentLanguage(conf.currentLanguage); googie.setDecoration(false); googie.decorateTextarea(rcmail.env.composebody); + + return googie; }; } From 56e3eaab9086686244fdc069174ee68767192a7d Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Wed, 17 Jul 2024 14:14:21 +0200 Subject: [PATCH 16/56] Strip check for 'plugin.'-prefix of JS-calls This is done in rcmail_output_html already. --- program/js/app.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/program/js/app.js b/program/js/app.js index 960df12f4e4..4e210fcc36d 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -820,11 +820,6 @@ function rcube_webmail() { this.log("Unexpected data in '#js-data'! This is not an array: ", args); } var command = args.shift(); - if (command.startsWith('plugin.')) { - // Plugin code is reached via events. - this.triggerEvent(command, args); - return; - } if (command.startsWith('parent.')) { command = command.replace(/^parent\./, ''); if (typeof parent.rcmail[command] !== 'function') { From f8795642a4e805fa0cfb039b7c7cd8d4ff8b1ac3 Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Wed, 17 Jul 2024 09:08:32 +0200 Subject: [PATCH 17/56] Trigger JS only via methods on rcmail, not events Having only one way results in clearer code, and using the "plugin."-prefix for triggering events isn't obvious, either. Tacking all function calls onto rcmail/rcube_webmail.prototype maybe isn't the best pattern either, because that object/prototype gets huge, but it's established in the code base, so it isn't suprising. Another advantage is that trying to call a missing functions fails loudly, whereas triggering an event that nothing listens for doesn't produce any error. The functionality is still present to not break plugins, but our own code doesn't use it anymore. --- plugins/jqueryui/jqueryui.php | 2 +- .../jqueryui/js/jqueryui-minicolors-init.js | 4 +- plugins/markasjunk/drivers/jsevent.php | 2 +- plugins/markasjunk/markasjunk.js | 52 +++++++++---------- plugins/new_user_dialog/new_user_dialog.js | 14 ++--- plugins/new_user_dialog/new_user_dialog.php | 2 +- plugins/newmail_notifier/newmail_notifier.js | 15 ++---- plugins/newmail_notifier/newmail_notifier.php | 2 +- program/js/app.js | 5 +- 9 files changed, 47 insertions(+), 51 deletions(-) diff --git a/plugins/jqueryui/jqueryui.php b/plugins/jqueryui/jqueryui.php index 639b8eb3a36..f856a8483c7 100644 --- a/plugins/jqueryui/jqueryui.php +++ b/plugins/jqueryui/jqueryui.php @@ -121,7 +121,7 @@ public static function miniColors() $rcube->output->include_css('plugins/jqueryui/' . $css); $rcube->output->include_script($script, 'head', false); $rcube->output->include_script('plugins/jqueryui/js/jqueryui-minicolors-init.js'); - $rcube->output->command('plugin.jqueryui-minicolors-init', $config_str); + $rcube->output->command('jqueryui_minicolors_init', $config_str); $rcube->output->set_env('minicolors_config', $config); } diff --git a/plugins/jqueryui/js/jqueryui-minicolors-init.js b/plugins/jqueryui/js/jqueryui-minicolors-init.js index fe591d26a6f..e5ba4c2feb5 100644 --- a/plugins/jqueryui/js/jqueryui-minicolors-init.js +++ b/plugins/jqueryui/js/jqueryui-minicolors-init.js @@ -1,4 +1,4 @@ -rcmail.addEventListener('jqueryui-minicolors-init', function (config) { +rcube_webmail.prototype.jqueryui_minicolors_init = function (config) { $.fn.miniColors = $.fn.minicolors; $("input.colors").minicolors(config); -}); +} diff --git a/plugins/markasjunk/drivers/jsevent.php b/plugins/markasjunk/drivers/jsevent.php index b29955a8817..8a3201ea44b 100644 --- a/plugins/markasjunk/drivers/jsevent.php +++ b/plugins/markasjunk/drivers/jsevent.php @@ -42,7 +42,7 @@ public function init() return; } - $rcmail->output->command('plugin.markasjunk-init', $this->addition_spam_folders, $this->suspicious_folders); + $rcmail->output->command('markasjunk_init', $this->addition_spam_folders, $this->suspicious_folders); } public function spam(&$uids, $mbox) diff --git a/plugins/markasjunk/markasjunk.js b/plugins/markasjunk/markasjunk.js index 9ffa751ea5b..e785b306065 100644 --- a/plugins/markasjunk/markasjunk.js +++ b/plugins/markasjunk/markasjunk.js @@ -95,6 +95,32 @@ rcube_webmail.prototype.markasjunk_is_spam_mbox = function () { return !this.is_multifolder_listing() && this.env.mailbox == this.env.markasjunk_spam_mailbox; }; +rcube_webmail.prototype.markasjunk_init = function (addition_spam_folders, suspicious_folders) { + rcmail.addEventListener('markasjunk-update', function (props) { + // ignore this special code when in a multifolder listing + if (rcmail.is_multifolder_listing()) { + return; + } + + if ($.inArray(rcmail.env.mailbox, addition_spam_folders) > -1) { + props.disp.spam = false; + props.disp.ham = true; + } + else if ($.inArray(rcmail.env.mailbox, suspicious_folders) > -1) { + props.disp.spam = true; + props.disp.ham = true; + + // from here it is also possible to alter the buttons themselves... + props.objs.spamobj.find('a > span').text('As possibly spam'); + } + else { + props.objs.spamobj.find('a > span').text(rcmail.get_label('markasjunk.markasjunk')); + } + + return props; + }); +} + if (window.rcmail) { rcmail.addEventListener('init', function () { // register command (directly enable in message view mode) @@ -145,30 +171,4 @@ if (window.rcmail) { return false; } }); - - rcmail.addEventListener('plugin.markasjunk-init', function (addition_spam_folders, suspicious_folders) { - rcmail.addEventListener('markasjunk-update', function (props) { - // ignore this special code when in a multifolder listing - if (rcmail.is_multifolder_listing()) { - return; - } - - if ($.inArray(rcmail.env.mailbox, addition_spam_folders) > -1) { - props.disp.spam = false; - props.disp.ham = true; - } - else if ($.inArray(rcmail.env.mailbox, suspicious_folders) > -1) { - props.disp.spam = true; - props.disp.ham = true; - - // from here it is also possible to alter the buttons themselves... - props.objs.spamobj.find('a > span').text('As possibly spam'); - } - else { - props.objs.spamobj.find('a > span').text(rcmail.get_label('markasjunk.markasjunk')); - } - - return props; - }); - }); } diff --git a/plugins/new_user_dialog/new_user_dialog.js b/plugins/new_user_dialog/new_user_dialog.js index aeca0771c67..eb50f95b51e 100644 --- a/plugins/new_user_dialog/new_user_dialog.js +++ b/plugins/new_user_dialog/new_user_dialog.js @@ -1,5 +1,5 @@ -rcmail.addEventListener('plugin.new_user_dialog_open', function (title) { - var newuserdialog = rcmail.show_popup_dialog($('#newuserdialog'), title, [{ +rcube_webmail.prototype.new_user_dialog_open = function (title) { + window.newuserdialog = rcmail.show_popup_dialog($('#newuserdialog'), title, [{ text: rcmail.get_label('save'), class: 'mainaction save', click: function () { @@ -22,8 +22,10 @@ rcmail.addEventListener('plugin.new_user_dialog_open', function (title) { }, } ); +} - rcmail.addEventListener('plugin.new_user_dialog_close', function (title) { - newuserdialog.dialog('destroy'); - }); -}); +rcube_webmail.prototype.new_user_dialog_close = function (title) { + if (window.newuserdialog) { + window.newuserdialog.dialog('destroy'); + } +} diff --git a/plugins/new_user_dialog/new_user_dialog.php b/plugins/new_user_dialog/new_user_dialog.php index 599bc6eb54f..7ca27960f54 100644 --- a/plugins/new_user_dialog/new_user_dialog.php +++ b/plugins/new_user_dialog/new_user_dialog.php @@ -161,7 +161,7 @@ public function save_data() // save prefs to not show dialog again $rcmail->user->save_prefs(['newuserdialog' => null]); // hide dialog - $rcmail->output->command('plugin.new_user_dialog_close'); + $rcmail->output->command('new_user_dialog_close'); $rcmail->output->show_message('successfullysaved', 'confirmation'); } else { // show error diff --git a/plugins/newmail_notifier/newmail_notifier.js b/plugins/newmail_notifier/newmail_notifier.js index f8e1edd52c0..c7e4afd5233 100644 --- a/plugins/newmail_notifier/newmail_notifier.js +++ b/plugins/newmail_notifier/newmail_notifier.js @@ -18,8 +18,7 @@ */ if (window.rcmail && rcmail.env.task == 'mail') { - rcmail.addEventListener('plugin.newmail_notifier', newmail_notifier_run) - .addEventListener('actionbefore', newmail_notifier_stop) + rcmail.addEventListener('actionbefore', newmail_notifier_stop) .addEventListener('init', function () { // bind to messages list select event, so favicon will be reverted on message preview too if (rcmail.message_list) { @@ -29,7 +28,7 @@ if (window.rcmail && rcmail.env.task == 'mail') { } // Executes notification methods -function newmail_notifier_run(prop) { +rcube_webmail.prototype.newmail_notifier_run = function (prop) { if (prop.basic) { newmail_notifier_basic(); } @@ -133,7 +132,7 @@ function newmail_notifier_desktop(body, disabled_callback) { } } -function newmail_notifier_test_desktop() { +rcube_webmail.prototype.newmail_notifier_test_desktop = function () { var status = newmail_notifier_desktop(rcmail.get_label('testbody', 'newmail_notifier'), function () { rcmail.display_message(rcmail.get_label('desktopdisabled', 'newmail_notifier'), 'error'); }); @@ -143,10 +142,6 @@ function newmail_notifier_test_desktop() { } } -function newmail_notifier_test_basic() { - newmail_notifier_basic(); -} +rcube_webmail.prototype.newmail_notifier_test_basic = newmail_notifier_basic; -function newmail_notifier_test_sound() { - newmail_notifier_sound(); -} +rcube_webmail.prototype.newmail_notifier_test_sound = newmail_notifier_sound; diff --git a/plugins/newmail_notifier/newmail_notifier.php b/plugins/newmail_notifier/newmail_notifier.php index cccf0fe207f..287744912cd 100644 --- a/plugins/newmail_notifier/newmail_notifier.php +++ b/plugins/newmail_notifier/newmail_notifier.php @@ -204,7 +204,7 @@ public function notify($args) $this->notified = true; $this->rc->output->set_env('newmail_notifier_timeout', $this->rc->config->get('newmail_notifier_desktop_timeout')); - $this->rc->output->command('plugin.newmail_notifier', + $this->rc->output->command('newmail_notifier_run', [ 'basic' => $this->opt['basic'], 'sound' => $this->opt['sound'], diff --git a/program/js/app.js b/program/js/app.js index 4e210fcc36d..0014f45e3cf 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -803,9 +803,8 @@ function rcube_webmail() { }; /** - * Handle "commands" passed in via #js-data. Through this, server code can - * trigger Javascript-methods to be called or events to be thrown (for - * plugin JS-code). + * Handle function calls passed in via #js-calls. Through this, server code + * can trigger Javascript-methods to be called. */ this.interpret_js_data = function () { // Do not use `.textContent`, and neither jQuery's `.text()` here, From 00ed0d866ea8613814e2df749f92796f0f86b9e7 Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Wed, 17 Jul 2024 09:14:55 +0200 Subject: [PATCH 18/56] Rename rcmail_output_html's `commands` to `js_calls` It's a more telling name, and avoids confusion with the `commands` in app.js. The previous method name is still available to avoid breaking plugin code, but it is marked as deprecated. --- index.php | 2 +- plugins/acl/acl.php | 8 ++-- plugins/archive/archive.php | 12 +++--- plugins/enigma/lib/enigma_ui.php | 24 +++++------ plugins/jqueryui/jqueryui.php | 2 +- .../lib/Roundcube/rcube_sieve_engine.php | 32 +++++++-------- plugins/managesieve/managesieve.php | 4 +- plugins/markasjunk/drivers/edit_headers.php | 2 +- plugins/markasjunk/drivers/jsevent.php | 2 +- plugins/markasjunk/drivers/sa_detach.php | 2 +- plugins/markasjunk/markasjunk.php | 6 +-- plugins/new_user_dialog/new_user_dialog.php | 4 +- plugins/newmail_notifier/newmail_notifier.php | 2 +- plugins/password/password.php | 26 ++++++------ .../vcard_attachments/vcard_attachments.php | 6 +-- program/actions/contacts/delete.php | 10 ++--- program/actions/contacts/group_create.php | 2 +- program/actions/contacts/group_delete.php | 2 +- program/actions/contacts/group_delmembers.php | 2 +- program/actions/contacts/group_rename.php | 2 +- program/actions/contacts/import.php | 6 +-- program/actions/contacts/index.php | 2 +- program/actions/contacts/list.php | 4 +- program/actions/contacts/mailto.php | 2 +- program/actions/contacts/move.php | 8 ++-- program/actions/contacts/save.php | 12 +++--- program/actions/contacts/search.php | 12 +++--- program/actions/contacts/search_create.php | 2 +- program/actions/contacts/search_delete.php | 4 +- program/actions/contacts/undo.php | 4 +- program/actions/contacts/upload_photo.php | 4 +- program/actions/mail/addcontact.php | 2 +- program/actions/mail/attachment_delete.php | 2 +- program/actions/mail/attachment_rename.php | 2 +- program/actions/mail/attachment_upload.php | 18 ++++---- program/actions/mail/autocomplete.php | 2 +- program/actions/mail/check_recent.php | 10 ++--- program/actions/mail/compose.php | 2 +- program/actions/mail/copy.php | 2 +- program/actions/mail/delete.php | 12 +++--- program/actions/mail/folder_expunge.php | 4 +- program/actions/mail/folder_purge.php | 12 +++--- program/actions/mail/getunread.php | 4 +- program/actions/mail/group_expand.php | 2 +- program/actions/mail/headers.php | 2 +- program/actions/mail/import.php | 2 +- program/actions/mail/index.php | 10 ++--- program/actions/mail/list.php | 8 ++-- program/actions/mail/list_contacts.php | 10 ++--- program/actions/mail/mark.php | 10 ++--- program/actions/mail/move.php | 16 ++++---- program/actions/mail/pagenav.php | 8 ++-- program/actions/mail/search.php | 8 ++-- program/actions/mail/search_contacts.php | 6 +-- program/actions/mail/send.php | 14 +++---- program/actions/mail/show.php | 2 +- program/actions/settings/folder_delete.php | 6 +-- program/actions/settings/folder_edit.php | 2 +- program/actions/settings/folder_purge.php | 4 +- program/actions/settings/folder_save.php | 6 +-- program/actions/settings/folder_size.php | 2 +- program/actions/settings/folder_subscribe.php | 4 +- .../actions/settings/folder_unsubscribe.php | 2 +- program/actions/settings/folders.php | 4 +- program/actions/settings/identity_delete.php | 2 +- program/actions/settings/identity_save.php | 4 +- program/actions/settings/index.php | 2 +- program/actions/settings/prefs_save.php | 4 +- program/actions/settings/response_delete.php | 4 +- program/actions/settings/response_get.php | 2 +- program/actions/settings/response_save.php | 4 +- program/actions/settings/upload.php | 4 +- program/include/rcmail_action.php | 10 ++--- program/include/rcmail_output.php | 2 +- program/include/rcmail_output_cli.php | 2 +- program/include/rcmail_output_html.php | 41 +++++++++++++------ program/include/rcmail_output_json.php | 39 ++++++++++++------ program/js/app.js | 8 ++-- tests/Actions/Contacts/EditTest.php | 4 +- tests/Actions/Mail/FolderExpungeTest.php | 10 ++--- tests/Actions/Mail/IndexTest.php | 6 +-- tests/Rcmail/OutputJsonTest.php | 6 +-- 82 files changed, 298 insertions(+), 268 deletions(-) diff --git a/index.php b/index.php index 83d9be2f9e9..5cb04c169fe 100644 --- a/index.php +++ b/index.php @@ -226,7 +226,7 @@ } if ($RCMAIL->output->ajax_call || $RCMAIL->output->get_env('framed')) { - $RCMAIL->output->command('session_error', $RCMAIL->url(['_err' => 'session'])); + $RCMAIL->output->add_js_call('session_error', $RCMAIL->url(['_err' => 'session'])); $RCMAIL->output->send('iframe'); } diff --git a/plugins/acl/acl.php b/plugins/acl/acl.php index 20aff988d1f..d86f750b222 100644 --- a/plugins/acl/acl.php +++ b/plugins/acl/acl.php @@ -135,7 +135,7 @@ public function acl_autocomplete() $users = array_values($keys); } - $this->rc->output->command('ksearch_query_results', $users, $search, $reqid); + $this->rc->output->add_js_call('ksearch_query_results', $users, $search, $reqid); $this->rc->output->send(); } @@ -533,7 +533,7 @@ private function action_save() if ($user != $self && $username != $self) { if ($this->rc->storage->set_acl($mbox, $user, $acl)) { $display = $this->resolve_acl_identifier($username, $title); - $this->rc->output->command('acl_update', [ + $this->rc->output->add_js_call('acl_update', [ 'id' => rcube_utils::html_identifier($user), 'username' => $username, 'title' => $title, @@ -566,7 +566,7 @@ private function action_delete() foreach ($user as $u) { $u = trim($u); if ($this->rc->storage->delete_acl($mbox, $u)) { - $this->rc->output->command('acl_remove_row', rcube_utils::html_identifier($u)); + $this->rc->output->add_js_call('acl_remove_row', rcube_utils::html_identifier($u)); } else { $error = true; } @@ -599,7 +599,7 @@ private function action_list() $out = preg_replace(['/^]+>/', '/<\/table>$/'], '', $out); - $this->rc->output->command('acl_list_update', $out); + $this->rc->output->add_js_call('acl_list_update', $out); } /** diff --git a/plugins/archive/archive.php b/plugins/archive/archive.php index e9178b97ada..3de20f27109 100644 --- a/plugins/archive/archive.php +++ b/plugins/archive/archive.php @@ -227,7 +227,7 @@ public function move_messages() // @phpstan-ignore-next-line if ($this->result['error']) { if (!$from_show_action) { - $rcmail->output->command('list_mailbox'); + $rcmail->output->add_js_call('list_mailbox'); } $rcmail->output->show_message($this->gettext('archiveerror'), 'warning'); @@ -236,7 +236,7 @@ public function move_messages() if (!empty($_POST['_refresh'])) { // FIXME: send updated message rows instead of reloading the entire list - $rcmail->output->command('refresh_list'); + $rcmail->output->add_js_call('refresh_list'); $addrows = false; } else { $addrows = true; @@ -249,9 +249,9 @@ public function move_messages() if ($from_show_action) { if ($next = rcube_utils::get_input_string('_next_uid', rcube_utils::INPUT_GPC)) { - $rcmail->output->command('show_message', $next); + $rcmail->output->add_js_call('show_message', $next); } else { - $rcmail->output->command('command', 'list'); + $rcmail->output->add_js_call('command', 'list'); } $rcmail->output->send(); @@ -287,8 +287,8 @@ public function move_messages() $rcmail->output->set_env('current_page', $page); $rcmail->output->set_env('pagecount', $pages); $rcmail->output->set_env('exists', $exists); - $rcmail->output->command('set_quota', rcmail_action::quota_content(null, $quota_root)); - $rcmail->output->command('set_rowcount', rcmail_action_mail_index::get_messagecount_text($msg_count), $mbox); + $rcmail->output->add_js_call('set_quota', rcmail_action::quota_content(null, $quota_root)); + $rcmail->output->add_js_call('set_rowcount', rcmail_action_mail_index::get_messagecount_text($msg_count), $mbox); if ($threading) { $count = rcube_utils::get_input_string('_count', rcube_utils::INPUT_POST); diff --git a/plugins/enigma/lib/enigma_ui.php b/plugins/enigma/lib/enigma_ui.php index 1eea580cace..f16a0cfd2d6 100644 --- a/plugins/enigma/lib/enigma_ui.php +++ b/plugins/enigma/lib/enigma_ui.php @@ -174,7 +174,7 @@ public function password_prompt($status, $params = []) } if (preg_match('/^(send|plugin.enigmaimport|plugin.enigmakeys)$/', $this->rc->action)) { - $this->rc->output->command('enigma_password_request', $data); + $this->rc->output->add_js_call('enigma_password_request', $data); } else { $this->rc->output->set_env('enigma_password_request', $data); } @@ -254,7 +254,7 @@ private function key_list() // Add rows foreach ($list as $key) { - $this->rc->output->command('enigma_add_list_row', [ + $this->rc->output->add_js_call('enigma_add_list_row', [ 'name' => rcube::Q($key->name), 'id' => $key->id, 'flags' => $key->is_private() ? 'p' : '', @@ -266,7 +266,7 @@ private function key_list() $this->rc->output->set_env('search_request', $search); $this->rc->output->set_env('pagecount', ceil($listsize / $pagesize)); $this->rc->output->set_env('current_page', $page); - $this->rc->output->command('set_rowcount', $this->get_rowcount_text($listsize, $size, $page)); + $this->rc->output->add_js_call('set_rowcount', $this->get_rowcount_text($listsize, $size, $page)); $this->rc->output->send(); } @@ -323,7 +323,7 @@ private function key_info() $this->data = $res; } else { // error $this->rc->output->show_message('enigma.keyopenerror', 'error'); - $this->rc->output->command('parent.enigma_loadframe'); + $this->rc->output->add_js_call('parent.enigma_loadframe'); $this->rc->output->send('iframe'); } @@ -528,14 +528,14 @@ private function key_import() if (is_array($result)) { if (rcube_utils::get_input_value('_generated', rcube_utils::INPUT_POST)) { - $this->rc->output->command('enigma_key_create_success'); + $this->rc->output->add_js_call('enigma_key_create_success'); $this->rc->output->show_message('enigma.keygeneratesuccess', 'confirmation'); } else { $this->rc->output->show_message('enigma.keysimportsuccess', 'confirmation', ['new' => $result['imported'], 'old' => $result['unchanged']]); if ($result['imported'] && !empty($_POST['_refresh'])) { - $this->rc->output->command('enigma_list', 1, false); + $this->rc->output->add_js_call('enigma_list', 1, false); } } } else { @@ -550,13 +550,13 @@ private function key_import() if (is_array($result)) { // reload list if any keys has been added if ($result['imported']) { - $this->rc->output->command('parent.enigma_list', 1); + $this->rc->output->add_js_call('parent.enigma_list', 1); } $this->rc->output->show_message('enigma.keysimportsuccess', 'confirmation', ['new' => $result['imported'], 'old' => $result['unchanged']]); - $this->rc->output->command('parent.enigma_import_success'); + $this->rc->output->add_js_call('parent.enigma_import_success'); } elseif ($result instanceof enigma_error && $result->getCode() == enigma_error::BADPASS) { $this->password_prompt($result); } else { @@ -710,7 +710,7 @@ private function key_generate() ]); if ($result instanceof enigma_key) { - $this->rc->output->command('enigma_key_create_success'); + $this->rc->output->add_js_call('enigma_key_create_success'); $this->rc->output->show_message('enigma.keygeneratesuccess', 'confirmation'); } else { $this->rc->output->show_message('enigma.keygenerateerror', 'error'); @@ -820,12 +820,12 @@ private function key_delete() if ($res !== true) { $this->rc->output->show_message('enigma.keyremoveerror', 'error'); - $this->rc->output->command('enigma_list'); + $this->rc->output->add_js_call('enigma_list'); $this->rc->output->send(); } } - $this->rc->output->command('enigma_list'); + $this->rc->output->add_js_call('enigma_list'); $this->rc->output->show_message('enigma.keyremovesuccess', 'confirmation'); $this->rc->output->send(); } @@ -1190,7 +1190,7 @@ public function message_ready($p) if (!empty($msg)) { if (!empty($vars['email'])) { - $this->rc->output->command('enigma_key_not_found', [ + $this->rc->output->add_js_call('enigma_key_not_found', [ 'email' => $vars['email'], 'text' => $this->rc->gettext(['name' => $msg, 'vars' => $vars]), 'title' => $this->enigma->gettext('keynotfound'), diff --git a/plugins/jqueryui/jqueryui.php b/plugins/jqueryui/jqueryui.php index f856a8483c7..0a3982f2e54 100644 --- a/plugins/jqueryui/jqueryui.php +++ b/plugins/jqueryui/jqueryui.php @@ -121,7 +121,7 @@ public static function miniColors() $rcube->output->include_css('plugins/jqueryui/' . $css); $rcube->output->include_script($script, 'head', false); $rcube->output->include_script('plugins/jqueryui/js/jqueryui-minicolors-init.js'); - $rcube->output->command('jqueryui_minicolors_init', $config_str); + $rcube->output->add_js_call('jqueryui_minicolors_init', $config_str); $rcube->output->set_env('minicolors_config', $config); } diff --git a/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php b/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php index 6393c4c3a0e..c4af697cb69 100644 --- a/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php +++ b/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php @@ -133,7 +133,7 @@ public function start($mode = null) // reload interface in case of possible error when specified script wasn't found (#1489412) if ($script_name !== null && !empty($list) && !in_array($script_name, $list)) { - $this->rc->output->command('reload', 500); + $this->rc->output->add_js_call('reload', 500); } // to disable 'Add filter' button set env variable @@ -306,7 +306,7 @@ public function actions() if ($result === true) { $this->rc->output->show_message('managesieve.filterdeleted', 'confirmation'); - $this->rc->output->command('managesieve_updatelist', 'del', ['id' => $fid]); + $this->rc->output->add_js_call('managesieve_updatelist', 'del', ['id' => $fid]); } else { $this->rc->output->show_message('managesieve.filterdeleteerror', 'error'); } @@ -344,7 +344,7 @@ public function actions() $result = $this->list_rules(); $this->rc->output->show_message('managesieve.moved', 'confirmation'); - $this->rc->output->command('managesieve_updatelist', 'list', + $this->rc->output->add_js_call('managesieve_updatelist', 'list', ['list' => $result, 'clear' => true, 'set' => $to]); } else { $this->rc->output->show_message('managesieve.moveerror', 'error'); @@ -367,7 +367,7 @@ public function actions() } else { $this->rc->output->show_message('managesieve.activated', 'confirmation'); } - $this->rc->output->command('managesieve_updatelist', 'update', + $this->rc->output->add_js_call('managesieve_updatelist', 'update', ['id' => $fid, 'disabled' => $rule['disabled']]); } else { if ($rule['disabled']) { @@ -386,7 +386,7 @@ public function actions() if ($result === true) { $this->rc->output->set_env('active_sets', $this->active); $this->rc->output->show_message('managesieve.setactivated', 'confirmation'); - $this->rc->output->command('managesieve_updatelist', 'setact', + $this->rc->output->add_js_call('managesieve_updatelist', 'setact', ['name' => $script_name, 'active' => true, 'all' => !$kep14]); } else { $this->rc->output->show_message('managesieve.setactivateerror', 'error'); @@ -402,7 +402,7 @@ public function actions() if ($result === true) { $this->rc->output->set_env('active_sets', $this->active); $this->rc->output->show_message('managesieve.setdeactivated', 'confirmation'); - $this->rc->output->command('managesieve_updatelist', 'setact', + $this->rc->output->add_js_call('managesieve_updatelist', 'setact', ['name' => $script_name, 'active' => false]); } else { $this->rc->output->show_message('managesieve.setdeactivateerror', 'error'); @@ -417,7 +417,7 @@ public function actions() if ($result === true) { $this->rc->output->show_message('managesieve.setdeleted', 'confirmation'); - $this->rc->output->command('managesieve_updatelist', 'setdel', ['name' => $script_name]); + $this->rc->output->add_js_call('managesieve_updatelist', 'setdel', ['name' => $script_name]); $this->rc->session->remove('managesieve_current'); } else { $this->rc->output->show_message('managesieve.setdeleteerror', 'error'); @@ -447,23 +447,23 @@ public function actions() } elseif ($action == 'list') { $result = $this->list_rules(); - $this->rc->output->command('managesieve_updatelist', 'list', ['list' => $result]); + $this->rc->output->add_js_call('managesieve_updatelist', 'list', ['list' => $result]); } elseif ($action == 'ruleadd') { $rid = rcube_utils::get_input_string('_rid', rcube_utils::INPUT_POST); $id = $this->genid(); $content = $this->rule_div($fid, $id, false, !empty($_SESSION['managesieve-compact-form'])); - $this->rc->output->command('managesieve_rulefill', $content, $id, $rid); + $this->rc->output->add_js_call('managesieve_rulefill', $content, $id, $rid); } elseif ($action == 'actionadd') { $aid = rcube_utils::get_input_string('_aid', rcube_utils::INPUT_POST); $id = $this->genid(); $content = $this->action_div($fid, $id, false); - $this->rc->output->command('managesieve_actionfill', $content, $id, $aid); + $this->rc->output->add_js_call('managesieve_actionfill', $content, $id, $aid); } elseif ($action == 'addresses') { $aid = rcube_utils::get_input_string('_aid', rcube_utils::INPUT_POST); - $this->rc->output->command('managesieve_vacation_addresses_update', $aid, $this->user_emails()); + $this->rc->output->add_js_call('managesieve_vacation_addresses_update', $aid, $this->user_emails()); } $this->rc->output->send(); @@ -515,7 +515,7 @@ public function saveraw() } } else { $this->rc->output->show_message('managesieve.setupdated', 'confirmation'); - $this->rc->output->command('parent.managesieve_updatelist', 'refresh'); + $this->rc->output->add_js_call('parent.managesieve_updatelist', 'refresh'); } $this->send(); @@ -602,7 +602,7 @@ public function save() $index = array_search($name, $list); $this->rc->output->show_message('managesieve.setcreated', 'confirmation'); - $this->rc->output->command('parent.managesieve_updatelist', 'setadd', + $this->rc->output->add_js_call('parent.managesieve_updatelist', 'setadd', ['name' => $name, 'index' => $index]); } elseif ($error) { $this->rc->output->show_message($error, 'error'); @@ -1217,9 +1217,9 @@ public function save() 'id' => $fid, 'disabled' => $this->form['disabled'], ]; - $this->rc->output->command('parent.managesieve_updatelist', isset($new) ? 'add' : 'update', $args); + $this->rc->output->add_js_call('parent.managesieve_updatelist', isset($new) ? 'add' : 'update', $args); } else { - $this->rc->output->command('managesieve_dialog_close'); + $this->rc->output->add_js_call('managesieve_dialog_close'); $this->rc->output->send('iframe'); } } else { @@ -2757,7 +2757,7 @@ protected function print_tips() return; } - $this->rc->output->command('managesieve_tip_register', $this->tips); + $this->rc->output->add_js_call('managesieve_tip_register', $this->tips); } protected function list_input($id, $name, $value, $size = null, $hidden = false, $attrib = []) diff --git a/plugins/managesieve/managesieve.php b/plugins/managesieve/managesieve.php index 1e160b60f83..da38fff2d72 100644 --- a/plugins/managesieve/managesieve.php +++ b/plugins/managesieve/managesieve.php @@ -206,7 +206,7 @@ public function mail_headers($args) $headers = $this->parse_headers($args['headers']); if ($this->rc->action == 'preview') { - $this->rc->output->command('parent.set_env', ['sieve_headers' => $headers]); + $this->rc->output->add_js_call('parent.set_env', ['sieve_headers' => $headers]); } else { $this->rc->output->set_env('sieve_headers', $headers); } @@ -228,7 +228,7 @@ public function managesieve_actions() $headers = $this->parse_headers($message->headers); $this->rc->output->set_env('sieve_headers', $headers); - $this->rc->output->command('managesieve_create', true); + $this->rc->output->add_js_call('managesieve_create', true); $this->rc->output->send(); } diff --git a/plugins/markasjunk/drivers/edit_headers.php b/plugins/markasjunk/drivers/edit_headers.php index f3c11baad6d..83980fcc719 100644 --- a/plugins/markasjunk/drivers/edit_headers.php +++ b/plugins/markasjunk/drivers/edit_headers.php @@ -57,7 +57,7 @@ private function _edit_headers(&$uids, $spam, $dst_mbox) $saved = $rcube->storage->save_message($dst_mbox, $raw_message); if ($saved !== false) { - $rcube->output->command('markasjunk_move', null, [$uid]); + $rcube->output->add_js_call('markasjunk_move', null, [$uid]); $new_uids[] = $saved; } } diff --git a/plugins/markasjunk/drivers/jsevent.php b/plugins/markasjunk/drivers/jsevent.php index 8a3201ea44b..dc9b2734764 100644 --- a/plugins/markasjunk/drivers/jsevent.php +++ b/plugins/markasjunk/drivers/jsevent.php @@ -42,7 +42,7 @@ public function init() return; } - $rcmail->output->command('markasjunk_init', $this->addition_spam_folders, $this->suspicious_folders); + $rcmail->output->add_js_call('markasjunk_init', $this->addition_spam_folders, $this->suspicious_folders); } public function spam(&$uids, $mbox) diff --git a/plugins/markasjunk/drivers/sa_detach.php b/plugins/markasjunk/drivers/sa_detach.php index b9d41de3804..b0753d692ef 100644 --- a/plugins/markasjunk/drivers/sa_detach.php +++ b/plugins/markasjunk/drivers/sa_detach.php @@ -51,7 +51,7 @@ public function ham(&$uids, $src_mbox, $dst_mbox) $orig_message_raw = $message->get_part_body($part->mime_id); if ($saved = $storage->save_message($dst_mbox, $orig_message_raw)) { - $rcube->output->command('markasjunk_move', null, [$uid]); + $rcube->output->add_js_call('markasjunk_move', null, [$uid]); $new_uids[] = $saved; } } diff --git a/plugins/markasjunk/markasjunk.php b/plugins/markasjunk/markasjunk.php index 7b2fde06dea..7fc27bcc268 100644 --- a/plugins/markasjunk/markasjunk.php +++ b/plugins/markasjunk/markasjunk.php @@ -153,12 +153,12 @@ public function mark_message() $result = $is_spam ? $this->_spam($messageset, $dest_mbox) : $this->_ham($messageset, $dest_mbox); if ($result) { if ($dest_mbox && ($mbox_name !== $dest_mbox || $multifolder)) { - $this->rcube->output->command('markasjunk_move', $dest_mbox, $this->_messageset_to_uids($messageset, $multifolder)); + $this->rcube->output->add_js_call('markasjunk_move', $dest_mbox, $this->_messageset_to_uids($messageset, $multifolder)); } else { - $this->rcube->output->command('command', 'list', $mbox_name); + $this->rcube->output->add_js_call('command', 'list', $mbox_name); } - $this->rcube->output->command('display_message', $this->gettext($is_spam ? 'reportedasjunk' : 'reportedasnotjunk'), 'confirmation'); + $this->rcube->output->add_js_call('display_message', $this->gettext($is_spam ? 'reportedasjunk' : 'reportedasnotjunk'), 'confirmation'); } $this->rcube->output->send(); diff --git a/plugins/new_user_dialog/new_user_dialog.php b/plugins/new_user_dialog/new_user_dialog.php index 7ca27960f54..9f591d29df0 100644 --- a/plugins/new_user_dialog/new_user_dialog.php +++ b/plugins/new_user_dialog/new_user_dialog.php @@ -104,7 +104,7 @@ public function render_page($p) )); $title = rcube::JQ($this->gettext('identitydialogtitle')); - $rcmail->output->command('plugin.new_user_dialog_open', $title); + $rcmail->output->add_js_call('new_user_dialog_open', $title); $this->include_script('new_user_dialog.js'); } } @@ -161,7 +161,7 @@ public function save_data() // save prefs to not show dialog again $rcmail->user->save_prefs(['newuserdialog' => null]); // hide dialog - $rcmail->output->command('new_user_dialog_close'); + $rcmail->output->add_js_call('new_user_dialog_close'); $rcmail->output->show_message('successfullysaved', 'confirmation'); } else { // show error diff --git a/plugins/newmail_notifier/newmail_notifier.php b/plugins/newmail_notifier/newmail_notifier.php index 287744912cd..a7ec7fa7afd 100644 --- a/plugins/newmail_notifier/newmail_notifier.php +++ b/plugins/newmail_notifier/newmail_notifier.php @@ -204,7 +204,7 @@ public function notify($args) $this->notified = true; $this->rc->output->set_env('newmail_notifier_timeout', $this->rc->config->get('newmail_notifier_desktop_timeout')); - $this->rc->output->command('newmail_notifier_run', + $this->rc->output->add_js_call('newmail_notifier_run', [ 'basic' => $this->opt['basic'], 'sound' => $this->opt['sound'], diff --git a/plugins/password/password.php b/plugins/password/password.php index 288430f9751..e370fb434a4 100644 --- a/plugins/password/password.php +++ b/plugins/password/password.php @@ -81,7 +81,7 @@ public function init() if ($this->rc->config->get('password_force_new_user')) { if ($this->rc->config->get('newuserpassword') && $this->check_host_login_exceptions()) { if (!($this->rc->task == 'settings' && strpos($this->rc->action, 'plugin.password') === 0)) { - $this->rc->output->command('redirect', '?_task=settings&_action=plugin.password&_first=1', false); + $this->rc->output->add_js_call('redirect', '?_task=settings&_action=plugin.password&_first=1', false); } } @@ -111,12 +111,12 @@ public function password_init() $this->rc->output->set_pagetitle($this->gettext('changepasswd')); if (rcube_utils::get_input_value('_first', rcube_utils::INPUT_GET)) { - $this->rc->output->command('display_message', $this->gettext('firstloginchange'), 'notice'); + $this->rc->output->add_js_call('display_message', $this->gettext('firstloginchange'), 'notice'); } elseif (!empty($_SESSION['password_expires'])) { if ($_SESSION['password_expires'] == 1) { - $this->rc->output->command('display_message', $this->gettext('passwdexpired'), 'error'); + $this->rc->output->add_js_call('display_message', $this->gettext('passwdexpired'), 'error'); } else { - $this->rc->output->command('display_message', $this->gettext([ + $this->rc->output->add_js_call('display_message', $this->gettext([ 'name' => 'passwdexpirewarning', 'vars' => ['expirationdatetime' => $_SESSION['password_expires']], ]), 'warning'); @@ -137,7 +137,7 @@ public function password_save() $force_save = $this->rc->config->get('password_force_save'); if (($confirm && !isset($_POST['_curpasswd'])) || !isset($_POST['_newpasswd']) || !strlen($_POST['_newpasswd'])) { - $this->rc->output->command('display_message', $this->gettext('nopassword'), 'error'); + $this->rc->output->add_js_call('display_message', $this->gettext('nopassword'), 'error'); } else { $charset = strtoupper($this->rc->config->get('password_charset', 'UTF-8')); $rc_charset = strtoupper($this->rc->output->get_charset()); @@ -158,26 +158,26 @@ public function password_save() $conpwd = rcube_charset::convert($conpwd, $rc_charset, $charset); if ($chk_pwd != $orig_pwd || preg_match('/[\x00-\x1F\x7F]/', $newpwd)) { - $this->rc->output->command('display_message', $this->gettext('passwordforbidden'), 'error'); + $this->rc->output->add_js_call('display_message', $this->gettext('passwordforbidden'), 'error'); } // other passwords validity checks elseif ($conpwd != $newpwd) { - $this->rc->output->command('display_message', $this->gettext('passwordinconsistency'), 'error'); + $this->rc->output->add_js_call('display_message', $this->gettext('passwordinconsistency'), 'error'); } elseif ($confirm && ($res = $this->_compare($sesspwd, $curpwd, PASSWORD_COMPARE_CURRENT))) { - $this->rc->output->command('display_message', $res, 'error'); + $this->rc->output->add_js_call('display_message', $res, 'error'); } elseif ($required_length && strlen($newpwd) < $required_length) { - $this->rc->output->command('display_message', $this->gettext( + $this->rc->output->add_js_call('display_message', $this->gettext( ['name' => 'passwordshort', 'vars' => ['length' => $required_length]]), 'error'); } elseif ($res = $this->_check_strength($newpwd)) { - $this->rc->output->command('display_message', $res, 'error'); + $this->rc->output->add_js_call('display_message', $res, 'error'); } // password is the same as the old one, warn user, return error elseif (!$force_save && ($res = $this->_compare($sesspwd, $newpwd, PASSWORD_COMPARE_NEW))) { - $this->rc->output->command('display_message', $res, 'error'); + $this->rc->output->add_js_call('display_message', $res, 'error'); } // try to save the password elseif (!($res = $this->_save($curpwd, $newpwd))) { - $this->rc->output->command('display_message', $this->gettext('successfullysaved'), 'confirmation'); + $this->rc->output->add_js_call('display_message', $this->gettext('successfullysaved'), 'confirmation'); // allow additional actions after password change (e.g. reset some backends) $plugin = $this->rc->plugins->exec_hook('password_change', [ @@ -201,7 +201,7 @@ public function password_save() // Remove expiration date/time $this->rc->session->remove('password_expires'); } else { - $this->rc->output->command('display_message', $res, 'error'); + $this->rc->output->add_js_call('display_message', $res, 'error'); } } diff --git a/plugins/vcard_attachments/vcard_attachments.php b/plugins/vcard_attachments/vcard_attachments.php index 973868d4b91..e713505f757 100644 --- a/plugins/vcard_attachments/vcard_attachments.php +++ b/plugins/vcard_attachments/vcard_attachments.php @@ -244,7 +244,7 @@ public function save_vcard() } if ($existing->count) { - // $rcmail->output->command('display_message', $this->gettext('contactexists'), 'warning'); + // $rcmail->output->add_js_call('display_message', $this->gettext('contactexists'), 'warning'); $valid = false; } } @@ -262,9 +262,9 @@ public function save_vcard() } if ($errors || empty($vcards)) { - $rcmail->output->command('display_message', $this->gettext('vcardsavefailed'), 'error'); + $rcmail->output->add_js_call('display_message', $this->gettext('vcardsavefailed'), 'error'); } else { - $rcmail->output->command('display_message', $this->gettext('importedsuccessfully'), 'confirmation'); + $rcmail->output->add_js_call('display_message', $this->gettext('importedsuccessfully'), 'confirmation'); } $rcmail->output->send(); diff --git a/program/actions/contacts/delete.php b/program/actions/contacts/delete.php index 3933e17555d..1ee11f93891 100644 --- a/program/actions/contacts/delete.php +++ b/program/actions/contacts/delete.php @@ -46,7 +46,7 @@ public function run($args = []) // more than one source, some of these sources can be readonly if (count($cids) == 1) { $rcmail->output->show_message('contactdelerror', 'error'); - $rcmail->output->command('list_contacts'); + $rcmail->output->add_js_call('list_contacts'); $rcmail->output->send(); } @@ -73,7 +73,7 @@ public function run($args = []) $group = rcube_utils::get_input_string('_gid', rcube_utils::INPUT_GP); $rcmail->output->show_message($error, 'error'); - $rcmail->output->command('list_contacts', $source, $group); + $rcmail->output->add_js_call('list_contacts', $source, $group); $rcmail->output->send(); } else { $delcnt += $deleted; @@ -111,7 +111,7 @@ public function run($args = []) // last page and it's empty, display previous one if ($result->count && $result->count <= ($page_size * ($page - 1))) { - $rcmail->output->command('list_page', 'prev'); + $rcmail->output->add_js_call('list_page', 'prev'); $rowcount = $rcmail->gettext('loading'); } // get records from the next page to add to the list @@ -139,7 +139,7 @@ public function run($args = []) // last page and it's empty, display previous one if ($result->count && $result->count <= ($page_size * ($page - 1))) { - $rcmail->output->command('list_page', 'prev'); + $rcmail->output->add_js_call('list_page', 'prev'); $rowcount = $rcmail->gettext('loading'); } // get records from the next page to add to the list @@ -155,7 +155,7 @@ public function run($args = []) // update message count display $rcmail->output->set_env('pagecount', isset($result) ? ceil($result->count / $page_size) : 0); - $rcmail->output->command('set_rowcount', $rowcount); + $rcmail->output->add_js_call('set_rowcount', $rowcount); // add new rows from next page (if any) if (!empty($records)) { diff --git a/program/actions/contacts/group_create.php b/program/actions/contacts/group_create.php index 8a9ce5c9504..9bd9e7b7ba0 100644 --- a/program/actions/contacts/group_create.php +++ b/program/actions/contacts/group_create.php @@ -54,7 +54,7 @@ public function run($args = []) if (!empty($created)) { $rcmail->output->show_message('groupcreated', 'confirmation'); - $rcmail->output->command('insert_contact_group', ['source' => $source] + $created); + $rcmail->output->add_js_call('insert_contact_group', ['source' => $source] + $created); } elseif (empty($created)) { $error = !empty($plugin['message']) ? $plugin['message'] : 'errorsaving'; $rcmail->output->show_message($error, 'error'); diff --git a/program/actions/contacts/group_delete.php b/program/actions/contacts/group_delete.php index 3c13d78cf6e..c6e8dadfa52 100644 --- a/program/actions/contacts/group_delete.php +++ b/program/actions/contacts/group_delete.php @@ -54,7 +54,7 @@ public function run($args = []) if (!empty($deleted)) { $rcmail->output->show_message('groupdeleted', 'confirmation'); - $rcmail->output->command('remove_group_item', ['source' => $source, 'id' => $gid]); + $rcmail->output->add_js_call('remove_group_item', ['source' => $source, 'id' => $gid]); } else { $error = !empty($plugin['message']) ? $plugin['message'] : 'errorsaving'; $rcmail->output->show_message($error, 'error'); diff --git a/program/actions/contacts/group_delmembers.php b/program/actions/contacts/group_delmembers.php index cdfe8502338..bf84fa2b96d 100644 --- a/program/actions/contacts/group_delmembers.php +++ b/program/actions/contacts/group_delmembers.php @@ -58,7 +58,7 @@ public function run($args = []) if (!empty($result)) { $rcmail->output->show_message('contactremovedfromgroup', 'confirmation'); - $rcmail->output->command('remove_group_contacts', ['source' => $source, 'gid' => $gid]); + $rcmail->output->add_js_call('remove_group_contacts', ['source' => $source, 'gid' => $gid]); } else { $error = !empty($plugin['message']) ? $plugin['message'] : 'errorsaving'; $rcmail->output->show_message($error, 'error'); diff --git a/program/actions/contacts/group_rename.php b/program/actions/contacts/group_rename.php index e26e173a612..4a6cc7956df 100644 --- a/program/actions/contacts/group_rename.php +++ b/program/actions/contacts/group_rename.php @@ -59,7 +59,7 @@ public function run($args = []) if (!empty($newname)) { $rcmail->output->show_message('grouprenamed', 'confirmation'); - $rcmail->output->command('update_contact_group', [ + $rcmail->output->add_js_call('update_contact_group', [ 'source' => $source, 'id' => $gid, 'name' => $newname, diff --git a/program/actions/contacts/import.php b/program/actions/contacts/import.php index 42de7046598..89149f25665 100644 --- a/program/actions/contacts/import.php +++ b/program/actions/contacts/import.php @@ -152,7 +152,7 @@ public function run($args = []) $_SESSION['contactcsvimport']['map'] = $map; // Re-enable the import button - $rcmail->output->command('parent.import_state_set', 'error'); + $rcmail->output->add_js_call('parent.import_state_set', 'error'); } elseif (count($vcards) > 0) { // import vcards self::$stats = new stdClass(); @@ -246,7 +246,7 @@ public function run($args = []) $importstep = 'import_confirm'; $_SESSION['contactcsvimport'] = null; - $rcmail->output->command('parent.import_state_set', self::$stats->inserted ? 'reload' : 'ok'); + $rcmail->output->add_js_call('parent.import_state_set', self::$stats->inserted ? 'reload' : 'ok'); } else { if ($upload_error == self::UPLOAD_ERR_CSV_FIELDS) { $rcmail->output->show_message('csvfilemismatch', 'error'); @@ -254,7 +254,7 @@ public function run($args = []) self::upload_error($upload_error); } - $rcmail->output->command('parent.import_state_set', 'error'); + $rcmail->output->add_js_call('parent.import_state_set', 'error'); } } diff --git a/program/actions/contacts/index.php b/program/actions/contacts/index.php index d3851194591..04a6d200b56 100644 --- a/program/actions/contacts/index.php +++ b/program/actions/contacts/index.php @@ -703,7 +703,7 @@ public static function js_contacts_list($result, $prefix = '') $classes[] = 'readonly'; } - $rcmail->output->command($prefix . 'add_contact_row', $row['ID'], $a_row_cols, implode(' ', $classes), + $rcmail->output->add_js_call($prefix . 'add_contact_row', $row['ID'], $a_row_cols, implode(' ', $classes), array_intersect_key($row, ['ID' => 1, 'readonly' => 1, '_type' => 1, 'email' => 1, 'name' => 1]) ); } diff --git a/program/actions/contacts/list.php b/program/actions/contacts/list.php index fbf91985be3..62f078306e5 100644 --- a/program/actions/contacts/list.php +++ b/program/actions/contacts/list.php @@ -79,11 +79,11 @@ public function run($args = []) } } - $rcmail->output->command('set_group_prop', $group_data); + $rcmail->output->add_js_call('set_group_prop', $group_data); // update message count display $rcmail->output->set_env('pagecount', ceil($result->count / $page_size)); - $rcmail->output->command('set_rowcount', self::get_rowcount_text($result)); + $rcmail->output->add_js_call('set_rowcount', self::get_rowcount_text($result)); // create javascript list self::js_contacts_list($result); diff --git a/program/actions/contacts/mailto.php b/program/actions/contacts/mailto.php index fab4b024703..9d3d2840aeb 100644 --- a/program/actions/contacts/mailto.php +++ b/program/actions/contacts/mailto.php @@ -79,7 +79,7 @@ public function run($args = []) $mailto_str = implode(', ', $mailto); $mailto_id = substr(md5($mailto_str), 0, 16); $_SESSION['mailto'][$mailto_id] = urlencode($mailto_str); - $rcmail->output->command('open_compose_step', ['_mailto' => $mailto_id]); + $rcmail->output->add_js_call('open_compose_step', ['_mailto' => $mailto_id]); } else { $rcmail->output->show_message('nocontactsfound', 'warning'); } diff --git a/program/actions/contacts/move.php b/program/actions/contacts/move.php index 0649d94b409..7a96c692099 100644 --- a/program/actions/contacts/move.php +++ b/program/actions/contacts/move.php @@ -159,7 +159,7 @@ public function run($args = []) } if (!$deleted || $deleted != $all) { - $rcmail->output->command('list_contacts'); + $rcmail->output->add_js_call('list_contacts'); } else { // update saved search after data changed if (($records = self::search_update(true)) !== false) { @@ -171,7 +171,7 @@ public function run($args = []) // last page and it's empty, display previous one if ($result->count && $result->count <= ($page_size * ($page - 1))) { - $rcmail->output->command('list_page', 'prev'); + $rcmail->output->add_js_call('list_page', 'prev'); $rowcount = $rcmail->gettext('loading'); } // get records from the next page to add to the list @@ -199,7 +199,7 @@ public function run($args = []) // last page and it's empty, display previous one if ($result->count && $result->count <= ($page_size * ($page - 1))) { - $rcmail->output->command('list_page', 'prev'); + $rcmail->output->add_js_call('list_page', 'prev'); $rowcount = $rcmail->gettext('loading'); } // get records from the next page to add to the list @@ -215,7 +215,7 @@ public function run($args = []) // update message count display $rcmail->output->set_env('pagecount', isset($result) ? ceil($result->count / $page_size) : 0); - $rcmail->output->command('set_rowcount', $rowcount); + $rcmail->output->add_js_call('set_rowcount', $rowcount); // add new rows from next page (if any) if (!empty($records)) { diff --git a/program/actions/contacts/save.php b/program/actions/contacts/save.php index cf4bd237780..dcd5f31a85c 100644 --- a/program/actions/contacts/save.php +++ b/program/actions/contacts/save.php @@ -95,7 +95,7 @@ public function run($args = []) // in search mode, just reload the list (#1490015) if (!empty($_REQUEST['_search'])) { - $rcmail->output->command('parent.command', 'list'); + $rcmail->output->add_js_call('parent.command', 'list'); $rcmail->output->send('iframe'); } @@ -114,7 +114,7 @@ public function run($args = []) // Plugins can decide to remove the contact on edit, e.g. automatic_addressbook // Best we can do is to refresh the list (#5522) if (empty($contact)) { - $rcmail->output->command('parent.command', 'list'); + $rcmail->output->add_js_call('parent.command', 'list'); $rcmail->output->send('iframe'); } @@ -133,7 +133,7 @@ public function run($args = []) $record['_type'] = 'person'; // update the changed col in list - $rcmail->output->command('parent.update_contact_row', $cid, $a_js_cols, $newcid, $source, $record); + $rcmail->output->add_js_call('parent.update_contact_row', $cid, $a_js_cols, $newcid, $source, $record); $rcmail->overwrite_action('show', ['contact' => $contact]); } else { @@ -193,7 +193,7 @@ public function run($args = []) if (($maxnum = $rcmail->config->get('max_group_members', 0)) && ($contacts->count()->count + 1 > $maxnum)) { // @FIXME: should we remove the contact? $msgtext = $rcmail->gettext(['name' => 'maxgroupmembersreached', 'vars' => ['max' => $maxnum]]); - $rcmail->output->command('parent.display_message', $msgtext, 'warning'); + $rcmail->output->add_js_call('parent.display_message', $msgtext, 'warning'); } else { $contacts->add_to_group($plugin['group_id'], $plugin['ids']); } @@ -203,8 +203,8 @@ public function run($args = []) // show confirmation $rcmail->output->show_message('successfullysaved', 'confirmation', null, false); - $rcmail->output->command('parent.set_rowcount', $rcmail->gettext('loading')); - $rcmail->output->command('parent.list_contacts'); + $rcmail->output->add_js_call('parent.set_rowcount', $rcmail->gettext('loading')); + $rcmail->output->add_js_call('parent.list_contacts'); $rcmail->output->send('iframe'); } else { diff --git a/program/actions/contacts/search.php b/program/actions/contacts/search.php index 92be7acd4ad..d35b7b1cdb9 100644 --- a/program/actions/contacts/search.php +++ b/program/actions/contacts/search.php @@ -171,7 +171,7 @@ public static function contact_search() $_SESSION['page'] = 1; if ($adv) { - $rcmail->output->command('list_contacts_clear'); + $rcmail->output->add_js_call('list_contacts_clear'); } if ($result->count > 0) { @@ -185,22 +185,22 @@ public static function contact_search() // update message count display $rcmail->output->set_env('search_request', $search_request); $rcmail->output->set_env('pagecount', ceil($result->count / $page_size)); - $rcmail->output->command('set_rowcount', self::get_rowcount_text($result)); + $rcmail->output->add_js_call('set_rowcount', self::get_rowcount_text($result)); // Re-set current source $rcmail->output->set_env('search_id', $sid); $rcmail->output->set_env('source', ''); $rcmail->output->set_env('group', ''); // Re-set list header - $rcmail->output->command('set_group_prop', null); + $rcmail->output->add_js_call('set_group_prop', null); if (!$sid) { // unselect currently selected directory/group - $rcmail->output->command('unselect_directory'); + $rcmail->output->add_js_call('unselect_directory'); // enable "Save search" command - $rcmail->output->command('enable_command', 'search-create', true); + $rcmail->output->add_js_call('enable_command', 'search-create', true); } - $rcmail->output->command('update_group_commands'); + $rcmail->output->add_js_call('update_group_commands'); // send response $rcmail->output->send(); diff --git a/program/actions/contacts/search_create.php b/program/actions/contacts/search_create.php index 8a3a12973c6..6b6f9f4e0b8 100644 --- a/program/actions/contacts/search_create.php +++ b/program/actions/contacts/search_create.php @@ -61,7 +61,7 @@ public function run($args = []) if (!empty($result)) { $rcmail->output->show_message('savedsearchcreated', 'confirmation'); - $rcmail->output->command('insert_saved_search', rcube::Q($name), rcube::Q($result)); + $rcmail->output->add_js_call('insert_saved_search', rcube::Q($name), rcube::Q($result)); } else { $error = !empty($plugin['message']) ? $plugin['message'] : 'savedsearchcreateerror'; $rcmail->output->show_message($error, 'error'); diff --git a/program/actions/contacts/search_delete.php b/program/actions/contacts/search_delete.php index 468ed6c085c..cbaa2519b60 100644 --- a/program/actions/contacts/search_delete.php +++ b/program/actions/contacts/search_delete.php @@ -48,9 +48,9 @@ public function run($args = []) if ($result) { $rcmail->output->show_message('savedsearchdeleted', 'confirmation'); - $rcmail->output->command('remove_search_item', rcube::Q($id)); + $rcmail->output->add_js_call('remove_search_item', rcube::Q($id)); // contact list will be cleared, clear also page counter - $rcmail->output->command('set_rowcount', $rcmail->gettext('nocontactsfound')); + $rcmail->output->add_js_call('set_rowcount', $rcmail->gettext('nocontactsfound')); $rcmail->output->set_env('pagecount', 0); } else { $error = !empty($plugin['message']) ? $plugin['message'] : 'savedsearchdeleteerror'; diff --git a/program/actions/contacts/undo.php b/program/actions/contacts/undo.php index 787688b69de..607e8a2a6b3 100644 --- a/program/actions/contacts/undo.php +++ b/program/actions/contacts/undo.php @@ -47,7 +47,7 @@ public function run($args = []) $error = !empty($plugin['message']) ? $plugin['message'] : 'contactrestoreerror'; $rcmail->output->show_message($error, 'error'); - $rcmail->output->command('list_contacts'); + $rcmail->output->add_js_call('list_contacts'); $rcmail->output->send(); } else { $delcnt += $restored; @@ -59,7 +59,7 @@ public function run($args = []) if ($delcnt) { $rcmail->output->show_message('contactrestored', 'confirmation'); - $rcmail->output->command('list_contacts'); + $rcmail->output->add_js_call('list_contacts'); } // send response diff --git a/program/actions/contacts/upload_photo.php b/program/actions/contacts/upload_photo.php index 290f235d86b..552e13fee9b 100644 --- a/program/actions/contacts/upload_photo.php +++ b/program/actions/contacts/upload_photo.php @@ -78,7 +78,7 @@ public function run($args = []) } if ($inserted) { - $rcmail->output->command('replace_contact_photo', $attachment['id']); + $rcmail->output->add_js_call('replace_contact_photo', $attachment['id']); } else { // upload failed self::upload_error($_FILES['_photo']['error'], $attachment); @@ -87,7 +87,7 @@ public function run($args = []) self::upload_failure(); } - $rcmail->output->command('photo_upload_end'); + $rcmail->output->add_js_call('photo_upload_end'); $rcmail->output->send('iframe'); } } diff --git a/program/actions/mail/addcontact.php b/program/actions/mail/addcontact.php index 89e79f4939b..1b185cfe9ea 100644 --- a/program/actions/mail/addcontact.php +++ b/program/actions/mail/addcontact.php @@ -87,7 +87,7 @@ public function run($args = []) $rcmail->output->show_message('addedsuccessfully', 'confirmation'); if (!empty($_POST['_reload'])) { - $rcmail->output->command('command', 'load-remote'); + $rcmail->output->add_js_call('command', 'load-remote'); } } else { $rcmail->output->show_message($error ?: 'errorsavingcontact', 'error', null, false); diff --git a/program/actions/mail/attachment_delete.php b/program/actions/mail/attachment_delete.php index 5bcbada41f5..3c89ae717c4 100644 --- a/program/actions/mail/attachment_delete.php +++ b/program/actions/mail/attachment_delete.php @@ -35,7 +35,7 @@ public function run($args = []) $rcmail = rcmail::get_instance(); if ($rcmail->delete_uploaded_file(self::$file_id)) { - $rcmail->output->command('remove_from_attachment_list', 'rcmfile' . self::$file_id); + $rcmail->output->add_js_call('remove_from_attachment_list', 'rcmfile' . self::$file_id); } $rcmail->output->send(); diff --git a/program/actions/mail/attachment_rename.php b/program/actions/mail/attachment_rename.php index c1ea08437db..58380fad0c2 100644 --- a/program/actions/mail/attachment_rename.php +++ b/program/actions/mail/attachment_rename.php @@ -38,7 +38,7 @@ public function run($args = []) $filename = trim($filename); if (strlen($filename) && $rcmail->update_uploaded_file(self::$file_id, ['name' => $filename])) { - $rcmail->output->command('rename_attachment_handler', 'rcmfile' . self::$file_id, $filename); + $rcmail->output->add_js_call('rename_attachment_handler', 'rcmfile' . self::$file_id, $filename); } $rcmail->output->send(); diff --git a/program/actions/mail/attachment_upload.php b/program/actions/mail/attachment_upload.php index 3177c61e8be..9813c54b5b3 100644 --- a/program/actions/mail/attachment_upload.php +++ b/program/actions/mail/attachment_upload.php @@ -86,8 +86,8 @@ public function run($args = []) if ($plugin['attachment']) { self::attachment_success($plugin['attachment'], $uploadid); } else { - $rcmail->output->command('display_message', $rcmail->gettext('filelinkerror'), 'error'); - $rcmail->output->command('remove_from_attachment_list', $uploadid); + $rcmail->output->add_js_call('display_message', $rcmail->gettext('filelinkerror'), 'error'); + $rcmail->output->add_js_call('remove_from_attachment_list', $uploadid); } $rcmail->output->send(); @@ -110,8 +110,8 @@ public function run($args = []) if ($err = self::check_message_size($filesize, $filetype)) { if (!in_array($err, $errors)) { - $rcmail->output->command('display_message', $err, 'error'); - $rcmail->output->command('remove_from_attachment_list', $uploadid); + $rcmail->output->add_js_call('display_message', $err, 'error'); + $rcmail->output->add_js_call('remove_from_attachment_list', $uploadid); $errors[] = $err; } @@ -143,19 +143,19 @@ public function run($args = []) if (!empty($attachment['error']) || $err != \UPLOAD_ERR_NO_FILE) { if (!in_array($msg, $errors)) { - $rcmail->output->command('display_message', $msg, 'error'); - $rcmail->output->command('remove_from_attachment_list', $uploadid); + $rcmail->output->add_js_call('display_message', $msg, 'error'); + $rcmail->output->add_js_call('remove_from_attachment_list', $uploadid); $errors[] = $msg; } } } } } elseif (self::upload_failure()) { - $rcmail->output->command('remove_from_attachment_list', $uploadid); + $rcmail->output->add_js_call('remove_from_attachment_list', $uploadid); } // send html page with JS calls as response - $rcmail->output->command('auto_save_start', false); + $rcmail->output->add_js_call('auto_save_start', false); $rcmail->output->send('iframe'); } @@ -226,7 +226,7 @@ public static function attachment_success($attachment, $uploadid) $content = $content_link . $delete_link; } - $rcmail->output->command('add2attachment_list', "rcmfile{$id}", [ + $rcmail->output->add_js_call('add2attachment_list', "rcmfile{$id}", [ 'html' => $content, 'name' => $attachment['name'], 'mimetype' => $attachment['mimetype'], diff --git a/program/actions/mail/autocomplete.php b/program/actions/mail/autocomplete.php index 10e06a09c49..459b9c554f2 100644 --- a/program/actions/mail/autocomplete.php +++ b/program/actions/mail/autocomplete.php @@ -181,7 +181,7 @@ public function run($args = []) $contacts = $plugin['contacts']; - $rcmail->output->command('ksearch_query_results', $contacts, $search, $reqid); + $rcmail->output->add_js_call('ksearch_query_results', $contacts, $search, $reqid); $rcmail->output->send(); } diff --git a/program/actions/mail/check_recent.php b/program/actions/mail/check_recent.php index b1cef31122b..beea46a3c36 100644 --- a/program/actions/mail/check_recent.php +++ b/program/actions/mail/check_recent.php @@ -112,7 +112,7 @@ public function run($args = []) } if (!empty($_POST['_quota'])) { - $rcmail->output->command('set_quota', self::quota_content(null, $mbox_name)); + $rcmail->output->add_js_call('set_quota', self::quota_content(null, $mbox_name)); } $rcmail->output->set_env('exists', $rcmail->storage->count($mbox_name, 'EXISTS', true)); @@ -139,18 +139,18 @@ public function run($args = []) $rcmail->output->set_env('messagecount', $all_count); $rcmail->output->set_env('pagecount', ceil($all_count / $page_size)); - $rcmail->output->command('set_rowcount', self::get_messagecount_text($all_count), $mbox_name); + $rcmail->output->add_js_call('set_rowcount', self::get_messagecount_text($all_count), $mbox_name); $rcmail->output->set_env('current_page', $all_count ? $page : 1); // remove old rows (and clear selection if new list is empty) - $rcmail->output->command('message_list.clear', $all_count ? false : true); + $rcmail->output->add_js_call('message_list.clear', $all_count ? false : true); if ($all_count) { $a_headers = $rcmail->storage->list_messages($mbox_name, null, self::sort_column(), self::sort_order()); // add message rows self::js_message_list($a_headers, false); // remove messages that don't exists from list selection array - $rcmail->output->command('update_selection'); + $rcmail->output->add_js_call('update_selection'); } $list_cleared = true; @@ -158,7 +158,7 @@ public function run($args = []) // set trash folder state if ($mbox_name === $trash) { - $rcmail->output->command('set_trash_count', $rcmail->storage->count($mbox_name, 'EXISTS', true)); + $rcmail->output->add_js_call('set_trash_count', $rcmail->storage->count($mbox_name, 'EXISTS', true)); } } diff --git a/program/actions/mail/compose.php b/program/actions/mail/compose.php index 29d2e94df59..5b309256a01 100644 --- a/program/actions/mail/compose.php +++ b/program/actions/mail/compose.php @@ -1241,7 +1241,7 @@ public static function write_forward_attachments() 'name' => 'msgsizeerrorfwd', 'vars' => ['num' => $size_errors, 'size' => $limit], ]); - $rcmail->output->command('display_message', $error, 'error'); + $rcmail->output->add_js_call('display_message', $error, 'error'); } } diff --git a/program/actions/mail/copy.php b/program/actions/mail/copy.php index 6db1a484ef9..18fa8d0b690 100644 --- a/program/actions/mail/copy.php +++ b/program/actions/mail/copy.php @@ -58,7 +58,7 @@ public function run($args = []) self::send_unread_count($target, true); - $rcmail->output->command('set_quota', self::quota_content(null, $multifolder ? $sources[0] : 'INBOX')); + $rcmail->output->add_js_call('set_quota', self::quota_content(null, $multifolder ? $sources[0] : 'INBOX')); } $rcmail->output->send(); diff --git a/program/actions/mail/delete.php b/program/actions/mail/delete.php index 74f498dd486..ecbb20d948d 100644 --- a/program/actions/mail/delete.php +++ b/program/actions/mail/delete.php @@ -57,7 +57,7 @@ public function run($args = []) if (empty($deleted)) { // send error message if ($_POST['_from'] != 'show') { - $rcmail->output->command('list_mailbox'); + $rcmail->output->add_js_call('list_mailbox'); } self::display_server_error('errordeleting'); @@ -75,9 +75,9 @@ public function run($args = []) if (!empty($_POST['_from']) && $_POST['_from'] == 'show') { if ($next = rcube_utils::get_input_string('_next_uid', rcube_utils::INPUT_GPC)) { - $rcmail->output->command('show_message', $next); + $rcmail->output->add_js_call('show_message', $next); } else { - $rcmail->output->command('command', 'list'); + $rcmail->output->add_js_call('command', 'list'); } $rcmail->output->send(); @@ -111,8 +111,8 @@ public function run($args = []) $rcmail->output->set_env('current_page', $page); $rcmail->output->set_env('pagecount', $pages); $rcmail->output->set_env('exists', $exists); - $rcmail->output->command('set_quota', self::quota_content(null, $multifolder ? $sources[0] : 'INBOX')); - $rcmail->output->command('set_rowcount', self::get_messagecount_text($msg_count), $mbox); + $rcmail->output->add_js_call('set_quota', self::quota_content(null, $multifolder ? $sources[0] : 'INBOX')); + $rcmail->output->add_js_call('set_rowcount', self::get_messagecount_text($msg_count), $mbox); if ($threading) { $count = rcube_utils::get_input_string('_count', rcube_utils::INPUT_POST); @@ -130,7 +130,7 @@ public function run($args = []) // set trash folder state if ($mbox === $trash) { - $rcmail->output->command('set_trash_count', $exists); + $rcmail->output->add_js_call('set_trash_count', $exists); } // send response diff --git a/program/actions/mail/folder_expunge.php b/program/actions/mail/folder_expunge.php index caa83c2a02c..426192ad39d 100644 --- a/program/actions/mail/folder_expunge.php +++ b/program/actions/mail/folder_expunge.php @@ -40,8 +40,8 @@ public function run($args = []) $rcmail->output->show_message('folderexpunged', 'confirmation'); if (!empty($_REQUEST['_reload'])) { - $rcmail->output->command('set_quota', self::quota_content(null, $mbox)); - $rcmail->output->command('message_list.clear'); + $rcmail->output->add_js_call('set_quota', self::quota_content(null, $mbox)); + $rcmail->output->add_js_call('message_list.clear'); $rcmail->action = 'list'; return; } diff --git a/program/actions/mail/folder_purge.php b/program/actions/mail/folder_purge.php index d820d1a7412..e54e734abb1 100644 --- a/program/actions/mail/folder_purge.php +++ b/program/actions/mail/folder_purge.php @@ -58,14 +58,14 @@ public function run($args = []) if ($success) { $rcmail->output->show_message('folderpurged', 'confirmation'); - $rcmail->output->command('set_unread_count', $mbox, 0); + $rcmail->output->add_js_call('set_unread_count', $mbox, 0); self::set_unseen_count($mbox, 0); // set trash folder state if ($mbox === $trash_mbox) { - $rcmail->output->command('set_trash_count', 0); + $rcmail->output->add_js_call('set_trash_count', 0); } elseif (strlen($trash_mbox)) { - $rcmail->output->command('set_trash_count', $rcmail->storage->count($trash_mbox, 'EXISTS')); + $rcmail->output->add_js_call('set_trash_count', $rcmail->storage->count($trash_mbox, 'EXISTS')); } if (!$delete && strlen($trash_mbox)) { @@ -76,9 +76,9 @@ public function run($args = []) $rcmail->output->set_env('messagecount', 0); $rcmail->output->set_env('pagecount', 0); $rcmail->output->set_env('exists', 0); - $rcmail->output->command('message_list.clear'); - $rcmail->output->command('set_rowcount', self::get_messagecount_text(), $mbox); - $rcmail->output->command('set_quota', self::quota_content(null, $mbox)); + $rcmail->output->add_js_call('message_list.clear'); + $rcmail->output->add_js_call('set_rowcount', self::get_messagecount_text(), $mbox); + $rcmail->output->add_js_call('set_quota', self::quota_content(null, $mbox)); } } else { self::display_server_error(); diff --git a/program/actions/mail/getunread.php b/program/actions/mail/getunread.php index 1b19df18d5a..8c5f2654675 100644 --- a/program/actions/mail/getunread.php +++ b/program/actions/mail/getunread.php @@ -52,14 +52,14 @@ public function run($args = []) // after possible message status change when opening a message // not in preview frame if ($unseen || $unseen_old === null || $mbox == $current) { - $rcmail->output->command('set_unread_count', $mbox, $unseen, $inbox && $mbox == 'INBOX'); + $rcmail->output->add_js_call('set_unread_count', $mbox, $unseen, $inbox && $mbox == 'INBOX'); } self::set_unseen_count($mbox, $unseen); // set trash folder state if ($mbox === $trash) { - $rcmail->output->command('set_trash_count', $rcmail->storage->count($mbox, 'EXISTS')); + $rcmail->output->add_js_call('set_trash_count', $rcmail->storage->count($mbox, 'EXISTS')); } } } diff --git a/program/actions/mail/group_expand.php b/program/actions/mail/group_expand.php index 545c8981fa1..8253f5a427b 100644 --- a/program/actions/mail/group_expand.php +++ b/program/actions/mail/group_expand.php @@ -49,7 +49,7 @@ public function run($args = []) } } - $rcmail->output->command('replace_group_recipients', $gid, implode(', ', array_unique($members))); + $rcmail->output->add_js_call('replace_group_recipients', $gid, implode(', ', array_unique($members))); } $rcmail->output->send(); diff --git a/program/actions/mail/headers.php b/program/actions/mail/headers.php index a04f873e938..bb8d2f2285b 100644 --- a/program/actions/mail/headers.php +++ b/program/actions/mail/headers.php @@ -72,7 +72,7 @@ public function run($args = []) if ($inline) { $rcmail->output->set_env('dialog_class', 'text-nowrap'); } else { - $rcmail->output->command('set_headers', $source); + $rcmail->output->add_js_call('set_headers', $source); } } elseif (!$inline) { $rcmail->output->show_message('messageopenerror', 'error'); diff --git a/program/actions/mail/import.php b/program/actions/mail/import.php index 6cc59fc25c0..6e5a7550804 100644 --- a/program/actions/mail/import.php +++ b/program/actions/mail/import.php @@ -73,7 +73,7 @@ public function run($args = []) if ($imported) { $rcmail->output->show_message($rcmail->gettext(['name' => 'importmessagesuccess', 'nr' => $imported, 'vars' => ['nr' => $imported]]), 'confirmation'); - $rcmail->output->command('command', 'list'); + $rcmail->output->add_js_call('command', 'list'); } else { $rcmail->output->show_message('importmessageerror', 'error'); } diff --git a/program/actions/mail/index.php b/program/actions/mail/index.php index 189140c13ae..eb43d7dbea8 100644 --- a/program/actions/mail/index.php +++ b/program/actions/mail/index.php @@ -491,10 +491,10 @@ public static function js_message_list($a_headers, $insert_top = false, $a_show_ // get name of smart From/To column in folder context $smart_col = self::message_list_smart_column_name(); - $rcmail->output->command('set_message_coltypes', array_values($a_show_cols), $thead, $smart_col); + $rcmail->output->add_js_call('set_message_coltypes', array_values($a_show_cols), $thead, $smart_col); if ($multifolder && $_SESSION['search_scope'] == 'all') { - $rcmail->output->command('select_folder', ''); + $rcmail->output->add_js_call('select_folder', ''); } $rcmail->output->set_env('multifolder_listing', $multifolder); @@ -607,11 +607,11 @@ public static function js_message_list($a_headers, $insert_top = false, $a_show_ $a_msg_cols = array_merge($a_msg_cols, $header->list_cols); } - $rcmail->output->command('add_message_row', $header->uid, $a_msg_cols, $a_msg_flags, $insert_top); + $rcmail->output->add_js_call('add_message_row', $header->uid, $a_msg_cols, $a_msg_flags, $insert_top); } if ($rcmail->storage->get_threading()) { - $rcmail->output->command('init_threads', $roots, $mbox); + $rcmail->output->add_js_call('init_threads', $roots, $mbox); } } @@ -835,7 +835,7 @@ public static function send_unread_count($mbox_name, $force = false, $count = nu } if ($unseen !== $old_unseen || ($mbox_name == 'INBOX')) { - $rcmail->output->command('set_unread_count', $mbox_name, $unseen, + $rcmail->output->add_js_call('set_unread_count', $mbox_name, $unseen, $mbox_name == 'INBOX', $unseen && $mark ? $mark : ''); } diff --git a/program/actions/mail/list.php b/program/actions/mail/list.php index 248a6b78b40..2a37b3530b9 100644 --- a/program/actions/mail/list.php +++ b/program/actions/mail/list.php @@ -115,11 +115,11 @@ public function run($args = []) $rcmail->output->set_env('threading', $threading); $rcmail->output->set_env('current_page', $page); $rcmail->output->set_env('exists', $exists); - $rcmail->output->command('set_rowcount', self::get_messagecount_text($count), $mbox_name); + $rcmail->output->add_js_call('set_rowcount', self::get_messagecount_text($count), $mbox_name); // remove old message rows if commanded by the client if (!empty($_REQUEST['_clear'])) { - $rcmail->output->command('clear_message_list'); + $rcmail->output->add_js_call('clear_message_list'); } // add message rows @@ -149,11 +149,11 @@ public function run($args = []) // set trash folder state if ($mbox_name === $rcmail->config->get('trash_mbox')) { - $rcmail->output->command('set_trash_count', $exists); + $rcmail->output->add_js_call('set_trash_count', $exists); } if ($page == 1) { - $rcmail->output->command('set_quota', self::quota_content(null, !empty($multifolder) ? 'INBOX' : $mbox_name)); + $rcmail->output->add_js_call('set_quota', self::quota_content(null, !empty($multifolder) ? 'INBOX' : $mbox_name)); } // send response diff --git a/program/actions/mail/list_contacts.php b/program/actions/mail/list_contacts.php index 0db084916d5..8878b6a0c71 100644 --- a/program/actions/mail/list_contacts.php +++ b/program/actions/mail/list_contacts.php @@ -121,7 +121,7 @@ public function run($args = []) $jsresult[$row_id] = format_email_recipient($email, $name); - $rcmail->output->command('add_contact_row', $row_id, [ + $rcmail->output->add_js_call('add_contact_row', $row_id, [ $keyname => html::a( ['title' => $email], rcube::Q($name ?: $email) @@ -137,7 +137,7 @@ public function run($args = []) // update env $rcmail->output->set_env('contactdata', $jsresult); $rcmail->output->set_env('pagecount', isset($result) ? ceil($result->count / $page_size) : 1); - $rcmail->output->command('set_page_buttons'); + $rcmail->output->add_js_call('set_page_buttons'); // send response $rcmail->output->send(); @@ -160,7 +160,7 @@ public static function compose_contact_groups($abook, $source_id, $search = null foreach ((array) $group['email'] as $email) { $row_id = 'G' . $group['ID']; $jsresult[$row_id] = format_email_recipient($email, $group['name']); - $rcmail->output->command('add_contact_row', $row_id, [ + $rcmail->output->add_js_call('add_contact_row', $row_id, [ 'contactgroup' => html::span(['title' => $email], rcube::Q($group['name'])), ], 'group'); } @@ -168,7 +168,7 @@ public static function compose_contact_groups($abook, $source_id, $search = null // make virtual groups clickable to list their members elseif (!empty($group['virtual'])) { $row_id = 'G' . $group['ID']; - $rcmail->output->command('add_contact_row', $row_id, [ + $rcmail->output->add_js_call('add_contact_row', $row_id, [ 'contactgroup' => html::a([ 'href' => '#list', 'rel' => $group['ID'], @@ -186,7 +186,7 @@ public static function compose_contact_groups($abook, $source_id, $search = null elseif (($result = $abook->count()) && $result->count) { $row_id = 'E' . $group['ID']; $jsresult[$row_id] = ['name' => $group['name'], 'source' => $source_id]; - $rcmail->output->command('add_contact_row', $row_id, [ + $rcmail->output->add_js_call('add_contact_row', $row_id, [ 'contactgroup' => rcube::Q($group['name'] . ' (' . intval($result->count) . ')'), ], 'group'); } diff --git a/program/actions/mail/mark.php b/program/actions/mail/mark.php index 42dba006de0..beb08fbf0c4 100644 --- a/program/actions/mail/mark.php +++ b/program/actions/mail/mark.php @@ -79,7 +79,7 @@ public function run($args = []) if (!$marked) { // send error message if ($from != 'show') { - $rcmail->output->command('list_mailbox'); + $rcmail->output->add_js_call('list_mailbox'); } self::display_server_error('errormarking'); @@ -96,7 +96,7 @@ public function run($args = []) } if ($read && !$skip_deleted) { - $rcmail->output->command('flag_deleted_as_read', $ruids); + $rcmail->output->add_js_call('flag_deleted_as_read', $ruids); } } @@ -109,9 +109,9 @@ public function run($args = []) } elseif ($flag == 'DELETED' && $skip_deleted) { if ($from == 'show') { if ($next = rcube_utils::get_input_value('_next_uid', rcube_utils::INPUT_GPC)) { - $rcmail->output->command('show_message', $next); + $rcmail->output->add_js_call('show_message', $next); } else { - $rcmail->output->command('command', 'list'); + $rcmail->output->add_js_call('command', 'list'); } } else { $search_request = rcube_utils::get_input_value('_search', rcube_utils::INPUT_GPC); @@ -145,7 +145,7 @@ public function run($args = []) $rcmail->output->set_env('messagecount', $msg_count); $rcmail->output->set_env('current_page', $page); $rcmail->output->set_env('pagecount', $pages); - $rcmail->output->command('set_rowcount', self::get_messagecount_text($msg_count), $mbox); + $rcmail->output->add_js_call('set_rowcount', self::get_messagecount_text($msg_count), $mbox); if ($threading) { $count = rcube_utils::get_input_value('_count', rcube_utils::INPUT_POST); diff --git a/program/actions/mail/move.php b/program/actions/mail/move.php index 6f69ac44bfc..b291fb3e1a1 100644 --- a/program/actions/mail/move.php +++ b/program/actions/mail/move.php @@ -66,7 +66,7 @@ public function run($args = []) if (!$success) { // send error message if (empty($_POST['_from']) || $_POST['_from'] != 'show') { - $rcmail->output->command('list_mailbox'); + $rcmail->output->add_js_call('list_mailbox'); } self::display_server_error('errormoving', null, $target == $trash ? 'delete' : ''); @@ -77,7 +77,7 @@ public function run($args = []) if (!empty($_POST['_refresh'])) { // FIXME: send updated message rows instead of reloading the entire list - $rcmail->output->command('refresh_list'); + $rcmail->output->add_js_call('refresh_list'); } else { $addrows = true; } @@ -91,9 +91,9 @@ public function run($args = []) if (!empty($_POST['_from']) && $_POST['_from'] == 'show') { if ($next = rcube_utils::get_input_string('_next_uid', rcube_utils::INPUT_GPC)) { - $rcmail->output->command('show_message', $next); + $rcmail->output->add_js_call('show_message', $next); } else { - $rcmail->output->command('command', 'list'); + $rcmail->output->add_js_call('command', 'list'); } $rcmail->output->send(); @@ -128,8 +128,8 @@ public function run($args = []) $rcmail->output->set_env('current_page', $page); $rcmail->output->set_env('pagecount', $pages); $rcmail->output->set_env('exists', $exists); - $rcmail->output->command('set_quota', self::quota_content(null, $multifolder ? $sources[0] : 'INBOX')); - $rcmail->output->command('set_rowcount', self::get_messagecount_text($msg_count), $mbox); + $rcmail->output->add_js_call('set_quota', self::quota_content(null, $multifolder ? $sources[0] : 'INBOX')); + $rcmail->output->add_js_call('set_rowcount', self::get_messagecount_text($msg_count), $mbox); if ($threading) { $count = rcube_utils::get_input_string('_count', rcube_utils::INPUT_POST); @@ -148,9 +148,9 @@ public function run($args = []) // set trash folder state if ($mbox === $trash) { - $rcmail->output->command('set_trash_count', $exists); + $rcmail->output->add_js_call('set_trash_count', $exists); } elseif ($target === $trash) { - $rcmail->output->command('set_trash_count', $rcmail->storage->count($trash, 'EXISTS', true)); + $rcmail->output->add_js_call('set_trash_count', $rcmail->storage->count($trash, 'EXISTS', true)); } // send response diff --git a/program/actions/mail/pagenav.php b/program/actions/mail/pagenav.php index 3213eb92249..078004d09aa 100644 --- a/program/actions/mail/pagenav.php +++ b/program/actions/mail/pagenav.php @@ -41,19 +41,19 @@ public function run($args = []) $last = $pos < $cnt - 1 ? $index->get_element('LAST') : 0; } else { // error, this will at least disable page navigation - $rcmail->output->command('set_rowcount', ''); + $rcmail->output->add_js_call('set_rowcount', ''); $rcmail->output->send(); } // Set UIDs and activate navigation buttons if (!empty($prev)) { $rcmail->output->set_env('prev_uid', $prev); - $rcmail->output->command('enable_command', 'previousmessage', 'firstmessage', true); + $rcmail->output->add_js_call('enable_command', 'previousmessage', 'firstmessage', true); } if (!empty($next)) { $rcmail->output->set_env('next_uid', $next); - $rcmail->output->command('enable_command', 'nextmessage', 'lastmessage', true); + $rcmail->output->add_js_call('enable_command', 'nextmessage', 'lastmessage', true); } if (!empty($first)) { @@ -68,7 +68,7 @@ public function run($args = []) $rcmail->output->set_env('messagecount', 1); // Set rowcount text - $rcmail->output->command('set_rowcount', $rcmail->gettext([ + $rcmail->output->add_js_call('set_rowcount', $rcmail->gettext([ 'name' => 'messagenrof', 'vars' => ['nr' => ($pos ?? 0) + 1, 'count' => $cnt], ])); diff --git a/program/actions/mail/search.php b/program/actions/mail/search.php index 3b2f986896f..f3a3ddc11af 100644 --- a/program/actions/mail/search.php +++ b/program/actions/mail/search.php @@ -139,7 +139,7 @@ public function run($args = []) // advice the client to re-send the (cross-folder) search request elseif (!empty($result) && !empty($result->incomplete)) { $count = 0; // keep UI locked - $rcmail->output->command('continue_search', $search_request); + $rcmail->output->add_js_call('continue_search', $search_request); } else { $count = 0; @@ -147,7 +147,7 @@ public function run($args = []) $rcmail->output->set_env('multifolder_listing', isset($result) ? !empty($result->multi) : false); if (isset($result) && !empty($result->multi) && $scope == 'all') { - $rcmail->output->command('select_folder', ''); + $rcmail->output->add_js_call('select_folder', ''); } } @@ -157,7 +157,7 @@ public function run($args = []) $rcmail->output->set_env('messagecount', $count); $rcmail->output->set_env('pagecount', ceil($count / $rcmail->storage->get_pagesize())); $rcmail->output->set_env('exists', !strlen($mbox) ? 0 : $rcmail->storage->count($mbox, 'EXISTS')); - $rcmail->output->command('set_rowcount', self::get_messagecount_text($count, 1), $mbox); + $rcmail->output->add_js_call('set_rowcount', self::get_messagecount_text($count, 1), $mbox); self::list_pagetitle(); @@ -167,7 +167,7 @@ public function run($args = []) } if (isset($result) && empty($result->incomplete)) { - $rcmail->output->command('set_quota', self::quota_content(null, !empty($result->multi) ? 'INBOX' : $mbox)); + $rcmail->output->add_js_call('set_quota', self::quota_content(null, !empty($result->multi) ? 'INBOX' : $mbox)); } $rcmail->output->send(); diff --git a/program/actions/mail/search_contacts.php b/program/actions/mail/search_contacts.php index 1e1e96df4c6..bc21114f05f 100644 --- a/program/actions/mail/search_contacts.php +++ b/program/actions/mail/search_contacts.php @@ -108,7 +108,7 @@ public function run($args = []) } $link = html::a(['title' => $title], $link_content); - $rcmail->output->command('add_contact_row', $row_id, [$keyname => $link], $classname); + $rcmail->output->add_js_call('add_contact_row', $row_id, [$keyname => $link], $classname); } } @@ -123,7 +123,7 @@ public function run($args = []) $rcmail->output->set_env('search_request', $search_request); $rcmail->output->set_env('source', ''); - $rcmail->output->command('unselect_directory'); + $rcmail->output->add_js_call('unselect_directory'); } elseif (!$group_count) { $rcmail->output->show_message('nocontactsfound', 'notice'); } @@ -131,7 +131,7 @@ public function run($args = []) // update env $rcmail->output->set_env('contactdata', $jsresult); $rcmail->output->set_env('pagecount', ceil($result->count / $page_size)); - $rcmail->output->command('set_page_buttons'); + $rcmail->output->add_js_call('set_page_buttons'); // send response $rcmail->output->send(); diff --git a/program/actions/mail/send.php b/program/actions/mail/send.php index f0e63445d21..18751900b89 100644 --- a/program/actions/mail/send.php +++ b/program/actions/mail/send.php @@ -159,7 +159,7 @@ public function run($args = []) } $rcmail->output->show_message('mispellingsfound', 'error'); - $rcmail->output->command('spellcheck_resume', $result); + $rcmail->output->add_js_call('spellcheck_resume', $result); $rcmail->output->send('iframe'); } } @@ -221,7 +221,7 @@ public function run($args = []) if (!$saved && $savedraft) { self::display_server_error('errorsaving'); // start the auto-save timer again - $rcmail->output->command('auto_save_start'); + $rcmail->output->add_js_call('auto_save_start'); $rcmail->output->send('iframe'); } @@ -264,12 +264,12 @@ public function run($args = []) // update "_draft_saveid" and the "cmp_hash" to prevent "Unsaved changes" warning $COMPOSE['param']['draft_uid'] = $plugin['uid']; - $rcmail->output->command('set_draft_id', $plugin['uid']); - $rcmail->output->command('compose_field_hash', true); + $rcmail->output->add_js_call('set_draft_id', $plugin['uid']); + $rcmail->output->add_js_call('compose_field_hash', true); } // start the auto-save timer again - $rcmail->output->command('auto_save_start'); + $rcmail->output->add_js_call('auto_save_start'); } else { // Collect folders which could contain the composed message, // we'll refresh the list if currently opened folder is one of them (#1490238) @@ -299,7 +299,7 @@ public function run($args = []) $rcmail->session->remove('compose_data_' . $COMPOSE_ID); $_SESSION['last_compose_session'] = $COMPOSE_ID; - $rcmail->output->command('remove_compose_data', $COMPOSE_ID); + $rcmail->output->add_js_call('remove_compose_data', $COMPOSE_ID); if ($store_folder) { $folders[] = $store_target; @@ -308,7 +308,7 @@ public function run($args = []) $msg = $rcmail->gettext($saveonly ? 'successfullysaved' : 'messagesent'); - $rcmail->output->command('sent_successfully', 'confirmation', $msg, $folders, $save_error); + $rcmail->output->add_js_call('sent_successfully', 'confirmation', $msg, $folders, $save_error); } $rcmail->output->send('iframe'); diff --git a/program/actions/mail/show.php b/program/actions/mail/show.php index 4b831754838..776693beccd 100644 --- a/program/actions/mail/show.php +++ b/program/actions/mail/show.php @@ -132,7 +132,7 @@ public function run($args = []) if ($v > 0) { $rcmail->output->set_env('mail_read_time', $v); } elseif ($v == 0) { - $rcmail->output->command('set_unread_message', $MESSAGE->uid, $mbox_name); + $rcmail->output->add_js_call('set_unread_message', $MESSAGE->uid, $mbox_name); $rcmail->plugins->exec_hook('message_read', [ 'uid' => $MESSAGE->uid, 'mailbox' => $mbox_name, diff --git a/program/actions/settings/folder_delete.php b/program/actions/settings/folder_delete.php index 2b00f2c94af..ee8320a6bef 100644 --- a/program/actions/settings/folder_delete.php +++ b/program/actions/settings/folder_delete.php @@ -51,11 +51,11 @@ public function run($args = []) if (!empty($deleted)) { // Remove folder and subfolders rows - $rcmail->output->command('remove_folder_row', $mbox); + $rcmail->output->add_js_call('remove_folder_row', $mbox); $rcmail->output->show_message('folderdeleted', 'confirmation'); // Clear content frame - $rcmail->output->command('subscription_select'); - $rcmail->output->command('set_quota', self::quota_content()); + $rcmail->output->add_js_call('subscription_select'); + $rcmail->output->add_js_call('set_quota', self::quota_content()); } else { self::display_server_error('errorsaving'); } diff --git a/program/actions/settings/folder_edit.php b/program/actions/settings/folder_edit.php index f3afe75cec4..3de9ea84694 100644 --- a/program/actions/settings/folder_edit.php +++ b/program/actions/settings/folder_edit.php @@ -306,7 +306,7 @@ public static function folder_form($attrib) $rcmail->output->set_env('folder', $mbox); if ($mbox !== '' && empty($_POST)) { - $rcmail->output->command('parent.set_quota', self::quota_content(null, $mbox)); + $rcmail->output->add_js_call('parent.set_quota', self::quota_content(null, $mbox)); } return $out; diff --git a/program/actions/settings/folder_purge.php b/program/actions/settings/folder_purge.php index bf92c5c25ff..95578af9174 100644 --- a/program/actions/settings/folder_purge.php +++ b/program/actions/settings/folder_purge.php @@ -53,13 +53,13 @@ public function run($args = []) if ($delete) { $rcmail->output->show_message('folderpurged', 'confirmation'); - $rcmail->output->command('set_quota', self::quota_content(null, $mbox)); + $rcmail->output->add_js_call('set_quota', self::quota_content(null, $mbox)); } else { $rcmail->output->show_message('messagemoved', 'confirmation'); } $_SESSION['unseen_count'][$mbox] = 0; - $rcmail->output->command('show_folder', $mbox, null, true); + $rcmail->output->add_js_call('show_folder', $mbox, null, true); } else { self::display_server_error('errorsaving'); } diff --git a/program/actions/settings/folder_save.php b/program/actions/settings/folder_save.php index dadb845a305..0fc21b41345 100644 --- a/program/actions/settings/folder_save.php +++ b/program/actions/settings/folder_save.php @@ -59,7 +59,7 @@ public function run($args = []) } if (!empty($error)) { - $rcmail->output->command('display_message', $error, 'error'); + $rcmail->output->add_js_call('display_message', $error, 'error'); } else { if (!empty($options['protected']) || !empty($options['norename'])) { $name_imap = $old_imap; @@ -84,7 +84,7 @@ public function run($args = []) } if (!empty($error)) { - $rcmail->output->command('display_message', $error, 'error'); + $rcmail->output->add_js_call('display_message', $error, 'error'); $folder = null; } else { $folder = [ @@ -140,7 +140,7 @@ public function run($args = []) $rcmail->output->show_message('foldercreated', 'confirmation'); // reset folder preview frame - $rcmail->output->command('subscription_select'); + $rcmail->output->add_js_call('subscription_select'); $rcmail->output->send('iframe'); } else { // show error message diff --git a/program/actions/settings/folder_size.php b/program/actions/settings/folder_size.php index e4b673b6d97..df4c62cbca9 100644 --- a/program/actions/settings/folder_size.php +++ b/program/actions/settings/folder_size.php @@ -39,7 +39,7 @@ public function run($args = []) // @TODO: check quota and show percentage usage of specified mailbox? if ($size !== false) { - $rcmail->output->command('folder_size_update', self::show_bytes($size)); + $rcmail->output->add_js_call('folder_size_update', self::show_bytes($size)); } else { self::display_server_error(); } diff --git a/program/actions/settings/folder_subscribe.php b/program/actions/settings/folder_subscribe.php index d4715218090..3f78df389a8 100644 --- a/program/actions/settings/folder_subscribe.php +++ b/program/actions/settings/folder_subscribe.php @@ -53,13 +53,13 @@ public function run($args = []) if (!empty($result)) { // Handle subscription of protected folder (#1487656) if ($rcmail->config->get('protect_default_folders') && $storage->is_special_folder($mbox)) { - $rcmail->output->command('disable_subscription', $mbox); + $rcmail->output->add_js_call('disable_subscription', $mbox); } $rcmail->output->show_message('foldersubscribed', 'confirmation'); } else { self::display_server_error('errorsaving'); - $rcmail->output->command('reset_subscription', $mbox, false); + $rcmail->output->add_js_call('reset_subscription', $mbox, false); } $rcmail->output->send(); diff --git a/program/actions/settings/folder_unsubscribe.php b/program/actions/settings/folder_unsubscribe.php index e8bc1cddeec..2e63db92645 100644 --- a/program/actions/settings/folder_unsubscribe.php +++ b/program/actions/settings/folder_unsubscribe.php @@ -42,7 +42,7 @@ public function run($args = []) $rcmail->output->show_message('folderunsubscribed', 'confirmation'); } else { self::display_server_error('errorsaving'); - $rcmail->output->command('reset_subscription', $mbox, true); + $rcmail->output->add_js_call('reset_subscription', $mbox, true); } $rcmail->output->send(); diff --git a/program/actions/settings/folders.php b/program/actions/settings/folders.php index eabce5504c3..249481408f5 100644 --- a/program/actions/settings/folders.php +++ b/program/actions/settings/folders.php @@ -366,10 +366,10 @@ public static function update_folder_row($name, $oldname = null, $subscribe = fa $protected = !empty($options['protected']) || !empty($options['noselect']); if ($oldname === null) { - $rcmail->output->command('add_folder_row', $name, $name_utf8, $display_name, + $rcmail->output->add_js_call('add_folder_row', $name, $name_utf8, $display_name, $protected, $subscribe, $class_name); } else { - $rcmail->output->command('replace_folder_row', $oldname, $name, $name_utf8, $display_name, + $rcmail->output->add_js_call('replace_folder_row', $oldname, $name, $name_utf8, $display_name, $protected, $class_name); } } diff --git a/program/actions/settings/identity_delete.php b/program/actions/settings/identity_delete.php index 6ececcca850..074b9fe9f71 100644 --- a/program/actions/settings/identity_delete.php +++ b/program/actions/settings/identity_delete.php @@ -41,7 +41,7 @@ public function run($args = []) if ($deleted === true) { $rcmail->output->show_message('deletedsuccessfully', 'confirmation', null, false); - $rcmail->output->command('remove_identity', $iid); + $rcmail->output->add_js_call('remove_identity', $iid); } else { $msg = !empty($plugin['message']) ? $plugin['message'] : ($deleted === -1 ? 'nodeletelastidentity' : 'errorsaving'); $rcmail->output->show_message($msg, 'error', null, false); diff --git a/program/actions/settings/identity_save.php b/program/actions/settings/identity_save.php index f718f74fbe2..11e36ab3690 100644 --- a/program/actions/settings/identity_save.php +++ b/program/actions/settings/identity_save.php @@ -144,7 +144,7 @@ public function run($args = []) // update the changed col in list $name = $save_data['name'] . ' <' . rcube_utils::idn_to_utf8($save_data['email']) . '>'; - $rcmail->output->command('parent.update_identity_row', $iid, rcube::Q(trim($name))); + $rcmail->output->add_js_call('parent.update_identity_row', $iid, rcube::Q(trim($name))); } else { // show error message $error = !empty($plugin['message']) ? $plugin['message'] : 'errorsaving'; @@ -185,7 +185,7 @@ public function run($args = []) // add a new row to the list $name = $save_data['name'] . ' <' . rcube_utils::idn_to_utf8($save_data['email']) . '>'; - $rcmail->output->command('parent.update_identity_row', $insert_id, rcube::Q(trim($name)), true); + $rcmail->output->add_js_call('parent.update_identity_row', $insert_id, rcube::Q(trim($name)), true); } else { // show error message $error = !empty($plugin['message']) ? $plugin['message'] : 'errorsaving'; diff --git a/program/actions/settings/index.php b/program/actions/settings/index.php index 5525d386e1f..d2a3af6a3f8 100644 --- a/program/actions/settings/index.php +++ b/program/actions/settings/index.php @@ -371,7 +371,7 @@ public static function user_prefs($current = null) if ($current) { $product_name = $rcmail->config->get('product_name', 'Roundcube Webmail'); - $rcmail->output->command('check_protocol_handler', $product_name, '#mailtoprotohandler'); + $rcmail->output->add_js_call('check_protocol_handler', $product_name, '#mailtoprotohandler'); } $blocks['browser']['options']['mailtoprotohandler'] = [ diff --git a/program/actions/settings/prefs_save.php b/program/actions/settings/prefs_save.php index 2c61456188d..94239148f69 100644 --- a/program/actions/settings/prefs_save.php +++ b/program/actions/settings/prefs_save.php @@ -171,7 +171,7 @@ public function run($args = []) // switch UI language if (isset($_POST['_language']) && $a_user_prefs['language'] != $_SESSION['language']) { $rcmail->load_language($a_user_prefs['language']); - $rcmail->output->command('reload', 500); + $rcmail->output->add_js_call('reload', 500); } // switch skin (if valid, otherwise unset the pref and fall back to default) @@ -179,7 +179,7 @@ public function run($args = []) if (!$rcmail->output->check_skin($a_user_prefs['skin'])) { unset($a_user_prefs['skin']); } elseif ($rcmail->config->get('skin') != $a_user_prefs['skin']) { - $rcmail->output->command('reload', 500); + $rcmail->output->add_js_call('reload', 500); } } diff --git a/program/actions/settings/response_delete.php b/program/actions/settings/response_delete.php index 5edf9786a65..6e0ac1f6a09 100644 --- a/program/actions/settings/response_delete.php +++ b/program/actions/settings/response_delete.php @@ -37,8 +37,8 @@ public function run($args = []) $deleted = !$plugin['abort'] ? $rcmail->user->delete_response($id) : $plugin['result']; if (!empty($deleted)) { - $rcmail->output->command('display_message', $rcmail->gettext('deletedsuccessfully'), 'confirmation'); - $rcmail->output->command('remove_response', $id); + $rcmail->output->add_js_call('display_message', $rcmail->gettext('deletedsuccessfully'), 'confirmation'); + $rcmail->output->add_js_call('remove_response', $id); } else { $msg = !empty($plugin['message']) ? $plugin['message'] : 'errorsaving'; $rcmail->output->show_message($msg, 'error'); diff --git a/program/actions/settings/response_get.php b/program/actions/settings/response_get.php index 3b8bf79e064..92899c2314b 100644 --- a/program/actions/settings/response_get.php +++ b/program/actions/settings/response_get.php @@ -51,7 +51,7 @@ public function run($args = []) $response['is_html'] = false; } - $rcmail->output->command('insert_response', $response); + $rcmail->output->add_js_call('insert_response', $response); } $rcmail->output->send(); diff --git a/program/actions/settings/response_save.php b/program/actions/settings/response_save.php index a3290bd5b41..ddd1b14728c 100644 --- a/program/actions/settings/response_save.php +++ b/program/actions/settings/response_save.php @@ -71,7 +71,7 @@ public function run($args = []) if ($updated) { $rcmail->output->show_message('successfullysaved', 'confirmation'); - $rcmail->output->command('parent.update_response_row', $id, rcube::Q($response['name'])); + $rcmail->output->add_js_call('parent.update_response_row', $id, rcube::Q($response['name'])); } else { // show error message $error = !empty($plugin['message']) ? $plugin['message'] : 'errorsaving'; @@ -93,7 +93,7 @@ public function run($args = []) $response['id'] = $_GET['_id'] = $insert_id; // add a new row to the list - $rcmail->output->command('parent.update_response_row', $insert_id, rcube::Q($response['name']), true); + $rcmail->output->add_js_call('parent.update_response_row', $insert_id, rcube::Q($response['name']), true); } else { $error = !empty($plugin['message']) ? $plugin['message'] : 'errorsaving'; $rcmail->output->show_message($error, 'error', null, false); diff --git a/program/actions/settings/upload.php b/program/actions/settings/upload.php index 1d3d49edb2f..018cf0ac691 100644 --- a/program/actions/settings/upload.php +++ b/program/actions/settings/upload.php @@ -87,7 +87,7 @@ public function run($args = []) $id = $attachment['id']; $content = rcube::Q($attachment['name']); - $rcmail->output->command('add2attachment_list', "rcmfile{$id}", [ + $rcmail->output->add_js_call('add2attachment_list', "rcmfile{$id}", [ 'html' => $content, 'name' => $attachment['name'], 'mimetype' => $attachment['mimetype'], @@ -108,7 +108,7 @@ public function run($args = []) } } } elseif (self::upload_failure()) { - $rcmail->output->command('remove_from_attachment_list', $uploadid); + $rcmail->output->add_js_call('remove_from_attachment_list', $uploadid); } $rcmail->output->send('iframe'); diff --git a/program/include/rcmail_action.php b/program/include/rcmail_action.php index 003d0b47a86..0d7813430a0 100644 --- a/program/include/rcmail_action.php +++ b/program/include/rcmail_action.php @@ -171,7 +171,7 @@ public static function quota_display($attrib) $quota = self::quota_content($attrib); $rcmail->output->add_gui_object('quotadisplay', $attrib['id']); - $rcmail->output->command('set_quota', $quota); + $rcmail->output->add_js_call('set_quota', $quota); return html::span($attrib, ' '); } @@ -431,8 +431,8 @@ public static function html_editor($mode = '', $editorId = null) } if (!empty($editorId)) { - $rcmail->output->command('enable_command', 'toggle-editor', true); - $rcmail->output->command('editor_init', null, $editorId); + $rcmail->output->add_js_call('enable_command', 'toggle-editor', true); + $rcmail->output->add_js_call('editor_init', null, $editorId); } $rcmail->output->include_script('tinymce/tinymce.min.js'); @@ -583,7 +583,7 @@ public static function upload_error($php_error, $attachment = null, $add_error = $msg = $rcmail->gettext('fileuploaderror'); } - $rcmail->output->command('display_message', $msg, 'error'); + $rcmail->output->add_js_call('display_message', $msg, 'error'); } /** @@ -610,7 +610,7 @@ public static function upload_failure() $msg = $rcmail->gettext('fileuploaderror'); } - $rcmail->output->command('display_message', $msg, 'error'); + $rcmail->output->add_js_call('display_message', $msg, 'error'); return true; } diff --git a/program/include/rcmail_output.php b/program/include/rcmail_output.php index 2d0d7d8fc22..77dc4a29cb1 100644 --- a/program/include/rcmail_output.php +++ b/program/include/rcmail_output.php @@ -107,7 +107,7 @@ public function reset() * @param string $cmd Method to call * @param mixed ...$args Method arguments */ - abstract public function command($cmd, ...$args); + abstract public function add_js_call($cmd, ...$args); /** * Add a localized label(s) to the client environment diff --git a/program/include/rcmail_output_cli.php b/program/include/rcmail_output_cli.php index b7a616e2bba..1f39a1d155f 100644 --- a/program/include/rcmail_output_cli.php +++ b/program/include/rcmail_output_cli.php @@ -30,7 +30,7 @@ class rcmail_output_cli extends rcmail_output * @see rcube_output::command() */ #[Override] - public function command($cmd, ...$args) + public function add_js_call($cmd, ...$args) { // NOP } diff --git a/program/include/rcmail_output_html.php b/program/include/rcmail_output_html.php index a4f121e6d44..d5752b30565 100644 --- a/program/include/rcmail_output_html.php +++ b/program/include/rcmail_output_html.php @@ -29,7 +29,7 @@ class rcmail_output_html extends rcmail_output protected $objects = []; protected $js_env = []; protected $js_labels = []; - protected $js_commands = []; + protected $js_calls = []; protected $skin_paths = []; protected $skin_extends = []; protected $skin_name = ''; @@ -483,7 +483,22 @@ protected function find_file_path($file, $skin_paths) */ public function add_gui_object($obj, $id) { - $this->js_commands[] = ['gui_object', $obj, $id]; + $this->js_calls[] = ['gui_object', $obj, $id]; + } + + /** + * Deprecated function to call a client method. + * Left here to allow a grace period for old code. + * + * @deprecated + * + * @param string $cmd Method to call + * @param mixed ...$args Method arguments + */ + public function command($cmd, ...$args) + { + rcube::write_log('errors', 'rcmail_output_html->command() is deprecated, replace it with a call to rcmail_output_html->add_js_call()'); + return $this->add_js_call($cmd, ...$args); } /** @@ -493,14 +508,14 @@ public function add_gui_object($obj, $id) * @param mixed ...$args Method arguments */ #[Override] - public function command($cmd, ...$args) + public function add_js_call($cmd, ...$args) { if (strpos($cmd, 'plugin.') !== false) { - $this->js_commands[] = ['triggerEvent', $cmd, $args[0]]; + $this->js_calls[] = ['triggerEvent', $cmd, $args[0]]; } else { array_unshift($args, $cmd); - $this->js_commands[] = $args; + $this->js_calls[] = $args; } } @@ -530,7 +545,7 @@ public function add_label(...$args) * @param bool $override Override last set message * @param int $timeout Message display time in seconds * - * @uses self::command() + * @uses self::add_js_call() */ #[Override] public function show_message($message, $type = 'notice', $vars = null, $override = true, $timeout = 0) @@ -547,7 +562,7 @@ public function show_message($message, $type = 'notice', $vars = null, $override } $this->message = $message; - $this->command('display_message', $msgtext, $type, $timeout * 1000); + $this->add_js_call('display_message', $msgtext, $type, $timeout * 1000); } } @@ -586,7 +601,7 @@ public function reset($all = false) $this->env = $this->js_env = $env; $this->framed = $framed || !empty($this->env['framed']); $this->js_labels = []; - $this->js_commands = []; + $this->js_calls = []; $this->header = ''; $this->footer = ''; $this->body = ''; @@ -852,7 +867,7 @@ protected function get_js_commands() $top_commands[] = ['hide_message', $unlock]; } - $commands = array_merge($top_commands, $this->js_commands); + $commands = array_merge($top_commands, $this->js_calls); // If this is framed, prefix all commands (which are the first element // of each sub-array) so they're called on the parent's `rcmail` @@ -1406,7 +1421,7 @@ protected function xml_command($matches) // define a container block (<< reindent once https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues/7179 is fixed) case 'container': if (!empty($attrib['name']) && !empty($attrib['id'])) { - $this->command('gui_container', $attrib['name'], $attrib['id']); + $this->add_js_call('gui_container', $attrib['name'], $attrib['id']); // let plugins insert some content here $hook = $this->app->plugins->exec_hook('template_container', $attrib + ['content' => '']); return $hook['content']; @@ -1760,7 +1775,7 @@ public function button($attrib) // register button in the system if (!empty($attrib['command'])) { - $this->js_commands[] = [ + $this->js_calls[] = [ 'register_button', $command, $attrib['id'], @@ -1982,7 +1997,7 @@ protected function _write($output = '') $page_header .= array_reduce((array) $this->script_files['head_bottom'], $merge_script_files); } - $page_footer .= html::div(['id' => 'js-data', 'style' => 'display: none', 'hidden' => true, 'data-js' => $this->get_js_commands()], ''); + $page_footer .= html::div(['id' => 'js-calls', 'style' => 'display: none', 'hidden' => true, 'data-js' => $this->get_js_commands()], ''); $page_footer .= $this->footer . "\n"; @@ -2355,7 +2370,7 @@ protected function preloader($attrib): void return; } - $this->command('preload_images', $images); + $this->add_js_call('preload_images', $images); } /** diff --git a/program/include/rcmail_output_json.php b/program/include/rcmail_output_json.php index 6c2902f4bd9..2c207ebd6e9 100644 --- a/program/include/rcmail_output_json.php +++ b/program/include/rcmail_output_json.php @@ -24,7 +24,7 @@ class rcmail_output_json extends rcmail_output { protected $texts = []; - protected $commands = []; + protected $js_calls = []; protected $callbacks = []; protected $message; protected $header_sent = false; @@ -63,7 +63,7 @@ public function set_pagetitle($title) $name = $this->config->get('product_name'); } - $this->command('set_pagetitle', empty($name) ? $title : $name . ' :: ' . $title); + $this->add_js_call('set_pagetitle', empty($name) ? $title : $name . ' :: ' . $title); } /** @@ -89,6 +89,21 @@ public function add_handlers($arr) // ignore } + /** + * Deprecated function to call a client method. + * Left here to allow a grace period for old code. + * + * @deprecated + * + * @param string $cmd Method to call + * @param mixed ...$args Method arguments + */ + public function command($cmd, ...$args) + { + rcube::write_log('errors', 'rcmail_output_json->command() is deprecated, replace it with a call to rcmail_output_html->add_js_call()'); + return $this->add_js_call($cmd, ...$args); + } + /** * Call a client method * @@ -96,14 +111,14 @@ public function add_handlers($arr) * @param mixed ...$args Additional arguments */ #[Override] - public function command($cmd, ...$args) + public function add_js_call($cmd, ...$args) { array_unshift($args, $cmd); if (strpos($args[0], 'plugin.') === 0) { $this->callbacks[] = $args; } else { - $this->commands[] = $args; + $this->js_calls[] = $args; } } @@ -133,7 +148,7 @@ public function add_label(...$args) * @param bool $override Override last set message * @param int $timeout Message displaying time in seconds * - * @uses self::command() + * @uses self::add_js_call() */ #[Override] public function show_message($message, $type = 'notice', $vars = null, $override = true, $timeout = 0) @@ -149,19 +164,19 @@ public function show_message($message, $type = 'notice', $vars = null, $override } $this->message = $message; - $this->command('display_message', $msgtext, $type, $timeout * 1000); + $this->add_js_call('display_message', $msgtext, $type, $timeout * 1000); } } /** - * Delete all stored env variables and commands + * Delete all stored env variables and js_calls */ #[Override] public function reset() { parent::reset(); $this->texts = []; - $this->commands = []; + $this->js_calls = []; } /** @@ -242,7 +257,7 @@ protected function remote_response($add = '') } // send function calls - $response['exec'] = $this->get_js_commands() . $add; + $response['exec'] = $this->get_js_calls() . $add; if (!empty($this->callbacks)) { $response['callbacks'] = $this->callbacks; @@ -259,13 +274,13 @@ protected function remote_response($add = '') } /** - * Return executable javascript code for all registered commands + * Return executable javascript code for all registered js_calls */ - protected function get_js_commands() + protected function get_js_calls() { $out = ''; - foreach ($this->commands as $args) { + foreach ($this->js_calls as $args) { $method = array_shift($args); foreach ($args as $i => $arg) { $args[$i] = self::json_serialize($arg, $this->devel_mode, false); diff --git a/program/js/app.js b/program/js/app.js index 0014f45e3cf..ac8624b154a 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -225,7 +225,7 @@ function rcube_webmail() { this.init = function () { var n; - this.interpret_js_data(); + this.interpret_js_calls(); this.task = this.env.task; @@ -806,17 +806,17 @@ function rcube_webmail() { * Handle function calls passed in via #js-calls. Through this, server code * can trigger Javascript-methods to be called. */ - this.interpret_js_data = function () { + this.interpret_js_calls = function () { // Do not use `.textContent`, and neither jQuery's `.text()` here, // because both modify the actual string! - var raw = document.getElementById('js-data').dataset.js; + var raw = document.getElementById('js-calls').dataset.js; if (!raw) { return; } var data = JSON.parse(raw); data.forEach((args) => { if (!Array.isArray(args)) { - this.log("Unexpected data in '#js-data'! This is not an array: ", args); + this.log("Unexpected data in '#js-calls'! This is not an array: ", args); } var command = args.shift(); if (command.startsWith('parent.')) { diff --git a/tests/Actions/Contacts/EditTest.php b/tests/Actions/Contacts/EditTest.php index a7b08e351f1..392f6300b9c 100644 --- a/tests/Actions/Contacts/EditTest.php +++ b/tests/Actions/Contacts/EditTest.php @@ -96,8 +96,8 @@ public function test_photo_drop_area() $output = $this->initOutput(\rcmail_action::MODE_HTTP, 'contacts', 'edit'); $result = \rcmail_action_contacts_edit::photo_drop_area(['id' => 'test']); - $commands = $output->getProperty('js_commands'); - $gui_object_commands = array_filter($commands, static function ($arr) { return $arr['0'] === 'gui_object'; }); + $js_calls = $output->getProperty('js_calls'); + $gui_object_commands = array_filter($js_calls, static function ($arr) { return $arr['0'] === 'gui_object'; }); $filedrop = $output->get_env('filedrop'); $this->assertSame([['gui_object', 'filedrop', 'test']], $gui_object_commands); diff --git a/tests/Actions/Mail/FolderExpungeTest.php b/tests/Actions/Mail/FolderExpungeTest.php index d21e1d40fbd..48c689fc237 100644 --- a/tests/Actions/Mail/FolderExpungeTest.php +++ b/tests/Actions/Mail/FolderExpungeTest.php @@ -67,21 +67,21 @@ public function test_folder_expunge_with_reload() $action->run(); - $commands = $output->getProperty('commands'); + $js_calls = $output->getProperty('js_calls'); $this->assertNull($output->getOutput()); $this->assertSame('list', \rcmail::get_instance()->action); - $this->assertCount(3, $commands); + $this->assertCount(3, $js_calls); $this->assertSame([ 'display_message', 'Folder successfully compacted.', 'confirmation', 0, ], - $commands[0] + $js_calls[0] ); - $this->assertSame('set_quota', $commands[1][0]); - $this->assertSame('message_list.clear', $commands[2][0]); + $this->assertSame('set_quota', $js_calls[1][0]); + $this->assertSame('message_list.clear', $js_calls[2][0]); } /** diff --git a/tests/Actions/Mail/IndexTest.php b/tests/Actions/Mail/IndexTest.php index 2e41e3ac322..112904adb83 100644 --- a/tests/Actions/Mail/IndexTest.php +++ b/tests/Actions/Mail/IndexTest.php @@ -156,9 +156,9 @@ public function test_js_message_list() $action->js_message_list([]); $this->assertFalse($output->get_env('multifolder_listing')); - $commands = $output->getProperty('commands'); - $this->assertCount(1, $commands); - $this->assertSame('set_message_coltypes', $commands[0][0]); + $js_calls = $output->getProperty('js_calls'); + $this->assertCount(1, $js_calls); + $this->assertSame('set_message_coltypes', $js_calls[0][0]); } /** diff --git a/tests/Rcmail/OutputJsonTest.php b/tests/Rcmail/OutputJsonTest.php index 532faaeda89..6d99af4abaf 100644 --- a/tests/Rcmail/OutputJsonTest.php +++ b/tests/Rcmail/OutputJsonTest.php @@ -18,11 +18,11 @@ public function test_show_message() $output = new \rcmail_output_json(); $reflection = new \ReflectionClass($output); - $commands = $reflection->getProperty('commands'); - $commands->setAccessible(true); + $js_calls = $reflection->getProperty('js_calls'); + $js_calls->setAccessible(true); $output->show_message('unknown'); - $this->assertSame([['display_message', 'unknown', 'notice', 0]], $commands->getValue($output)); + $this->assertSame([['display_message', 'unknown', 'notice', 0]], $js_calls->getValue($output)); } } From 0b7d4d10337a8b2afab20e306a3dff22bc2a6782 Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Thu, 13 Jun 2024 12:18:30 +0200 Subject: [PATCH 19/56] Add eventListeners from data-attributes This allows server code to specify events on attributes without using inline event listeners (which require a very lax CSP and should be avoided). --- program/js/app.js | 71 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/program/js/app.js b/program/js/app.js index ac8624b154a..9ab3f5e9bec 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -694,6 +694,8 @@ function rcube_webmail() { break; } + this.addAllEventListenersFromElements(); + // select first input field in an edit form if (this.gui_objects.editform) { $('input,select,textarea', this.gui_objects.editform) @@ -10696,6 +10698,75 @@ function rcube_webmail() { img.src = urls[i]; } }; + + /** + * Add eventListeners parsed from element datasets. This allows to avoid + * inline javascript event handlers (which require a very lax CSP). + * The first element of the array is the function on `rcmail` to call. + * All further elements of the array are arguments that are handed to the + * called function. + * In that list, '__THIS__' is replaced with the element that the event was + * attached to, and '__EVENT__' is the event itself. + * E.g. with this element: + * something + * A click will cause a call to `this[aMethodToCall]("arg1")`, and a double + * click will cause a call to `this[something](theDoubleClickedElement)`. + */ + this.addEventListenerFromElement = function (elem, eventName) { + var value = elem.dataset['on' + eventName]; + if (!value) { + return; + } + var eventArgs = JSON.parse(value); + var methodName = eventArgs.shift(); + if (!methodName) { + this.log("data-on-event '" + eventName + "' has invalid value (no method name)"); + return; + } + if (typeof this[methodName] !== 'function') { + this.log("'" + methodName + "' is not a valid method name of this class"); + return; + } + // Inject a reference to the event target element, if required. + eventArgs = eventArgs.map(function (arg) { + if (arg === '__THIS__') { + return elem; + } + return arg; + }); + elem.addEventListener(eventName, (ev) => { + // Inject a reference to the event object, if required. + var localEventArgs = eventArgs.map(function (arg) { + if (arg === '__EVENT__') { + return ev; + } + return arg; + }); + this[methodName](...localEventArgs); + }); + }; + + this.addEventListenerFromElements = function (eventName) { + $('[data-on' + eventName + ']').each((_idx, elem) => { + this.addEventListenerFromElement(elem, eventName); + }); + }; + + this.addAllEventListenersFromElements = function () { + [ + 'click', + 'dblclick', + 'mouseup', + 'mousedown', + 'mouseout', + 'mouseover', + 'error', + 'load', + 'change', + ].forEach(function (name) { + ref.addEventListenerFromElements(name); + }); + }; } // end object rcube_webmail From 1d773b6a7028c4a36c6c1ccd75497e4e8917ca69 Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Fri, 21 Jun 2024 17:48:29 +0200 Subject: [PATCH 20/56] De-inline event handlers from plugins+actions --- .../acl/skins/elastic/templates/table.html | 2 +- plugins/archive/archive.php | 2 +- plugins/enigma/lib/enigma_ui.php | 8 +- plugins/help/help.php | 2 +- .../lib/Roundcube/rcube_sieve_engine.php | 119 +++++++++++++----- .../lib/Roundcube/rcube_sieve_vacation.php | 4 +- plugins/managesieve/managesieve.js | 8 ++ .../skins/elastic/templates/managesieve.html | 2 +- plugins/newmail_notifier/newmail_notifier.php | 2 +- .../vcard_attachments/vcard_attachments.php | 4 +- plugins/vcard_attachments/vcardattach.js | 2 + program/actions/contacts/delete.php | 2 +- program/actions/contacts/index.php | 23 ++-- program/actions/contacts/show.php | 6 +- program/actions/mail/attachment_upload.php | 12 +- program/actions/mail/check_recent.php | 2 +- program/actions/mail/compose.php | 38 +++--- program/actions/mail/folder_expunge.php | 2 +- program/actions/mail/folder_purge.php | 2 +- program/actions/mail/index.php | 52 ++++---- program/actions/mail/list_contacts.php | 3 +- program/actions/mail/show.php | 26 ++-- program/actions/settings/folder_edit.php | 6 +- program/actions/settings/folders.php | 4 +- program/actions/settings/identity_edit.php | 2 +- program/actions/settings/index.php | 18 +-- program/actions/settings/response_edit.php | 2 +- program/include/rcmail_string_replacer.php | 5 +- program/js/actions.js | 60 +++++++++ program/js/mail-action-show.js | 19 +++ tests/Actions/Contacts/ShowTest.php | 2 +- tests/Actions/Mail/FolderExpungeTest.php | 2 +- tests/Actions/Mail/IndexTest.php | 16 +-- 33 files changed, 298 insertions(+), 161 deletions(-) create mode 100644 program/js/actions.js create mode 100644 program/js/mail-action-show.js diff --git a/plugins/acl/skins/elastic/templates/table.html b/plugins/acl/skins/elastic/templates/table.html index 0a4b581337e..84032704036 100644 --- a/plugins/acl/skins/elastic/templates/table.html +++ b/plugins/acl/skins/elastic/templates/table.html @@ -18,7 +18,7 @@

- + diff --git a/plugins/archive/archive.php b/plugins/archive/archive.php index 3de20f27109..6a0428a22f7 100644 --- a/plugins/archive/archive.php +++ b/plugins/archive/archive.php @@ -403,7 +403,7 @@ public function prefs_table($args) 'maxlength' => 30, 'folder_filter' => 'mail', 'folder_rights' => 'w', - 'onchange' => "if ($(this).val() == 'INBOX') $(this).val('')", + 'data-onchange' => json_encode(['reset_value_if_inbox', '__THIS__']), 'class' => 'custom-select', ]); } else { diff --git a/plugins/enigma/lib/enigma_ui.php b/plugins/enigma/lib/enigma_ui.php index f16a0cfd2d6..9b569dec904 100644 --- a/plugins/enigma/lib/enigma_ui.php +++ b/plugins/enigma/lib/enigma_ui.php @@ -611,7 +611,7 @@ public function tpl_key_import_form($attrib) $max_filesize = rcmail_action::upload_init(); $upload_button = new html_button([ 'class' => 'button import', - 'onclick' => "return rcmail.command('plugin.enigma-import','',this,event)", + 'data-onclick' => json_encode(['command', 'plugin.enigma-import', '', '__THIS__', '__EVENT__']), ]); $form = html::div(null, html::p(null, rcube::Q($this->enigma->gettext('keyimporttext'), 'show')) @@ -639,7 +639,7 @@ public function tpl_key_import_form($attrib) $search_button = new html_button([ 'class' => 'button search', - 'onclick' => "return rcmail.command('plugin.enigma-import-search','',this,event)", + 'data-onclick' => json_encode(['command', 'plugin.enigma-import-search', '', '__THIS__', '__EVENT__']), ]); $form = html::div(null, @@ -848,7 +848,7 @@ private function compose_ui() $this->enigma->add_button([ 'type' => 'link', 'command' => 'plugin.enigma', - 'onclick' => "rcmail.command('menu-open', 'enigmamenu', event.target, event)", + 'data-onclick' => json_encode(['command', 'menu-open', 'enigmamenu', '__THIS__', '__EVENT__']), 'class' => 'button enigma', 'title' => 'encryptionoptions', 'label' => 'encryption', @@ -1107,7 +1107,7 @@ public function message_output($p) $p['content'] = html::p(['class' => 'enigmaattachment boxinformation aligned-buttons'], html::span(null, rcube::Q($this->enigma->gettext('keyattfound'))) . html::tag('button', [ - 'onclick' => 'return ' . rcmail_output::JS_OBJECT_NAME . ".enigma_import_attachment('" . rcube::JQ($part) . "')", + 'data-onclick' => json_encode(['enigma_import_attachment', $part]), 'title' => $this->enigma->gettext('keyattimport'), 'class' => 'import btn-sm', ], rcube::Q($this->rc->gettext('import')) diff --git a/plugins/help/help.php b/plugins/help/help.php index d3d1e5a0a61..81c18cc92c3 100644 --- a/plugins/help/help.php +++ b/plugins/help/help.php @@ -108,7 +108,7 @@ public function tablink($attrib) // so button() will translate it correctly $attrib['title'] = $attrib['label']; - $attrib['onclick'] = sprintf("return show_help_content('%s', event)", $attrib['action']); + $attrib['data-onclick'] = json_encode(['show_help_content', $attrib['action'], '__EVENT__']); return $rcmail->output->button($attrib); } diff --git a/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php b/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php index c4af697cb69..6053941d5c9 100644 --- a/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php +++ b/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php @@ -1321,7 +1321,7 @@ public function filtersets_list($attrib, $no_env = false) 'name' => '_set', 'id' => $attrib['id'], 'class' => 'custom-select', - 'onchange' => $this->rc->task != 'mail' ? 'rcmail.managesieve_set()' : '', + 'data-onchange' => $this->rc->task != 'mail' ? json_encode(['managesieve_set']) : '', ]); if ($list) { @@ -1567,7 +1567,7 @@ public function filter_form($attrib) if ($compact) { $select = new html_select(['name' => '_join', 'id' => '_join', 'class' => 'custom-select', - 'onchange' => 'rule_join_radio(this.value)']); + 'data-onchange' => json_encode(['managesieve_rule_join_radio_with_this_value'])]); foreach (['allof', 'anyof', 'any'] as $val) { $select->add($this->plugin->gettext('filter' . $val), $val); @@ -1592,7 +1592,7 @@ public function filter_form($attrib) // any, allof, anyof radio buttons $field_id = '_allof'; $input_join = new html_radiobutton(['name' => '_join', 'id' => $field_id, 'value' => 'allof', - 'onclick' => 'rule_join_radio(\'allof\')', 'class' => 'radio']); + 'data-onclick' => json_encode(['managesieve_rule_join_radio', 'allof']), 'class' => 'radio']); if (isset($scr) && !$any) { $input_join = $input_join->show($scr['join'] ? 'allof' : ''); @@ -1604,7 +1604,7 @@ public function filter_form($attrib) $field_id = '_anyof'; $input_join = new html_radiobutton(['name' => '_join', 'id' => $field_id, 'value' => 'anyof', - 'onclick' => 'rule_join_radio(\'anyof\')', 'class' => 'radio']); + 'data-onclick' => json_encode(['managesieve_rule_join_radio', 'anyof']), 'class' => 'radio']); if (isset($scr) && !$any) { $input_join = $input_join->show($scr['join'] ? '' : 'anyof'); @@ -1616,7 +1616,7 @@ public function filter_form($attrib) $field_id = '_any'; $input_join = new html_radiobutton(['name' => '_join', 'id' => $field_id, 'value' => 'any', - 'onclick' => 'rule_join_radio(\'any\')', 'class' => 'radio']); + 'data-onclick' => json_encode(['managesieve_rule_join_radio', 'any']), 'class' => 'radio']); $input_join = $input_join->show($any ? 'any' : ''); @@ -1687,7 +1687,7 @@ public function rule_div($fid, $id, $div = true, $compact = false) // headers select $select_header = new html_select(['name' => "_header[{$id}]", 'id' => 'header' . $id, - 'onchange' => 'rule_header_select(' . $id . ')', 'class' => 'custom-select']); + 'data-onchange' => json_encode(['rule_header_select', $id]), 'class' => 'custom-select']); foreach ($this->headers as $index => $header) { $header = $this->rc->text_exists($index) ? $this->plugin->gettext($index) : $header; @@ -1872,7 +1872,7 @@ public function rule_div($fid, $id, $div = true, $compact = false) 'name' => "_rule_spamtest_op[{$id}]", 'id' => 'rule_spamtest_op' . $id, 'class' => 'input-group-prepend custom-select', - 'onchange' => 'rule_spamtest_select(' . $id . ')', + 'data-onchange' => json_encode(['rule_spamtest_select', $id]), ]); $select_spamtest_op->add(rcube::Q($this->plugin->gettext('spamtestisunknown')), ''); $select_spamtest_op->add(rcube::Q($this->plugin->gettext('spamtestisgreaterthan')), 'value-gt'); @@ -1909,7 +1909,7 @@ public function rule_div($fid, $id, $div = true, $compact = false) 'name' => "_rule_mod[{$id}]", 'id' => 'rule_mod_op' . $id, 'class' => 'custom-select', - 'onchange' => 'rule_mod_select(' . $id . ')', + 'data-onchange' => json_encode(['rule_mod_select', $id]), ]); $select_mod->add(rcube::Q($this->plugin->gettext('none')), ''); $select_mod->add(rcube::Q($this->plugin->gettext('address')), 'address'); @@ -1955,7 +1955,7 @@ public function rule_div($fid, $id, $div = true, $compact = false) $select_mime = new html_select([ 'name' => "_rule_mime_type[{$id}]", 'id' => 'rule_mime_type' . $id, - 'style' => 'min-width:8em', 'onchange' => 'rule_mime_select(' . $id . ')', + 'style' => 'min-width:8em', 'data-onchange' => json_encode(['rule_mime_select', $id]), 'class' => 'custom-select', ]); $select_mime->add('-', ''); @@ -1994,7 +1994,7 @@ public function rule_div($fid, $id, $div = true, $compact = false) 'name' => "_rule_trans[{$id}]", 'id' => 'rule_trans_op' . $id, 'class' => 'custom-select', - 'onchange' => 'rule_trans_select(' . $id . ')', + 'data-onchange' => json_encode(['rule_trans_select', $id]), ]); $select_mod->add(rcube::Q($this->plugin->gettext('text')), 'text'); $select_mod->add(rcube::Q($this->plugin->gettext('undecoded')), 'raw'); @@ -2108,25 +2108,62 @@ public function rule_div($fid, $id, $div = true, $compact = false) $out .= ''; if (!$compact) { - $out .= ''; + $out .= html::tag('td', ['class' => 'advbutton'], + html::a([ + 'href' => '#', + 'id' => "ruleadv{$id}", + 'title' => $adv_title, + 'data-onclick' => json_encode(['rule_adv_switch', $id, '__THIS__']), + 'class' => 'show', + ], + html::span(['class' => 'inner'], $adv_title) + ) + ); } - $out .= ''; - $out .= ''; + $out .= html::tag('td', ['class' => 'rowactions'], + html::div(['class' => 'flexbox'], $aout) + ); + $out .= html::tag('td', ['class' => 'rowtargets'], [ + $tout, + html::div([ + 'id' => "rule_advanced{$id}", + 'style' => 'display:none', + 'class' => 'advanced', + ], + $mout), + ]); + $out .= ''; $out .= '
'; - $out .= sprintf('' - . '%s', $id, $adv_title, $id, $adv_title); - $out .= '
' . $aout . '
' . $tout . "\n"; - $out .= ''; - $out .= ''; if ($compact) { - $out .= sprintf('' - . '%s', $id, $adv_title, $id, $adv_title); + $out .= html::a([ + 'href' => '#', + 'id' => "ruleadv{$id}", + 'title' => $adv_title, + 'data-onclick' => json_encode(['rule_adv_switch', $id, '__THIS__']), + 'class' => 'advanced show', + ], + html::span(['class' => 'inner'], $adv_title) + ); } - $out .= sprintf('' - . '%s', $id, $add_title, $id, $add_title); - $out .= sprintf('' - . '%s', $id, $del_title, $id, $rows_num < 2 ? ' disabled' : '', $del_title); + $out .= html::a([ + 'href' => '#', + 'id' => "ruleadd{$id}", + 'title' => $add_title, + 'data-onclick' => json_encode(['managesieve_ruleadd', $id]), + 'class' => 'button create add', + ], + html::span(['class' => 'inner'], $add_title) + ); + $out .= html::a([ + 'href' => '#', + 'id' => "ruledel{$id}", + 'title' => $del_title, + 'data-onclick' => json_encode(['managesieve_ruledel', $id]), + 'class' => 'button delete del ' . $rows_num < 2 ? 'disabled' : '', + ], + html::span(['class' => 'inner'], $del_title) + ); $out .= '
'; @@ -2211,7 +2248,7 @@ public function action_div($fid, $id, $div = true) 'name' => "_action_type[{$id}]", 'id' => 'action_type' . $id, 'class' => 'custom-select', - 'onchange' => "action_type_select({$id})", + 'data-onchange' => json_encode(['managesieve_action_type_select', $id]), ]); if (in_array('fileinto', $this->exts)) { $select_action->add($this->plugin->gettext('messagemoveto'), 'fileinto'); @@ -2373,7 +2410,7 @@ public function action_div($fid, $id, $div = true) $out .= $this->list_input($id, 'action_addresses', $action['addresses'] ?? null, 30, false, ['class' => $this->error_class($id, 'action', 'addresses', 'action_addresses')] ) - . html::a(['href' => '#', 'onclick' => rcmail_output::JS_OBJECT_NAME . ".managesieve_vacation_addresses({$id})"], + . html::a(['href' => '#', 'data-onclick' => json_encode(['managesieve_vacation_addresses', $id])], rcube::Q($this->plugin->gettext('filladdresses'))); $out .= '
' . rcube::Q($this->plugin->gettext('vacationinterval')) . '
'; $out .= '
' . html::tag('input', [ @@ -2671,12 +2708,28 @@ public function action_div($fid, $id, $div = true) // add/del buttons $add_label = rcube::Q($this->plugin->gettext('add')); $del_label = rcube::Q($this->plugin->gettext('del')); - $out .= ''; - $out .= sprintf('' - . '%s', $id, $add_label, $id, $add_label); - $out .= sprintf('' - . '%s', $id, $del_label, $id, $rows_num < 2 ? ' disabled' : '', $del_label); - $out .= ''; + $out .= html::tag('td', ['class' => 'rowbuttons'], + html::a( + [ + 'href' => '#', + 'id' => "actionadd{$id}", + 'title' => $add_label, + 'data-onclick' => json_encode(['managesieve_actionadd', $id]), + 'class' => 'button create add', + ], + html::span(['class' => 'inner'], $add_label), + ), + html::a( + [ + 'href' => '#', + 'id' => "actiondel{$id}", + 'title' => $del_label, + 'data-onclick' => json_encode(['managesieve_actiondel', $id]), + 'class' => 'button delete del ' . ($rows_num < 2 ? 'disabled' : ''), + ], + html::span(['class' => 'inner'], $del_label), + ) + ); $out .= ''; @@ -3254,7 +3307,7 @@ protected function match_type_selector($name, $id, $test, $rule = null, $mode = 'id' => "{$name}{$id}", 'style' => 'display:' . (!in_array($rule, ['size', 'duplicate', 'spamtest']) ? 'inline' : 'none'), 'class' => 'operator_selector col-6 custom-select', - 'onchange' => "{$name}_select(this, '{$id}')", + 'data-onchange' => json_encode(["managesieve_{$name}_select", '__THIS__', $id]), ]); $select_op->add(rcube::Q($this->plugin->gettext('filtercontains')), 'contains'); diff --git a/plugins/managesieve/lib/Roundcube/rcube_sieve_vacation.php b/plugins/managesieve/lib/Roundcube/rcube_sieve_vacation.php index ac7abe7e6a8..dc83e487b12 100644 --- a/plugins/managesieve/lib/Roundcube/rcube_sieve_vacation.php +++ b/plugins/managesieve/lib/Roundcube/rcube_sieve_vacation.php @@ -393,12 +393,12 @@ public function vacation_form($attrib) . (!empty($this->vacation['addresses']) ? rcube::Q(implode("\n", (array) $this->vacation['addresses']), 'strict', false) : '') . ''; $status = new html_select(['name' => 'vacation_status', 'id' => 'vacation_status', 'class' => 'custom-select']); - $action = new html_select(['name' => 'vacation_action', 'id' => 'vacation_action', 'class' => 'custom-select', 'onchange' => 'vacation_action_select()']); + $action = new html_select(['name' => 'vacation_action', 'id' => 'vacation_action', 'class' => 'custom-select', 'data-onchange' => json_encode(['vacation_action_select'])]); $addresses_link = new html_inputfield([ 'type' => 'button', 'href' => '#', 'class' => 'button', - 'onclick' => rcmail_output::JS_OBJECT_NAME . '.managesieve_vacation_addresses()', + 'data-onclick' => json_encode(['managesieve_vacation_addresses']), ]); $redirect = !empty($this->vacation['action']) diff --git a/plugins/managesieve/managesieve.js b/plugins/managesieve/managesieve.js index c9b43fa6227..1694e0f9882 100644 --- a/plugins/managesieve/managesieve.js +++ b/plugins/managesieve/managesieve.js @@ -747,6 +747,8 @@ function rule_op_select(obj, id, header) { target.style.display = obj.value.match(/^(exists|notexists)$/) || header.match(/^(size|spamtest|message)$/) ? 'none' : ''; } +rcube_webmail.prototype.managesieve_rule_op_select = rule_op_select; + function rule_trans_select(id) { var obj = document.getElementById('rule_trans_op' + id), target = document.getElementById('rule_trans_type' + id); @@ -847,6 +849,8 @@ function action_type_select(id) { } } +rcube_webmail.prototype.managesieve_action_type_select = action_type_select; + function vacation_action_select() { var selected = $('#vacation_action').val(); @@ -1283,3 +1287,7 @@ rcube_webmail.prototype.managesieve_dialog_resize = function (o) { dialog.dialog('option', { height: Math.min(h - 20, height + 120), width: Math.min(w - 20, width + 65) }); }; + +rcube_webmail.prototype.ui_switch_nav_list = function (elem) { + UI.switch_nav_list(elem); +}; diff --git a/plugins/managesieve/skins/elastic/templates/managesieve.html b/plugins/managesieve/skins/elastic/templates/managesieve.html index e3201f89326..cb75961038e 100644 --- a/plugins/managesieve/skins/elastic/templates/managesieve.html +++ b/plugins/managesieve/skins/elastic/templates/managesieve.html @@ -18,7 +18,7 @@

:

-
@@ -121,7 +121,7 @@

diff --git a/skins/elastic/templates/bounce.html b/skins/elastic/templates/bounce.html index f9fbf02f374..425fee48285 100644 --- a/skins/elastic/templates/bounce.html +++ b/skins/elastic/templates/bounce.html @@ -35,7 +35,7 @@

- + @@ -46,7 +46,7 @@

- + diff --git a/skins/elastic/templates/compose.html b/skins/elastic/templates/compose.html index 0f7cad8caf0..47355f66c56 100644 --- a/skins/elastic/templates/compose.html +++ b/skins/elastic/templates/compose.html @@ -21,7 +21,7 @@

- +
@@ -78,7 +78,7 @@

@@ -138,7 +138,7 @@

- " tabindex="1"> + " tabindex="1"> " tabindex="1"> @@ -152,10 +152,10 @@

- " tabindex="1"> + " tabindex="1"> - + @@ -166,10 +166,10 @@

- " tabindex="1"> + " tabindex="1"> - + @@ -180,10 +180,10 @@

- " tabindex="1"> + " tabindex="1"> - + @@ -194,10 +194,10 @@

- " tabindex="1"> + " tabindex="1"> - + @@ -248,7 +248,7 @@

- + diff --git a/skins/elastic/templates/folders.html b/skins/elastic/templates/folders.html index c3632744b24..627a8538b22 100644 --- a/skins/elastic/templates/folders.html +++ b/skins/elastic/templates/folders.html @@ -19,7 +19,7 @@

- +
diff --git a/skins/elastic/templates/includes/mail-menu.html b/skins/elastic/templates/includes/mail-menu.html index b50d7810154..7aa852910ba 100644 --- a/skins/elastic/templates/includes/mail-menu.html +++ b/skins/elastic/templates/includes/mail-menu.html @@ -64,7 +64,7 @@

+ name="messageimport" data-onclick='["ui_import_dialog"]' /> diff --git a/skins/elastic/templates/includes/menu.html b/skins/elastic/templates/includes/menu.html index 352eeb58a82..9525d359dbf 100644 --- a/skins/elastic/templates/includes/menu.html +++ b/skins/elastic/templates/includes/menu.html @@ -34,7 +34,7 @@

+ class="about" innerClass="inner" data-onclick='["ui_about_dialog", "__THIS__"]' /> diff --git a/skins/elastic/templates/mail.html b/skins/elastic/templates/mail.html index 2e483e9d6e1..f521917e275 100644 --- a/skins/elastic/templates/mail.html +++ b/skins/elastic/templates/mail.html @@ -96,7 +96,7 @@

- +
@@ -132,8 +132,8 @@

@@ -152,7 +152,7 @@

diff --git a/skins/elastic/templates/folders.html b/skins/elastic/templates/folders.html index 627a8538b22..ddfc9ebaa0a 100644 --- a/skins/elastic/templates/folders.html +++ b/skins/elastic/templates/folders.html @@ -19,7 +19,7 @@

- +
diff --git a/skins/elastic/templates/includes/mail-menu.html b/skins/elastic/templates/includes/mail-menu.html index 7aa852910ba..1f9cd102150 100644 --- a/skins/elastic/templates/includes/mail-menu.html +++ b/skins/elastic/templates/includes/mail-menu.html @@ -64,7 +64,7 @@

+ name="messageimport" data-event-handle="ui_import_dialog" /> diff --git a/skins/elastic/templates/includes/menu.html b/skins/elastic/templates/includes/menu.html index 9525d359dbf..62063ddddfb 100644 --- a/skins/elastic/templates/includes/menu.html +++ b/skins/elastic/templates/includes/menu.html @@ -34,7 +34,7 @@

+ class="about" innerClass="inner" data-event-handle="ui_about_dialog" /> diff --git a/skins/elastic/templates/mail.html b/skins/elastic/templates/mail.html index f521917e275..b6ba070b805 100644 --- a/skins/elastic/templates/mail.html +++ b/skins/elastic/templates/mail.html @@ -96,7 +96,7 @@

- +
@@ -132,8 +132,8 @@

@@ -152,7 +152,7 @@