From e77475c9cda35da968c3040063f10c18b5ef22ba Mon Sep 17 00:00:00 2001 From: Amani Nyumu Date: Fri, 3 Jan 2025 17:12:49 +0100 Subject: [PATCH 01/20] feat(backend): add scheduled send functionality resolve conflicts --- language/az.php | 1 + language/de.php | 1 + language/en.php | 1 + language/es.php | 1 + language/et.php | 1 + language/fa.php | 1 + language/fr.php | 1 + language/hu.php | 1 + language/id.php | 1 + language/it.php | 1 + language/ja.php | 1 + language/nl.php | 1 + language/pt-BR.php | 1 + language/ro.php | 1 + language/ru.php | 1 + language/zh-Hans.php | 1 + modules/core/functions.php | 96 ++ modules/core/message_list_functions.php | 6 +- modules/core/site.css | 2 +- modules/core/site.js | 974 +++++++++--------- modules/imap/functions.php | 180 +--- modules/imap/handler_modules.php | 9 +- modules/imap/hm-imap.php | 12 +- modules/imap/js_modules/route_handlers.js | 8 +- .../utils/handleNexterDateAction.js | 71 ++ modules/imap/output_modules.php | 7 +- modules/imap/site.css | 4 +- modules/imap/site.js | 1 + modules/profiles/functions.php | 4 +- modules/profiles/hm-profiles.php | 15 + modules/smtp/functions.php | 106 ++ modules/smtp/hm-mime-message.php | 14 +- modules/smtp/js_modules/route_handlers.js | 76 +- modules/smtp/modules.php | 183 +++- modules/smtp/setup.php | 28 +- modules/smtp/site.js | 25 +- 36 files changed, 1158 insertions(+), 679 deletions(-) create mode 100644 modules/imap/js_modules/utils/handleNexterDateAction.js diff --git a/language/az.php b/language/az.php index 5e1c54a187..e3f91e1ced 100755 --- a/language/az.php +++ b/language/az.php @@ -621,6 +621,7 @@ 'You must provide a name for your script' => false, 'Empty script' => false, 'Please create a profile for saving sent messages option' => false, + 'You have %d scheduled messages that won\'t be executed if you quit' => false, 'Attachment storage unavailable, please contact your site administrator' => false, 'Your subject is empty!' => false, 'Your body is empty!' => false, diff --git a/language/de.php b/language/de.php index 7150e9c182..8bd66b1a43 100755 --- a/language/de.php +++ b/language/de.php @@ -618,6 +618,7 @@ 'You must provide a name for your script' => false, 'Empty script' => false, 'Please create a profile for saving sent messages option' => false, + 'You have %d scheduled messages that won\'t be executed if you quit' => false, 'Attachment storage unavailable, please contact your site administrator' => false, 'Your subject is empty!' => false, 'Your body is empty!' => false, diff --git a/language/en.php b/language/en.php index f630f6a2d3..5cd8c300ef 100755 --- a/language/en.php +++ b/language/en.php @@ -636,6 +636,7 @@ 'You must provide a name for your script' => false, 'Empty script' => false, 'Please create a profile for saving sent messages option' => false, + 'You have %d scheduled messages that won\'t be executed if you quit' => false, 'Attachment storage unavailable, please contact your site administrator' => false, 'Your subject is empty!' => false, 'Your body is empty!' => false, diff --git a/language/es.php b/language/es.php index fa551b1ea9..c253b1f363 100755 --- a/language/es.php +++ b/language/es.php @@ -618,6 +618,7 @@ 'You must provide a name for your script' => false, 'Empty script' => false, 'Please create a profile for saving sent messages option' => false, + 'You have %d scheduled messages that won\'t be executed if you quit' => false, 'Attachment storage unavailable, please contact your site administrator' => false, 'Your subject is empty!' => false, 'Your body is empty!' => false, diff --git a/language/et.php b/language/et.php index 61b7b6addb..53e6207a9f 100755 --- a/language/et.php +++ b/language/et.php @@ -626,6 +626,7 @@ 'You must provide a name for your script' => false, 'Empty script' => false, 'Please create a profile for saving sent messages option' => false, + 'You have %d scheduled messages that won\'t be executed if you quit' => false, 'Attachment storage unavailable, please contact your site administrator' => false, 'Your subject is empty!' => false, 'Your body is empty!' => false, diff --git a/language/fa.php b/language/fa.php index b2ad0e2221..9431100de7 100755 --- a/language/fa.php +++ b/language/fa.php @@ -670,6 +670,7 @@ 'You must provide a name for your script' => false, 'Empty script' => false, 'Please create a profile for saving sent messages option' => false, + 'You have %d scheduled messages that won\'t be executed if you quit' => false, 'Attachment storage unavailable, please contact your site administrator' => false, 'Your subject is empty!' => false, 'Your body is empty!' => false, diff --git a/language/fr.php b/language/fr.php index b60c7c3592..8e0550fc0e 100755 --- a/language/fr.php +++ b/language/fr.php @@ -617,6 +617,7 @@ 'You must provide a name for your script' => false, 'Empty script' => false, 'Please create a profile for saving sent messages option' => false, + 'You have %d scheduled messages that won\'t be executed if you quit' => 'Vous avez %d messages programmés qui ne seront pas exécutés si vous quittez', 'Attachment storage unavailable, please contact your site administrator' => false, 'Your subject is empty!' => false, 'Your body is empty!' => false, diff --git a/language/hu.php b/language/hu.php index e91cd6a66d..7e9b587059 100755 --- a/language/hu.php +++ b/language/hu.php @@ -618,6 +618,7 @@ 'You must provide a name for your script' => false, 'Empty script' => false, 'Please create a profile for saving sent messages option' => false, + 'You have %d scheduled messages that won\'t be executed if you quit' => false, 'Attachment storage unavailable, please contact your site administrator' => false, 'Your subject is empty!' => false, 'Your body is empty!' => false, diff --git a/language/id.php b/language/id.php index 07b205ab8b..95c4ac6a5f 100755 --- a/language/id.php +++ b/language/id.php @@ -625,6 +625,7 @@ 'You must provide a name for your script' => false, 'Empty script' => false, 'Please create a profile for saving sent messages option' => false, + 'You have %d scheduled messages that won\'t be executed if you quit' => false, 'Attachment storage unavailable, please contact your site administrator' => false, 'Your subject is empty!' => false, 'Your body is empty!' => false, diff --git a/language/it.php b/language/it.php index 5147b4e8ad..d720e4214a 100755 --- a/language/it.php +++ b/language/it.php @@ -618,6 +618,7 @@ 'You must provide a name for your script' => false, 'Empty script' => false, 'Please create a profile for saving sent messages option' => false, + 'You have %d scheduled messages that won\'t be executed if you quit' => false, 'Attachment storage unavailable, please contact your site administrator' => false, 'Your subject is empty!' => false, 'Your body is empty!' => false, diff --git a/language/ja.php b/language/ja.php index 30596c066d..7c7d601ac5 100755 --- a/language/ja.php +++ b/language/ja.php @@ -618,6 +618,7 @@ 'You must provide a name for your script' => false, 'Empty script' => false, 'Please create a profile for saving sent messages option' => false, + 'You have %d scheduled messages that won\'t be executed if you quit' => false, 'Attachment storage unavailable, please contact your site administrator' => false, 'Your subject is empty!' => false, 'Your body is empty!' => false, diff --git a/language/nl.php b/language/nl.php index 1d1db295cc..82e94158d6 100755 --- a/language/nl.php +++ b/language/nl.php @@ -618,6 +618,7 @@ 'You must provide a name for your script' => false, 'Empty script' => false, 'Please create a profile for saving sent messages option' => false, + 'You have %d scheduled messages that won\'t be executed if you quit' => false, 'Attachment storage unavailable, please contact your site administrator' => false, 'Your subject is empty!' => false, 'Your body is empty!' => false, diff --git a/language/pt-BR.php b/language/pt-BR.php index 1fcf9142ec..7ad23625b8 100755 --- a/language/pt-BR.php +++ b/language/pt-BR.php @@ -617,6 +617,7 @@ 'You must provide a name for your script' => false, 'Empty script' => false, 'Please create a profile for saving sent messages option' => false, + 'You have %d scheduled messages that won\'t be executed if you quit' => false, 'Attachment storage unavailable, please contact your site administrator' => false, 'Your subject is empty!' => false, 'Your body is empty!' => false, diff --git a/language/ro.php b/language/ro.php index 5a52506d25..c019b88263 100755 --- a/language/ro.php +++ b/language/ro.php @@ -617,6 +617,7 @@ 'You must provide a name for your script' => false, 'Empty script' => false, 'Please create a profile for saving sent messages option' => false, + 'You have %d scheduled messages that won\'t be executed if you quit' => false, 'Attachment storage unavailable, please contact your site administrator' => false, 'Your subject is empty!' => false, 'Your body is empty!' => false, diff --git a/language/ru.php b/language/ru.php index 60813edac2..70cd16aa1c 100755 --- a/language/ru.php +++ b/language/ru.php @@ -619,6 +619,7 @@ 'You must provide a name for your script' => false, 'Empty script' => false, 'Please create a profile for saving sent messages option' => false, + 'You have %d scheduled messages that won\'t be executed if you quit' => false, 'Attachment storage unavailable, please contact your site administrator' => false, 'Your subject is empty!' => false, 'Your body is empty!' => false, diff --git a/language/zh-Hans.php b/language/zh-Hans.php index 8baf2310ff..526e932c4a 100644 --- a/language/zh-Hans.php +++ b/language/zh-Hans.php @@ -639,6 +639,7 @@ 'You must provide a name for your script' => '请提供脚本名称', 'Empty script' => '空脚本', 'Please create a profile for saving sent messages option' => '请创建用于保存已发送信息选项的配置文件', + 'You have %d scheduled messages that won\'t be executed if you quit' => false, 'Attachment storage unavailable, please contact your site administrator' => '附件存储不可用,请联系您的网站管理员', 'Your subject is empty!' => '主题为空!', 'Your body is empty!' => '内容为空!', diff --git a/modules/core/functions.php b/modules/core/functions.php index 1e224fff70..a422a0858b 100644 --- a/modules/core/functions.php +++ b/modules/core/functions.php @@ -637,3 +637,99 @@ function privacy_setting_callback($val, $key, $mod) { } return $val; } + +if (!hm_exists('get_scheduled_date')) { +function get_scheduled_date($format, $only_label = false) { + if ($format == 'later_in_day') { + $date_string = 'today 18:00'; + $label = 'Later in the day'; + } elseif ($format == 'tomorrow') { + $date_string = '+1 day 08:00'; + $label = 'Tomorrow'; + } elseif ($format == 'next_weekend') { + $date_string = 'next Saturday 08:00'; + $label = 'Next weekend'; + } elseif ($format == 'next_week') { + $date_string = 'next week 08:00'; + $label = 'Next week'; + } elseif ($format == 'next_month') { + $date_string = 'next month 08:00'; + $label = 'Next month'; + } else { + $date_string = $format; + $label = 'Certain date'; + } + $time = strtotime($date_string); + if ($only_label) { + return [$label, date('D, H:i', $time)]; + } + return date('D, d M Y H:i', $time); +}} + +/** + * @subpackage imap/functions + */ +if (!hm_exists('nexter_formats')) { +function nexter_formats() { + $values = array( + 'tomorrow', + 'next_weekend', + 'next_week', + 'next_month' + ); + if (date('H') <= 16) { + array_push($values, 'later_in_day'); + } + return $values; +}} + +if (!hm_exists('schedule_dropdown')) { +function schedule_dropdown($output, $send_now = false) { + $values = nexter_formats(); + + $txt = ''; + if ($send_now) { + $txt .= ''; + } + + return $txt; +}} + +/** + * @subpackage imap/functions + */ +if (!hm_exists('parse_nexter_header')) { + function parse_nexter_header($header, $name) + { + $header = str_replace("$name: ", '', $header); + $result = []; + foreach (explode(';', $header) as $kv) + { + $kv = trim($kv); + $spacePos = strpos($kv, ' '); + if ($spacePos > 0) { + $result[rtrim(substr($kv, 0, $spacePos), ':')] = trim(substr($kv, $spacePos+1)); + } else { + $result[$kv] = true; + } + } + return $result; + }} diff --git a/modules/core/message_list_functions.php b/modules/core/message_list_functions.php index c580ae339c..6ee3d62162 100644 --- a/modules/core/message_list_functions.php +++ b/modules/core/message_list_functions.php @@ -336,11 +336,11 @@ function subject_callback($vals, $style, $output_mod) { */ if (!hm_exists('date_callback')) { function date_callback($vals, $style, $output_mod) { - $snooze_class = isset($vals[2]) && $vals[2]? ' snoozed_date': ''; + $nexter_class = isset($vals[2]) && $vals[2]? ' nexter_date': ''; if ($style == 'news') { - return sprintf('
%s
', $snooze_class, $output_mod->html_safe($vals[0]), $output_mod->html_safe($vals[1])); + return sprintf('
%s
', $nexter_class, $output_mod->html_safe($vals[0]), $output_mod->html_safe($vals[1])); } - return sprintf('%s', $snooze_class, $output_mod->html_safe(date('r', $vals[1])), $output_mod->html_safe($vals[0]), $output_mod->html_safe($vals[1])); + return sprintf('%s', $nexter_class, $output_mod->html_safe(date('r', $vals[1])), $output_mod->html_safe($vals[0]), $output_mod->html_safe($vals[1])); }} function dates_holders_callback($vals) { diff --git a/modules/core/site.css b/modules/core/site.css index 61b290d146..e33ce8b88b 100644 --- a/modules/core/site.css +++ b/modules/core/site.css @@ -1276,7 +1276,7 @@ div.unseen, .mobile .imap_sort { width: 100%; } -.snoozed_date { +.nexter_date { color: var(--bs-primary) !important; } diff --git a/modules/core/site.js b/modules/core/site.js index 4fe5d652e0..01908668af 100644 --- a/modules/core/site.js +++ b/modules/core/site.js @@ -47,7 +47,7 @@ var Hm_Ajax = { active_reqs: 0, icon_loading_id: false, - get_ajax_hook_name: function(args) { + get_ajax_hook_name: function (args) { var index; for (index in args) { if (args[index]['name'] == 'hm_ajax_hook') { @@ -57,7 +57,7 @@ var Hm_Ajax = { return; }, - request: function(args, callback, extra, no_icon, batch_callback, on_failure, signal) { + request: function (args, callback, extra, no_icon, batch_callback, on_failure, signal) { var bcb = false; if (typeof batch_callback != 'undefined' && $.inArray(batch_callback, this.batch_callbacks) === -1) { bcb = batch_callback.toString(); @@ -79,27 +79,27 @@ var Hm_Ajax = { return ajax.make_request(args, callback, extra, name, on_failure, batch_callback, signal); }, - show_loading_icon: function() { + show_loading_icon: function () { if (Hm_Ajax.icon_loading_id !== false) { return; } - var hm_loading_pos = $('.loading_icon').width()/40; + var hm_loading_pos = $('.loading_icon').width() / 40; $('.loading_icon').show(); function move_background_image() { hm_loading_pos = hm_loading_pos + 50; - $('.loading_icon').css('background-position', hm_loading_pos+'px 0'); + $('.loading_icon').css('background-position', hm_loading_pos + 'px 0'); Hm_Ajax.icon_loading_id = setTimeout(move_background_image, 100); } move_background_image(); }, - stop_loading_icon : function(loading_id) { + stop_loading_icon: function (loading_id) { clearTimeout(loading_id); $('.loading_icon').hide(); Hm_Ajax.icon_loading_id = false; }, - process_callback_hooks: function(name, res) { + process_callback_hooks: function (name, res) { var hook; var func; for (var i in Hm_Ajax.callback_hooks) { @@ -116,190 +116,182 @@ var Hm_Ajax = { } }, - add_callback_hook: function(request_name, hook_function) { + add_callback_hook: function (request_name, hook_function) { Hm_Ajax.callback_hooks.push([request_name, hook_function]); } }; /* ajax request wrapper */ -var Hm_Ajax_Request = function() { return { - callback: false, - name: false, - batch_callback: false, - index: 0, - on_failure: false, - start_time: 0, - - xhr_fetch: function(config) { - var xhr = new XMLHttpRequest(); - var data = ''; - if (config.data) { - data = this.format_xhr_data(config.data); - } - const url = new URL(window.location.href); - if (window.location.next) { - url.search = window.location.next.split('?')[1]; - } - for (const param of url.searchParams) { - const configItem = config.data.find(item => item.name === param[0]); - if (configItem) { - url.searchParams.set(configItem.name, configItem.value); - } - } - - xhr.open('POST', url.toString()) - if (config.signal) { - config.signal.addEventListener('abort', function() { - xhr.abort(); +var Hm_Ajax_Request = function () { + return { + callback: false, + name: false, + batch_callback: false, + index: 0, + on_failure: false, + start_time: 0, + + xhr_fetch: function (config) { + var xhr = new XMLHttpRequest(); + var data = ''; + if (config.data) { + data = this.format_xhr_data(config.data); + } + const url = window.location.next ?? window.location.href; + xhr.open('POST', url) + if (config.signal) { + config.signal.addEventListener('abort', function () { + xhr.abort(); + }); + } + xhr.addEventListener('load', function () { + config.callback.done(Hm_Utils.json_decode(xhr.response, true), xhr); + config.callback.always(Hm_Utils.json_decode(xhr.response, true)); }); - } - xhr.addEventListener('load', function() { - config.callback.done(Hm_Utils.json_decode(xhr.response, true), xhr); - config.callback.always(Hm_Utils.json_decode(xhr.response, true)); - }); - xhr.addEventListener('error', function() { - Hm_Ajax.stop_loading_icon(Hm_Ajax.icon_loading_id); - config.callback.fail(xhr); - config.callback.always(Hm_Utils.json_decode(xhr.response, true)); - }); - xhr.addEventListener('abort', function() { - Hm_Ajax.stop_loading_icon(Hm_Ajax.icon_loading_id); - config.callback.always(Hm_Utils.json_decode(xhr.response, true)); + xhr.addEventListener('error', function () { + Hm_Ajax.stop_loading_icon(Hm_Ajax.icon_loading_id); + config.callback.fail(xhr); + config.callback.always(Hm_Utils.json_decode(xhr.response, true)); + }); + xhr.addEventListener('abort', function () { + Hm_Ajax.stop_loading_icon(Hm_Ajax.icon_loading_id); + config.callback.always(Hm_Utils.json_decode(xhr.response, true)); - }); - xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); - xhr.setRequestHeader('X-Requested-with', 'xmlhttprequest'); - xhr.send(data); - }, + }); + xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + xhr.setRequestHeader('X-Requested-with', 'xmlhttprequest'); + xhr.send(data); + }, - format_xhr_data: function(data) { - var res = [] - for (var i in data) { - res.push(encodeURIComponent(data[i]['name']) + '=' + encodeURIComponent(data[i]['value'])); - } - return res.join('&'); - }, + format_xhr_data: function (data) { + var res = [] + for (var i in data) { + res.push(encodeURIComponent(data[i]['name']) + '=' + encodeURIComponent(data[i]['value'])); + } + return res.join('&'); + }, - make_request: function(args, callback, extra, request_name, on_failure, batch_callback, signal) { - var name; - var arg; - this.batch_callback = batch_callback; - this.name = request_name; - this.callback = callback; - if (on_failure) { - this.on_failure = true; - } - if (extra) { - for (name in extra) { - args.push({'name': name, 'value': extra[name]}); + make_request: function (args, callback, extra, request_name, on_failure, batch_callback, signal) { + var name; + var arg; + this.batch_callback = batch_callback; + this.name = request_name; + this.callback = callback; + if (on_failure) { + this.on_failure = true; + } + if (extra) { + for (name in extra) { + args.push({ 'name': name, 'value': extra[name] }); + } } - } - var key_found = false; - for (arg in args) { - if (args[arg].name == 'hm_page_key') { - key_found = true; - break; + var key_found = false; + for (arg in args) { + if (args[arg].name == 'hm_page_key') { + key_found = true; + break; + } } - } - if (!key_found) { - args.push({'name': 'hm_page_key', 'value': $('#hm_page_key').val()}); - } - var dt = new Date(); - this.start_time = dt.getTime(); - this.xhr_fetch({url: '', data: args, callback: this, signal}); - return false; - }, + if (!key_found) { + args.push({ 'name': 'hm_page_key', 'value': $('#hm_page_key').val() }); + } + var dt = new Date(); + this.start_time = dt.getTime(); + this.xhr_fetch({ url: '', data: args, callback: this, signal }); + return false; + }, - done: function(res, xhr) { - if (Hm_Ajax.aborted) { - return; - } - else if (!res || typeof res == 'string' && (res == 'null' || res.indexOf('<') === 0 || res == '{}')) { - this.fail(xhr); - return; - } - else { - $('.offline').hide(); - if (hm_encrypt_ajax_requests()) { - res = Hm_Utils.json_decode(Hm_Crypt.decrypt(res.payload)); + done: function (res, xhr) { + if (Hm_Ajax.aborted) { + return; } - if ((res.state && res.state == 'not callable') || !res.router_login_state) { - this.fail(xhr, true); + else if (!res || typeof res == 'string' && (res == 'null' || res.indexOf('<') === 0 || res == '{}')) { + this.fail(xhr); return; } - if (Hm_Ajax.err_condition) { - Hm_Ajax.err_condition = false; - Hm_Notices.hide(true); - } - if (res.router_user_msgs && !$.isEmptyObject(res.router_user_msgs)) { - Hm_Notices.show(res.router_user_msgs); - } - if (res.folder_status) { - for (const name in res.folder_status) { - if (name === getListPathParam()) { - Hm_Folders.unread_counts[name] = res.folder_status[name]['unseen']; - Hm_Folders.update_unread_counts(); - const messages = new Hm_MessagesStore(name, Hm_Utils.get_url_page_number()); - messages.load().then(() => { - if (messages.count != res.folder_status[name].messages) { - messages.load(true).then(() => { - display_imap_mailbox(messages.rows, messages.list); - }) - } - }); + else { + $('.offline').hide(); + if (hm_encrypt_ajax_requests()) { + res = Hm_Utils.json_decode(Hm_Crypt.decrypt(res.payload)); + } + if ((res.state && res.state == 'not callable') || !res.router_login_state) { + this.fail(xhr, true); + return; + } + if (Hm_Ajax.err_condition) { + Hm_Ajax.err_condition = false; + Hm_Notices.hide(true); + } + if (res.router_user_msgs && !$.isEmptyObject(res.router_user_msgs)) { + Hm_Notices.show(res.router_user_msgs); + } + if (res.folder_status) { + for (const name in res.folder_status) { + if (name === getListPathParam()) { + Hm_Folders.unread_counts[name] = res.folder_status[name]['unseen']; + Hm_Folders.update_unread_counts(); + const messages = new Hm_MessagesStore(name, Hm_Utils.get_url_page_number()); + messages.load().then(() => { + if (messages.count != res.folder_status[name].messages) { + messages.load(true).then(() => { + display_imap_mailbox(messages.rows, messages.links); + }) + } + }); + } } } + if (this.callback) { + this.callback(res); + } + Hm_Ajax.process_callback_hooks(this.name, res); } - if (this.callback) { - this.callback(res); - } - Hm_Ajax.process_callback_hooks(this.name, res); - } - }, + }, - run_on_failure: function() { - if (this.on_failure && this.callback) { - this.callback(false); - } - return false; - }, + run_on_failure: function () { + if (this.on_failure && this.callback) { + this.callback(false); + } + return false; + }, - fail: function(xhr, not_callable) { - if (not_callable === true || (xhr.status && xhr.status == 500)) { - Hm_Notices.show([err_msg('Server Error')]); - } - else { - $('.offline').show(); + fail: function (xhr, not_callable) { + if (not_callable === true || (xhr.status && xhr.status == 500)) { + Hm_Notices.show([err_msg('Server Error')]); + } + else { + $('.offline').show(); + } + Hm_Ajax.err_condition = true; + this.run_on_failure(); + }, + + always: function (res) { + Hm_Ajax.active_reqs--; + var batch_count = 1; + if (this.batch_callback) { + if (typeof Hm_Ajax.batch_callbacks[this.batch_callback.toString()] != 'undefined') { + batch_count = --Hm_Ajax.batch_callbacks[this.batch_callback.toString()]; + } + } + Hm_Message_List.set_row_events(); + if (batch_count === 0) { + Hm_Ajax.batch_callbacks[this.batch_callback.toString()] = 0; + Hm_Ajax.aborted = false; + Hm_Ajax.p_callbacks = []; + this.batch_callback(res); + this.batch_callback = false; + Hm_Ajax.stop_loading_icon(Hm_Ajax.icon_loading_id); + $('body').removeClass('wait'); + } + if (Hm_Ajax.active_reqs == 0) { + Hm_Ajax.stop_loading_icon(Hm_Ajax.icon_loading_id); + $('body').removeClass('wait'); + } + res = null; } - Hm_Ajax.err_condition = true; - this.run_on_failure(); - }, - - always: function(res) { - Hm_Ajax.active_reqs--; - var batch_count = 1; - if (this.batch_callback) { - if (typeof Hm_Ajax.batch_callbacks[this.batch_callback.toString()] != 'undefined') { - batch_count = --Hm_Ajax.batch_callbacks[this.batch_callback.toString()]; - } - } - Hm_Message_List.set_row_events(); - if (batch_count === 0) { - Hm_Ajax.batch_callbacks[this.batch_callback.toString()] = 0; - Hm_Ajax.aborted = false; - Hm_Ajax.p_callbacks = []; - this.batch_callback(res); - this.batch_callback = false; - Hm_Ajax.stop_loading_icon(Hm_Ajax.icon_loading_id); - $('body').removeClass('wait'); - } - if (Hm_Ajax.active_reqs == 0) { - Hm_Ajax.stop_loading_icon(Hm_Ajax.icon_loading_id); - $('body').removeClass('wait'); - } - res = null; } -}}; +}; /** * Show a modal dialog with a title, content and buttons. @@ -336,7 +328,7 @@ function Hm_Modal(options) { @@ -390,7 +382,7 @@ function Hm_Modal(options) { var Hm_Notices = { hide_id: false, - show: function(msgs) { + show: function (msgs) { var message = ''; var type = ''; for (var i in msgs) { @@ -407,7 +399,7 @@ var Hm_Notices = { } }, - hide: function(now) { + hide: function (now) { if (Hm_Notices.hide_id) { clearTimeout(Hm_Notices.hide_id); } @@ -416,7 +408,7 @@ var Hm_Notices = { Hm_Utils.clear_sys_messages(); } else { - Hm_Notices.hide_id = setTimeout(function() { + Hm_Notices.hide_id = setTimeout(function () { $('.sys_messages').addClass('d-none'); Hm_Utils.clear_sys_messages(); }, 5000); @@ -429,7 +421,7 @@ var Hm_Timer = { jobs: [], interval: 1000, - add_job: function(job, interval, defer, custom_defer) { + add_job: function (job, interval, defer, custom_defer) { if (custom_defer) { Hm_Timer.jobs.push([job, interval, custom_defer]); } @@ -437,11 +429,11 @@ var Hm_Timer = { Hm_Timer.jobs.push([job, interval, interval]); } if (!defer) { - try { job(); } catch(e) { console.log(e); } + try { job(); } catch (e) { console.log(e); } } }, - cancel: function(job) { + cancel: function (job) { for (var index in Hm_Timer.jobs) { if (Hm_Timer.jobs[index][0] == job) { Hm_Timer.jobs.splice(index, 1); @@ -451,7 +443,7 @@ var Hm_Timer = { return false; }, - fire: function() { + fire: function () { var job; var index; for (index in Hm_Timer.jobs) { @@ -460,7 +452,7 @@ var Hm_Timer = { if (job[2] === 0) { job[2] = job[1]; Hm_Timer.jobs[index] = job; - try { job[0](); } catch(e) { console.log(e); } + try { job[0](); } catch (e) { console.log(e); } } } setTimeout(Hm_Timer.fire, Hm_Timer.interval); @@ -500,39 +492,49 @@ function Message_List() { if (completed) { for (index in this.callbacks) { func = this.callbacks[index]; - try { func(); } catch(e) { console.log(e); } + try { func(); } catch (e) { console.log(e); } } } fixLtrInRtl(); }; - this.update = function(msgs, id) { - Hm_Utils.tbody(id).html(''); + this.update = function (msgs) { + Hm_Utils.tbody().html(''); for (const index in msgs) { const row = msgs[index][0]; Hm_Utils.tbody(id).append(row); } }; - this.set_tab_index = function() { + this.set_tab_index = function () { var msg_rows = Hm_Utils.rows(); var count = 1; - msg_rows.each(function() { + msg_rows.each(function () { $(this).attr('tabindex', count); count++; }); }; - this.sort = function(fld) { + this.sort = function (fld) { var listitems = Hm_Utils.rows(); var aval; var bval; - var sort_result = listitems.sort(function(a, b) { - const sortField = fld.replace('-', ''); - if (['arrival', 'date'].includes(sortField)) { - aval = new Date($(`input.${sortField}`, $('td.dates', a)).val()); - bval = new Date($(`input.${sortField}`, $('td.dates', b)).val()); - if (fld.startsWith('-')) { + var sort_result = listitems.sort(function (a, b) { + switch (Math.abs(fld)) { + case 1: + case 2: + case 3: + aval = $($('td', a)[Math.abs(fld)]).text().replace(/^\s+/g, ''); + bval = $($('td', b)[Math.abs(fld)]).text().replace(/^\s+/g, ''); + break; + case 4: + default: + aval = $('input', $($('td', a)[Math.abs(fld)])).val(); + bval = $('input', $($('td', b)[Math.abs(fld)])).val(); + break; + } + if (fld == 4 || fld == -4 || !fld) { + if (fld == -4) { return aval - bval; } return bval - aval; @@ -547,13 +549,13 @@ function Message_List() { }); this.sort_fld = fld; Hm_Utils.tbody().html(''); - for (var i = 0, len=sort_result.length; i < len; i++) { + for (var i = 0, len = sort_result.length; i < len; i++) { Hm_Utils.tbody().append(sort_result[i]); } this.save_updated_list(); }; - this.insert_into_message_list = function(row, msg_rows) { + this.insert_into_message_list = function (row, msg_rows) { var sort_fld = this.sort_fld; if (typeof sort_fld == 'undefined' || sort_fld == null) { sort_fld = 4; @@ -562,10 +564,10 @@ function Message_List() { if (sort_fld == 4 || sort_fld == -4) { var timestr2; var timestr = $('.msg_timestamp', $(row)).val(); - $('tr', msg_rows).each(function() { + $('tr', msg_rows).each(function () { timestr2 = $('.msg_timestamp', $(this)).val(); - if ((sort_fld == -4 && (timestr2*1) >= (timestr*1)) || - (sort_fld == 4 && (timestr*1) >= (timestr2*1))) { + if ((sort_fld == -4 && (timestr2 * 1) >= (timestr * 1)) || + (sort_fld == 4 && (timestr * 1) >= (timestr2 * 1))) { element = $(this); return false; } @@ -574,10 +576,10 @@ function Message_List() { else { var bval; var aval = $($('td', $(row))[Math.abs(sort_fld)]).text().replace(/^\s+/g, ''); - $('tr', msg_rows).each(function() { + $('tr', msg_rows).each(function () { bval = $($('td', $(this))[Math.abs(sort_fld)]).text().replace(/^\s+/g, ''); if ((sort_fld < 0 && aval.toUpperCase().localeCompare(bval.toUpperCase()) > 0) || - (sort_fld > 0 && bval.toUpperCase().localeCompare(aval.toUpperCase()) > 0)) { + (sort_fld > 0 && bval.toUpperCase().localeCompare(aval.toUpperCase()) > 0)) { element = $(this); return false; } @@ -590,16 +592,16 @@ function Message_List() { else { msg_rows.append(row); } - self.just_inserted.push($('.from', $(row)).text()+' - '+$('.subject', $(row)).text()); + self.just_inserted.push($('.from', $(row)).text() + ' - ' + $('.subject', $(row)).text()); }; - this.reset_checkboxes = function() { + this.reset_checkboxes = function () { this.toggle_msg_controls(); this.set_row_events(); }; - this.toggle_msg_controls = function() { - if ($('input[type=checkbox]', $('.message_table')).filter(function() {return this.checked; }).length > 0) { + this.toggle_msg_controls = function () { + if ($('input[type=checkbox]', $('.message_table')).filter(function () { return this.checked; }).length > 0) { $('.msg_controls').addClass('d-flex'); $('.msg_controls').removeClass('d-none'); $('.mailbox_list_title').addClass('hide'); @@ -611,7 +613,7 @@ function Message_List() { } }; - this.update_after_action = function(action_type, selected) { + this.update_after_action = function (action_type, selected) { var remove = false; if (action_type == 'read' && getListPathParam() == 'unread') { remove = true; @@ -640,20 +642,20 @@ function Message_List() { this.reset_checkboxes(); }; - this.save_updated_list = function() { + this.save_updated_list = function () { if (this.page_caches.hasOwnProperty(getListPathParam())) { this.set_message_list_state(this.page_caches[getListPathParam()]); - Hm_Utils.save_to_local_storage('sort_'+getListPathParam(), this.sort_fld); + Hm_Utils.save_to_local_storage('sort_' + getListPathParam(), this.sort_fld); } }; - this.remove_after_action = function(action_type, selected) { + this.remove_after_action = function (action_type, selected) { var removed = 0; var class_name = false; var index; for (index in selected) { class_name = selected[index]; - $('.'+Hm_Utils.clean_selector(class_name)).remove(); + $('.' + Hm_Utils.clean_selector(class_name)).remove(); if (action_type == 'delete') { this.deleted.push(class_name); } @@ -662,14 +664,14 @@ function Message_List() { return removed; }; - this.read_after_action = function(action_type, selected) { + this.read_after_action = function (action_type, selected) { var read = 0; var row; var index; var class_name = false; for (index in selected) { class_name = selected[index]; - row = $('.'+Hm_Utils.clean_selector(class_name)); + row = $('.' + Hm_Utils.clean_selector(class_name)); if (action_type == 'read') { $('.subject > div', row).removeClass('unseen'); row.removeClass('unseen'); @@ -683,14 +685,14 @@ function Message_List() { return read; }; - this.flag_after_action = function(action_type, selected) { + this.flag_after_action = function (action_type, selected) { var flagged = 0; var class_name; var row; var index; for (index in selected) { class_name = selected[index]; - row = $('.'+Hm_Utils.clean_selector(class_name)); + row = $('.' + Hm_Utils.clean_selector(class_name)); if (action_type == 'flag') { $('.icon', row).html(hm_flag_image_src()); } @@ -702,7 +704,7 @@ function Message_List() { return flagged; }; - this.load_sources = function() { + this.load_sources = function () { var index; var source; if (!self.background) { @@ -715,7 +717,7 @@ function Message_List() { return false; }; - this.select_combined_view = function() { + this.select_combined_view = function () { if (self.page_caches.hasOwnProperty(getListPathParam())) { self.setup_combined_view(self.page_caches[getListPathParam()]); } @@ -727,23 +729,24 @@ function Message_List() { self.setup_combined_view(false); } } - var sort_type = Hm_Utils.get_from_local_storage('sort_'+getListPathParam()); + var sort_type = Hm_Utils.get_from_local_storage('sort_' + getListPathParam()); if (sort_type != null) { this.sort_fld = sort_type; $('.combined_sort').val(sort_type); } - $('.core_msg_control').on("click", function(e) { + $('.core_msg_control').on("click", function (e) { e.preventDefault(); - return self.message_action($(this).data('action')); }); - $('.toggle_link').on("click", function() { return self.toggle_rows(); }); - $('.refresh_link').on("click", function() { return self.load_sources(); }); + return self.message_action($(this).data('action')); + }); + $('.toggle_link').on("click", function () { return self.toggle_rows(); }); + $('.refresh_link').on("click", function () { return self.load_sources(); }); }; - this.add_sources = function(sources) { + this.add_sources = function (sources) { self.sources = sources; }; - this.setup_combined_view = function(cache_name) { + this.setup_combined_view = function (cache_name) { self.add_sources(hm_data_sources()); var data = Hm_Utils.get_from_local_storage(cache_name); var interval = Hm_Utils.get_from_global('combined_view_refresh_interval', 60); @@ -762,53 +765,53 @@ function Message_List() { } }; - this.clear_read_messages = function() { + this.clear_read_messages = function () { var class_name; var list = Hm_Utils.get_from_local_storage('read_message_list'); if (list && list.length) { list = Hm_Utils.json_decode(list); for (class_name in list) { - $('.'+Hm_Utils.clean_selector(class_name)).remove(); + $('.' + Hm_Utils.clean_selector(class_name)).remove(); } Hm_Utils.save_to_local_storage('read_message_list', ''); } }; /* TODO: remove module specific refs */ - this.update_title = function(list_path = getListPathParam()) { + this.update_title = function (list_path = getListPathParam()) { var count = 0; var rows = Hm_Utils.rows(); var tbody = Hm_Utils.tbody(); if (list_path == 'unread') { count = rows.length; - document.title = count+' '+hm_trans('Unread'); + document.title = count + ' ' + hm_trans('Unread'); } else if (list_path == 'flagged') { count = rows.length; - document.title = count+' '+hm_trans('Flagged'); + document.title = count + ' ' + hm_trans('Flagged'); } else if (list_path == 'combined_inbox') { count = $('tr .unseen', tbody).length; - document.title = count+' '+hm_trans('Unread in Everything'); + document.title = count + ' ' + hm_trans('Unread in Everything'); } else if (list_path == 'email') { count = $('tr .unseen', tbody).length; - document.title = count+' '+hm_trans('Unread in Email'); + document.title = count + ' ' + hm_trans('Unread in Email'); } else if (list_path == 'feeds') { count = $('tr .unseen', tbody).length; - document.title = count+' '+hm_trans('Unread in Feeds'); + document.title = count + ' ' + hm_trans('Unread in Feeds'); } }; - this.message_action = function(action_type) { + this.message_action = function (action_type) { if (action_type == 'delete' && !hm_delete_prompt()) { return false; } var msg_list = $('.message_table'); var selected = []; var current_list = self.filter_list(); - $('input[type=checkbox]', msg_list).each(function() { + $('input[type=checkbox]', msg_list).each(function () { if (this.checked) { selected.push($(this).val()); } @@ -816,10 +819,10 @@ function Message_List() { if (selected.length > 0) { var updated = false; Hm_Ajax.request( - [{'name': 'hm_ajax_hook', 'value': 'ajax_message_action'}, - {'name': 'action_type', 'value': action_type}, - {'name': 'message_ids', 'value': selected}], - function(res) { + [{ 'name': 'hm_ajax_hook', 'value': 'ajax_message_action' }, + { 'name': 'action_type', 'value': action_type }, + { 'name': 'message_ids', 'value': selected }], + function (res) { if (!res) { $('.message_table_body').replaceWith(current_list); self.save_updated_list(); @@ -844,10 +847,9 @@ function Message_List() { } }; - this.prev_next_links = function(msgUid, lisPath = getListPathParam()) { - let prevUrl; - let nextUrl; - + this.prev_next_links = function (msgUid, lisPath = getListPathParam()) { + let phref; + let nhref; const target = $('.msg_headers tr').last(); const messages = new Hm_MessagesStore(lisPath, Hm_Utils.get_url_page_number()); messages.load(false, true); @@ -858,31 +860,31 @@ function Message_List() { prevUrl = new URL(prevSubject.prop('href')); prevUrl.searchParams.set('list_parent', lisPath); const subject = prevSubject.text(); - const plink = ' '+subject+''; - $(''+plink+'').insertBefore(target); + const plink = ' ' + subject + ''; + $('' + plink + '').insertBefore(target); } if (next) { const nextSubject = $(next['0']).find('.subject a'); nextUrl = new URL(nextSubject.prop('href')); nextUrl.searchParams.set('list_parent', lisPath); const subject = nextSubject.text(); - - const nlink = ' '+subject+''; - $(''+nlink+'').insertBefore(target); + + const nlink = ' ' + subject + ''; + $('' + nlink + '').insertBefore(target); } return [prevUrl?.href, nextUrl?.href]; }; - this.check_empty_list = function() { + this.check_empty_list = function () { var count = Hm_Utils.rows().length; if (!count) { if (!$('.empty_list').length) { if (getPageNameParam() == 'search') { - $('.search_content').append('
'+hm_empty_folder()+'
'); + $('.search_content').append('
' + hm_empty_folder() + '
'); } else { - $('.message_list').append('
'+hm_empty_folder()+'
'); + $('.message_list').append('
' + hm_empty_folder() + '
'); } } } @@ -892,7 +894,7 @@ function Message_List() { return count === 0; }; - this.track_read_messages = function(class_name) { + this.track_read_messages = function (class_name) { var read_messages = Hm_Utils.get_from_local_storage('read_message_list'); if (read_messages && read_messages.length) { read_messages = Hm_Utils.json_decode(read_messages); @@ -909,9 +911,9 @@ function Message_List() { return added; }; - this.adjust_unread_total = function(amount, replace) { + this.adjust_unread_total = function (amount, replace) { var missing = $('.total_unread_count').text() === '' ? true : false; - var current = $('.total_unread_count').text()*1; + var current = $('.total_unread_count').text() * 1; var new_total; if (replace && amount == current && amount != 0) { return; @@ -929,7 +931,7 @@ function Message_List() { new_total = 0; } if (new_total != current || missing) { - $('.total_unread_count').html(' '+new_total+' '); + $('.total_unread_count').html(' ' + new_total + ' '); } if (new_total > current && getPageNameParam() != 'message_list' && getListPathParam() != 'unread') { $('.menu_unread > a').css('font-weight', 'bold'); @@ -941,14 +943,14 @@ function Message_List() { self.past_total = current; }; - this.toggle_rows = function() { + this.toggle_rows = function () { $('input[type=checkbox]', $('.message_table')).each(function () { this.checked = !this.checked; }); self.toggle_msg_controls(); return false; }; - this.filter_list = function() { - var data = Hm_Utils.rows().clone().filter(function() { + this.filter_list = function () { + var data = Hm_Utils.rows().clone().filter(function () { if (this.className == 'inline_msg') { return false; } @@ -959,7 +961,7 @@ function Message_List() { return res; }; - this.set_message_list_state = function(list_type) { + this.set_message_list_state = function (list_type) { var data = this.filter_list(); data.find('*[style]').attr('style', ''); data.find('input[type=checkbox]').removeAttr('checked'); @@ -975,10 +977,10 @@ function Message_List() { } }; - this.select_range = function(a, b) { + this.select_range = function (a, b) { var start = false; var end = false; - $('input[type=checkbox]', $('.message_table')).each(function() { + $('input[type=checkbox]', $('.message_table')).each(function () { if (end) { return false; } @@ -997,7 +999,7 @@ function Message_List() { }); }; - this.process_shift_click = function(el) { + this.process_shift_click = function (el) { var id = el.prop('id'); if (id == self.last_click) { return; @@ -1005,12 +1007,12 @@ function Message_List() { self.select_range(id, self.last_click); }; - this.set_row_events = function() { + this.set_row_events = function () { Hm_Utils.rows().off('click'); - Hm_Utils.rows().on('click', function(e) { self.process_row_click(e); }); + Hm_Utils.rows().on('click', function (e) { self.process_row_click(e); }); } - this.process_row_click = function(e) { + this.process_row_click = function (e) { if (e.target.tagName === 'A') { return; } @@ -1036,48 +1038,47 @@ function Message_List() { return false; } - this.select_on_row_click = function(shift, ctrl, el, target) { + this.select_on_row_click = function (shift, ctrl, el, target) { if (shift) { if (self.last_click) { self.process_shift_click(el); } - $('#'+el.prop('id')).prop('checked', 'checked'); + $('#' + el.prop('id')).prop('checked', 'checked'); self.last_click = el.prop('id'); } else if (ctrl) { if (el.prop('checked')) { - $('#'+el.prop('id')).prop('checked', false); + $('#' + el.prop('id')).prop('checked', false); } else { - $('#'+el.prop('id')).prop('checked', 'checked'); + $('#' + el.prop('id')).prop('checked', 'checked'); self.last_click = el.prop('id'); } } } - this.set_all_mail_state = function() { self.set_message_list_state('formatted_all_mail'); }; - this.set_combined_inbox_state = function() { self.set_message_list_state('formatted_combined_inbox'); }; - this.set_flagged_state = function() { self.set_message_list_state('formatted_flagged_data'); }; - this.set_unread_state = function() { self.set_message_list_state('formatted_unread_data'); }; - this.set_search_state = function() { self.set_message_list_state('formatted_search_data'); }; - this.set_junk_state = function() { self.set_message_list_state('formatted_junk_data'); }; - this.set_snoozed_state = function() { self.set_message_list_state('formatted_snoozed_data'); }; - this.set_trash_state = function() { self.set_message_list_state('formatted_trash_data'); }; - this.set_draft_state = function() { self.set_message_list_state('formatted_drafts_data'); }; - this.set_tag_state = function() { self.set_message_list_state('formatted_tag_data'); }; + this.set_all_mail_state = function () { self.set_message_list_state('formatted_all_mail'); }; + this.set_combined_inbox_state = function () { self.set_message_list_state('formatted_combined_inbox'); }; + this.set_flagged_state = function () { self.set_message_list_state('formatted_flagged_data'); }; + this.set_unread_state = function () { self.set_message_list_state('formatted_unread_data'); }; + this.set_search_state = function () { self.set_message_list_state('formatted_search_data'); }; + this.set_junk_state = function () { self.set_message_list_state('formatted_junk_data'); }; + this.set_trash_state = function () { self.set_message_list_state('formatted_trash_data'); }; + this.set_draft_state = function () { self.set_message_list_state('formatted_drafts_data'); }; + this.set_tag_state = function () { self.set_message_list_state('formatted_tag_data'); }; }; /* folder list */ var Hm_Folders = { expand_after_update: false, unread_counts: {}, - observer : false, + observer: false, - save_folder_list: function() { + save_folder_list: function () { Hm_Utils.save_to_local_storage('formatted_folder_list', $('.folder_list').html()); }, - load_unread_counts: function() { + load_unread_counts: function () { var res = Hm_Utils.json_decode(Hm_Utils.get_from_local_storage('unread_counts')); if (!res) { Hm_Folders.unread_counts = {}; @@ -1087,9 +1088,9 @@ var Hm_Folders = { } }, - update_unread_counts: function(folder) { + update_unread_counts: function (folder) { if (folder) { - $('.unread_'+folder).html(' '+Hm_Folders.unread_counts[folder]+' '); + $('.unread_' + folder).html(' ' + Hm_Folders.unread_counts[folder] + ' '); } else { var name; @@ -1101,17 +1102,17 @@ var Hm_Folders = { if (count) { if (getListPathParam() == name && getPageNameParam() == 'message_list') { var title = document.title.replace(/^\[\d+\]/, ''); - document.title = '['+count+'] '+title; + document.title = '[' + count + '] ' + title; /* HERE */ } - $('.unread_'+name).html(' '+count+' '); + $('.unread_' + name).html(' ' + count + ' '); } } } Hm_Utils.save_to_local_storage('unread_counts', Hm_Utils.json_encode(Hm_Folders.unread_counts)); }, - open_folder_list: function() { + open_folder_list: function () { $('.folder_list').show(); $('.folder_toggle').toggle(); if (hm_mobile()) { @@ -1124,7 +1125,7 @@ var Hm_Folders = { return false; }, - toggle_folder_list: function() { + toggle_folder_list: function () { if ($('.folder_list').css('display') == 'none') { Hm_Folders.open_folder_list(); } @@ -1133,7 +1134,7 @@ var Hm_Folders = { } }, - hide_folder_list: function(forget) { + hide_folder_list: function (forget) { $('.folder_list').hide(); $('.folder_toggle').show(); if (!forget) { @@ -1144,7 +1145,7 @@ var Hm_Folders = { return false; }, - reload_folders: function(force, expand_after_update) { + reload_folders: function (force, expand_after_update) { if (document.cookie.indexOf('hm_reload_folders=1') > -1 || force) { Hm_Folders.expand_after_update = expand_after_update; var ui_state = Hm_Utils.preserve_local_settings(); @@ -1156,28 +1157,28 @@ var Hm_Folders = { return false; }, - sort_list: function(class_name, exclude_name, last_name) { - var folder = $('.'+class_name+' ul'); + sort_list: function (class_name, exclude_name, last_name) { + var folder = $('.' + class_name + ' ul'); var listitems; if (exclude_name) { - listitems = $('li:not(.'+exclude_name+')', folder); + listitems = $('li:not(.' + exclude_name + ')', folder); } else { listitems = $('li', folder); } - listitems = listitems.sort(function(a, b) { + listitems = listitems.sort(function (a, b) { if (last_name && ($(a).attr('class') == last_name || $(b).attr('class') == last_name)) { return false; } if ($(b).text().toUpperCase() == 'ALL') { return true; } - return $(a).text().toUpperCase().localeCompare($(b).text().toUpperCase()); + return $(a).text().toUpperCase().localeCompare($(b).text().toUpperCase()); }); - $.each(listitems, function(_, itm) { folder.append(itm); }); + $.each(listitems, function (_, itm) { folder.append(itm); }); }, - update_folder_list_display: function(res) { + update_folder_list_display: function (res) { $('.folder_list').html(res.formatted_folder_list); Hm_Folders.sort_list('email_folders', 'menu_email'); Hm_Folders.sort_list('feeds_folders', 'menu_feeds', 'feeds_add_new'); @@ -1193,9 +1194,9 @@ var Hm_Folders = { hl_save_link(); }, - update_folder_list: function() { + update_folder_list: function () { Hm_Ajax.request( - [{'name': 'hm_ajax_hook', 'value': 'ajax_hm_folders'}], + [{ 'name': 'hm_ajax_hook', 'value': 'ajax_hm_folders' }], Hm_Folders.update_folder_list_display, [], true @@ -1203,72 +1204,72 @@ var Hm_Folders = { return false; }, - folder_list_events: function() { - $('.imap_folder_link').on("click", function() { return expand_imap_folders($(this)); }); - $('.src_name').on('click', function() { + folder_list_events: function () { + $('.imap_folder_link').on("click", function () { return expand_imap_folders($(this)); }); + $('.src_name').on('click', function () { let transformValue = ''; if ($(this).attr('aria-expanded') == 'true') { transformValue = 'rotate(180deg)'; - + } else { transformValue = 'rotate(0deg)'; } - + $(this).find('i').css('transform', transformValue); }); - $('.update_message_list').on("click", function(e) { + $('.update_message_list').on("click", function (e) { var text = e.target.innerHTML; e.target.innerHTML = '
Loading...
'; Hm_Folders.update_folder_list(); - Hm_Ajax.add_callback_hook('hm_reload_folders', function() { + Hm_Ajax.add_callback_hook('hm_reload_folders', function () { e.target.innerHTML = text; }); return false; }); - $('.hide_folders').on("click", function() { return Hm_Folders.hide_folder_list(); }); - $('.logout_link').on("click", function(e) { return Hm_Utils.confirm_logout(); }); + $('.hide_folders').on("click", function () { return Hm_Folders.hide_folder_list(); }); + $('.logout_link').on("click", function (e) { return Hm_Utils.confirm_logout(); }); if (hm_search_terms()) { $('.search_terms').val(hm_search_terms()); } - $('.search_terms').on('search', function() { - Hm_Ajax.request([{'name': 'hm_ajax_hook', 'value': 'ajax_reset_search'}]); + $('.search_terms').on('search', function () { + Hm_Ajax.request([{ 'name': 'hm_ajax_hook', 'value': 'ajax_reset_search' }]); }); }, - hl_selected_menu: function() { + hl_selected_menu: function () { const page = getPageNameParam(); const path = getListPathParam(); - + $('.folder_list').find('*').removeClass('selected_menu'); if (path) { if (page == 'message_list' || page == 'message') { - $("[data-id='"+Hm_Utils.clean_selector(path)+"']").closest('li').addClass('selected_menu'); - $('.menu_'+Hm_Utils.clean_selector(path)).addClass('selected_menu'); + $("[data-id='" + Hm_Utils.clean_selector(path) + "']").closest('li').addClass('selected_menu'); + $('.menu_' + Hm_Utils.clean_selector(path)).addClass('selected_menu'); } else { - $('.menu_'+path).addClass('selected_menu'); + $('.menu_' + path).addClass('selected_menu'); } } else { - $('.menu_'+page).addClass('selected_menu'); + $('.menu_' + page).addClass('selected_menu'); } }, - listen_for_new_messages: function() { + listen_for_new_messages: function () { var target = $('.total_unread_count').get(0); if (!Hm_Folders.observer) { - Hm_Folders.observer = new MutationObserver(function(mutations) { + Hm_Folders.observer = new MutationObserver(function (mutations) { $('body').trigger('new_message'); }); } else { Hm_Folders.observer.disconnect(); } - Hm_Folders.observer.observe(target, {attributes: true, childList: true, characterData: true}); + Hm_Folders.observer.observe(target, { attributes: true, childList: true, characterData: true }); }, - load_from_local_storage: function() { + load_from_local_storage: function () { var folder_list = Hm_Utils.get_from_local_storage('formatted_folder_list'); if (folder_list) { $('.folder_list').html(folder_list); @@ -1287,21 +1288,21 @@ var Hm_Folders = { return false; }, - toggle_folders_event: function() { - $('.folder_toggle').on("click", function() { return Hm_Folders.open_folder_list(); }); + toggle_folders_event: function () { + $('.folder_toggle').on("click", function () { return Hm_Folders.open_folder_list(); }); } }; /* misc */ var Hm_Utils = { - get_url_page_number: function() { + get_url_page_number: function () { var index; var match_result; var page_number = 1; var params = location.search.substr(1).split('&'); var param_len = params.length; - for (index=0; index < param_len; index++) { + for (index = 0; index < param_len; index++) { match_result = params[index].match(/list_page=(\d+)/); if (match_result) { page_number = match_result[1]; @@ -1311,14 +1312,14 @@ var Hm_Utils = { return page_number; }, - get_from_global: function(name, def) { + get_from_global: function (name, def) { if (globals[name]) { return globals[name]; } return def; }, - preserve_local_settings: function() { + preserve_local_settings: function () { var i; var result = {}; var prefix = window.location.pathname.length; @@ -1331,33 +1332,33 @@ var Hm_Utils = { return result; }, - restore_local_settings: function(settings) { + restore_local_settings: function (settings) { var i; for (i in settings) { Hm_Utils.save_to_local_storage(i, settings[i]); } }, - reset_search_form: function() { + reset_search_form: function () { Hm_Utils.save_to_local_storage('formatted_search_data', ''); - Hm_Ajax.request([{'name': 'hm_ajax_hook', 'value': 'ajax_reset_search'}], - function(res) { window.location = '?page=search'; }, false, true); + Hm_Ajax.request([{ 'name': 'hm_ajax_hook', 'value': 'ajax_reset_search' }], + function (res) { window.location = '?page=search'; }, false, true); return false; }, - confirm_logout: function() { - if (! $('#unsaved_changes').length || $('#unsaved_changes').val() == 0) { + confirm_logout: function () { + if (!$('#unsaved_changes').length || $('#unsaved_changes').val() == 0) { document.getElementById('logout_without_saving').click(); } else { - var confirmLogoutModal = new bootstrap.Modal(document.getElementById('confirmLogoutModal'), {keyboard: true}) + var confirmLogoutModal = new bootstrap.Modal(document.getElementById('confirmLogoutModal'), { keyboard: true }) confirmLogoutModal.show(); $('.confirm_logout').show(); } return false; }, - get_path_type: function(path) { + get_path_type: function (path) { if (path.indexOf('_') != -1) { var path_parts = path.split('_'); return path_parts[0]; @@ -1365,7 +1366,7 @@ var Hm_Utils = { return false; }, - parse_folder_path: function(path, path_type) { + parse_folder_path: function (path, path_type) { if (!path_type) { path_type = Hm_Utils.get_path_type(path); } @@ -1396,7 +1397,7 @@ var Hm_Utils = { folder = parts[3]; } if (type && server_id) { - return {'type': type, 'server_id' : server_id, 'folder' : folder, 'uid': uid}; + return { 'type': type, 'server_id': server_id, 'folder': folder, 'uid': uid }; } } else if (path_type == 'feeds') { @@ -1409,13 +1410,13 @@ var Hm_Utils = { uid = parts[2]; } if (type && server_id) { - return {'type': type, 'server_id' : server_id, 'uid': uid}; + return { 'type': type, 'server_id': server_id, 'uid': uid }; } } return false; }, - toggle_section: function(class_name, force_on, force_off) { + toggle_section: function (class_name, force_on, force_off) { if ($(class_name).length) { if (force_off) { $(class_name).css('display', 'block'); @@ -1429,7 +1430,7 @@ var Hm_Utils = { return false; }, - toggle_page_section: function(class_name) { + toggle_page_section: function (class_name) { if ($(class_name).length) { $(class_name).toggle(); Hm_Utils.save_to_local_storage(class_name, $(class_name).css('display')); @@ -1437,12 +1438,12 @@ var Hm_Utils = { return false; }, - get_from_local_storage: function(key) { + get_from_local_storage: function (key) { var prefix = window.location.pathname; - key = prefix+key; + key = prefix + key; var res = false; if (hm_encrypt_local_storage()) { - res = Hm_Crypt.decrypt(sessionStorage.getItem(key)); + res = Hm_Crypt.decrypt(sessionStorage.getItem(key)); } else { res = sessionStorage.getItem(key); @@ -1450,7 +1451,7 @@ var Hm_Utils = { return res; }, - search_from_local_storage: function(pattern) { + search_from_local_storage: function (pattern) { const results = []; const key_pattern = new RegExp(pattern); for (let i = 0; i < sessionStorage.length; i++) { @@ -1463,14 +1464,14 @@ var Hm_Utils = { return results; }, - save_to_local_storage: function(key, val) { + save_to_local_storage: function (key, val) { var prefix = window.location.pathname; - key = prefix+key; + key = prefix + key; if (hm_encrypt_local_storage()) { val = Hm_Crypt.encrypt(val); } - if (Storage !== void(0)) { - try { sessionStorage.setItem(key, val); } catch(e) { + if (Storage !== void (0)) { + try { sessionStorage.setItem(key, val); } catch (e) { sessionStorage.clear(); sessionStorage.setItem(key, val); } @@ -1482,25 +1483,25 @@ var Hm_Utils = { return false; }, - clean_selector: function(str) { + clean_selector: function (str) { return str.replace(/(:|\.|\[|\]|\/)/g, "\\$1"); }, - toggle_long_headers: function() { + toggle_long_headers: function () { $('.long_header').toggle(); $('.all_headers').toggle(); $('.small_headers').toggle(); return false; }, - set_unsaved_changes: function(state) { + set_unsaved_changes: function (state) { $('#unsaved_changes').val(state); }, /** * Shows pending messages added with the add_sys_message method */ - show_sys_messages: function() { + show_sys_messages: function () { $('.sys_messages').removeClass('d-none'); }, @@ -1509,12 +1510,12 @@ var Hm_Utils = { * @param {*} msg : The alert message to display * @param {*} type : The type of message to display, depending on the type of boostrap5 alert (primary, secondary, success, danger, warning, info, light, dark ) */ - add_sys_message: function(msg, type = 'info') { + add_sys_message: function (msg, type = 'info') { if (!msg) { return; } const icon = type == 'success' ? 'bi-check-circle' : 'bi-exclamation-circle'; - $('.sys_messages').append(''); + $('.sys_messages').append(''); this.show_sys_messages(); }, @@ -1522,11 +1523,11 @@ var Hm_Utils = { $('.sys_messages').html(''); }, - cancel_logout_event: function() { - $('.cancel_logout').on("click", function() { $('.confirm_logout').hide(); return false; }); + cancel_logout_event: function () { + $('.cancel_logout').on("click", function () { $('.confirm_logout').hide(); return false; }); }, - json_encode: function(val) { + json_encode: function (val) { try { return JSON.stringify(val); } @@ -1535,7 +1536,7 @@ var Hm_Utils = { } }, - json_decode: function(val, original) { + json_decode: function (val, original) { try { return JSON.parse(val); } @@ -1547,25 +1548,22 @@ var Hm_Utils = { } }, - rows: function(id) { - return this.tbody(id).find('tr').not('.inline_msg'); + rows: function () { + return $('.message_table_body > tr').not('.inline_msg'); }, - tbody: function(id) { - if (id) { - return $('#'+id); - } + tbody: function () { return $('.message_table_body'); }, - html_entities: function(str) { + html_entities: function (str) { return String(str).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); }, - test_connection: function() { + test_connection: function () { $('.offline').hide(); Hm_Ajax.request( - [{'name': 'hm_ajax_hook', 'value': 'ajax_test'}], + [{ 'name': 'hm_ajax_hook', 'value': 'ajax_test' }], false, [], false, false, false); }, @@ -1576,7 +1574,7 @@ var Hm_Utils = { }, redirect: function (path) { - if (! path) { + if (!path) { path = window.location.href; } window.location.href = path; @@ -1588,7 +1586,7 @@ var Hm_Utils = { }; var Hm_Crypt = { - decrypt: function(ciphertext) { + decrypt: function (ciphertext) { try { ciphertext = atob(ciphertext); if (!ciphertext || ciphertext.length < 200) { @@ -1610,16 +1608,16 @@ var Hm_Crypt = { } var iv = forge.pkcs5.pbkdf2(secret, salt, 100, 16, digest); var decipher = forge.cipher.createDecipher('AES-CBC', key); - decipher.start({iv: iv}); + decipher.start({ iv: iv }); decipher.update(forge.util.createBuffer(payload, 'raw')); decipher.finish(); return forge.util.decodeUtf8(decipher.output.data); - } catch(e) { + } catch (e) { return false; } }, - encrypt: function(plaintext) { + encrypt: function (plaintext) { try { var secret = $('#hm_page_key').val(); var salt = forge.random.getBytesSync(128); @@ -1629,28 +1627,28 @@ var Hm_Crypt = { var iv = forge.pkcs5.pbkdf2(secret, salt, 100, 16, digest); var hmac = forge.hmac.create(); var cipher = forge.cipher.createCipher('AES-CBC', key); - cipher.start({iv: iv}); + cipher.start({ iv: iv }); cipher.update(forge.util.createBuffer(plaintext, 'utf8')); cipher.finish(); hmac.start(digest, hmac_key); hmac.update(cipher.output.data); - return btoa(salt+hmac.digest().data+cipher.output.data); - } catch(e) { + return btoa(salt + hmac.digest().data + cipher.output.data); + } catch (e) { return false; } }, } -var update_password = function(id) { - var pass = $('#update_pw_'+id).val(); +var update_password = function (id) { + var pass = $('#update_pw_' + id).val(); if (pass && pass.length) { Hm_Ajax.request( - [{'name': 'hm_ajax_hook', 'value': 'ajax_update_server_pw'}, - {'name': 'password', 'value': pass}, - {'name': 'server_pw_id', 'value': id}], - function(res) { + [{ 'name': 'hm_ajax_hook', 'value': 'ajax_update_server_pw' }, + { 'name': 'password', 'value': pass }, + { 'name': 'server_pw_id', 'value': id }], + function (res) { if (res.connect_status) { - $('.div_'+id).remove(); + $('.div_' + id).remove(); if ($('.home_password_dialogs div').length == 1) { $('.home_password_dialogs').remove(); } @@ -1660,13 +1658,13 @@ var update_password = function(id) { } } -var elog = function(val) { +var elog = function (val) { if (hm_debug()) { console.log(val); } }; -var hl_save_link = function() { +var hl_save_link = function () { if ($('.save_reminder').length) { $('.menu_save a').css('font-weight', 'bold'); } @@ -1675,7 +1673,7 @@ var hl_save_link = function() { } }; -var reset_default_value_checkbox = function() { +var reset_default_value_checkbox = function () { var checkbox = $(this).closest('.tooltip_restore').prev('input[type="checkbox"]'); var default_value = checkbox.data('default-value'); default_value = (default_value === 'true'); @@ -1683,27 +1681,27 @@ var reset_default_value_checkbox = function() { checkbox.prop('disabled', true); }; -var reset_default_timezone = function() { +var reset_default_timezone = function () { var hm_default_timezone = window.hm_default_timezone; $('#timezone').val(hm_default_timezone); } -var reset_default_value_select = function() { +var reset_default_value_select = function () { var dropdown = $(this).closest('.tooltip_restore').prev('select'); var default_value = dropdown.data('default-value'); dropdown.val(default_value); } -var reset_default_value_input = function() { +var reset_default_value_input = function () { var inputField = $(this).closest('.tooltip_restore').prev('input'); var default_value = inputField.data('default-value'); inputField.val(default_value); } -var decrease_servers = function(section) { +var decrease_servers = function (section) { const element = document.querySelector(`.server_count .${section}_server_count`); const value = parseInt(element.textContent); if (value > 0) { - element.innerHTML = value - 1; + element.innerHTML = value - 1; } if (value === 1) { @@ -1713,11 +1711,11 @@ var decrease_servers = function(section) { } }; -var err_msg = function(msg) { - return "ERR"+hm_trans(msg); +var err_msg = function (msg) { + return "ERR" + hm_trans(msg); }; -var hm_spinner = function(type = 'border', size = '') { +var hm_spinner = function (type = 'border', size = '') { return `
Loading... @@ -1725,13 +1723,7 @@ var hm_spinner = function(type = 'border', size = '') {
` }; -var hm_spinner_text = function(text, id = 'spinner-text') { - return `
- ${text} - -
`; -}; -var fillImapData = function(details) { +var fillImapData = function (details) { $('#srv_setup_stepper_imap_address').val(details.server); $('#srv_setup_stepper_imap_port').val(details.port); $('#srv_setup_stepper_imap_server_id').val(details.id); @@ -1740,31 +1732,31 @@ var fillImapData = function(details) { $('#srv_setup_stepper_imap_sieve_host').val(details.sieve_config_host); $("#srv_setup_stepper_enable_sieve").trigger("click", false); $('#srv_setup_stepper_imap_sieve_mode_tls') - .prop('checked', details.tls) - .trigger('change'); + .prop('checked', details.tls) + .trigger('change'); } - if(details.tls) { + if (details.tls) { $("input[name='srv_setup_stepper_imap_tls'][value='true']").prop("checked", true); } else { $("input[name='srv_setup_stepper_imap_tls'][value='false']").prop("checked", true); } }; -var fillSmtpData = function(details) { +var fillSmtpData = function (details) { $('#srv_setup_stepper_smtp_server_id').val(details.id); $('#srv_setup_stepper_smtp_address').val(details.server); $('#srv_setup_stepper_smtp_port').val(details.port); }; -var fillJmapData = function(details) { +var fillJmapData = function (details) { $('#srv_setup_stepper_imap_server_id').val(details.id); $('#srv_setup_stepper_only_jmap').trigger('click'); $('#srv_setup_stepper_jmap_address').val(details.server); $('#srv_setup_stepper_imap_hide_from_c_page').prop("checked", details.hide); }; -var imap_smtp_edit_action = function(event) { +var imap_smtp_edit_action = function (event) { resetQuickSetupForm(); event.preventDefault(); Hm_Notices.hide(true); @@ -1782,7 +1774,7 @@ var imap_smtp_edit_action = function(event) { fillJmapData(details); } else if ($(this).data('type') == 'imap') { fillImapData(details); - var smtpDetails = $('[data-type="smtp"][data-id="'+details.name+'"]'); + var smtpDetails = $('[data-type="smtp"][data-id="' + details.name + '"]'); if (smtpDetails.length) { fillSmtpData(smtpDetails.data('server-details')); } else { @@ -1790,7 +1782,7 @@ var imap_smtp_edit_action = function(event) { } } else { fillSmtpData(details); - var imapDetails = $('[data-type="imap"][data-id="'+details.name+'"]'); + var imapDetails = $('[data-type="imap"][data-id="' + details.name + '"]'); if (imapDetails.length) { fillImapData(imapDetails.data('server-details')); } else { @@ -1799,7 +1791,8 @@ var imap_smtp_edit_action = function(event) { } }; -var hasLeadingOrTrailingSpaces = function(str) { + +var hasLeadingOrTrailingSpaces = function (str) { return str !== str.trim(); }; @@ -1807,7 +1800,8 @@ var hasLeadingOrTrailingSpaces = function(str) { var Hm_Message_List = new Message_List(); function sortHandlerForMessageListAndSearchPage() { - $('.source_link').on("click", function() { $('.list_sources').toggle(); $('#list_controls_menu').hide(); return false; }); + $('.combined_sort').on("change", function () { Hm_Message_List.sort($(this).val()); }); + $('.source_link').on("click", function () { $('.list_sources').toggle(); $('#list_controls_menu').hide(); return false; }); if (getListPathParam() == 'unread' && $('.menu_unread > a').css('font-weight') == 'bold') { $('.menu_unread > a').css('font-weight', 'normal'); Hm_Folders.save_folder_list(); @@ -1815,7 +1809,7 @@ function sortHandlerForMessageListAndSearchPage() { } /* executes on onload, has access to other module code */ -$(function() { +$(function () { /* Remove disabled attribute to send checkbox */ $('.save_settings').on("click", function (e) { $('.general_setting input[type=checkbox]').each(function () { @@ -1824,7 +1818,7 @@ $(function() { } }); }) - $('.reset_factory_button').on('click', function() { return hm_delete_prompt(); }); + $('.reset_factory_button').on('click', function () { return hm_delete_prompt(); }); /* check for folder reload */ var reloaded = Hm_Folders.reload_folders(); @@ -1835,7 +1829,7 @@ $(function() { /* fire up the job scheduler */ Hm_Timer.fire(); - + /* show any pending notices */ Hm_Utils.show_sys_messages(); @@ -1846,18 +1840,18 @@ $(function() { hl_save_link(); if (hm_mailto()) { - try { navigator.registerProtocolHandler("mailto", "?page=compose&compose_to=%s", "Cypht"); } catch(e) {} + try { navigator.registerProtocolHandler("mailto", "?page=compose&compose_to=%s", "Cypht"); } catch (e) { } } if (hm_mobile()) { - swipe_event(document.body, function() { Hm_Folders.open_folder_list(); }, 'right'); - swipe_event(document.body, function() { Hm_Folders.hide_folder_list(); }, 'left'); + swipe_event(document.body, function () { Hm_Folders.open_folder_list(); }, 'right'); + swipe_event(document.body, function () { Hm_Folders.hide_folder_list(); }, 'left'); $('.list_controls.on_mobile').show(); $('.list_controls.no_mobile').hide(); } else { $('.list_controls.on_mobile').hide(); } - $('.offline').on("click", function() { Hm_Utils.test_connection(); }); + $('.offline').on("click", function () { Hm_Utils.test_connection(); }); if (hm_check_dirty_flag()) { $('form:not(.search_terms)').areYouSure(); @@ -1907,7 +1901,7 @@ function fixLtrInRtl() { return [] } - setTimeout(function(){ + setTimeout(function () { var elements = getElements() for (var index = 0; index < elements.length; index++) { if (isTextEnglish(elements[index].textContent)) { @@ -1925,7 +1919,7 @@ function listControlsMenu() { $('.list_sources').hide(); } -var resetStepperButtons = function() { +var resetStepperButtons = function () { $('.step_config-actions button').removeAttr('disabled'); $('#stepper-action-finish').text($('#stepper-action-finish').text().slice(0, -3)); }; @@ -1962,14 +1956,14 @@ function submitSmtpImapServer() { { name: 'srv_setup_stepper_smtp_server_id', value: $('#srv_setup_stepper_smtp_server_id').val() } ]; - Hm_Ajax.request(requestData, function(res) { + Hm_Ajax.request(requestData, function (res) { resetStepperButtons(); if (res.just_saved_credentials) { if (res.imap_server_id) { Hm_Ajax.request( - [{'name': 'hm_ajax_hook', 'value': 'ajax_imap_accept_special_folders'}, - {'name': 'imap_server_id', value: res.imap_server_id}, - {'name': 'imap_service_name', value: res.imap_service_name}], + [{ 'name': 'hm_ajax_hook', 'value': 'ajax_imap_accept_special_folders' }, + { 'name': 'imap_server_id', value: res.imap_server_id }, + { 'name': 'imap_service_name', value: res.imap_service_name }], function () { resetQuickSetupForm(); Hm_Utils.redirect(); @@ -2014,23 +2008,23 @@ function resetQuickSetupForm() { } function handleCreateProfileCheckboxChange(checkbox) { - if(checkbox.checked) { - $(checkbox).closest('.form-check').next().show(); - }else{ - $(checkbox).closest('.form-check').next().hide(); + if (checkbox.checked) { + $('#srv_setup_stepper_profile_bloc').show(); + } else { + $('#srv_setup_stepper_profile_bloc').hide(); } } -function handleSieveStatusChange (checkbox) { - if(checkbox.checked) { +function handleSieveStatusChange(checkbox) { + if (checkbox.checked) { $('#srv_setup_stepper_imap_sieve_host_bloc').show(); - }else{ + } else { $('#srv_setup_stepper_imap_sieve_host_bloc').hide(); } } function handleSmtpImapCheckboxChange(checkbox) { if (checkbox.id === 'srv_setup_stepper_is_receiver') { - if(checkbox.checked) { + if (checkbox.checked) { $('#step_config-imap_bloc').show(); $('#step_config_combined_view').show(); $('#srv_setup_stepper_jmap_select_box').show(); @@ -2045,15 +2039,15 @@ function handleSmtpImapCheckboxChange(checkbox) { if (checkbox.id === 'srv_setup_stepper_is_sender') { console.log("checkbox.checked", checkbox.checked) - if(checkbox.checked) $('#step_config-smtp_bloc').show(); + if (checkbox.checked) $('#step_config-smtp_bloc').show(); else $('#step_config-smtp_bloc').hide(); } if ($('#srv_setup_stepper_is_sender').prop('checked') && $('#srv_setup_stepper_is_receiver').prop('checked')) { $('#srv_setup_stepper_profile_bloc').show(); $('#srv_setup_stepper_profile_checkbox_bloc').show(); - - } else if(! $('#srv_setup_stepper_is_sender').prop('checked') || ! $('#srv_setup_stepper_is_receiver').prop('checked')) { + + } else if (!$('#srv_setup_stepper_is_sender').prop('checked') || !$('#srv_setup_stepper_is_receiver').prop('checked')) { $('#srv_setup_stepper_profile_bloc').hide(); $('#srv_setup_stepper_profile_checkbox_bloc').hide(); } @@ -2063,7 +2057,7 @@ function handleJmapCheckboxChange(checkbox) { if (checkbox.checked) { $('#step_config-jmap_bloc').show(); $('#step_config-imap_bloc').hide(); - if (! $('#srv_setup_stepper_enable_sieve').prop('checked')) { + if (!$('#srv_setup_stepper_enable_sieve').prop('checked')) { $('#srv_setup_stepper_imap_sieve_host_bloc').hide(); } } else { @@ -2074,9 +2068,9 @@ function handleJmapCheckboxChange(checkbox) { function handleProviderChange(select) { let providerKey = select.value; - if(providerKey) { + if (providerKey) { getServiceDetails(providerKey); - }else{ + } else { $("#srv_setup_stepper_smtp_address").val(''); $("#srv_setup_stepper_smtp_port").val(465); $("#srv_setup_stepper_imap_address").val(''); @@ -2090,13 +2084,13 @@ function setDefaultReplyTo(val) { } } function display_config_step(stepNumber) { - if(stepNumber === 2) { + if (stepNumber === 2) { var isValid = true; - [ {key: 'srv_setup_stepper_profile_name', value: $('#srv_setup_stepper_profile_name').val()}, - {key: 'srv_setup_stepper_email', value: $('#srv_setup_stepper_email').val()}, - {key: 'srv_setup_stepper_password', value: $('#srv_setup_stepper_password').val()}].forEach((item) => { + [{ key: 'srv_setup_stepper_profile_name', value: $('#srv_setup_stepper_profile_name').val() }, + { key: 'srv_setup_stepper_email', value: $('#srv_setup_stepper_email').val() }, + { key: 'srv_setup_stepper_password', value: $('#srv_setup_stepper_password').val() }].forEach((item) => { if (!item.value) { if (item.key == 'srv_setup_stepper_password' && ($('#srv_setup_stepper_imap_server_id').val() || $('#srv_setup_stepper_smtp_server_id').val())) { $(`#${item.key}-error`).text(''); @@ -2104,7 +2098,7 @@ function display_config_step(stepNumber) { $(`#${item.key}-error`).text('Required'); isValid = false; } - + } else { $(`#${item.key}-error`).text(''); } @@ -2119,46 +2113,46 @@ function display_config_step(stepNumber) { setDefaultReplyTo($('#srv_setup_stepper_email').val()); } - if(stepNumber === 3) { + if (stepNumber === 3) { var requiredFields = []; var isValid = true; - if(!$('#srv_setup_stepper_is_sender').is(':checked') && !$('#srv_setup_stepper_is_receiver').is(':checked')){ + if (!$('#srv_setup_stepper_is_sender').is(':checked') && !$('#srv_setup_stepper_is_receiver').is(':checked')) { $('#srv_setup_stepper_serve_type-error').text('Required'); return; } - if($('#srv_setup_stepper_is_sender').is(':checked') && + if ($('#srv_setup_stepper_is_sender').is(':checked') && $('#srv_setup_stepper_is_receiver').is(':checked') && - $('#srv_setup_stepper_only_jmap').is(':checked')){ + $('#srv_setup_stepper_only_jmap').is(':checked')) { requiredFields.push( - {key: 'srv_setup_stepper_jmap_address', value: $('#srv_setup_stepper_jmap_address').val()}, + { key: 'srv_setup_stepper_jmap_address', value: $('#srv_setup_stepper_jmap_address').val() }, ) - }else { - if($('#srv_setup_stepper_is_sender').is(':checked')){ + } else { + if ($('#srv_setup_stepper_is_sender').is(':checked')) { requiredFields.push( - {key: 'srv_setup_stepper_smtp_address', value: $('#srv_setup_stepper_smtp_address').val()}, - {key: 'srv_setup_stepper_smtp_port', value: $('#srv_setup_stepper_smtp_port').val()}, + { key: 'srv_setup_stepper_smtp_address', value: $('#srv_setup_stepper_smtp_address').val() }, + { key: 'srv_setup_stepper_smtp_port', value: $('#srv_setup_stepper_smtp_port').val() }, ) } - if($('#srv_setup_stepper_is_receiver').is(':checked')) { + if ($('#srv_setup_stepper_is_receiver').is(':checked')) { requiredFields.push( - {key: 'srv_setup_stepper_imap_address', value: $('#srv_setup_stepper_imap_address').val()}, - {key: 'srv_setup_stepper_imap_port', value: $('#srv_setup_stepper_imap_port').val()}, + { key: 'srv_setup_stepper_imap_address', value: $('#srv_setup_stepper_imap_address').val() }, + { key: 'srv_setup_stepper_imap_port', value: $('#srv_setup_stepper_imap_port').val() }, ) } } - if($('#srv_setup_stepper_enable_sieve').is(':checked')) { + if ($('#srv_setup_stepper_enable_sieve').is(':checked')) { requiredFields.push( - {key: 'srv_setup_stepper_imap_sieve_host', value: $('#srv_setup_stepper_imap_sieve_host').val()}, - {key: 'srv_setup_stepper_imap_sieve_mode_tls', value: $('#srv_setup_stepper_imap_sieve_mode_tls').val()}, + { key: 'srv_setup_stepper_imap_sieve_host', value: $('#srv_setup_stepper_imap_sieve_host').val() }, + { key: 'srv_setup_stepper_imap_sieve_mode_tls', value: $('#srv_setup_stepper_imap_sieve_mode_tls').val() }, ) } requiredFields.forEach((item) => { - if(!item.value) { + if (!item.value) { $(`#${item.key}-error`).text('Required'); isValid = false; } @@ -2166,7 +2160,7 @@ function display_config_step(stepNumber) { }) - if(!isValid) return + if (!isValid) return submitSmtpImapServer(); return @@ -2182,32 +2176,32 @@ function display_config_step(stepNumber) { if (selectedStep) { selectedStep.style.display = 'block'; - if(stepNumber === 0) $('.srv_setup_stepper_btn').show(); + if (stepNumber === 0) $('.srv_setup_stepper_btn').show(); } } -function getServiceDetails(providerKey){ - if(providerKey) { +function getServiceDetails(providerKey) { + if (providerKey) { $("#srv_setup_stepper_provider").val(providerKey); Hm_Ajax.request( [ - {'name': 'hm_ajax_hook', 'value': 'ajax_get_nux_service_details'}, - {'name': 'nux_service', 'value': providerKey},], - function(res) { - if(res.service_details){ + { 'name': 'hm_ajax_hook', 'value': 'ajax_get_nux_service_details' }, + { 'name': 'nux_service', 'value': providerKey },], + function (res) { + if (res.service_details) { let serverConfig = JSON.parse(res.service_details) $("#srv_setup_stepper_smtp_address").val(serverConfig.smtp.server); $("#srv_setup_stepper_smtp_port").val(serverConfig.smtp.port); - if(serverConfig.smtp.tls)$("input[name='srv_setup_stepper_smtp_tls'][value='true']").prop("checked", true); + if (serverConfig.smtp.tls) $("input[name='srv_setup_stepper_smtp_tls'][value='true']").prop("checked", true); else $("input[name='srv_setup_stepper_smtp_tls'][value='false']").prop("checked", true); $("#srv_setup_stepper_imap_address").val(serverConfig.server); $("#srv_setup_stepper_imap_port").val(serverConfig.port); - if(serverConfig.tls)$("input[name='srv_setup_stepper_imap_tls'][value='true']").prop("checked", true); + if (serverConfig.tls) $("input[name='srv_setup_stepper_imap_tls'][value='true']").prop("checked", true); else $("input[name='srv_setup_stepper_imap_tls'][value='false']").prop("checked", true); if (serverConfig.hasOwnProperty('sieve')) { @@ -2259,7 +2253,7 @@ function getEmailProviderKey(email) { const emailParts = email.split("@"); - if(emailParts.length !== 2) return ""; + if (emailParts.length !== 2) return ""; const provider = emailParts[1].toLowerCase(); @@ -2388,14 +2382,14 @@ const handleExternalResources = (inline) => { }; const observeMessageTextMutationAndHandleExternalResources = (inline) => { - const message = document.querySelector('.msg_text'); + const message = document.querySelector('.msg_text'); if (message) { new MutationObserver(function (mutations) { mutations.forEach(function (mutation) { if (mutation.addedNodes.length > 0) { mutation.addedNodes.forEach(function (node) { if (node.classList.contains('msg_text_inner')) { - handleExternalResources(inline); + handleExternalResources(inline); } }); } @@ -2405,3 +2399,55 @@ const observeMessageTextMutationAndHandleExternalResources = (inline) => { }); } }; + +function setupActionSchedule(callback) { + $(document).on('click', '.nexter_date_picker', function (e) { + document.querySelector('.nexter_input_date').showPicker(); + }); + $(document).on('click', '.nexter_date_helper', function (e) { + e.preventDefault(); + $('.nexter_input').val($(this).attr('data-value')).trigger('change'); + }); + $(document).on('input', '.nexter_input_date', function (e) { + var now = new Date(); + now.setMinutes(now.getMinutes() + 1); + $(this).attr('min', now.toJSON().slice(0, 16)); + if (new Date($(this).val()).getTime() <= now.getTime()) { + $('.nexter_date_picker').css('border', '1px solid red'); + } else { + $('.nexter_date_picker').css({ 'border': 'unset', 'border-top': '1px solid #ddd' }); + } + }); + $(document).on('change', '.nexter_input_date', function (e) { + if ($(this).val() && new Date().getTime() < new Date($(this).val()).getTime()) { + $('.nexter_input').val($(this).val()).trigger('change'); + } + }); + $(document).on('change', '.nexter_input', callback); +} + +function setupActionSnooze(callback) { + $(document).on('click', '.nexter_date_picker_snooze', function (e) { + document.querySelector('.nexter_input_date_snooze').showPicker(); + }); + $(document).on('click', '.nexter_date_helper_snooze', function (e) { + e.preventDefault(); + $('.nexter_input_snooze').val($(this).attr('data-value')).trigger('change'); + }); + $(document).on('input', '.nexter_input_date_snooze', function (e) { + var now = new Date(); + now.setMinutes(now.getMinutes() + 1); + $(this).attr('min', now.toJSON().slice(0, 16)); + if (new Date($(this).val()).getTime() <= now.getTime()) { + $('.nexter_date_picker_snooze').css('border', '1px solid red'); + } else { + $('.nexter_date_picker_snooze').css({ 'border': 'unset', 'border-top': '1px solid #ddd' }); + } + }); + $(document).on('change', '.nexter_input_date_snooze', function (e) { + if ($(this).val() && new Date().getTime() < new Date($(this).val()).getTime()) { + $('.nexter_input_snooze').val($(this).val()).trigger('change'); + } + }); + $(document).on('change', '.nexter_input_snooze', callback); +} diff --git a/modules/imap/functions.php b/modules/imap/functions.php index 5f41493923..bf49e28b63 100644 --- a/modules/imap/functions.php +++ b/modules/imap/functions.php @@ -252,10 +252,14 @@ function format_imap_message_list($msg_list, $output_module, $parent_list=false, $nofrom = ' nofrom'; } $is_snoozed = !empty($msg['x_snoozed']) && hex2bin($msg['folder']) == 'Snoozed'; + $is_scheduled = !empty($msg['x_schedule']) && hex2bin($msg['folder']) == 'Scheduled'; if ($is_snoozed) { - $snooze_header = parse_snooze_header('X-Snoozed: '.$msg['x_snoozed']); + $snooze_header = parse_nexter_header('X-Snoozed: '.$msg['x_snoozed'], 'X-Snoozed'); $date = $snooze_header['until']; $timestamp = strtotime($date); + } elseif ($is_scheduled) { + $date = $msg['x_schedule']; + $timestamp = strtotime($date); } else { if ($list_sort == 'date') { $date_field = 'date'; @@ -331,9 +335,8 @@ function format_imap_message_list($msg_list, $output_module, $parent_list=false, array('safe_output_callback', 'source', $source, $icon), array('safe_output_callback', 'from'.$nofrom, $from, null, str_replace(array($from, '<', '>'), '', $msg['from'])), array('subject_callback', $subject, $url, $flags, null, $preview_msg), - array('date_callback', $date, $timestamp, $is_snoozed), - array('icon_callback', $flags), - array('dates_holders_callback', $msg['internal_date'], $msg['date']), + array('date_callback', $date, $timestamp, $is_snoozed || $is_scheduled), + array('icon_callback', $flags) ), $id, $style, @@ -1343,7 +1346,7 @@ function snooze_message($mailbox, $msg_id, $folder, $snooze_tag) { preg_match("/^X-Snoozed:.*(\r?\n[ \t]+.*)*\r?\n?/im", $msg, $matches); if (count($matches)) { $msg = str_replace($matches[0], '', $msg); - $old_folder = parse_snooze_header($matches[0])['from']; + $old_folder = parse_nexter_header($matches[0], 'X-Snoozed')['from']; } if ($snooze_tag) { $from = $old_folder ?? $folder; @@ -1370,7 +1373,7 @@ function snooze_message($mailbox, $msg_id, $folder, $snooze_tag) { } } } else { - $snooze_headers = parse_snooze_header($matches[0]); + $snooze_headers = parse_nexter_header($matches[0], 'X-Snoozed'); $original_folder = $snooze_headers['from']; if ($mailbox->store_message($original_folder, $msg)) { $deleteResult = $mailbox->message_action($snooze_folder, 'DELETE', array($msg_id)); @@ -1457,21 +1460,21 @@ function snooze_formats() { */ if (!hm_exists('snooze_dropdown')) { function snooze_dropdown($output, $unsnooze = false) { - $values = snooze_formats(); + $values = nexter_formats(); $txt = ''; @@ -1605,126 +1608,41 @@ function connect_to_imap_server($address, $name, $port, $user, $pass, $tls, $ima } } -/** - * @param array $sources - * @param object $cache - * @param array $search - */ -function getCombinedMessagesLists($sources, $cache, $search) { - $defaultSearch = [ - 'filter' => 'ALL', - 'sort' => 'ARRIVAL', - 'reverse' => true, - 'terms' => [], - 'limit' => 10, - 'offsets' => [], - 'defaultOffset' => 0, - 'listPage' => 1 - ]; - $search = array_merge($defaultSearch, $search); - - $filter = $search['filter']; - $sort = $search['sort']; - $reverse = $search['reverse']; - $searchTerms = $search['terms']; - $limit = $search['limit']; - $offsets = $search['offsets']; - $listPage = $search['listPage']; - - $totalMessages = 0; - $offset = $search['defaultOffset']; - $messagesLists = []; - $status = []; - foreach ($sources as $index => $dataSource) { - - if ($offsets && $listPage > 1) { - if (isset($offsets[$index]) && (int) $offsets[$index] > 0) { - $offset = (int) $offsets[$index] * ($listPage - 1); - } - } - - $mailbox = Hm_IMAP_List::get_connected_mailbox($dataSource['id'], $cache); - if ($mailbox && $mailbox->authed()) { - $connection = $mailbox->get_connection(); - - $folder = $dataSource['folder']; - $mailbox->select_folder(hex2bin($folder)); - $state = $connection->get_mailbox_status(hex2bin($folder)); - $status['imap_'.$dataSource['id'].'_'.$folder] = $state; - - if ($mailbox->is_imap()) { - if ($connection->is_supported( 'SORT' )) { - $sortedUids = $connection->get_message_sort_order($sort, $reverse, $filter); - } else { - $sortedUids = $connection->sort_by_fetch($sort, $reverse, $filter); - } - - $uids = $mailbox->search(hex2bin($folder), $filter, $sortedUids, $searchTerms); - } else { - // EWS - $uids = $connection->search($folder, $sort, $reverse, $filter, 0, $limit, $searchTerms); - } - - $total = count($uids); - $uids = array_slice($uids, $offset, $limit); - - $headers = $mailbox->get_message_list(hex2bin($folder), $uids); - $messages = []; - foreach ($uids as $uid) { - if (isset($headers[$uid])) { - $messages[] = $headers[$uid]; - } - } +if (!hm_exists('save_sent_msg')) { +function save_sent_msg($handler, $imap_id, $imap, $imap_details, $msg, $msg_id, $show_errors = true) { + $specials = get_special_folders($handler, $imap_id); + if (array_key_exists('sent', $specials) && $specials['sent']) { + $sent_folder = $specials['sent']; + } - $messagesLists[] = array_map(function($msg) use ($dataSource, $folder) { - $msg['server_id'] = $dataSource['id']; - $msg['server_name'] = $dataSource['name']; - $msg['folder'] = $folder; - return $msg; - }, $messages); - $totalMessages += $total; + if (!$sent_folder) { + $auto_sent = $imap->get_special_use_mailboxes('sent'); + if (!array_key_exists('sent', $auto_sent)) { + return; } + $sent_folder = $auto_sent['sent']; } - - return ['lists' => $messagesLists, 'total' => $totalMessages, 'status' => $status]; -} - -function flattenMessagesLists($messagesLists, $listSize) { - $endList = []; - $sizesTaken = []; - - $max = $listSize * count($messagesLists); - - while (count($endList) < $listSize * count($messagesLists) && count(array_filter($messagesLists, fn ($list) => count($list) > 0)) > 0) { - foreach ($messagesLists as $index => $list) { - if (count($list) > 0) { - $part = array_slice($list, 0, $listSize); - $endList = array_merge($endList, $part); - $messagesLists[$index] = array_slice($list, $listSize); - $sizesTaken[$index] = isset($sizesTaken[$index]) ? $sizesTaken[$index] + count($part) : count($part); - $totalTakens = array_sum(array_values($sizesTaken)); - if ($totalTakens > $max) { - $sizesTaken[$index] = $sizesTaken[$index] - ($totalTakens - $max); - } - } else { - $sizesTaken[$index] = isset($sizesTaken[$index]) ? $sizesTaken[$index] : 0; + if (!$sent_folder) { + Hm_Debug::add(sprintf("Unable to save sent message, no sent folder for IMAP %s", $imap_details['server'])); + } + $uid = null; + if ($sent_folder) { + Hm_Debug::add(sprintf("Attempting to save sent message for IMAP server %s in folder %s", $imap_details['server'], $sent_folder)); + if ($imap->append_start($sent_folder, strlen($msg), true)) { + $imap->append_feed($msg."\r\n"); + if (!$imap->append_end() && $show_errors) { + Hm_Msgs::add('ERRAn error occurred saving the sent message'); } } - } - - $endList = array_slice($endList, 0, $max); - return ['messages' => $endList, 'offsets' => $sizesTaken]; -} - -function sortCombinedMessages($list, $sort) { - usort($list, function($a, $b) use ($sort) { - $sortField = str_replace(['arrival', '-'], ['internal_date', ''], $sort); - if (strpos($sort, '-') === 0) { - return strtotime($a[$sortField]) - strtotime($b[$sortField]); + $mailbox_page = $imap->get_mailbox_page($sent_folder, 'ARRIVAL', true, 'ALL', 0, 10); + foreach ($mailbox_page[1] as $mail) { + $msg_header = $imap->get_message_headers($mail['uid']); + if ($msg_header['Message-Id'] === $msg_id) { + $uid = $mail['uid']; + break; + } } - return strtotime($b[$sortField]) - strtotime($a[$sortField]); - }); - - return $list; -} + } + return $uid; +}} diff --git a/modules/imap/handler_modules.php b/modules/imap/handler_modules.php index a172bdac89..a59db42fe1 100644 --- a/modules/imap/handler_modules.php +++ b/modules/imap/handler_modules.php @@ -361,7 +361,10 @@ public function process() { break; } } - if ($uid && $this->user_config->get('review_sent_email_setting', true)) { + } + $uid = save_sent_msg($this, $imap_id, $imap, $imap_details, $msg, $mime->get_headers()['Message-Id']); + if ($uid) { + if ($uid && $this->user_config->get('review_sent_email_setting', false)) { $this->out('redirect_url', '?page=message&uid='.$uid.'&list_path=imap_'.$imap_id.'_'.bin2hex($sent_folder)); } } @@ -1037,7 +1040,7 @@ public function process() { $snooze_tag = null; if ($form['imap_snooze_until'] != 'unsnooze') { $at = date('D, d M Y H:i:s O'); - $until = get_snooze_date($form['imap_snooze_until']); + $until = get_scheduled_date($form['imap_snooze_until']); $snooze_tag = "X-Snoozed: at $at; until $until"; } $ids = explode(',', $form['imap_snooze_ids']); @@ -1088,7 +1091,7 @@ public function process() { $msg_headers = $mailbox->get_message_headers($folder, $msg['uid']); if (isset($msg_headers['X-Snoozed'])) { try { - $snooze_headers = parse_snooze_header($msg_headers['X-Snoozed']); + $snooze_headers = parse_nexter_header($msg_headers['X-Snoozed'], 'X-Snoozed'); if (new DateTime($snooze_headers['until']) <= new DateTime()) { snooze_message($mailbox, $msg['uid'], $folder, null); } diff --git a/modules/imap/hm-imap.php b/modules/imap/hm-imap.php index b60599f018..07434df5e1 100644 --- a/modules/imap/hm-imap.php +++ b/modules/imap/hm-imap.php @@ -970,7 +970,7 @@ public function get_message_list($uids, $raw=false, $include_content_body = fals if ($this->is_supported( 'X-GM-EXT-1' )) { $command .= 'X-GM-MSGID X-GM-THRID X-GM-LABELS '; } - $command .= "BODY.PEEK[HEADER.FIELDS (SUBJECT X-AUTO-BCC FROM DATE CONTENT-TYPE X-PRIORITY TO LIST-ARCHIVE REFERENCES MESSAGE-ID X-SNOOZED)]"; + $command .= "BODY.PEEK[HEADER.FIELDS (SUBJECT X-AUTO-BCC FROM DATE CONTENT-TYPE X-PRIORITY TO LIST-ARCHIVE REFERENCES MESSAGE-ID X-SNOOZED X-SCHEDULE X-PROFILE-ID X-DELIVERY)])"; if ($include_content_body) { $command .= " BODY.PEEK[0.1]"; } @@ -984,8 +984,8 @@ public function get_message_list($uids, $raw=false, $include_content_body = fals $res = $this->get_response(false, true); $status = $this->check_response($res, true); $tags = array('X-GM-MSGID' => 'google_msg_id', 'X-GM-THRID' => 'google_thread_id', 'X-GM-LABELS' => 'google_labels', 'UID' => 'uid', 'FLAGS' => 'flags', 'RFC822.SIZE' => 'size', 'INTERNALDATE' => 'internal_date'); - $junk = array('X-AUTO-BCC', 'MESSAGE-ID', 'REFERENCES', 'X-SNOOZED', 'LIST-ARCHIVE', 'SUBJECT', 'FROM', 'CONTENT-TYPE', 'TO', '(', ')', ']', 'X-PRIORITY', 'DATE'); - $flds = array('x-auto-bcc' => 'x_auto_bcc', 'message-id' => 'message_id', 'references' => 'references', 'x-snoozed' => 'x_snoozed', 'list-archive' => 'list_archive', 'date' => 'date', 'from' => 'from', 'to' => 'to', 'subject' => 'subject', 'content-type' => 'content_type', 'x-priority' => 'x_priority', 'body' => 'content_body'); + $junk = array('X-AUTO-BCC', 'MESSAGE-ID', 'REFERENCES', 'X-SNOOZED', 'X-SCHEDULE', 'X-PROFILE-ID', 'X-DELIVERY', 'LIST-ARCHIVE', 'SUBJECT', 'FROM', 'CONTENT-TYPE', 'TO', '(', ')', ']', 'X-PRIORITY', 'DATE'); + $flds = array('x-auto-bcc' => 'x_auto_bcc', 'message-id' => 'message_id', 'references' => 'references', 'x-snoozed' => 'x_snoozed', 'x-schedule' => 'x_schedule', 'x-profile-id' => 'x_profile_id', 'x-delivery' => 'x_delivery', 'list-archive' => 'list_archive', 'date' => 'date', 'from' => 'from', 'to' => 'to', 'subject' => 'subject', 'content-type' => 'content_type', 'x-priority' => 'x_priority'); $headers = array(); foreach ($res as $n => $vals) { @@ -1008,6 +1008,9 @@ public function get_message_list($uids, $raw=false, $include_content_body = fals $google_labels = ''; $x_auto_bcc = ''; $x_snoozed = ''; + $x_schedule = ''; + $x_profile_id = ''; + $x_delivery = ''; $count = count($vals); for ($i=0;$i<$count;$i++) { if ($vals[$i] == 'BODY[HEADER.FIELDS') { @@ -1070,8 +1073,7 @@ public function get_message_list($uids, $raw=false, $include_content_body = fals 'timestamp' => time(), 'charset' => $cset, 'x-priority' => $x_priority, 'google_msg_id' => $google_msg_id, 'google_thread_id' => $google_thread_id, 'google_labels' => $google_labels, 'list_archive' => $list_archive, 'references' => $references, 'message_id' => $message_id, 'x_auto_bcc' => $x_auto_bcc, - 'x_snoozed' => $x_snoozed); - $headers[$uid]['preview_msg'] = $flds['body'] != "content_body" ? $flds['body'] : ""; + 'x_snoozed' => $x_snoozed, 'x_schedule' => $x_schedule, 'x_profile_id' => $x_profile_id, 'x_delivery' => $x_delivery); if ($raw) { $headers[$uid] = array_map('trim', $headers[$uid]); diff --git a/modules/imap/js_modules/route_handlers.js b/modules/imap/js_modules/route_handlers.js index 7f9d9ab0a4..f24a08f0aa 100644 --- a/modules/imap/js_modules/route_handlers.js +++ b/modules/imap/js_modules/route_handlers.js @@ -4,10 +4,14 @@ function applyImapMessageListPageHandlers(routeParams) { imap_setup_snooze(); imap_setup_tags(); + Hm_Message_List.set_row_events(); + + processNextActionDate(); + if (window.inlineMessageMessageListAndSearchPageHandler) inlineMessageMessageListAndSearchPageHandler(routeParams); if (window.wpMessageListPageHandler) wpMessageListPageHandler(routeParams); - return async function() { + return async function () { const [refreshIntervalId, abortController] = await setupPageResult; abortController.abort(); clearInterval(refreshIntervalId); @@ -33,4 +37,4 @@ function applyImapMessageContentPageHandlers(routeParams) { if (window.pgpMessageContentPageHandler) pgpMessageContentPageHandler(); if (window.wpMessageContentPageHandler) wpMessageContentPageHandler(routeParams); -} \ No newline at end of file +} diff --git a/modules/imap/js_modules/utils/handleNexterDateAction.js b/modules/imap/js_modules/utils/handleNexterDateAction.js new file mode 100644 index 0000000000..02c303a9fc --- /dev/null +++ b/modules/imap/js_modules/utils/handleNexterDateAction.js @@ -0,0 +1,71 @@ +function processNextActionDate(e) { + let isReloading = false; + + let reload_and_redirect = async function () { + if (isReloading) { + return; + } + isReloading = true; + + showRoutingToast(); + + try { + Hm_Folders.reload_folders(true); + let path = getListPathParam(); + await navigate(`?page=message_list&list_path=${path}`); + } finally { + hideRoutingToast(); + isReloading = false; + } + }; + + + + let collectCheckedIds = function () { + let ids = []; + $('input[type=checkbox]').each(function () { + if (this.checked && this.id.search('imap') !== -1) { + let parts = this.id.split('_'); + ids.push(parts[1] + '_' + parts[2] + '_' + parts[3]); + } + }); + if (ids.length === 0) { + return; + } + return ids; + }; + + setupActionSchedule(function () { + let ids = collectCheckedIds(); + + Hm_Ajax.request( + [ + { 'name': 'hm_ajax_hook', 'value': 'ajax_re_schedule_message_sending' }, + { 'name': 'scheduled_msg_ids', 'value': ids }, + { 'name': 'schedule_date', 'value': $(this).val() } + ], + function (res) { + if (res.scheduled_msg_count > 0) { + reload_and_redirect(); + } + } + ); + }); + + setupActionSnooze(function () { + let ids = collectCheckedIds(); + + Hm_Ajax.request( + [ + { 'name': 'hm_ajax_hook', 'value': 'ajax_imap_snooze' }, + { 'name': 'imap_snooze_ids', 'value': ids }, + { 'name': 'imap_snooze_until', 'value': $(this).val() } + ], + function (res) { + if (res.snoozed_messages > 0) { + reload_and_redirect(); + } + } + ); + }); +} \ No newline at end of file diff --git a/modules/imap/output_modules.php b/modules/imap/output_modules.php index d0128dfdc3..1a0ded6516 100644 --- a/modules/imap/output_modules.php +++ b/modules/imap/output_modules.php @@ -206,9 +206,9 @@ protected function output() { $txt .= $this->html_safe($value).''; } elseif ($fld == 'x-snoozed') { - $snooze_header = parse_snooze_header($value); + $snooze_header = parse_nexter_header($value, 'X-Snoozed'); $txt .= ''; - $txt .= $this->trans('Snoozed').''.$this->trans('Until').' '.$this->html_safe($snooze_header['until']).' Unsnooze'; + $txt .= $this->trans('Snoozed').''.$this->trans('Until').' '.$this->html_safe($snooze_header['until']).' Unsnooze'; } elseif ($fld == 'date') { try { @@ -390,6 +390,9 @@ protected function output() { if($this->get('tags')){ $txt .= ' | '. tags_dropdown($this, $headers); } + if (isset($headers['X-Schedule'])) { + $txt .= ' | ' . schedule_dropdown($this, true); + } $is_draft = isset($headers['Flags']) && mb_stristr($headers['Flags'], 'draft'); if ($this->get('sieve_filters_enabled') && !$is_draft) { diff --git a/modules/imap/site.css b/modules/imap/site.css index f291345bab..db8576ec53 100644 --- a/modules/imap/site.css +++ b/modules/imap/site.css @@ -126,7 +126,7 @@ .header_links { padding-top: 10px !important; } -.header_links #dropdownMenuSnooze { +.header_links #dropdownMenuNexterDate { padding: 0; border: unset; font-variant: inherit; @@ -134,7 +134,7 @@ font-size: inherit; vertical-align: baseline; } -.header_links #dropdownMenuSnooze:hover { +.header_links #dropdownMenuNexterDate:hover { background-color: inherit; color: inherit; } diff --git a/modules/imap/site.js b/modules/imap/site.js index 7abc57c206..4797e22871 100644 --- a/modules/imap/site.js +++ b/modules/imap/site.js @@ -1224,6 +1224,7 @@ $(function() { setTimeout(search_selected_for_imap, 100); }); + if (hm_is_logged()) { imap_unsnooze_messages(); setInterval(imap_unsnooze_messages, 60000); diff --git a/modules/profiles/functions.php b/modules/profiles/functions.php index dcfc100056..d75922ab6a 100644 --- a/modules/profiles/functions.php +++ b/modules/profiles/functions.php @@ -12,8 +12,8 @@ function add_profile($name, $signature, $reply_to, $is_default, $email, $server, 'replyto' => $reply_to, 'default' => $is_default, 'address' => $email, - 'server' => $server, - 'user' => $user, + 'server' => $imap_server_id, + 'user' => $email, 'type' => 'imap' ); $id = Hm_Profiles::add($profile); diff --git a/modules/profiles/hm-profiles.php b/modules/profiles/hm-profiles.php index 16997e0582..046d6e6648 100644 --- a/modules/profiles/hm-profiles.php +++ b/modules/profiles/hm-profiles.php @@ -109,4 +109,19 @@ public static function loadLegacy($hmod) { } } } + + /** + * @param string $field The name of the field to search within. + * @param mixed $value The value to search for within the specified field. + * @return array An array containing profiles that match the search criteria. + */ + public static function search($field, $value) { + $res = array(); + foreach (self::getAll() as $profile) { + if (!empty($profile[$field]) && $profile[$field] == $value) { + $res[] = $profile; + } + } + return $res; + } } diff --git a/modules/smtp/functions.php b/modules/smtp/functions.php index 68a8db3b16..742068cc49 100644 --- a/modules/smtp/functions.php +++ b/modules/smtp/functions.php @@ -61,3 +61,109 @@ function get_reply_type($request) { return false; } } + +/** + * @subpackage smtp/functions + */ +if (!hm_exists('send_scheduled_message')) { +function send_scheduled_message($handler, $imap, $msg_id, $server_id, $send_now = false) { + $msg_headers = $imap->get_message_headers($msg_id); + $imap_details = Hm_IMAP_List::dump($server_id); + if (empty($imap_details)) { + return false; + } + + try { + if (empty($msg_headers['X-Schedule'])) { + return false; + } + + if (new DateTime($msg_headers['X-Schedule']) <= new DateTime() || $send_now) { + $profile = Hm_Profiles::get($msg_headers['X-Profile-ID']); + if (!$profile) { + $profiles = Hm_Profiles::search('server', $imap_details['server']); + + if (!$profiles) { + Hm_Debug::add(sprintf('ERRCannot find profiles corresponding with IMAP server: %s', $imap_details['server'])); + return false; + } + $profile = $profiles[0]; + } + + $smtp = Hm_SMTP_List::connect($profile['smtp_id'], false); + + if (smtp_authed($smtp)) { + if (isset($msg_headers['X-Delivery'])) { + $from_params = 'RET=HDRS'; + $recipients_params = 'NOTIFY=SUCCESS,FAILURE'; + } else { + $from_params = ''; + $recipients_params = ''; + } + + $recipients = []; + foreach (['To', 'Cc', 'Bcc'] as $fld) { + if (array_key_exists($fld, $msg_headers)) { + $recipients = array_merge($recipients, Hm_MIME_Msg::find_addresses($msg_headers[$fld])); + } + } + + $msg_content = $imap->get_message_content($msg_id, 0); + $from = process_address_fld($msg_headers['From']); + + $err_msg = $smtp->send_message($from[0]['email'], $recipients, $msg_content, $from_params, $recipients_params); + + if (!$err_msg) { + if ($imap->message_action('DELETE', [$msg_id])) { + $imap->message_action('EXPUNGE', [$msg_id]); + } + save_sent_msg($handler, $server_id, $imap, $imap_details, $msg_content, $msg_id, false); + return true; + } + } + } + } catch (Exception $e) { + Hm_Debug::add(sprintf('ERRCannot send message: %s', $msg_headers['subject'])); + } + return false; +}} + +/** + * @subpackage smtp/functions + */ +if (!hm_exists('reschedule_message_sending')) { +function reschedule_message_sending($handler, $imap, $msg_id, $folder, $new_date, $server_id) { + if (!$imap->select_mailbox($folder)) { + return; + } + if ($new_date == 'now') { + return send_scheduled_message($handler, $imap, $msg_id, $server_id, true); + } + $msg = $imap->get_message_content($msg_id, 0); + $new_date = get_scheduled_date($new_date); + preg_match("/^X-Schedule:.*(\r?\n[ \t]+.*)*\r?\n?/im", $msg, $matches); + if (count($matches)) { + $msg = str_replace($matches[0], "X-Schedule: {$new_date}\n", $msg); + } else { + return; + } + $msg = str_replace("\r\n", "\n", $msg); + $msg = str_replace("\n", "\r\n", $msg); + $msg = rtrim($msg)."\r\n"; + + $schedule_folder = 'Scheduled'; + if (!count($imap->get_mailbox_status($schedule_folder))) { + return; + } + $res = false; + if ($imap->select_mailbox($schedule_folder) && $imap->append_start($schedule_folder, strlen($msg))) { + $imap->append_feed($msg."\r\n"); + if ($imap->append_end()) { + if ($imap->select_mailbox($folder) && $imap->message_action('DELETE', array($msg_id))) { + $imap->message_action('EXPUNGE', array($msg_id)); + $res = true; + } + } + } + return $res; +}} diff --git a/modules/smtp/hm-mime-message.php b/modules/smtp/hm-mime-message.php index 74a856c2fb..5329c6a5fe 100644 --- a/modules/smtp/hm-mime-message.php +++ b/modules/smtp/hm-mime-message.php @@ -21,10 +21,15 @@ class Hm_MIME_Msg { private $final_msg = ''; /* build mime message data */ - function __construct($to, $subject, $body, $from, $html=false, $cc='', $bcc='', $in_reply_to_id='', $from_name='', $reply_to='') { + function __construct($to, $subject, $body, $from, $html=false, $cc='', $bcc='', $in_reply_to_id='', $from_name='', $reply_to='', $delivery_receipt='', $schedule='', $profile_id = '') { if ($cc) { $this->headers['Cc'] = $cc; } + if ($schedule) { + $this->headers['X-Schedule'] = $schedule; + $this->headers['X-Profile-ID'] = $profile_id; + } + if ($in_reply_to_id) { $this->headers['In-Reply-To'] = $in_reply_to_id; } @@ -41,6 +46,9 @@ function __construct($to, $subject, $body, $from, $html=false, $cc='', $bcc='', else { $this->headers['Reply-To'] = $from; } + if ($delivery_receipt) { + $this->headers['X-Delivery'] = $delivery_receipt; + } $this->headers['To'] = $to; $this->headers['Subject'] = html_entity_decode($subject, ENT_QUOTES); $this->headers['Date'] = date('r'); @@ -190,7 +198,7 @@ function prep_fld($val, $name) { return $this->encode_fld($val); } - function find_addresses($str) { + static function find_addresses($str) { $res = array(); foreach (process_address_fld($str) as $vals) { $res[] = $vals['email']; @@ -210,7 +218,7 @@ function get_recipient_addresses() { else { continue; } - $res = array_merge($res, $this->find_addresses($v)); + $res = array_merge($res, self::find_addresses($v)); } return $res; } diff --git a/modules/smtp/js_modules/route_handlers.js b/modules/smtp/js_modules/route_handlers.js index 5179c5e495..d7715baeec 100644 --- a/modules/smtp/js_modules/route_handlers.js +++ b/modules/smtp/js_modules/route_handlers.js @@ -2,18 +2,28 @@ function applySmtpComposePageHandlers() { init_resumable_upload() + let isScheduledMode = null; + + setupActionSchedule(function () { + let schedule = $('.nexter_input').val(); + $('.smtp_send_placeholder').trigger('click'); + + save_compose_state(false, true, schedule); + isScheduledMode = schedule; + }); + if (window.HTMLEditor) { useKindEditor(); } var interval = Hm_Utils.get_from_global('compose_save_interval', 30); - Hm_Timer.add_job(function() { save_compose_state(); }, interval, true); - $('.draft_title').on("click", function() { $('.draft_list').toggle(); }); - $('.toggle_recipients').on("click", function() { return toggle_recip_flds(); }); + Hm_Timer.add_job(function () { save_compose_state(); }, interval, true); + $('.draft_title').on("click", function () { $('.draft_list').toggle(); }); + $('.toggle_recipients').on("click", function () { return toggle_recip_flds(); }); $('.smtp_reset').on("click", reset_smtp_form); - $('.delete_draft').on("click", function() { smtp_delete_draft($(this).data('id')); }); - $('.smtp_save').on("click", function() { save_compose_state(false, true); }); - $('.smtp_send_archive').on("click", function() { send_archive(false, true); }); + $('.delete_draft').on("click", function () { smtp_delete_draft($(this).data('id')); }); + $('.smtp_save').on("click", function () { save_compose_state(false, true); }); + $('.smtp_send_archive').on("click", function () { send_archive(false, true); }); const modal = new Hm_Modal({ modalId: 'emptySubjectBodyModal', @@ -85,7 +95,7 @@ function applySmtpComposePageHandlers() { ======================================== */ function showModal() { - if (! modal.modalContent.html()) { + if (!modal.modalContent.html()) { modal.addFooterBtn(hm_trans('Send anyway'), 'btn-warning', handleSendAnyway); if (showBtnSendAnywayDontWarnFuture) { modal.addFooterBtn(hm_trans("Send anyway and don't warn in the future"), 'btn-warning', handleSendAnywayAndDontWarnMe); @@ -99,26 +109,26 @@ function applySmtpComposePageHandlers() { return new Promise((resolve) => { const checkValue = () => { if ($(selector).val() !== targetValue) { - resolve(); + resolve(); } else { - setTimeout(checkValue, 100); + setTimeout(checkValue, 100); } }; - checkValue(); + checkValue(); }); } async function handleSendAnyway() { if ($('.compose_draft_id').val() == '0') { - Hm_Notices.show([hm_trans('Please wait, sending message...')]); - await waitForValueChange('.compose_draft_id', '0'); + Hm_Notices.show([hm_trans('Please wait, sending message...')]); + await waitForValueChange('.compose_draft_id', '0'); } - - if (handleMissingAttachment()) { - document.getElementsByClassName("smtp_send")[0].click(); + if (isScheduledMode == null) { + document.getElementsByClassName("smtp_send")[0].click(); + } } else { e.preventDefault(); } @@ -162,53 +172,53 @@ function applySmtpComposePageHandlers() { return true; } }); - $('.compose_form').on('submit', function() { + $('.compose_form').on('submit', function () { process_compose_form(); }); if ($('.compose_cc').val() || $('.compose_bcc').val()) { toggle_recip_flds(); } if (window.location.href.search('&reply=1') !== -1 || window.location.href.search('&reply_all=1') !== -1) { - replace_cursor_positon ($('textarea[name="compose_body"]')); + replace_cursor_positon($('textarea[name="compose_body"]')); } if (window.location.href.search('&forward=1') !== -1) { - setTimeout(function() { + setTimeout(function () { save_compose_state(); }, 100); } if ($('.sys_messages').text() != 'Message Sent') { get_smtp_profile($('.compose_server').val()); } - $('.compose_server').on('change', function() { + $('.compose_server').on('change', function () { get_smtp_profile($('.compose_server').val()); }); - if($('.compose_attach_button').attr('disabled') == 'disabled'){ + if ($('.compose_attach_button').attr('disabled') == 'disabled') { check_attachment_dir_access(); }; $('.compose_container').attr('ondrop', 'move_recipient_to_section(event)').attr('ondragover', 'allow_drop(event)'); - $('.compose_to, .compose_cc, .compose_bcc').on('keypress', function(e) { - if(e.which == 13) { + $('.compose_to, .compose_cc, .compose_bcc').on('keypress', function (e) { + if (e.which == 13) { e.preventDefault(); text_to_bubbles(this); } }); - $('.compose_to, .compose_cc, .compose_bcc').on('blur', function(e) { + $('.compose_to, .compose_cc, .compose_bcc').on('blur', function (e) { e.preventDefault(); text_to_bubbles(this); }); - $('.compose_subject, .compose_body, .compose_server, .smtp_send_placeholder, .smtp_send_archive').on('focus', function(e) { - $('.compose_to, .compose_cc, .compose_bcc').each(function() { + $('.compose_subject, .compose_body, .compose_server, .smtp_send_placeholder, .smtp_send_archive').on('focus', function (e) { + $('.compose_to, .compose_cc, .compose_bcc').each(function () { bubbles_to_text(this); }); }); - $('.compose_to, .compose_cc, .compose_bcc').on('focus', function(e) { + $('.compose_to, .compose_cc, .compose_bcc').on('focus', function (e) { text_to_bubbles(this); }); - $('.compose_container').on('click', function() { + $('.compose_container').on('click', function () { $(this).find('input').focus(); }); - $(document).on('click', '.bubble_close', function(e) { + $(document).on('click', '.bubble_close', function (e) { e.stopPropagation(); $(".bubble_dropdown-content").remove(); $(this).parent().remove(); @@ -222,7 +232,7 @@ function applySmtpComposePageHandlers() { var excludedEmail = null; const excludeEmail = function () { - var newRecipients = recipientsInput.val().split(',').filter(function(email) { + var newRecipients = recipientsInput.val().split(',').filter(function (email) { if (email.includes(selectedEmail)) { excludedEmail = email; return false; @@ -234,7 +244,7 @@ function applySmtpComposePageHandlers() { if (recipientsInput.val().includes(selectedEmail)) { excludeEmail(); - $(document).on('change', '#compose_smtp_id', function() { + $(document).on('change', '#compose_smtp_id', function () { if ($(this).val() !== selectedVal) { if (!recipientsInput.val().includes(selectedEmail)) { recipientsInput.val(recipientsInput.val() + ', ' + excludedEmail); @@ -245,6 +255,12 @@ function applySmtpComposePageHandlers() { }); } + + $('.compose_to').on('keyup', function (e) { autocomplete_contact(e, '.compose_to', '#to_contacts'); }); + $('.compose_cc').on('keyup', function (e) { autocomplete_contact(e, '.compose_cc', '#cc_contacts'); }); + $('.compose_bcc').on('keyup', function (e) { autocomplete_contact(e, '.compose_bcc', '#bcc_contacts'); }); + $('.compose_to').focus(); + if (window.pgpComposePageHandler) pgpComposePageHandler(); if (window.profilesComposePageHandler) profilesComposePageHandler(); } \ No newline at end of file diff --git a/modules/smtp/modules.php b/modules/smtp/modules.php index 6c2b2e41fb..cba75e6456 100644 --- a/modules/smtp/modules.php +++ b/modules/smtp/modules.php @@ -113,6 +113,8 @@ public function process() { } } + + /** * @subpackage smtp/handler */ @@ -208,6 +210,7 @@ public function process() { } } + /** * @subpackage smtp/handler */ @@ -317,6 +320,8 @@ public function process() { $draft_id = array_key_exists('draft_id', $this->request->post) ? $this->request->post['draft_id'] : false; $draft_notice = array_key_exists('draft_notice', $this->request->post) ? $this->request->post['draft_notice'] : false; $uploaded_files = array_key_exists('uploaded_files', $this->request->post) ? $this->request->post['uploaded_files'] : false; + $delivery_receipt = array_key_exists('compose_delivery_receipt', $this->request->post) ? $this->request->post['compose_delivery_receipt'] : false; + $schedule = array_key_exists('schedule', $this->request->post) ? $this->request->post['schedule'] : ''; if (array_key_exists('delete_uploaded_files', $this->request->post) && $this->request->post['delete_uploaded_files']) { delete_uploaded_files($this->session, $draft_id); @@ -333,7 +338,7 @@ public function process() { if ($this->get('save_draft_to_imap') === false) { $from = isset($profile) ? $profile['replyto'] : ''; $name = isset($profile) ? $profile['name'] : ''; - $mime = prepare_draft_mime($msg_attrs, $uploaded_files, $from, $name); + $mime = prepare_draft_mime($msg_attrs, $uploaded_files, $from, $name, $profile['id']); $this->out('draft_mime', $mime); return; } @@ -343,15 +348,20 @@ public function process() { foreach($uploaded_files as $key => $file) { $uploaded_files[$key] = $this->config->get('attachment_dir').DIRECTORY_SEPARATOR.$userpath.DIRECTORY_SEPARATOR.$file; } - $new_draft_id = save_imap_draft($msg_attrs, $draft_id, $this->session, $this, $this->cache, $uploaded_files, $profile); + $new_draft_id = save_imap_draft(array('draft_smtp' => $smtp, 'draft_to' => $to, 'draft_body' => $body, + 'draft_subject' => $subject, 'draft_cc' => $cc, 'draft_bcc' => $bcc, + 'draft_in_reply_to' => $inreplyto, 'delivery_receipt' => $delivery_receipt, 'schedule' => $schedule), $draft_id, $this->session, + $this, $this->cache, $uploaded_files, $profile); if ($new_draft_id >= 0) { if ($draft_notice) { - Hm_Msgs::add('Draft saved'); + $msg = $schedule ? 'Message scheduled to be sent later' : 'Draft saved'; + Hm_Msgs::add($msg); } $this->out('draft_id', $new_draft_id); } elseif ($draft_notice) { - Hm_Msgs::add('ERRUnable to save draft'); + $msg = $schedule ? 'Something went wrong when scheduling draft' : 'Unable to save draft'; + Hm_Msgs::add('ERR' . $msg); } return; } @@ -662,6 +672,9 @@ public function process() { 'draft_subject' => $form['compose_subject'], 'draft_smtp' => $smtp_id ); + $delivery_receipt = !empty($this->request->post['compose_delivery_receipt']); + $from_params = ''; + $recipients_params = ''; /* parse attachments */ $uploaded_files = []; @@ -706,7 +719,7 @@ public function process() { } /* build message */ - $mime = new Hm_MIME_Msg($to, $subject, $body, $from, $body_type, $cc, $bcc, $in_reply_to, $from_name, $reply_to); + $mime = new Hm_MIME_Msg($to, $subject, $body, $from, $body_type, $cc, $bcc, $in_reply_to, $from_name, $reply_to, $delivery_receipt); /* add attachments */ $mime->add_attachments($uploaded_files); @@ -888,6 +901,97 @@ protected function output() { } } +/** + * Send scheduled messages + * @subpackage smtp/handler + */ +class Hm_Handler_send_scheduled_messages extends Hm_Handler_Module { + /** + * Send delayed messages + * This should use cron + */ + public function process() { + if (!($this->module_is_supported('imap') || $this->module_is_supported('profiles'))) { + return; + } + + $servers = Hm_IMAP_List::dump(); + $scheduled_msg_count = 0; + + foreach (array_keys($servers) as $server_id) { + $cache = Hm_IMAP_List::get_cache($this->cache, $server_id); + $imap = Hm_IMAP_List::connect($server_id, $cache); + + if (imap_authed($imap)) { + $folder = 'Scheduled'; + $ret = $imap->get_mailbox_page($folder, 'DATE', false, 'ALL'); + + foreach ($ret[1] as $msg) { + $msg_headers = $imap->get_message_headers($msg['uid']); + + try { + if (!empty($msg_headers['X-Schedule'])) { + $scheduled_msg_count++; + } else { + continue; + } + + if (send_scheduled_message($this, $imap, $msg, $server_id)) { + $scheduled_msg_count--; + } + } catch (Exception $e) { + Hm_Debug::add(sprintf('ERRCannot send message: %s', $msg_headers['subject'])); + } + } + } + } + + $this->out('scheduled_msg_count', $scheduled_msg_count); +}} + +/** + * Changes the schedule of the message + * @subpackage smtp/handler + */ +class Hm_Handler_re_schedule_message_sending extends Hm_Handler_Module { + public function process() { + if (!($this->module_is_supported('imap') || $this->module_is_supported('profiles'))) { + return; + } + list($success, $form) = $this->process_form(array('schedule_date', 'scheduled_msg_ids')); + if (!$success) { + return; + } + $scheduled_msg_count = 0; + $new_schedule_date = $form['schedule_date']; + if ($form['schedule_date'] != 'now') { + $new_schedule_date = get_scheduled_date($form['schedule_date']); + } + $ids = explode(',', $form['scheduled_msg_ids']); + foreach ($ids as $msg_part) { + list($imap_server_id, $msg_id, $folder) = explode('_', $msg_part); + $cache = Hm_IMAP_List::get_cache($this->cache, $imap_server_id); + $imap = Hm_IMAP_List::connect($imap_server_id, $cache); + if (imap_authed($imap)) { + $folder = hex2bin($folder); + if (reschedule_message_sending($this, $imap, $msg_id, $folder, $new_schedule_date, $imap_server_id)) { + $scheduled_msg_count++; + } + } + } + $this->out('scheduled_msg_count', $scheduled_msg_count); + if ($scheduled_msg_count == count($ids)) { + $msg = 'Operation successful'; + } elseif ($scheduled_msg_count > 0) { + $msg = 'Some messages have been scheduled for sending'; + } else { + $msg = 'ERRFailed to schedule sending for messages'; + } + Hm_Msgs::add($msg); + $this->save_hm_msgs(); + } +} + /** * @subpackage keyboard_shortcuts/output */ @@ -1239,11 +1343,15 @@ protected function output() { } } } - $res .= ''. smtp_server_dropdown($this->module_output(), $this, $recip, $selected_id). - ''; - + '
+ + '. + schedule_dropdown($this). + '
'; if ($this->get('list_path') && ($reply_type == 'reply' || $reply_type == 'reply_all')) { $res .= ''; } @@ -1540,6 +1648,20 @@ protected function output() { } } +/** + * Add scheduled send to the message list controls + * @subpackage imap/output + */ +class Hm_Output_scheduled_send_msg_control extends Hm_Output_Module { + protected function output() { + $parts = explode('_', $this->get('list_path')); + if ($parts[0] == 'imap' && hex2bin($parts[2]) == 'Scheduled') { + $res = schedule_dropdown($this, true); + $this->concat('msg_controls_extra', $res); + } + } +} + /** * @subpackage smtp/functions */ @@ -1875,10 +1997,8 @@ function get_uploaded_files_from_array($uploaded_files) { } } -function prepare_draft_mime($atts, $uploaded_files, $from = false, $name = '') { - if (! empty($uploaded_files) && ! is_array($uploaded_files[0])) { - $uploaded_files = get_uploaded_files_from_array($uploaded_files); - } +function prepare_draft_mime($atts, $uploaded_files, $from = false, $name = '', $profile_id = null) { + $uploaded_files = get_uploaded_files_from_array($uploaded_files); $mime = new Hm_MIME_Msg( $atts['draft_to'], $atts['draft_subject'], @@ -1889,7 +2009,10 @@ function prepare_draft_mime($atts, $uploaded_files, $from = false, $name = '') { $atts['draft_bcc'], '', $name, - $atts['draft_in_reply_to'] + $atts['draft_in_reply_to'], + $atts['delivery_receipt'], + $atts['schedule'], + $profile_id ); $mime->add_attachments($uploaded_files); @@ -1928,28 +2051,40 @@ function save_imap_draft($atts, $id, $session, $mod, $mod_cache, $uploaded_files $specials = get_special_folders($mod, $imap_profile['id']); - if (!array_key_exists('draft', $specials) || !$specials['draft']) { + if ((!array_key_exists('draft', $specials) || !$specials['draft']) && !array_key_exists('schedule', $atts)) { Hm_Msgs::add('ERRThere is no draft directory configured for this account.'); return -1; } - $mailbox = Hm_IMAP_List::get_connected_mailbox($imap_profile['id'], $mod_cache); - if (! $mailbox || ! $mailbox->authed()) { - return -1; + $cache = Hm_IMAP_List::get_cache($mod_cache, $imap_profile['id']); + $imap = Hm_IMAP_List::connect($imap_profile['id'], $cache); + + if (!empty($atts['schedule'])) { + $folder ='Scheduled'; + if (!count($imap->get_mailbox_status($folder))) { + $imap->create_mailbox($folder); + } + $atts['schedule'] = get_scheduled_date($atts['schedule']); + } else { + $folder = $specials['draft']; } + $imap->select_mailbox($folder); - $mime = prepare_draft_mime($atts, $uploaded_files, $from, $name); + $mime = prepare_draft_mime($atts, $uploaded_files, $from, $name, $profile['id']); $res = $mime->process_attachments(); - + $msg = str_replace("\r\n", "\n", $mime->get_mime_msg()); $msg = str_replace("\n", "\r\n", $msg); $msg = rtrim($msg)."\r\n"; - if (! $mailbox->store_message($specials['draft'], $msg, false, true)) { - Hm_Msgs::add('ERRAn error occurred saving the draft message'); - return -1; + if ($imap->append_start($folder, mb_strlen($msg), false, true)) { + $imap->append_feed($msg."\r\n"); + if (!$imap->append_end()) { + Hm_Msgs::add('ERRAn error occurred saving the draft message'); + return -1; + } } - $messages = $mailbox->get_messages($specials['draft'], 'ARRIVAL', true, 'DRAFT', 0, 10); + $mailbox_page = $imap->get_mailbox_page($folder, 'ARRIVAL', true, 'DRAFT', 0, 10); // Remove old version from the mailbox if ($id) { @@ -2230,4 +2365,4 @@ function recip_count_check($headers, $omod) { if ($recip_count > MAX_RECIPIENT_WARNING) { Hm_Msgs::add('ERRMessage contains more than the maximum number of recipients, proceed with caution'); } -}} +}} \ No newline at end of file diff --git a/modules/smtp/setup.php b/modules/smtp/setup.php index 8d1cb62fb3..4ea4455e54 100644 --- a/modules/smtp/setup.php +++ b/modules/smtp/setup.php @@ -22,6 +22,7 @@ add_handler('compose', 'load_smtp_is_imap_forward_as_attachment', true, 'smtp', 'load_user_data', 'after'); add_handler('compose', 'load_smtp_is_imap_forward', true, 'smtp', 'load_smtp_is_imap_forward_as_attachment', 'after'); + add_handler('functional_api', 'default_smtp_server', true, 'smtp'); add_handler('profiles', 'load_smtp_servers_from_config', true, 'smtp', 'load_user_data', 'after'); @@ -103,6 +104,20 @@ add_handler('settings', 'process_enable_compose_delivery_receipt_setting', true, 'core', 'save_user_settings', 'before'); add_output('settings', 'enable_compose_delivery_receipt_setting', true, 'core', 'start_general_settings', 'after'); +/* send delayed emails */ +setup_base_ajax_page('ajax_send_scheduled_messages', 'core'); +add_handler('ajax_send_scheduled_messages', 'load_imap_servers_from_config', true, 'imap', 'load_user_data', 'after'); +add_handler('ajax_send_scheduled_messages', 'load_smtp_servers_from_config', true, 'smtp', 'load_user_data', 'after'); +add_handler('ajax_send_scheduled_messages', 'compose_profile_data', true, 'profiles'); +add_handler('ajax_send_scheduled_messages', 'send_scheduled_messages', true, 'smtp'); + +setup_base_ajax_page('ajax_re_schedule_message_sending', 'core'); +add_handler('ajax_re_schedule_message_sending', 'load_imap_servers_from_config', true, 'imap', 'load_user_data', 'after'); +add_handler('ajax_re_schedule_message_sending', 'load_smtp_servers_from_config', true, 'smtp', 'load_user_data', 'after'); +add_handler('ajax_re_schedule_message_sending', 'compose_profile_data', true, 'profiles'); +add_handler('ajax_re_schedule_message_sending', 're_schedule_message_sending', true, 'smtp'); + +add_output('message_list', 'scheduled_send_msg_control', true, 'smtp', 'imap_custom_controls', 'after'); return array( 'allowed_pages' => array( @@ -113,7 +128,9 @@ 'ajax_profiles_status', 'ajax_attachment_reminder_check', 'ajax_get_test_chunk', - 'ajax_upload_chunk' + 'ajax_upload_chunk', + 'ajax_send_scheduled_messages', + 'ajax_re_schedule_message_sending' ), 'allowed_get' => array( 'imap_draft' => FILTER_VALIDATE_INT, @@ -146,9 +163,13 @@ 'msg_sent_and_archived' => array(FILTER_VALIDATE_BOOLEAN, false), 'sent_msg_id' => array(FILTER_VALIDATE_BOOLEAN, false), 'enable_attachment_reminder' => array(FILTER_VALIDATE_BOOLEAN, false), + 'scheduled_msg_count' => array(FILTER_VALIDATE_INT, false), ), 'allowed_post' => array( 'post_archive' => FILTER_VALIDATE_INT, + 'send_tomorrow_morning' => FILTER_DEFAULT, + 'send_today_afternoon' => FILTER_DEFAULT, + 'schedule_sending' => FILTER_DEFAULT, 'attachment_id' => FILTER_DEFAULT, 'smtp_compose_type' => FILTER_VALIDATE_INT, 'new_smtp_name' => FILTER_DEFAULT, @@ -187,6 +208,9 @@ 'uploaded_files' => FILTER_DEFAULT, 'send_uploaded_files' => FILTER_DEFAULT, 'next_email_post' => FILTER_DEFAULT, - 'enable_attachment_reminder' => FILTER_VALIDATE_INT + 'enable_attachment_reminder' => FILTER_VALIDATE_INT, + 'schedule' => FILTER_DEFAULT, + 'schedule_date' => FILTER_DEFAULT, + 'scheduled_msg_ids' => FILTER_DEFAULT, ) ); diff --git a/modules/smtp/site.js b/modules/smtp/site.js index 67db768f89..aec9ab1819 100644 --- a/modules/smtp/site.js +++ b/modules/smtp/site.js @@ -28,7 +28,7 @@ var smtp_test_action = function(event) { false, {'smtp_connect': 1} ); -}; +}; var smtp_delete_action = function(event) { if (!hm_delete_prompt()) { @@ -69,7 +69,7 @@ var send_archive = function() { document.getElementsByClassName("smtp_send_placeholder")[0].click(); } -var save_compose_state = function(no_files, notice) { +var save_compose_state = function(no_files, notice, schedule, callback) { var no_icon = true; if (notice) { no_icon = false; @@ -82,6 +82,7 @@ var save_compose_state = function(no_files, notice) { var cc = $('.compose_cc').val(); var bcc = $('.compose_bcc').val(); var inreplyto = $('.compose_in_reply_to').val(); + var delivery_receipt = $('#compose_delivery_receipt').prop('checked'); var draft_id = $('.compose_draft_id').val(); if (globals.draft_state == body+subject+to+smtp+cc+bcc+uploaded_files) { @@ -105,6 +106,8 @@ var save_compose_state = function(no_files, notice) { {'name': 'draft_in_reply_to', 'value': inreplyto}, {'name': 'delete_uploaded_files', 'value': no_files}, {'name': 'draft_to', 'value': to}, + {'name': 'schedule', 'value': schedule}, + {'name': 'compose_delivery_receipt', 'value': delivery_receipt}, {'name': 'uploaded_files', 'value': uploaded_files}], function(res) { if (res.draft_id) { @@ -113,6 +116,15 @@ var save_compose_state = function(no_files, notice) { if (res.draft_subject) { $('.draft_list .draft_'+draft_id+' a').text(res.draft_subject); } + + if (schedule) { + $(".compose_form")[0].reset(); + return; + } + + if (callback) { + callback(res); + } }, [], no_icon @@ -138,7 +150,7 @@ function smtpServersPageHandler() { } } -var reset_smtp_form = function() { +var reset_smtp_form = function(save = true) { $('.compose_body').val(''); $('.compose_subject').val(''); $('.compose_to').val(''); @@ -146,7 +158,10 @@ var reset_smtp_form = function() { $('.compose_bcc').val(''); $('.ke-content', $('iframe').contents()).html(''); $('.uploaded_files').html(''); - save_compose_state(true); + $('#compose_delivery_receipt').prop('checked', false); + if (save) { + save_compose_state(true); + } }; var replace_cursor_positon = function (txtElement) { @@ -374,4 +389,4 @@ function smtpSettingsPageHandler() { [] ); }); -} +} \ No newline at end of file From 6e35072c289395d389d22facb8b7ef49dd3fdad8 Mon Sep 17 00:00:00 2001 From: Amani Nyumu Date: Fri, 3 Jan 2025 11:18:24 +0100 Subject: [PATCH 02/20] feat(backend): add scheduled send functionality Removed redundant showRoutingToast() and hideRoutingToast() calls in reload_and_redirect function as navigate() handles them. Streamlined the process for scheduling and snoozing actions. --- modules/imap/js_modules/utils/handleNexterDateAction.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/modules/imap/js_modules/utils/handleNexterDateAction.js b/modules/imap/js_modules/utils/handleNexterDateAction.js index 02c303a9fc..622ecbffb9 100644 --- a/modules/imap/js_modules/utils/handleNexterDateAction.js +++ b/modules/imap/js_modules/utils/handleNexterDateAction.js @@ -7,14 +7,11 @@ function processNextActionDate(e) { } isReloading = true; - showRoutingToast(); - try { Hm_Folders.reload_folders(true); let path = getListPathParam(); await navigate(`?page=message_list&list_path=${path}`); } finally { - hideRoutingToast(); isReloading = false; } }; From 3dcce00efea615294d22b04a1b41b7819838f01a Mon Sep 17 00:00:00 2001 From: Amani Nyumu Date: Tue, 7 Jan 2025 21:48:38 +0100 Subject: [PATCH 03/20] feat(backend): clarify reload_and_redirect logic by removing unnecessary isReloading --- .../utils/handleNexterDateAction.js | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/modules/imap/js_modules/utils/handleNexterDateAction.js b/modules/imap/js_modules/utils/handleNexterDateAction.js index 622ecbffb9..201a60f35d 100644 --- a/modules/imap/js_modules/utils/handleNexterDateAction.js +++ b/modules/imap/js_modules/utils/handleNexterDateAction.js @@ -1,23 +1,10 @@ function processNextActionDate(e) { - let isReloading = false; - let reload_and_redirect = async function () { - if (isReloading) { - return; - } - isReloading = true; - - try { - Hm_Folders.reload_folders(true); - let path = getListPathParam(); - await navigate(`?page=message_list&list_path=${path}`); - } finally { - isReloading = false; - } + Hm_Folders.reload_folders(true); + let path = getListPathParam(); + await navigate(`?page=message_list&list_path=${path}`); }; - - let collectCheckedIds = function () { let ids = []; $('input[type=checkbox]').each(function () { From 1b3678d741d00f048e1efbf06af3a1f4ebb26058 Mon Sep 17 00:00:00 2001 From: Amani Nyumu Date: Tue, 7 Jan 2025 22:47:02 +0100 Subject: [PATCH 04/20] feat(backend): refactor the code to fix the server error Uncaught Error: Call to undefined method Hm_Mailbox::get_mailbox_status() --- modules/smtp/modules.php | 42 +++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/modules/smtp/modules.php b/modules/smtp/modules.php index cba75e6456..641bb21236 100644 --- a/modules/smtp/modules.php +++ b/modules/smtp/modules.php @@ -322,6 +322,9 @@ public function process() { $uploaded_files = array_key_exists('uploaded_files', $this->request->post) ? $this->request->post['uploaded_files'] : false; $delivery_receipt = array_key_exists('compose_delivery_receipt', $this->request->post) ? $this->request->post['compose_delivery_receipt'] : false; $schedule = array_key_exists('schedule', $this->request->post) ? $this->request->post['schedule'] : ''; + if ($schedule == "undefined") { + $schedule = ""; + } if (array_key_exists('delete_uploaded_files', $this->request->post) && $this->request->post['delete_uploaded_files']) { delete_uploaded_files($this->session, $draft_id); @@ -2029,13 +2032,11 @@ function save_imap_draft($atts, $id, $session, $mod, $mod_cache, $uploaded_files $from = false; $name = ''; $uploaded_files = get_uploaded_files_from_array($uploaded_files); - if ($profile && $profile['type'] == 'imap' && $mod->module_is_supported('imap')) { $from = $profile['replyto']; $name = $profile['name']; $imap_profile = Hm_IMAP_List::fetch($profile['user'], $profile['server']); } - if (!$imap_profile || empty($imap_profile)) { $imap_profile = find_imap_by_smtp( $mod->user_config->get('imap_servers'), @@ -2055,44 +2056,42 @@ function save_imap_draft($atts, $id, $session, $mod, $mod_cache, $uploaded_files Hm_Msgs::add('ERRThere is no draft directory configured for this account.'); return -1; } - $cache = Hm_IMAP_List::get_cache($mod_cache, $imap_profile['id']); - $imap = Hm_IMAP_List::connect($imap_profile['id'], $cache); - + $mailbox = Hm_IMAP_List::get_connected_mailbox($imap_profile['id'], $mod_cache); + if (! $mailbox || ! $mailbox->authed()) { + return -1; + } + if (!empty($atts['schedule'])) { $folder ='Scheduled'; - if (!count($imap->get_mailbox_status($folder))) { - $imap->create_mailbox($folder); + if (!count($mailbox->get_folder_status($folder))) { + $mailbox->create_folder($folder); } $atts['schedule'] = get_scheduled_date($atts['schedule']); } else { $folder = $specials['draft']; } - $imap->select_mailbox($folder); - $mime = prepare_draft_mime($atts, $uploaded_files, $from, $name, $profile['id']); + $mime = prepare_draft_mime($atts, $uploaded_files, $from, $name); $res = $mime->process_attachments(); - + $msg = str_replace("\r\n", "\n", $mime->get_mime_msg()); $msg = str_replace("\n", "\r\n", $msg); $msg = rtrim($msg)."\r\n"; - if ($imap->append_start($folder, mb_strlen($msg), false, true)) { - $imap->append_feed($msg."\r\n"); - if (!$imap->append_end()) { - Hm_Msgs::add('ERRAn error occurred saving the draft message'); - return -1; - } + if (! $mailbox->store_message($folder, $msg, false, true)) { + Hm_Msgs::add('ERRAn error occurred saving the draft message'); + return -1; } - $mailbox_page = $imap->get_mailbox_page($folder, 'ARRIVAL', true, 'DRAFT', 0, 10); - + $messages = $mailbox->get_messages($specials['draft'], 'ARRIVAL', true, 'DRAFT', 0, 10); + // Remove old version from the mailbox if ($id) { $mailbox->message_action($specials['draft'], 'DELETE', array($id)); $mailbox->message_action($specials['draft'], 'EXPUNGE', array($id)); } - - foreach ($messages[1] as $mail) { + if (!empty($messages[1])) { + foreach ($messages[1] as $mail) { $msg_header = $mailbox->get_message_headers($specials['draft'], $mail['uid']); // Convert all header keys to lowercase $msg_header_lower = array_change_key_case($msg_header, CASE_LOWER); @@ -2107,8 +2106,7 @@ function save_imap_draft($atts, $id, $session, $mod, $mod_cache, $uploaded_files return $mail['uid']; } } - } - return -1; + }} }} /** From ba304126de342b0f40b8b51c178a47bddbca2204 Mon Sep 17 00:00:00 2001 From: Amani Nyumu Date: Fri, 3 Jan 2025 17:12:49 +0100 Subject: [PATCH 05/20] feat(backend): add scheduled send functionality resolve conflicts in modules/imap/functions.php --- modules/smtp/modules.php | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/modules/smtp/modules.php b/modules/smtp/modules.php index 641bb21236..b31ae820e4 100644 --- a/modules/smtp/modules.php +++ b/modules/smtp/modules.php @@ -322,6 +322,7 @@ public function process() { $uploaded_files = array_key_exists('uploaded_files', $this->request->post) ? $this->request->post['uploaded_files'] : false; $delivery_receipt = array_key_exists('compose_delivery_receipt', $this->request->post) ? $this->request->post['compose_delivery_receipt'] : false; $schedule = array_key_exists('schedule', $this->request->post) ? $this->request->post['schedule'] : ''; + if ($schedule == "undefined") { $schedule = ""; } @@ -2056,9 +2057,17 @@ function save_imap_draft($atts, $id, $session, $mod, $mod_cache, $uploaded_files Hm_Msgs::add('ERRThere is no draft directory configured for this account.'); return -1; } - $mailbox = Hm_IMAP_List::get_connected_mailbox($imap_profile['id'], $mod_cache); - if (! $mailbox || ! $mailbox->authed()) { - return -1; + $cache = Hm_IMAP_List::get_cache($mod_cache, $imap_profile['id']); + $imap = Hm_IMAP_List::connect($imap_profile['id'], $cache); + + if (!empty($atts['schedule'])) { + $folder ='Scheduled'; + if (!count($imap->get_mailbox_status($folder))) { + $imap->create_mailbox($folder); + } + $atts['schedule'] = get_scheduled_date($atts['schedule']); + } else { + $folder = $specials['draft']; } if (!empty($atts['schedule'])) { @@ -2071,20 +2080,24 @@ function save_imap_draft($atts, $id, $session, $mod, $mod_cache, $uploaded_files $folder = $specials['draft']; } - $mime = prepare_draft_mime($atts, $uploaded_files, $from, $name); - $res = $mime->process_attachments(); + $mime = prepare_draft_mime($atts, $uploaded_files, $from, $name, $profile['id']); + $res = $mime->process_attachments(); + $msg = str_replace("\r\n", "\n", $mime->get_mime_msg()); $msg = str_replace("\n", "\r\n", $msg); $msg = rtrim($msg)."\r\n"; - if (! $mailbox->store_message($folder, $msg, false, true)) { - Hm_Msgs::add('ERRAn error occurred saving the draft message'); - return -1; + if ($imap->append_start($folder, mb_strlen($msg), false, true)) { + $imap->append_feed($msg."\r\n"); + if (!$imap->append_end()) { + Hm_Msgs::add('ERRAn error occurred saving the draft message'); + return -1; + } } - $messages = $mailbox->get_messages($specials['draft'], 'ARRIVAL', true, 'DRAFT', 0, 10); - + $mailbox_page = $imap->get_mailbox_page($folder, 'ARRIVAL', true, 'DRAFT', 0, 10); + // Remove old version from the mailbox if ($id) { $mailbox->message_action($specials['draft'], 'DELETE', array($id)); From edfbcc73ac6aa41aeda30484425ca99b5ee35f72 Mon Sep 17 00:00:00 2001 From: Amani Nyumu Date: Sat, 18 Jan 2025 19:06:28 +0100 Subject: [PATCH 06/20] feat(backend): change Hm_IMAP_List to Hm_Mailbox --- modules/core/site.js | 897 +++++++++++----------- modules/imap/functions.php | 74 ++ modules/imap/handler_modules.php | 38 +- modules/imap/hm-imap.php | 1 + modules/imap/js_modules/route_handlers.js | 4 +- modules/imap/site.js | 1 - modules/smtp/js_modules/route_handlers.js | 62 +- modules/smtp/modules.php | 236 +++--- 8 files changed, 676 insertions(+), 637 deletions(-) diff --git a/modules/core/site.js b/modules/core/site.js index 01908668af..652fe26d53 100644 --- a/modules/core/site.js +++ b/modules/core/site.js @@ -47,7 +47,7 @@ var Hm_Ajax = { active_reqs: 0, icon_loading_id: false, - get_ajax_hook_name: function (args) { + get_ajax_hook_name: function(args) { var index; for (index in args) { if (args[index]['name'] == 'hm_ajax_hook') { @@ -57,7 +57,7 @@ var Hm_Ajax = { return; }, - request: function (args, callback, extra, no_icon, batch_callback, on_failure, signal) { + request: function(args, callback, extra, no_icon, batch_callback, on_failure, signal) { var bcb = false; if (typeof batch_callback != 'undefined' && $.inArray(batch_callback, this.batch_callbacks) === -1) { bcb = batch_callback.toString(); @@ -79,27 +79,27 @@ var Hm_Ajax = { return ajax.make_request(args, callback, extra, name, on_failure, batch_callback, signal); }, - show_loading_icon: function () { + show_loading_icon: function() { if (Hm_Ajax.icon_loading_id !== false) { return; } - var hm_loading_pos = $('.loading_icon').width() / 40; + var hm_loading_pos = $('.loading_icon').width()/40; $('.loading_icon').show(); function move_background_image() { hm_loading_pos = hm_loading_pos + 50; - $('.loading_icon').css('background-position', hm_loading_pos + 'px 0'); + $('.loading_icon').css('background-position', hm_loading_pos+'px 0'); Hm_Ajax.icon_loading_id = setTimeout(move_background_image, 100); } move_background_image(); }, - stop_loading_icon: function (loading_id) { + stop_loading_icon : function(loading_id) { clearTimeout(loading_id); $('.loading_icon').hide(); Hm_Ajax.icon_loading_id = false; }, - process_callback_hooks: function (name, res) { + process_callback_hooks: function(name, res) { var hook; var func; for (var i in Hm_Ajax.callback_hooks) { @@ -116,182 +116,190 @@ var Hm_Ajax = { } }, - add_callback_hook: function (request_name, hook_function) { + add_callback_hook: function(request_name, hook_function) { Hm_Ajax.callback_hooks.push([request_name, hook_function]); } }; /* ajax request wrapper */ -var Hm_Ajax_Request = function () { - return { - callback: false, - name: false, - batch_callback: false, - index: 0, - on_failure: false, - start_time: 0, - - xhr_fetch: function (config) { - var xhr = new XMLHttpRequest(); - var data = ''; - if (config.data) { - data = this.format_xhr_data(config.data); - } - const url = window.location.next ?? window.location.href; - xhr.open('POST', url) - if (config.signal) { - config.signal.addEventListener('abort', function () { - xhr.abort(); - }); - } - xhr.addEventListener('load', function () { - config.callback.done(Hm_Utils.json_decode(xhr.response, true), xhr); - config.callback.always(Hm_Utils.json_decode(xhr.response, true)); - }); - xhr.addEventListener('error', function () { - Hm_Ajax.stop_loading_icon(Hm_Ajax.icon_loading_id); - config.callback.fail(xhr); - config.callback.always(Hm_Utils.json_decode(xhr.response, true)); +var Hm_Ajax_Request = function() { return { + callback: false, + name: false, + batch_callback: false, + index: 0, + on_failure: false, + start_time: 0, + + xhr_fetch: function(config) { + var xhr = new XMLHttpRequest(); + var data = ''; + if (config.data) { + data = this.format_xhr_data(config.data); + } + const url = new URL(window.location.href); + if (window.location.next) { + url.search = window.location.next.split('?')[1]; + } + for (const param of url.searchParams) { + const configItem = config.data.find(item => item.name === param[0]); + if (configItem) { + url.searchParams.set(configItem.name, configItem.value); + } + } + + xhr.open('POST', url.toString()) + if (config.signal) { + config.signal.addEventListener('abort', function() { + xhr.abort(); }); - xhr.addEventListener('abort', function () { - Hm_Ajax.stop_loading_icon(Hm_Ajax.icon_loading_id); - config.callback.always(Hm_Utils.json_decode(xhr.response, true)); + } + xhr.addEventListener('load', function() { + config.callback.done(Hm_Utils.json_decode(xhr.response, true), xhr); + config.callback.always(Hm_Utils.json_decode(xhr.response, true)); + }); + xhr.addEventListener('error', function() { + Hm_Ajax.stop_loading_icon(Hm_Ajax.icon_loading_id); + config.callback.fail(xhr); + config.callback.always(Hm_Utils.json_decode(xhr.response, true)); + }); + xhr.addEventListener('abort', function() { + Hm_Ajax.stop_loading_icon(Hm_Ajax.icon_loading_id); + config.callback.always(Hm_Utils.json_decode(xhr.response, true)); - }); - xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); - xhr.setRequestHeader('X-Requested-with', 'xmlhttprequest'); - xhr.send(data); - }, + }); + xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + xhr.setRequestHeader('X-Requested-with', 'xmlhttprequest'); + xhr.send(data); + }, - format_xhr_data: function (data) { - var res = [] - for (var i in data) { - res.push(encodeURIComponent(data[i]['name']) + '=' + encodeURIComponent(data[i]['value'])); - } - return res.join('&'); - }, + format_xhr_data: function(data) { + var res = [] + for (var i in data) { + res.push(encodeURIComponent(data[i]['name']) + '=' + encodeURIComponent(data[i]['value'])); + } + return res.join('&'); + }, - make_request: function (args, callback, extra, request_name, on_failure, batch_callback, signal) { - var name; - var arg; - this.batch_callback = batch_callback; - this.name = request_name; - this.callback = callback; - if (on_failure) { - this.on_failure = true; - } - if (extra) { - for (name in extra) { - args.push({ 'name': name, 'value': extra[name] }); - } - } - var key_found = false; - for (arg in args) { - if (args[arg].name == 'hm_page_key') { - key_found = true; - break; - } + make_request: function(args, callback, extra, request_name, on_failure, batch_callback, signal) { + var name; + var arg; + this.batch_callback = batch_callback; + this.name = request_name; + this.callback = callback; + if (on_failure) { + this.on_failure = true; + } + if (extra) { + for (name in extra) { + args.push({'name': name, 'value': extra[name]}); } - if (!key_found) { - args.push({ 'name': 'hm_page_key', 'value': $('#hm_page_key').val() }); + } + var key_found = false; + for (arg in args) { + if (args[arg].name == 'hm_page_key') { + key_found = true; + break; } - var dt = new Date(); - this.start_time = dt.getTime(); - this.xhr_fetch({ url: '', data: args, callback: this, signal }); - return false; - }, + } + if (!key_found) { + args.push({'name': 'hm_page_key', 'value': $('#hm_page_key').val()}); + } + var dt = new Date(); + this.start_time = dt.getTime(); + this.xhr_fetch({url: '', data: args, callback: this, signal}); + return false; + }, - done: function (res, xhr) { - if (Hm_Ajax.aborted) { - return; + done: function(res, xhr) { + if (Hm_Ajax.aborted) { + return; + } + else if (!res || typeof res == 'string' && (res == 'null' || res.indexOf('<') === 0 || res == '{}')) { + this.fail(xhr); + return; + } + else { + $('.offline').hide(); + if (hm_encrypt_ajax_requests()) { + res = Hm_Utils.json_decode(Hm_Crypt.decrypt(res.payload)); } - else if (!res || typeof res == 'string' && (res == 'null' || res.indexOf('<') === 0 || res == '{}')) { - this.fail(xhr); + if ((res.state && res.state == 'not callable') || !res.router_login_state) { + this.fail(xhr, true); return; } - else { - $('.offline').hide(); - if (hm_encrypt_ajax_requests()) { - res = Hm_Utils.json_decode(Hm_Crypt.decrypt(res.payload)); - } - if ((res.state && res.state == 'not callable') || !res.router_login_state) { - this.fail(xhr, true); - return; - } - if (Hm_Ajax.err_condition) { - Hm_Ajax.err_condition = false; - Hm_Notices.hide(true); - } - if (res.router_user_msgs && !$.isEmptyObject(res.router_user_msgs)) { - Hm_Notices.show(res.router_user_msgs); - } - if (res.folder_status) { - for (const name in res.folder_status) { - if (name === getListPathParam()) { - Hm_Folders.unread_counts[name] = res.folder_status[name]['unseen']; - Hm_Folders.update_unread_counts(); - const messages = new Hm_MessagesStore(name, Hm_Utils.get_url_page_number()); - messages.load().then(() => { - if (messages.count != res.folder_status[name].messages) { - messages.load(true).then(() => { - display_imap_mailbox(messages.rows, messages.links); - }) - } - }); - } + if (Hm_Ajax.err_condition) { + Hm_Ajax.err_condition = false; + Hm_Notices.hide(true); + } + if (res.router_user_msgs && !$.isEmptyObject(res.router_user_msgs)) { + Hm_Notices.show(res.router_user_msgs); + } + if (res.folder_status) { + for (const name in res.folder_status) { + if (name === getListPathParam()) { + Hm_Folders.unread_counts[name] = res.folder_status[name]['unseen']; + Hm_Folders.update_unread_counts(); + const messages = new Hm_MessagesStore(name, Hm_Utils.get_url_page_number()); + messages.load().then(() => { + if (messages.count != res.folder_status[name].messages) { + messages.load(true).then(() => { + display_imap_mailbox(messages.rows, messages.list); + }) + } + }); } } - if (this.callback) { - this.callback(res); - } - Hm_Ajax.process_callback_hooks(this.name, res); } - }, - - run_on_failure: function () { - if (this.on_failure && this.callback) { - this.callback(false); + if (this.callback) { + this.callback(res); } - return false; - }, + Hm_Ajax.process_callback_hooks(this.name, res); + } + }, - fail: function (xhr, not_callable) { - if (not_callable === true || (xhr.status && xhr.status == 500)) { - Hm_Notices.show([err_msg('Server Error')]); - } - else { - $('.offline').show(); - } - Hm_Ajax.err_condition = true; - this.run_on_failure(); - }, - - always: function (res) { - Hm_Ajax.active_reqs--; - var batch_count = 1; - if (this.batch_callback) { - if (typeof Hm_Ajax.batch_callbacks[this.batch_callback.toString()] != 'undefined') { - batch_count = --Hm_Ajax.batch_callbacks[this.batch_callback.toString()]; - } - } - Hm_Message_List.set_row_events(); - if (batch_count === 0) { - Hm_Ajax.batch_callbacks[this.batch_callback.toString()] = 0; - Hm_Ajax.aborted = false; - Hm_Ajax.p_callbacks = []; - this.batch_callback(res); - this.batch_callback = false; - Hm_Ajax.stop_loading_icon(Hm_Ajax.icon_loading_id); - $('body').removeClass('wait'); - } - if (Hm_Ajax.active_reqs == 0) { - Hm_Ajax.stop_loading_icon(Hm_Ajax.icon_loading_id); - $('body').removeClass('wait'); - } - res = null; + run_on_failure: function() { + if (this.on_failure && this.callback) { + this.callback(false); } + return false; + }, + + fail: function(xhr, not_callable) { + if (not_callable === true || (xhr.status && xhr.status == 500)) { + Hm_Notices.show([err_msg('Server Error')]); + } + else { + $('.offline').show(); + } + Hm_Ajax.err_condition = true; + this.run_on_failure(); + }, + + always: function(res) { + Hm_Ajax.active_reqs--; + var batch_count = 1; + if (this.batch_callback) { + if (typeof Hm_Ajax.batch_callbacks[this.batch_callback.toString()] != 'undefined') { + batch_count = --Hm_Ajax.batch_callbacks[this.batch_callback.toString()]; + } + } + Hm_Message_List.set_row_events(); + if (batch_count === 0) { + Hm_Ajax.batch_callbacks[this.batch_callback.toString()] = 0; + Hm_Ajax.aborted = false; + Hm_Ajax.p_callbacks = []; + this.batch_callback(res); + this.batch_callback = false; + Hm_Ajax.stop_loading_icon(Hm_Ajax.icon_loading_id); + $('body').removeClass('wait'); + } + if (Hm_Ajax.active_reqs == 0) { + Hm_Ajax.stop_loading_icon(Hm_Ajax.icon_loading_id); + $('body').removeClass('wait'); + } + res = null; } -}; +}}; /** * Show a modal dialog with a title, content and buttons. @@ -328,7 +336,7 @@ function Hm_Modal(options) {
@@ -382,7 +390,7 @@ function Hm_Modal(options) { var Hm_Notices = { hide_id: false, - show: function (msgs) { + show: function(msgs) { var message = ''; var type = ''; for (var i in msgs) { @@ -399,7 +407,7 @@ var Hm_Notices = { } }, - hide: function (now) { + hide: function(now) { if (Hm_Notices.hide_id) { clearTimeout(Hm_Notices.hide_id); } @@ -408,7 +416,7 @@ var Hm_Notices = { Hm_Utils.clear_sys_messages(); } else { - Hm_Notices.hide_id = setTimeout(function () { + Hm_Notices.hide_id = setTimeout(function() { $('.sys_messages').addClass('d-none'); Hm_Utils.clear_sys_messages(); }, 5000); @@ -421,7 +429,7 @@ var Hm_Timer = { jobs: [], interval: 1000, - add_job: function (job, interval, defer, custom_defer) { + add_job: function(job, interval, defer, custom_defer) { if (custom_defer) { Hm_Timer.jobs.push([job, interval, custom_defer]); } @@ -429,11 +437,11 @@ var Hm_Timer = { Hm_Timer.jobs.push([job, interval, interval]); } if (!defer) { - try { job(); } catch (e) { console.log(e); } + try { job(); } catch(e) { console.log(e); } } }, - cancel: function (job) { + cancel: function(job) { for (var index in Hm_Timer.jobs) { if (Hm_Timer.jobs[index][0] == job) { Hm_Timer.jobs.splice(index, 1); @@ -443,7 +451,7 @@ var Hm_Timer = { return false; }, - fire: function () { + fire: function() { var job; var index; for (index in Hm_Timer.jobs) { @@ -452,7 +460,7 @@ var Hm_Timer = { if (job[2] === 0) { job[2] = job[1]; Hm_Timer.jobs[index] = job; - try { job[0](); } catch (e) { console.log(e); } + try { job[0](); } catch(e) { console.log(e); } } } setTimeout(Hm_Timer.fire, Hm_Timer.interval); @@ -492,34 +500,34 @@ function Message_List() { if (completed) { for (index in this.callbacks) { func = this.callbacks[index]; - try { func(); } catch (e) { console.log(e); } + try { func(); } catch(e) { console.log(e); } } } fixLtrInRtl(); }; - this.update = function (msgs) { - Hm_Utils.tbody().html(''); + this.update = function(msgs, id) { + Hm_Utils.tbody(id).html(''); for (const index in msgs) { const row = msgs[index][0]; Hm_Utils.tbody(id).append(row); } }; - this.set_tab_index = function () { + this.set_tab_index = function() { var msg_rows = Hm_Utils.rows(); var count = 1; - msg_rows.each(function () { + msg_rows.each(function() { $(this).attr('tabindex', count); count++; }); }; - this.sort = function (fld) { + this.sort = function(fld) { var listitems = Hm_Utils.rows(); var aval; var bval; - var sort_result = listitems.sort(function (a, b) { + var sort_result = listitems.sort(function(a, b) { switch (Math.abs(fld)) { case 1: case 2: @@ -549,13 +557,13 @@ function Message_List() { }); this.sort_fld = fld; Hm_Utils.tbody().html(''); - for (var i = 0, len = sort_result.length; i < len; i++) { + for (var i = 0, len=sort_result.length; i < len; i++) { Hm_Utils.tbody().append(sort_result[i]); } this.save_updated_list(); }; - this.insert_into_message_list = function (row, msg_rows) { + this.insert_into_message_list = function(row, msg_rows) { var sort_fld = this.sort_fld; if (typeof sort_fld == 'undefined' || sort_fld == null) { sort_fld = 4; @@ -564,10 +572,10 @@ function Message_List() { if (sort_fld == 4 || sort_fld == -4) { var timestr2; var timestr = $('.msg_timestamp', $(row)).val(); - $('tr', msg_rows).each(function () { + $('tr', msg_rows).each(function() { timestr2 = $('.msg_timestamp', $(this)).val(); - if ((sort_fld == -4 && (timestr2 * 1) >= (timestr * 1)) || - (sort_fld == 4 && (timestr * 1) >= (timestr2 * 1))) { + if ((sort_fld == -4 && (timestr2*1) >= (timestr*1)) || + (sort_fld == 4 && (timestr*1) >= (timestr2*1))) { element = $(this); return false; } @@ -576,10 +584,10 @@ function Message_List() { else { var bval; var aval = $($('td', $(row))[Math.abs(sort_fld)]).text().replace(/^\s+/g, ''); - $('tr', msg_rows).each(function () { + $('tr', msg_rows).each(function() { bval = $($('td', $(this))[Math.abs(sort_fld)]).text().replace(/^\s+/g, ''); if ((sort_fld < 0 && aval.toUpperCase().localeCompare(bval.toUpperCase()) > 0) || - (sort_fld > 0 && bval.toUpperCase().localeCompare(aval.toUpperCase()) > 0)) { + (sort_fld > 0 && bval.toUpperCase().localeCompare(aval.toUpperCase()) > 0)) { element = $(this); return false; } @@ -592,16 +600,16 @@ function Message_List() { else { msg_rows.append(row); } - self.just_inserted.push($('.from', $(row)).text() + ' - ' + $('.subject', $(row)).text()); + self.just_inserted.push($('.from', $(row)).text()+' - '+$('.subject', $(row)).text()); }; - this.reset_checkboxes = function () { + this.reset_checkboxes = function() { this.toggle_msg_controls(); this.set_row_events(); }; - this.toggle_msg_controls = function () { - if ($('input[type=checkbox]', $('.message_table')).filter(function () { return this.checked; }).length > 0) { + this.toggle_msg_controls = function() { + if ($('input[type=checkbox]', $('.message_table')).filter(function() {return this.checked; }).length > 0) { $('.msg_controls').addClass('d-flex'); $('.msg_controls').removeClass('d-none'); $('.mailbox_list_title').addClass('hide'); @@ -613,7 +621,7 @@ function Message_List() { } }; - this.update_after_action = function (action_type, selected) { + this.update_after_action = function(action_type, selected) { var remove = false; if (action_type == 'read' && getListPathParam() == 'unread') { remove = true; @@ -642,20 +650,20 @@ function Message_List() { this.reset_checkboxes(); }; - this.save_updated_list = function () { + this.save_updated_list = function() { if (this.page_caches.hasOwnProperty(getListPathParam())) { this.set_message_list_state(this.page_caches[getListPathParam()]); - Hm_Utils.save_to_local_storage('sort_' + getListPathParam(), this.sort_fld); + Hm_Utils.save_to_local_storage('sort_'+getListPathParam(), this.sort_fld); } }; - this.remove_after_action = function (action_type, selected) { + this.remove_after_action = function(action_type, selected) { var removed = 0; var class_name = false; var index; for (index in selected) { class_name = selected[index]; - $('.' + Hm_Utils.clean_selector(class_name)).remove(); + $('.'+Hm_Utils.clean_selector(class_name)).remove(); if (action_type == 'delete') { this.deleted.push(class_name); } @@ -664,14 +672,14 @@ function Message_List() { return removed; }; - this.read_after_action = function (action_type, selected) { + this.read_after_action = function(action_type, selected) { var read = 0; var row; var index; var class_name = false; for (index in selected) { class_name = selected[index]; - row = $('.' + Hm_Utils.clean_selector(class_name)); + row = $('.'+Hm_Utils.clean_selector(class_name)); if (action_type == 'read') { $('.subject > div', row).removeClass('unseen'); row.removeClass('unseen'); @@ -685,14 +693,14 @@ function Message_List() { return read; }; - this.flag_after_action = function (action_type, selected) { + this.flag_after_action = function(action_type, selected) { var flagged = 0; var class_name; var row; var index; for (index in selected) { class_name = selected[index]; - row = $('.' + Hm_Utils.clean_selector(class_name)); + row = $('.'+Hm_Utils.clean_selector(class_name)); if (action_type == 'flag') { $('.icon', row).html(hm_flag_image_src()); } @@ -704,7 +712,7 @@ function Message_List() { return flagged; }; - this.load_sources = function () { + this.load_sources = function() { var index; var source; if (!self.background) { @@ -717,7 +725,7 @@ function Message_List() { return false; }; - this.select_combined_view = function () { + this.select_combined_view = function() { if (self.page_caches.hasOwnProperty(getListPathParam())) { self.setup_combined_view(self.page_caches[getListPathParam()]); } @@ -729,24 +737,23 @@ function Message_List() { self.setup_combined_view(false); } } - var sort_type = Hm_Utils.get_from_local_storage('sort_' + getListPathParam()); + var sort_type = Hm_Utils.get_from_local_storage('sort_'+getListPathParam()); if (sort_type != null) { this.sort_fld = sort_type; $('.combined_sort').val(sort_type); } - $('.core_msg_control').on("click", function (e) { + $('.core_msg_control').on("click", function(e) { e.preventDefault(); - return self.message_action($(this).data('action')); - }); - $('.toggle_link').on("click", function () { return self.toggle_rows(); }); - $('.refresh_link').on("click", function () { return self.load_sources(); }); + return self.message_action($(this).data('action')); }); + $('.toggle_link').on("click", function() { return self.toggle_rows(); }); + $('.refresh_link').on("click", function() { return self.load_sources(); }); }; - this.add_sources = function (sources) { + this.add_sources = function(sources) { self.sources = sources; }; - this.setup_combined_view = function (cache_name) { + this.setup_combined_view = function(cache_name) { self.add_sources(hm_data_sources()); var data = Hm_Utils.get_from_local_storage(cache_name); var interval = Hm_Utils.get_from_global('combined_view_refresh_interval', 60); @@ -765,53 +772,53 @@ function Message_List() { } }; - this.clear_read_messages = function () { + this.clear_read_messages = function() { var class_name; var list = Hm_Utils.get_from_local_storage('read_message_list'); if (list && list.length) { list = Hm_Utils.json_decode(list); for (class_name in list) { - $('.' + Hm_Utils.clean_selector(class_name)).remove(); + $('.'+Hm_Utils.clean_selector(class_name)).remove(); } Hm_Utils.save_to_local_storage('read_message_list', ''); } }; /* TODO: remove module specific refs */ - this.update_title = function (list_path = getListPathParam()) { + this.update_title = function(list_path = getListPathParam()) { var count = 0; var rows = Hm_Utils.rows(); var tbody = Hm_Utils.tbody(); if (list_path == 'unread') { count = rows.length; - document.title = count + ' ' + hm_trans('Unread'); + document.title = count+' '+hm_trans('Unread'); } else if (list_path == 'flagged') { count = rows.length; - document.title = count + ' ' + hm_trans('Flagged'); + document.title = count+' '+hm_trans('Flagged'); } else if (list_path == 'combined_inbox') { count = $('tr .unseen', tbody).length; - document.title = count + ' ' + hm_trans('Unread in Everything'); + document.title = count+' '+hm_trans('Unread in Everything'); } else if (list_path == 'email') { count = $('tr .unseen', tbody).length; - document.title = count + ' ' + hm_trans('Unread in Email'); + document.title = count+' '+hm_trans('Unread in Email'); } else if (list_path == 'feeds') { count = $('tr .unseen', tbody).length; - document.title = count + ' ' + hm_trans('Unread in Feeds'); + document.title = count+' '+hm_trans('Unread in Feeds'); } }; - this.message_action = function (action_type) { + this.message_action = function(action_type) { if (action_type == 'delete' && !hm_delete_prompt()) { return false; } var msg_list = $('.message_table'); var selected = []; var current_list = self.filter_list(); - $('input[type=checkbox]', msg_list).each(function () { + $('input[type=checkbox]', msg_list).each(function() { if (this.checked) { selected.push($(this).val()); } @@ -819,10 +826,10 @@ function Message_List() { if (selected.length > 0) { var updated = false; Hm_Ajax.request( - [{ 'name': 'hm_ajax_hook', 'value': 'ajax_message_action' }, - { 'name': 'action_type', 'value': action_type }, - { 'name': 'message_ids', 'value': selected }], - function (res) { + [{'name': 'hm_ajax_hook', 'value': 'ajax_message_action'}, + {'name': 'action_type', 'value': action_type}, + {'name': 'message_ids', 'value': selected}], + function(res) { if (!res) { $('.message_table_body').replaceWith(current_list); self.save_updated_list(); @@ -847,9 +854,10 @@ function Message_List() { } }; - this.prev_next_links = function (msgUid, lisPath = getListPathParam()) { - let phref; - let nhref; + this.prev_next_links = function(msgUid, lisPath = getListPathParam()) { + let prevUrl; + let nextUrl; + const target = $('.msg_headers tr').last(); const messages = new Hm_MessagesStore(lisPath, Hm_Utils.get_url_page_number()); messages.load(false, true); @@ -860,31 +868,31 @@ function Message_List() { prevUrl = new URL(prevSubject.prop('href')); prevUrl.searchParams.set('list_parent', lisPath); const subject = prevSubject.text(); - const plink = ' ' + subject + ''; - $('' + plink + '').insertBefore(target); + const plink = ' '+subject+''; + $(''+plink+'').insertBefore(target); } if (next) { const nextSubject = $(next['0']).find('.subject a'); nextUrl = new URL(nextSubject.prop('href')); nextUrl.searchParams.set('list_parent', lisPath); const subject = nextSubject.text(); - - const nlink = ' ' + subject + ''; - $('' + nlink + '').insertBefore(target); + + const nlink = ' '+subject+''; + $(''+nlink+'').insertBefore(target); } return [prevUrl?.href, nextUrl?.href]; }; - this.check_empty_list = function () { + this.check_empty_list = function() { var count = Hm_Utils.rows().length; if (!count) { if (!$('.empty_list').length) { if (getPageNameParam() == 'search') { - $('.search_content').append('
' + hm_empty_folder() + '
'); + $('.search_content').append('
'+hm_empty_folder()+'
'); } else { - $('.message_list').append('
' + hm_empty_folder() + '
'); + $('.message_list').append('
'+hm_empty_folder()+'
'); } } } @@ -894,7 +902,7 @@ function Message_List() { return count === 0; }; - this.track_read_messages = function (class_name) { + this.track_read_messages = function(class_name) { var read_messages = Hm_Utils.get_from_local_storage('read_message_list'); if (read_messages && read_messages.length) { read_messages = Hm_Utils.json_decode(read_messages); @@ -911,9 +919,9 @@ function Message_List() { return added; }; - this.adjust_unread_total = function (amount, replace) { + this.adjust_unread_total = function(amount, replace) { var missing = $('.total_unread_count').text() === '' ? true : false; - var current = $('.total_unread_count').text() * 1; + var current = $('.total_unread_count').text()*1; var new_total; if (replace && amount == current && amount != 0) { return; @@ -931,7 +939,7 @@ function Message_List() { new_total = 0; } if (new_total != current || missing) { - $('.total_unread_count').html(' ' + new_total + ' '); + $('.total_unread_count').html(' '+new_total+' '); } if (new_total > current && getPageNameParam() != 'message_list' && getListPathParam() != 'unread') { $('.menu_unread > a').css('font-weight', 'bold'); @@ -943,14 +951,14 @@ function Message_List() { self.past_total = current; }; - this.toggle_rows = function () { + this.toggle_rows = function() { $('input[type=checkbox]', $('.message_table')).each(function () { this.checked = !this.checked; }); self.toggle_msg_controls(); return false; }; - this.filter_list = function () { - var data = Hm_Utils.rows().clone().filter(function () { + this.filter_list = function() { + var data = Hm_Utils.rows().clone().filter(function() { if (this.className == 'inline_msg') { return false; } @@ -961,7 +969,7 @@ function Message_List() { return res; }; - this.set_message_list_state = function (list_type) { + this.set_message_list_state = function(list_type) { var data = this.filter_list(); data.find('*[style]').attr('style', ''); data.find('input[type=checkbox]').removeAttr('checked'); @@ -977,10 +985,10 @@ function Message_List() { } }; - this.select_range = function (a, b) { + this.select_range = function(a, b) { var start = false; var end = false; - $('input[type=checkbox]', $('.message_table')).each(function () { + $('input[type=checkbox]', $('.message_table')).each(function() { if (end) { return false; } @@ -999,7 +1007,7 @@ function Message_List() { }); }; - this.process_shift_click = function (el) { + this.process_shift_click = function(el) { var id = el.prop('id'); if (id == self.last_click) { return; @@ -1007,12 +1015,12 @@ function Message_List() { self.select_range(id, self.last_click); }; - this.set_row_events = function () { + this.set_row_events = function() { Hm_Utils.rows().off('click'); - Hm_Utils.rows().on('click', function (e) { self.process_row_click(e); }); + Hm_Utils.rows().on('click', function(e) { self.process_row_click(e); }); } - this.process_row_click = function (e) { + this.process_row_click = function(e) { if (e.target.tagName === 'A') { return; } @@ -1038,47 +1046,48 @@ function Message_List() { return false; } - this.select_on_row_click = function (shift, ctrl, el, target) { + this.select_on_row_click = function(shift, ctrl, el, target) { if (shift) { if (self.last_click) { self.process_shift_click(el); } - $('#' + el.prop('id')).prop('checked', 'checked'); + $('#'+el.prop('id')).prop('checked', 'checked'); self.last_click = el.prop('id'); } else if (ctrl) { if (el.prop('checked')) { - $('#' + el.prop('id')).prop('checked', false); + $('#'+el.prop('id')).prop('checked', false); } else { - $('#' + el.prop('id')).prop('checked', 'checked'); + $('#'+el.prop('id')).prop('checked', 'checked'); self.last_click = el.prop('id'); } } } - this.set_all_mail_state = function () { self.set_message_list_state('formatted_all_mail'); }; - this.set_combined_inbox_state = function () { self.set_message_list_state('formatted_combined_inbox'); }; - this.set_flagged_state = function () { self.set_message_list_state('formatted_flagged_data'); }; - this.set_unread_state = function () { self.set_message_list_state('formatted_unread_data'); }; - this.set_search_state = function () { self.set_message_list_state('formatted_search_data'); }; - this.set_junk_state = function () { self.set_message_list_state('formatted_junk_data'); }; - this.set_trash_state = function () { self.set_message_list_state('formatted_trash_data'); }; - this.set_draft_state = function () { self.set_message_list_state('formatted_drafts_data'); }; - this.set_tag_state = function () { self.set_message_list_state('formatted_tag_data'); }; + this.set_all_mail_state = function() { self.set_message_list_state('formatted_all_mail'); }; + this.set_combined_inbox_state = function() { self.set_message_list_state('formatted_combined_inbox'); }; + this.set_flagged_state = function() { self.set_message_list_state('formatted_flagged_data'); }; + this.set_unread_state = function() { self.set_message_list_state('formatted_unread_data'); }; + this.set_search_state = function() { self.set_message_list_state('formatted_search_data'); }; + this.set_junk_state = function() { self.set_message_list_state('formatted_junk_data'); }; + this.set_snoozed_state = function() { self.set_message_list_state('formatted_snoozed_data'); }; + this.set_trash_state = function() { self.set_message_list_state('formatted_trash_data'); }; + this.set_draft_state = function() { self.set_message_list_state('formatted_drafts_data'); }; + this.set_tag_state = function() { self.set_message_list_state('formatted_tag_data'); }; }; /* folder list */ var Hm_Folders = { expand_after_update: false, unread_counts: {}, - observer: false, + observer : false, - save_folder_list: function () { + save_folder_list: function() { Hm_Utils.save_to_local_storage('formatted_folder_list', $('.folder_list').html()); }, - load_unread_counts: function () { + load_unread_counts: function() { var res = Hm_Utils.json_decode(Hm_Utils.get_from_local_storage('unread_counts')); if (!res) { Hm_Folders.unread_counts = {}; @@ -1088,9 +1097,9 @@ var Hm_Folders = { } }, - update_unread_counts: function (folder) { + update_unread_counts: function(folder) { if (folder) { - $('.unread_' + folder).html(' ' + Hm_Folders.unread_counts[folder] + ' '); + $('.unread_'+folder).html(' '+Hm_Folders.unread_counts[folder]+' '); } else { var name; @@ -1102,17 +1111,17 @@ var Hm_Folders = { if (count) { if (getListPathParam() == name && getPageNameParam() == 'message_list') { var title = document.title.replace(/^\[\d+\]/, ''); - document.title = '[' + count + '] ' + title; + document.title = '['+count+'] '+title; /* HERE */ } - $('.unread_' + name).html(' ' + count + ' '); + $('.unread_'+name).html(' '+count+' '); } } } Hm_Utils.save_to_local_storage('unread_counts', Hm_Utils.json_encode(Hm_Folders.unread_counts)); }, - open_folder_list: function () { + open_folder_list: function() { $('.folder_list').show(); $('.folder_toggle').toggle(); if (hm_mobile()) { @@ -1125,7 +1134,7 @@ var Hm_Folders = { return false; }, - toggle_folder_list: function () { + toggle_folder_list: function() { if ($('.folder_list').css('display') == 'none') { Hm_Folders.open_folder_list(); } @@ -1134,7 +1143,7 @@ var Hm_Folders = { } }, - hide_folder_list: function (forget) { + hide_folder_list: function(forget) { $('.folder_list').hide(); $('.folder_toggle').show(); if (!forget) { @@ -1145,7 +1154,7 @@ var Hm_Folders = { return false; }, - reload_folders: function (force, expand_after_update) { + reload_folders: function(force, expand_after_update) { if (document.cookie.indexOf('hm_reload_folders=1') > -1 || force) { Hm_Folders.expand_after_update = expand_after_update; var ui_state = Hm_Utils.preserve_local_settings(); @@ -1157,28 +1166,28 @@ var Hm_Folders = { return false; }, - sort_list: function (class_name, exclude_name, last_name) { - var folder = $('.' + class_name + ' ul'); + sort_list: function(class_name, exclude_name, last_name) { + var folder = $('.'+class_name+' ul'); var listitems; if (exclude_name) { - listitems = $('li:not(.' + exclude_name + ')', folder); + listitems = $('li:not(.'+exclude_name+')', folder); } else { listitems = $('li', folder); } - listitems = listitems.sort(function (a, b) { + listitems = listitems.sort(function(a, b) { if (last_name && ($(a).attr('class') == last_name || $(b).attr('class') == last_name)) { return false; } if ($(b).text().toUpperCase() == 'ALL') { return true; } - return $(a).text().toUpperCase().localeCompare($(b).text().toUpperCase()); + return $(a).text().toUpperCase().localeCompare($(b).text().toUpperCase()); }); - $.each(listitems, function (_, itm) { folder.append(itm); }); + $.each(listitems, function(_, itm) { folder.append(itm); }); }, - update_folder_list_display: function (res) { + update_folder_list_display: function(res) { $('.folder_list').html(res.formatted_folder_list); Hm_Folders.sort_list('email_folders', 'menu_email'); Hm_Folders.sort_list('feeds_folders', 'menu_feeds', 'feeds_add_new'); @@ -1194,9 +1203,9 @@ var Hm_Folders = { hl_save_link(); }, - update_folder_list: function () { + update_folder_list: function() { Hm_Ajax.request( - [{ 'name': 'hm_ajax_hook', 'value': 'ajax_hm_folders' }], + [{'name': 'hm_ajax_hook', 'value': 'ajax_hm_folders'}], Hm_Folders.update_folder_list_display, [], true @@ -1204,72 +1213,72 @@ var Hm_Folders = { return false; }, - folder_list_events: function () { - $('.imap_folder_link').on("click", function () { return expand_imap_folders($(this)); }); - $('.src_name').on('click', function () { + folder_list_events: function() { + $('.imap_folder_link').on("click", function() { return expand_imap_folders($(this)); }); + $('.src_name').on('click', function() { let transformValue = ''; if ($(this).attr('aria-expanded') == 'true') { transformValue = 'rotate(180deg)'; - + } else { transformValue = 'rotate(0deg)'; } - + $(this).find('i').css('transform', transformValue); }); - $('.update_message_list').on("click", function (e) { + $('.update_message_list').on("click", function(e) { var text = e.target.innerHTML; e.target.innerHTML = '
Loading...
'; Hm_Folders.update_folder_list(); - Hm_Ajax.add_callback_hook('hm_reload_folders', function () { + Hm_Ajax.add_callback_hook('hm_reload_folders', function() { e.target.innerHTML = text; }); return false; }); - $('.hide_folders').on("click", function () { return Hm_Folders.hide_folder_list(); }); - $('.logout_link').on("click", function (e) { return Hm_Utils.confirm_logout(); }); + $('.hide_folders').on("click", function() { return Hm_Folders.hide_folder_list(); }); + $('.logout_link').on("click", function(e) { return Hm_Utils.confirm_logout(); }); if (hm_search_terms()) { $('.search_terms').val(hm_search_terms()); } - $('.search_terms').on('search', function () { - Hm_Ajax.request([{ 'name': 'hm_ajax_hook', 'value': 'ajax_reset_search' }]); + $('.search_terms').on('search', function() { + Hm_Ajax.request([{'name': 'hm_ajax_hook', 'value': 'ajax_reset_search'}]); }); }, - hl_selected_menu: function () { + hl_selected_menu: function() { const page = getPageNameParam(); const path = getListPathParam(); - + $('.folder_list').find('*').removeClass('selected_menu'); if (path) { if (page == 'message_list' || page == 'message') { - $("[data-id='" + Hm_Utils.clean_selector(path) + "']").closest('li').addClass('selected_menu'); - $('.menu_' + Hm_Utils.clean_selector(path)).addClass('selected_menu'); + $("[data-id='"+Hm_Utils.clean_selector(path)+"']").closest('li').addClass('selected_menu'); + $('.menu_'+Hm_Utils.clean_selector(path)).addClass('selected_menu'); } else { - $('.menu_' + path).addClass('selected_menu'); + $('.menu_'+path).addClass('selected_menu'); } } else { - $('.menu_' + page).addClass('selected_menu'); + $('.menu_'+page).addClass('selected_menu'); } }, - listen_for_new_messages: function () { + listen_for_new_messages: function() { var target = $('.total_unread_count').get(0); if (!Hm_Folders.observer) { - Hm_Folders.observer = new MutationObserver(function (mutations) { + Hm_Folders.observer = new MutationObserver(function(mutations) { $('body').trigger('new_message'); }); } else { Hm_Folders.observer.disconnect(); } - Hm_Folders.observer.observe(target, { attributes: true, childList: true, characterData: true }); + Hm_Folders.observer.observe(target, {attributes: true, childList: true, characterData: true}); }, - load_from_local_storage: function () { + load_from_local_storage: function() { var folder_list = Hm_Utils.get_from_local_storage('formatted_folder_list'); if (folder_list) { $('.folder_list').html(folder_list); @@ -1288,21 +1297,21 @@ var Hm_Folders = { return false; }, - toggle_folders_event: function () { - $('.folder_toggle').on("click", function () { return Hm_Folders.open_folder_list(); }); + toggle_folders_event: function() { + $('.folder_toggle').on("click", function() { return Hm_Folders.open_folder_list(); }); } }; /* misc */ var Hm_Utils = { - get_url_page_number: function () { + get_url_page_number: function() { var index; var match_result; var page_number = 1; var params = location.search.substr(1).split('&'); var param_len = params.length; - for (index = 0; index < param_len; index++) { + for (index=0; index < param_len; index++) { match_result = params[index].match(/list_page=(\d+)/); if (match_result) { page_number = match_result[1]; @@ -1312,14 +1321,14 @@ var Hm_Utils = { return page_number; }, - get_from_global: function (name, def) { + get_from_global: function(name, def) { if (globals[name]) { return globals[name]; } return def; }, - preserve_local_settings: function () { + preserve_local_settings: function() { var i; var result = {}; var prefix = window.location.pathname.length; @@ -1332,33 +1341,33 @@ var Hm_Utils = { return result; }, - restore_local_settings: function (settings) { + restore_local_settings: function(settings) { var i; for (i in settings) { Hm_Utils.save_to_local_storage(i, settings[i]); } }, - reset_search_form: function () { + reset_search_form: function() { Hm_Utils.save_to_local_storage('formatted_search_data', ''); - Hm_Ajax.request([{ 'name': 'hm_ajax_hook', 'value': 'ajax_reset_search' }], - function (res) { window.location = '?page=search'; }, false, true); + Hm_Ajax.request([{'name': 'hm_ajax_hook', 'value': 'ajax_reset_search'}], + function(res) { window.location = '?page=search'; }, false, true); return false; }, - confirm_logout: function () { - if (!$('#unsaved_changes').length || $('#unsaved_changes').val() == 0) { + confirm_logout: function() { + if (! $('#unsaved_changes').length || $('#unsaved_changes').val() == 0) { document.getElementById('logout_without_saving').click(); } else { - var confirmLogoutModal = new bootstrap.Modal(document.getElementById('confirmLogoutModal'), { keyboard: true }) + var confirmLogoutModal = new bootstrap.Modal(document.getElementById('confirmLogoutModal'), {keyboard: true}) confirmLogoutModal.show(); $('.confirm_logout').show(); } return false; }, - get_path_type: function (path) { + get_path_type: function(path) { if (path.indexOf('_') != -1) { var path_parts = path.split('_'); return path_parts[0]; @@ -1366,7 +1375,7 @@ var Hm_Utils = { return false; }, - parse_folder_path: function (path, path_type) { + parse_folder_path: function(path, path_type) { if (!path_type) { path_type = Hm_Utils.get_path_type(path); } @@ -1397,7 +1406,7 @@ var Hm_Utils = { folder = parts[3]; } if (type && server_id) { - return { 'type': type, 'server_id': server_id, 'folder': folder, 'uid': uid }; + return {'type': type, 'server_id' : server_id, 'folder' : folder, 'uid': uid}; } } else if (path_type == 'feeds') { @@ -1410,13 +1419,13 @@ var Hm_Utils = { uid = parts[2]; } if (type && server_id) { - return { 'type': type, 'server_id': server_id, 'uid': uid }; + return {'type': type, 'server_id' : server_id, 'uid': uid}; } } return false; }, - toggle_section: function (class_name, force_on, force_off) { + toggle_section: function(class_name, force_on, force_off) { if ($(class_name).length) { if (force_off) { $(class_name).css('display', 'block'); @@ -1430,7 +1439,7 @@ var Hm_Utils = { return false; }, - toggle_page_section: function (class_name) { + toggle_page_section: function(class_name) { if ($(class_name).length) { $(class_name).toggle(); Hm_Utils.save_to_local_storage(class_name, $(class_name).css('display')); @@ -1438,12 +1447,12 @@ var Hm_Utils = { return false; }, - get_from_local_storage: function (key) { + get_from_local_storage: function(key) { var prefix = window.location.pathname; - key = prefix + key; + key = prefix+key; var res = false; if (hm_encrypt_local_storage()) { - res = Hm_Crypt.decrypt(sessionStorage.getItem(key)); + res = Hm_Crypt.decrypt(sessionStorage.getItem(key)); } else { res = sessionStorage.getItem(key); @@ -1451,7 +1460,7 @@ var Hm_Utils = { return res; }, - search_from_local_storage: function (pattern) { + search_from_local_storage: function(pattern) { const results = []; const key_pattern = new RegExp(pattern); for (let i = 0; i < sessionStorage.length; i++) { @@ -1464,14 +1473,14 @@ var Hm_Utils = { return results; }, - save_to_local_storage: function (key, val) { + save_to_local_storage: function(key, val) { var prefix = window.location.pathname; - key = prefix + key; + key = prefix+key; if (hm_encrypt_local_storage()) { val = Hm_Crypt.encrypt(val); } - if (Storage !== void (0)) { - try { sessionStorage.setItem(key, val); } catch (e) { + if (Storage !== void(0)) { + try { sessionStorage.setItem(key, val); } catch(e) { sessionStorage.clear(); sessionStorage.setItem(key, val); } @@ -1483,25 +1492,25 @@ var Hm_Utils = { return false; }, - clean_selector: function (str) { + clean_selector: function(str) { return str.replace(/(:|\.|\[|\]|\/)/g, "\\$1"); }, - toggle_long_headers: function () { + toggle_long_headers: function() { $('.long_header').toggle(); $('.all_headers').toggle(); $('.small_headers').toggle(); return false; }, - set_unsaved_changes: function (state) { + set_unsaved_changes: function(state) { $('#unsaved_changes').val(state); }, /** * Shows pending messages added with the add_sys_message method */ - show_sys_messages: function () { + show_sys_messages: function() { $('.sys_messages').removeClass('d-none'); }, @@ -1510,12 +1519,12 @@ var Hm_Utils = { * @param {*} msg : The alert message to display * @param {*} type : The type of message to display, depending on the type of boostrap5 alert (primary, secondary, success, danger, warning, info, light, dark ) */ - add_sys_message: function (msg, type = 'info') { + add_sys_message: function(msg, type = 'info') { if (!msg) { return; } const icon = type == 'success' ? 'bi-check-circle' : 'bi-exclamation-circle'; - $('.sys_messages').append(''); + $('.sys_messages').append(''); this.show_sys_messages(); }, @@ -1523,11 +1532,11 @@ var Hm_Utils = { $('.sys_messages').html(''); }, - cancel_logout_event: function () { - $('.cancel_logout').on("click", function () { $('.confirm_logout').hide(); return false; }); + cancel_logout_event: function() { + $('.cancel_logout').on("click", function() { $('.confirm_logout').hide(); return false; }); }, - json_encode: function (val) { + json_encode: function(val) { try { return JSON.stringify(val); } @@ -1536,7 +1545,7 @@ var Hm_Utils = { } }, - json_decode: function (val, original) { + json_decode: function(val, original) { try { return JSON.parse(val); } @@ -1548,22 +1557,25 @@ var Hm_Utils = { } }, - rows: function () { - return $('.message_table_body > tr').not('.inline_msg'); + rows: function(id) { + return this.tbody(id).find('tr').not('.inline_msg'); }, - tbody: function () { + tbody: function(id) { + if (id) { + return $('#'+id); + } return $('.message_table_body'); }, - html_entities: function (str) { + html_entities: function(str) { return String(str).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); }, - test_connection: function () { + test_connection: function() { $('.offline').hide(); Hm_Ajax.request( - [{ 'name': 'hm_ajax_hook', 'value': 'ajax_test' }], + [{'name': 'hm_ajax_hook', 'value': 'ajax_test'}], false, [], false, false, false); }, @@ -1574,7 +1586,7 @@ var Hm_Utils = { }, redirect: function (path) { - if (!path) { + if (! path) { path = window.location.href; } window.location.href = path; @@ -1586,7 +1598,7 @@ var Hm_Utils = { }; var Hm_Crypt = { - decrypt: function (ciphertext) { + decrypt: function(ciphertext) { try { ciphertext = atob(ciphertext); if (!ciphertext || ciphertext.length < 200) { @@ -1608,16 +1620,16 @@ var Hm_Crypt = { } var iv = forge.pkcs5.pbkdf2(secret, salt, 100, 16, digest); var decipher = forge.cipher.createDecipher('AES-CBC', key); - decipher.start({ iv: iv }); + decipher.start({iv: iv}); decipher.update(forge.util.createBuffer(payload, 'raw')); decipher.finish(); return forge.util.decodeUtf8(decipher.output.data); - } catch (e) { + } catch(e) { return false; } }, - encrypt: function (plaintext) { + encrypt: function(plaintext) { try { var secret = $('#hm_page_key').val(); var salt = forge.random.getBytesSync(128); @@ -1627,28 +1639,28 @@ var Hm_Crypt = { var iv = forge.pkcs5.pbkdf2(secret, salt, 100, 16, digest); var hmac = forge.hmac.create(); var cipher = forge.cipher.createCipher('AES-CBC', key); - cipher.start({ iv: iv }); + cipher.start({iv: iv}); cipher.update(forge.util.createBuffer(plaintext, 'utf8')); cipher.finish(); hmac.start(digest, hmac_key); hmac.update(cipher.output.data); - return btoa(salt + hmac.digest().data + cipher.output.data); - } catch (e) { + return btoa(salt+hmac.digest().data+cipher.output.data); + } catch(e) { return false; } }, } -var update_password = function (id) { - var pass = $('#update_pw_' + id).val(); +var update_password = function(id) { + var pass = $('#update_pw_'+id).val(); if (pass && pass.length) { Hm_Ajax.request( - [{ 'name': 'hm_ajax_hook', 'value': 'ajax_update_server_pw' }, - { 'name': 'password', 'value': pass }, - { 'name': 'server_pw_id', 'value': id }], - function (res) { + [{'name': 'hm_ajax_hook', 'value': 'ajax_update_server_pw'}, + {'name': 'password', 'value': pass}, + {'name': 'server_pw_id', 'value': id}], + function(res) { if (res.connect_status) { - $('.div_' + id).remove(); + $('.div_'+id).remove(); if ($('.home_password_dialogs div').length == 1) { $('.home_password_dialogs').remove(); } @@ -1658,13 +1670,13 @@ var update_password = function (id) { } } -var elog = function (val) { +var elog = function(val) { if (hm_debug()) { console.log(val); } }; -var hl_save_link = function () { +var hl_save_link = function() { if ($('.save_reminder').length) { $('.menu_save a').css('font-weight', 'bold'); } @@ -1673,7 +1685,7 @@ var hl_save_link = function () { } }; -var reset_default_value_checkbox = function () { +var reset_default_value_checkbox = function() { var checkbox = $(this).closest('.tooltip_restore').prev('input[type="checkbox"]'); var default_value = checkbox.data('default-value'); default_value = (default_value === 'true'); @@ -1681,27 +1693,27 @@ var reset_default_value_checkbox = function () { checkbox.prop('disabled', true); }; -var reset_default_timezone = function () { +var reset_default_timezone = function() { var hm_default_timezone = window.hm_default_timezone; $('#timezone').val(hm_default_timezone); } -var reset_default_value_select = function () { +var reset_default_value_select = function() { var dropdown = $(this).closest('.tooltip_restore').prev('select'); var default_value = dropdown.data('default-value'); dropdown.val(default_value); } -var reset_default_value_input = function () { +var reset_default_value_input = function() { var inputField = $(this).closest('.tooltip_restore').prev('input'); var default_value = inputField.data('default-value'); inputField.val(default_value); } -var decrease_servers = function (section) { +var decrease_servers = function(section) { const element = document.querySelector(`.server_count .${section}_server_count`); const value = parseInt(element.textContent); if (value > 0) { - element.innerHTML = value - 1; + element.innerHTML = value - 1; } if (value === 1) { @@ -1711,11 +1723,11 @@ var decrease_servers = function (section) { } }; -var err_msg = function (msg) { - return "ERR" + hm_trans(msg); +var err_msg = function(msg) { + return "ERR"+hm_trans(msg); }; -var hm_spinner = function (type = 'border', size = '') { +var hm_spinner = function(type = 'border', size = '') { return `
Loading... @@ -1723,7 +1735,7 @@ var hm_spinner = function (type = 'border', size = '') {
` }; -var fillImapData = function (details) { +var fillImapData = function(details) { $('#srv_setup_stepper_imap_address').val(details.server); $('#srv_setup_stepper_imap_port').val(details.port); $('#srv_setup_stepper_imap_server_id').val(details.id); @@ -1732,31 +1744,31 @@ var fillImapData = function (details) { $('#srv_setup_stepper_imap_sieve_host').val(details.sieve_config_host); $("#srv_setup_stepper_enable_sieve").trigger("click", false); $('#srv_setup_stepper_imap_sieve_mode_tls') - .prop('checked', details.tls) - .trigger('change'); + .prop('checked', details.tls) + .trigger('change'); } - if (details.tls) { + if(details.tls) { $("input[name='srv_setup_stepper_imap_tls'][value='true']").prop("checked", true); } else { $("input[name='srv_setup_stepper_imap_tls'][value='false']").prop("checked", true); } }; -var fillSmtpData = function (details) { +var fillSmtpData = function(details) { $('#srv_setup_stepper_smtp_server_id').val(details.id); $('#srv_setup_stepper_smtp_address').val(details.server); $('#srv_setup_stepper_smtp_port').val(details.port); }; -var fillJmapData = function (details) { +var fillJmapData = function(details) { $('#srv_setup_stepper_imap_server_id').val(details.id); $('#srv_setup_stepper_only_jmap').trigger('click'); $('#srv_setup_stepper_jmap_address').val(details.server); $('#srv_setup_stepper_imap_hide_from_c_page').prop("checked", details.hide); }; -var imap_smtp_edit_action = function (event) { +var imap_smtp_edit_action = function(event) { resetQuickSetupForm(); event.preventDefault(); Hm_Notices.hide(true); @@ -1774,7 +1786,7 @@ var imap_smtp_edit_action = function (event) { fillJmapData(details); } else if ($(this).data('type') == 'imap') { fillImapData(details); - var smtpDetails = $('[data-type="smtp"][data-id="' + details.name + '"]'); + var smtpDetails = $('[data-type="smtp"][data-id="'+details.name+'"]'); if (smtpDetails.length) { fillSmtpData(smtpDetails.data('server-details')); } else { @@ -1782,7 +1794,7 @@ var imap_smtp_edit_action = function (event) { } } else { fillSmtpData(details); - var imapDetails = $('[data-type="imap"][data-id="' + details.name + '"]'); + var imapDetails = $('[data-type="imap"][data-id="'+details.name+'"]'); if (imapDetails.length) { fillImapData(imapDetails.data('server-details')); } else { @@ -1791,8 +1803,7 @@ var imap_smtp_edit_action = function (event) { } }; - -var hasLeadingOrTrailingSpaces = function (str) { +var hasLeadingOrTrailingSpaces = function(str) { return str !== str.trim(); }; @@ -1800,8 +1811,8 @@ var hasLeadingOrTrailingSpaces = function (str) { var Hm_Message_List = new Message_List(); function sortHandlerForMessageListAndSearchPage() { - $('.combined_sort').on("change", function () { Hm_Message_List.sort($(this).val()); }); - $('.source_link').on("click", function () { $('.list_sources').toggle(); $('#list_controls_menu').hide(); return false; }); + $('.combined_sort').on("change", function() { Hm_Message_List.sort($(this).val()); }); + $('.source_link').on("click", function() { $('.list_sources').toggle(); $('#list_controls_menu').hide(); return false; }); if (getListPathParam() == 'unread' && $('.menu_unread > a').css('font-weight') == 'bold') { $('.menu_unread > a').css('font-weight', 'normal'); Hm_Folders.save_folder_list(); @@ -1809,7 +1820,7 @@ function sortHandlerForMessageListAndSearchPage() { } /* executes on onload, has access to other module code */ -$(function () { +$(function() { /* Remove disabled attribute to send checkbox */ $('.save_settings').on("click", function (e) { $('.general_setting input[type=checkbox]').each(function () { @@ -1818,7 +1829,7 @@ $(function () { } }); }) - $('.reset_factory_button').on('click', function () { return hm_delete_prompt(); }); + $('.reset_factory_button').on('click', function() { return hm_delete_prompt(); }); /* check for folder reload */ var reloaded = Hm_Folders.reload_folders(); @@ -1829,7 +1840,7 @@ $(function () { /* fire up the job scheduler */ Hm_Timer.fire(); - + /* show any pending notices */ Hm_Utils.show_sys_messages(); @@ -1840,18 +1851,18 @@ $(function () { hl_save_link(); if (hm_mailto()) { - try { navigator.registerProtocolHandler("mailto", "?page=compose&compose_to=%s", "Cypht"); } catch (e) { } + try { navigator.registerProtocolHandler("mailto", "?page=compose&compose_to=%s", "Cypht"); } catch(e) {} } if (hm_mobile()) { - swipe_event(document.body, function () { Hm_Folders.open_folder_list(); }, 'right'); - swipe_event(document.body, function () { Hm_Folders.hide_folder_list(); }, 'left'); + swipe_event(document.body, function() { Hm_Folders.open_folder_list(); }, 'right'); + swipe_event(document.body, function() { Hm_Folders.hide_folder_list(); }, 'left'); $('.list_controls.on_mobile').show(); $('.list_controls.no_mobile').hide(); } else { $('.list_controls.on_mobile').hide(); } - $('.offline').on("click", function () { Hm_Utils.test_connection(); }); + $('.offline').on("click", function() { Hm_Utils.test_connection(); }); if (hm_check_dirty_flag()) { $('form:not(.search_terms)').areYouSure(); @@ -1901,7 +1912,7 @@ function fixLtrInRtl() { return [] } - setTimeout(function () { + setTimeout(function(){ var elements = getElements() for (var index = 0; index < elements.length; index++) { if (isTextEnglish(elements[index].textContent)) { @@ -1919,7 +1930,7 @@ function listControlsMenu() { $('.list_sources').hide(); } -var resetStepperButtons = function () { +var resetStepperButtons = function() { $('.step_config-actions button').removeAttr('disabled'); $('#stepper-action-finish').text($('#stepper-action-finish').text().slice(0, -3)); }; @@ -1956,14 +1967,14 @@ function submitSmtpImapServer() { { name: 'srv_setup_stepper_smtp_server_id', value: $('#srv_setup_stepper_smtp_server_id').val() } ]; - Hm_Ajax.request(requestData, function (res) { + Hm_Ajax.request(requestData, function(res) { resetStepperButtons(); if (res.just_saved_credentials) { if (res.imap_server_id) { Hm_Ajax.request( - [{ 'name': 'hm_ajax_hook', 'value': 'ajax_imap_accept_special_folders' }, - { 'name': 'imap_server_id', value: res.imap_server_id }, - { 'name': 'imap_service_name', value: res.imap_service_name }], + [{'name': 'hm_ajax_hook', 'value': 'ajax_imap_accept_special_folders'}, + {'name': 'imap_server_id', value: res.imap_server_id}, + {'name': 'imap_service_name', value: res.imap_service_name}], function () { resetQuickSetupForm(); Hm_Utils.redirect(); @@ -2008,23 +2019,23 @@ function resetQuickSetupForm() { } function handleCreateProfileCheckboxChange(checkbox) { - if (checkbox.checked) { - $('#srv_setup_stepper_profile_bloc').show(); - } else { - $('#srv_setup_stepper_profile_bloc').hide(); + if(checkbox.checked) { + $(checkbox).closest('.form-check').next().show(); + }else{ + $(checkbox).closest('.form-check').next().hide(); } } -function handleSieveStatusChange(checkbox) { - if (checkbox.checked) { +function handleSieveStatusChange (checkbox) { + if(checkbox.checked) { $('#srv_setup_stepper_imap_sieve_host_bloc').show(); - } else { + }else{ $('#srv_setup_stepper_imap_sieve_host_bloc').hide(); } } function handleSmtpImapCheckboxChange(checkbox) { if (checkbox.id === 'srv_setup_stepper_is_receiver') { - if (checkbox.checked) { + if(checkbox.checked) { $('#step_config-imap_bloc').show(); $('#step_config_combined_view').show(); $('#srv_setup_stepper_jmap_select_box').show(); @@ -2039,15 +2050,15 @@ function handleSmtpImapCheckboxChange(checkbox) { if (checkbox.id === 'srv_setup_stepper_is_sender') { console.log("checkbox.checked", checkbox.checked) - if (checkbox.checked) $('#step_config-smtp_bloc').show(); + if(checkbox.checked) $('#step_config-smtp_bloc').show(); else $('#step_config-smtp_bloc').hide(); } if ($('#srv_setup_stepper_is_sender').prop('checked') && $('#srv_setup_stepper_is_receiver').prop('checked')) { $('#srv_setup_stepper_profile_bloc').show(); $('#srv_setup_stepper_profile_checkbox_bloc').show(); - - } else if (!$('#srv_setup_stepper_is_sender').prop('checked') || !$('#srv_setup_stepper_is_receiver').prop('checked')) { + + } else if(! $('#srv_setup_stepper_is_sender').prop('checked') || ! $('#srv_setup_stepper_is_receiver').prop('checked')) { $('#srv_setup_stepper_profile_bloc').hide(); $('#srv_setup_stepper_profile_checkbox_bloc').hide(); } @@ -2057,7 +2068,7 @@ function handleJmapCheckboxChange(checkbox) { if (checkbox.checked) { $('#step_config-jmap_bloc').show(); $('#step_config-imap_bloc').hide(); - if (!$('#srv_setup_stepper_enable_sieve').prop('checked')) { + if (! $('#srv_setup_stepper_enable_sieve').prop('checked')) { $('#srv_setup_stepper_imap_sieve_host_bloc').hide(); } } else { @@ -2068,9 +2079,9 @@ function handleJmapCheckboxChange(checkbox) { function handleProviderChange(select) { let providerKey = select.value; - if (providerKey) { + if(providerKey) { getServiceDetails(providerKey); - } else { + }else{ $("#srv_setup_stepper_smtp_address").val(''); $("#srv_setup_stepper_smtp_port").val(465); $("#srv_setup_stepper_imap_address").val(''); @@ -2084,13 +2095,13 @@ function setDefaultReplyTo(val) { } } function display_config_step(stepNumber) { - if (stepNumber === 2) { + if(stepNumber === 2) { var isValid = true; - [{ key: 'srv_setup_stepper_profile_name', value: $('#srv_setup_stepper_profile_name').val() }, - { key: 'srv_setup_stepper_email', value: $('#srv_setup_stepper_email').val() }, - { key: 'srv_setup_stepper_password', value: $('#srv_setup_stepper_password').val() }].forEach((item) => { + [ {key: 'srv_setup_stepper_profile_name', value: $('#srv_setup_stepper_profile_name').val()}, + {key: 'srv_setup_stepper_email', value: $('#srv_setup_stepper_email').val()}, + {key: 'srv_setup_stepper_password', value: $('#srv_setup_stepper_password').val()}].forEach((item) => { if (!item.value) { if (item.key == 'srv_setup_stepper_password' && ($('#srv_setup_stepper_imap_server_id').val() || $('#srv_setup_stepper_smtp_server_id').val())) { $(`#${item.key}-error`).text(''); @@ -2098,7 +2109,7 @@ function display_config_step(stepNumber) { $(`#${item.key}-error`).text('Required'); isValid = false; } - + } else { $(`#${item.key}-error`).text(''); } @@ -2113,46 +2124,46 @@ function display_config_step(stepNumber) { setDefaultReplyTo($('#srv_setup_stepper_email').val()); } - if (stepNumber === 3) { + if(stepNumber === 3) { var requiredFields = []; var isValid = true; - if (!$('#srv_setup_stepper_is_sender').is(':checked') && !$('#srv_setup_stepper_is_receiver').is(':checked')) { + if(!$('#srv_setup_stepper_is_sender').is(':checked') && !$('#srv_setup_stepper_is_receiver').is(':checked')){ $('#srv_setup_stepper_serve_type-error').text('Required'); return; } - if ($('#srv_setup_stepper_is_sender').is(':checked') && + if($('#srv_setup_stepper_is_sender').is(':checked') && $('#srv_setup_stepper_is_receiver').is(':checked') && - $('#srv_setup_stepper_only_jmap').is(':checked')) { + $('#srv_setup_stepper_only_jmap').is(':checked')){ requiredFields.push( - { key: 'srv_setup_stepper_jmap_address', value: $('#srv_setup_stepper_jmap_address').val() }, + {key: 'srv_setup_stepper_jmap_address', value: $('#srv_setup_stepper_jmap_address').val()}, ) - } else { - if ($('#srv_setup_stepper_is_sender').is(':checked')) { + }else { + if($('#srv_setup_stepper_is_sender').is(':checked')){ requiredFields.push( - { key: 'srv_setup_stepper_smtp_address', value: $('#srv_setup_stepper_smtp_address').val() }, - { key: 'srv_setup_stepper_smtp_port', value: $('#srv_setup_stepper_smtp_port').val() }, + {key: 'srv_setup_stepper_smtp_address', value: $('#srv_setup_stepper_smtp_address').val()}, + {key: 'srv_setup_stepper_smtp_port', value: $('#srv_setup_stepper_smtp_port').val()}, ) } - if ($('#srv_setup_stepper_is_receiver').is(':checked')) { + if($('#srv_setup_stepper_is_receiver').is(':checked')) { requiredFields.push( - { key: 'srv_setup_stepper_imap_address', value: $('#srv_setup_stepper_imap_address').val() }, - { key: 'srv_setup_stepper_imap_port', value: $('#srv_setup_stepper_imap_port').val() }, + {key: 'srv_setup_stepper_imap_address', value: $('#srv_setup_stepper_imap_address').val()}, + {key: 'srv_setup_stepper_imap_port', value: $('#srv_setup_stepper_imap_port').val()}, ) } } - if ($('#srv_setup_stepper_enable_sieve').is(':checked')) { + if($('#srv_setup_stepper_enable_sieve').is(':checked')) { requiredFields.push( - { key: 'srv_setup_stepper_imap_sieve_host', value: $('#srv_setup_stepper_imap_sieve_host').val() }, - { key: 'srv_setup_stepper_imap_sieve_mode_tls', value: $('#srv_setup_stepper_imap_sieve_mode_tls').val() }, + {key: 'srv_setup_stepper_imap_sieve_host', value: $('#srv_setup_stepper_imap_sieve_host').val()}, + {key: 'srv_setup_stepper_imap_sieve_mode_tls', value: $('#srv_setup_stepper_imap_sieve_mode_tls').val()}, ) } requiredFields.forEach((item) => { - if (!item.value) { + if(!item.value) { $(`#${item.key}-error`).text('Required'); isValid = false; } @@ -2160,7 +2171,7 @@ function display_config_step(stepNumber) { }) - if (!isValid) return + if(!isValid) return submitSmtpImapServer(); return @@ -2176,32 +2187,32 @@ function display_config_step(stepNumber) { if (selectedStep) { selectedStep.style.display = 'block'; - if (stepNumber === 0) $('.srv_setup_stepper_btn').show(); + if(stepNumber === 0) $('.srv_setup_stepper_btn').show(); } } -function getServiceDetails(providerKey) { - if (providerKey) { +function getServiceDetails(providerKey){ + if(providerKey) { $("#srv_setup_stepper_provider").val(providerKey); Hm_Ajax.request( [ - { 'name': 'hm_ajax_hook', 'value': 'ajax_get_nux_service_details' }, - { 'name': 'nux_service', 'value': providerKey },], - function (res) { - if (res.service_details) { + {'name': 'hm_ajax_hook', 'value': 'ajax_get_nux_service_details'}, + {'name': 'nux_service', 'value': providerKey},], + function(res) { + if(res.service_details){ let serverConfig = JSON.parse(res.service_details) $("#srv_setup_stepper_smtp_address").val(serverConfig.smtp.server); $("#srv_setup_stepper_smtp_port").val(serverConfig.smtp.port); - if (serverConfig.smtp.tls) $("input[name='srv_setup_stepper_smtp_tls'][value='true']").prop("checked", true); + if(serverConfig.smtp.tls)$("input[name='srv_setup_stepper_smtp_tls'][value='true']").prop("checked", true); else $("input[name='srv_setup_stepper_smtp_tls'][value='false']").prop("checked", true); $("#srv_setup_stepper_imap_address").val(serverConfig.server); $("#srv_setup_stepper_imap_port").val(serverConfig.port); - if (serverConfig.tls) $("input[name='srv_setup_stepper_imap_tls'][value='true']").prop("checked", true); + if(serverConfig.tls)$("input[name='srv_setup_stepper_imap_tls'][value='true']").prop("checked", true); else $("input[name='srv_setup_stepper_imap_tls'][value='false']").prop("checked", true); if (serverConfig.hasOwnProperty('sieve')) { @@ -2253,7 +2264,7 @@ function getEmailProviderKey(email) { const emailParts = email.split("@"); - if (emailParts.length !== 2) return ""; + if(emailParts.length !== 2) return ""; const provider = emailParts[1].toLowerCase(); @@ -2382,14 +2393,14 @@ const handleExternalResources = (inline) => { }; const observeMessageTextMutationAndHandleExternalResources = (inline) => { - const message = document.querySelector('.msg_text'); + const message = document.querySelector('.msg_text'); if (message) { new MutationObserver(function (mutations) { mutations.forEach(function (mutation) { if (mutation.addedNodes.length > 0) { mutation.addedNodes.forEach(function (node) { if (node.classList.contains('msg_text_inner')) { - handleExternalResources(inline); + handleExternalResources(inline); } }); } diff --git a/modules/imap/functions.php b/modules/imap/functions.php index bf49e28b63..ad08c0566c 100644 --- a/modules/imap/functions.php +++ b/modules/imap/functions.php @@ -1608,6 +1608,80 @@ function connect_to_imap_server($address, $name, $port, $user, $pass, $tls, $ima } } +function getCombinedMessagesLists($sources, $cache, $searchTerms, $listPage, $limit, $offsets = [], $defaultOffset = 0, $filter = 'ALL') { + $totalMessages = 0; + $offset = $defaultOffset; + $messagesLists = []; + $status = []; + foreach ($sources as $index => $dataSource) { + + if ($offsets && $listPage > 1) { + if (isset($offsets[$index]) && (int) $offsets[$index] > 0) { + $offset = (int) $offsets[$index] * ($listPage - 1); + } + } + + $mailbox = Hm_IMAP_List::get_connected_mailbox($dataSource['id'], $cache); + if ($mailbox && $mailbox->authed()) { + $folder = $dataSource['folder']; + $state = $mailbox->get_connection()->get_mailbox_status(hex2bin($folder)); + $status['imap_'.$dataSource['id'].'_'.$folder] = $state; + + $uids = $mailbox->search(hex2bin($folder), $filter, false, $searchTerms); + $total = count($uids); + // most recent messages at the top + $uids = array_reverse($uids); + $uids = array_slice($uids, $offset, $limit); + + $headers = $mailbox->get_message_list(hex2bin($folder), $uids); + $messages = []; + foreach ($uids as $uid) { + if (isset($headers[$uid])) { + $messages[] = $headers[$uid]; + } + } + + $messagesLists[] = array_map(function($msg) use ($dataSource, $folder) { + $msg['server_id'] = $dataSource['id']; + $msg['server_name'] = $dataSource['name']; + $msg['folder'] = $folder; + return $msg; + }, $messages); + $totalMessages += $total; + } + } + + return ['lists' => $messagesLists, 'total' => $totalMessages, 'status' => $status]; +} + +function flattenMessagesLists($messagesLists, $listSize) { + $endList = []; + $sizesTaken = []; + + $max = $listSize * count($messagesLists); + + while (count($endList) < $listSize * count($messagesLists) && count(array_filter($messagesLists, fn ($list) => count($list) > 0)) > 0) { + foreach ($messagesLists as $index => $list) { + if (count($list) > 0) { + $part = array_slice($list, 0, $listSize); + $endList = array_merge($endList, $part); + $messagesLists[$index] = array_slice($list, $listSize); + $sizesTaken[$index] = isset($sizesTaken[$index]) ? $sizesTaken[$index] + count($part) : count($part); + $totalTakens = array_sum(array_values($sizesTaken)); + if ($totalTakens > $max) { + $sizesTaken[$index] = $sizesTaken[$index] - ($totalTakens - $max); + } + } else { + $sizesTaken[$index] = isset($sizesTaken[$index]) ? $sizesTaken[$index] : 0; + } + } + } + + $endList = array_slice($endList, 0, $max); + + return ['messages' => $endList, 'offsets' => $sizesTaken]; +} + if (!hm_exists('save_sent_msg')) { function save_sent_msg($handler, $imap_id, $imap, $imap_details, $msg, $msg_id, $show_errors = true) { $specials = get_special_folders($handler, $imap_id); diff --git a/modules/imap/handler_modules.php b/modules/imap/handler_modules.php index a59db42fe1..c311e02b21 100644 --- a/modules/imap/handler_modules.php +++ b/modules/imap/handler_modules.php @@ -332,41 +332,9 @@ public function process() { $sent_folder = false; $mailbox = Hm_IMAP_List::get_connected_mailbox($imap_id, $this->cache); if ($mailbox && $mailbox->authed()) { - $specials = get_special_folders($this, $imap_id); - if (array_key_exists('sent', $specials) && $specials['sent']) { - $sent_folder = $specials['sent']; - } - - if (!$sent_folder) { - $auto_sent = $mailbox->get_special_use_mailboxes('sent'); - if (!array_key_exists('sent', $auto_sent)) { - return; - } - $sent_folder = $auto_sent['sent']; - } - if (!$sent_folder) { - Hm_Debug::add(sprintf("Unable to save sent message, no sent folder for IMAP %s", $imap_details['server'])); - } - if ($sent_folder) { - Hm_Debug::add(sprintf("Attempting to save sent message for IMAP server %s in folder %s", $imap_details['server'], $sent_folder)); - if (! $mailbox->store_message($sent_folder, $msg)) { - Hm_Msgs::add('ERRAn error occurred saving the sent message'); - } - $uid = null; - $mailbox_page = $mailbox->get_messages($sent_folder, 'ARRIVAL', true, 'ALL', 0, 10); - foreach ($mailbox_page[1] as $mail) { - $msg_header = $mailbox->get_message_headers($sent_folder, $mail['uid']); - if ($msg_header['Message-Id'] === $mime->get_headers()['Message-Id']) { - $uid = $mail['uid']; - break; - } - } - } - $uid = save_sent_msg($this, $imap_id, $imap, $imap_details, $msg, $mime->get_headers()['Message-Id']); - if ($uid) { - if ($uid && $this->user_config->get('review_sent_email_setting', false)) { - $this->out('redirect_url', '?page=message&uid='.$uid.'&list_path=imap_'.$imap_id.'_'.bin2hex($sent_folder)); - } + $uid = save_sent_msg($this, $imap_id, $mailbox, $imap_details, $msg, $mime->get_headers()['Message-Id']); + if ($uid && $this->user_config->get('review_sent_email_setting', false)) { + $this->out('redirect_url', '?page=message&uid='.$uid.'&list_path=imap_'.$imap_id.'_'.bin2hex($sent_folder)); } } } diff --git a/modules/imap/hm-imap.php b/modules/imap/hm-imap.php index 07434df5e1..676a58adbd 100644 --- a/modules/imap/hm-imap.php +++ b/modules/imap/hm-imap.php @@ -1074,6 +1074,7 @@ public function get_message_list($uids, $raw=false, $include_content_body = fals 'google_thread_id' => $google_thread_id, 'google_labels' => $google_labels, 'list_archive' => $list_archive, 'references' => $references, 'message_id' => $message_id, 'x_auto_bcc' => $x_auto_bcc, 'x_snoozed' => $x_snoozed, 'x_schedule' => $x_schedule, 'x_profile_id' => $x_profile_id, 'x_delivery' => $x_delivery); + $headers[$uid]['preview_msg'] = $flds['body'] != "content_body" ? $flds['body'] : ""; if ($raw) { $headers[$uid] = array_map('trim', $headers[$uid]); diff --git a/modules/imap/js_modules/route_handlers.js b/modules/imap/js_modules/route_handlers.js index f24a08f0aa..9dd9c1f424 100644 --- a/modules/imap/js_modules/route_handlers.js +++ b/modules/imap/js_modules/route_handlers.js @@ -11,7 +11,7 @@ function applyImapMessageListPageHandlers(routeParams) { if (window.inlineMessageMessageListAndSearchPageHandler) inlineMessageMessageListAndSearchPageHandler(routeParams); if (window.wpMessageListPageHandler) wpMessageListPageHandler(routeParams); - return async function () { + return async function() { const [refreshIntervalId, abortController] = await setupPageResult; abortController.abort(); clearInterval(refreshIntervalId); @@ -37,4 +37,4 @@ function applyImapMessageContentPageHandlers(routeParams) { if (window.pgpMessageContentPageHandler) pgpMessageContentPageHandler(); if (window.wpMessageContentPageHandler) wpMessageContentPageHandler(routeParams); -} +} \ No newline at end of file diff --git a/modules/imap/site.js b/modules/imap/site.js index 4797e22871..7abc57c206 100644 --- a/modules/imap/site.js +++ b/modules/imap/site.js @@ -1224,7 +1224,6 @@ $(function() { setTimeout(search_selected_for_imap, 100); }); - if (hm_is_logged()) { imap_unsnooze_messages(); setInterval(imap_unsnooze_messages, 60000); diff --git a/modules/smtp/js_modules/route_handlers.js b/modules/smtp/js_modules/route_handlers.js index d7715baeec..f6f08b75ea 100644 --- a/modules/smtp/js_modules/route_handlers.js +++ b/modules/smtp/js_modules/route_handlers.js @@ -17,13 +17,13 @@ function applySmtpComposePageHandlers() { } var interval = Hm_Utils.get_from_global('compose_save_interval', 30); - Hm_Timer.add_job(function () { save_compose_state(); }, interval, true); - $('.draft_title').on("click", function () { $('.draft_list').toggle(); }); - $('.toggle_recipients').on("click", function () { return toggle_recip_flds(); }); + Hm_Timer.add_job(function() { save_compose_state(); }, interval, true); + $('.draft_title').on("click", function() { $('.draft_list').toggle(); }); + $('.toggle_recipients').on("click", function() { return toggle_recip_flds(); }); $('.smtp_reset').on("click", reset_smtp_form); - $('.delete_draft').on("click", function () { smtp_delete_draft($(this).data('id')); }); - $('.smtp_save').on("click", function () { save_compose_state(false, true); }); - $('.smtp_send_archive').on("click", function () { send_archive(false, true); }); + $('.delete_draft').on("click", function() { smtp_delete_draft($(this).data('id')); }); + $('.smtp_save').on("click", function() { save_compose_state(false, true); }); + $('.smtp_send_archive').on("click", function() { send_archive(false, true); }); const modal = new Hm_Modal({ modalId: 'emptySubjectBodyModal', @@ -95,7 +95,7 @@ function applySmtpComposePageHandlers() { ======================================== */ function showModal() { - if (!modal.modalContent.html()) { + if (! modal.modalContent.html()) { modal.addFooterBtn(hm_trans('Send anyway'), 'btn-warning', handleSendAnyway); if (showBtnSendAnywayDontWarnFuture) { modal.addFooterBtn(hm_trans("Send anyway and don't warn in the future"), 'btn-warning', handleSendAnywayAndDontWarnMe); @@ -109,22 +109,24 @@ function applySmtpComposePageHandlers() { return new Promise((resolve) => { const checkValue = () => { if ($(selector).val() !== targetValue) { - resolve(); + resolve(); } else { - setTimeout(checkValue, 100); + setTimeout(checkValue, 100); } }; - checkValue(); + checkValue(); }); } async function handleSendAnyway() { if ($('.compose_draft_id').val() == '0') { - Hm_Notices.show([hm_trans('Please wait, sending message...')]); - await waitForValueChange('.compose_draft_id', '0'); + Hm_Notices.show([hm_trans('Please wait, sending message...')]); + await waitForValueChange('.compose_draft_id', '0'); } + + if (handleMissingAttachment()) { if (isScheduledMode == null) { document.getElementsByClassName("smtp_send")[0].click(); @@ -172,53 +174,53 @@ function applySmtpComposePageHandlers() { return true; } }); - $('.compose_form').on('submit', function () { + $('.compose_form').on('submit', function() { process_compose_form(); }); if ($('.compose_cc').val() || $('.compose_bcc').val()) { toggle_recip_flds(); } if (window.location.href.search('&reply=1') !== -1 || window.location.href.search('&reply_all=1') !== -1) { - replace_cursor_positon($('textarea[name="compose_body"]')); + replace_cursor_positon ($('textarea[name="compose_body"]')); } if (window.location.href.search('&forward=1') !== -1) { - setTimeout(function () { + setTimeout(function() { save_compose_state(); }, 100); } if ($('.sys_messages').text() != 'Message Sent') { get_smtp_profile($('.compose_server').val()); } - $('.compose_server').on('change', function () { + $('.compose_server').on('change', function() { get_smtp_profile($('.compose_server').val()); }); - if ($('.compose_attach_button').attr('disabled') == 'disabled') { + if($('.compose_attach_button').attr('disabled') == 'disabled'){ check_attachment_dir_access(); }; $('.compose_container').attr('ondrop', 'move_recipient_to_section(event)').attr('ondragover', 'allow_drop(event)'); - $('.compose_to, .compose_cc, .compose_bcc').on('keypress', function (e) { - if (e.which == 13) { + $('.compose_to, .compose_cc, .compose_bcc').on('keypress', function(e) { + if(e.which == 13) { e.preventDefault(); text_to_bubbles(this); } }); - $('.compose_to, .compose_cc, .compose_bcc').on('blur', function (e) { + $('.compose_to, .compose_cc, .compose_bcc').on('blur', function(e) { e.preventDefault(); text_to_bubbles(this); }); - $('.compose_subject, .compose_body, .compose_server, .smtp_send_placeholder, .smtp_send_archive').on('focus', function (e) { - $('.compose_to, .compose_cc, .compose_bcc').each(function () { + $('.compose_subject, .compose_body, .compose_server, .smtp_send_placeholder, .smtp_send_archive').on('focus', function(e) { + $('.compose_to, .compose_cc, .compose_bcc').each(function() { bubbles_to_text(this); }); }); - $('.compose_to, .compose_cc, .compose_bcc').on('focus', function (e) { + $('.compose_to, .compose_cc, .compose_bcc').on('focus', function(e) { text_to_bubbles(this); }); - $('.compose_container').on('click', function () { + $('.compose_container').on('click', function() { $(this).find('input').focus(); }); - $(document).on('click', '.bubble_close', function (e) { + $(document).on('click', '.bubble_close', function(e) { e.stopPropagation(); $(".bubble_dropdown-content").remove(); $(this).parent().remove(); @@ -232,7 +234,7 @@ function applySmtpComposePageHandlers() { var excludedEmail = null; const excludeEmail = function () { - var newRecipients = recipientsInput.val().split(',').filter(function (email) { + var newRecipients = recipientsInput.val().split(',').filter(function(email) { if (email.includes(selectedEmail)) { excludedEmail = email; return false; @@ -244,7 +246,7 @@ function applySmtpComposePageHandlers() { if (recipientsInput.val().includes(selectedEmail)) { excludeEmail(); - $(document).on('change', '#compose_smtp_id', function () { + $(document).on('change', '#compose_smtp_id', function() { if ($(this).val() !== selectedVal) { if (!recipientsInput.val().includes(selectedEmail)) { recipientsInput.val(recipientsInput.val() + ', ' + excludedEmail); @@ -255,12 +257,6 @@ function applySmtpComposePageHandlers() { }); } - - $('.compose_to').on('keyup', function (e) { autocomplete_contact(e, '.compose_to', '#to_contacts'); }); - $('.compose_cc').on('keyup', function (e) { autocomplete_contact(e, '.compose_cc', '#cc_contacts'); }); - $('.compose_bcc').on('keyup', function (e) { autocomplete_contact(e, '.compose_bcc', '#bcc_contacts'); }); - $('.compose_to').focus(); - if (window.pgpComposePageHandler) pgpComposePageHandler(); if (window.profilesComposePageHandler) profilesComposePageHandler(); } \ No newline at end of file diff --git a/modules/smtp/modules.php b/modules/smtp/modules.php index b31ae820e4..a37b4ca3ca 100644 --- a/modules/smtp/modules.php +++ b/modules/smtp/modules.php @@ -113,8 +113,6 @@ public function process() { } } - - /** * @subpackage smtp/handler */ @@ -210,7 +208,6 @@ public function process() { } } - /** * @subpackage smtp/handler */ @@ -321,8 +318,8 @@ public function process() { $draft_notice = array_key_exists('draft_notice', $this->request->post) ? $this->request->post['draft_notice'] : false; $uploaded_files = array_key_exists('uploaded_files', $this->request->post) ? $this->request->post['uploaded_files'] : false; $delivery_receipt = array_key_exists('compose_delivery_receipt', $this->request->post) ? $this->request->post['compose_delivery_receipt'] : false; - $schedule = array_key_exists('schedule', $this->request->post) ? $this->request->post['schedule'] : ''; + $schedule = array_key_exists('schedule', $this->request->post) ? $this->request->post['schedule'] : ''; if ($schedule == "undefined") { $schedule = ""; } @@ -905,97 +902,6 @@ protected function output() { } } -/** - * Send scheduled messages - * @subpackage smtp/handler - */ -class Hm_Handler_send_scheduled_messages extends Hm_Handler_Module { - /** - * Send delayed messages - * This should use cron - */ - public function process() { - if (!($this->module_is_supported('imap') || $this->module_is_supported('profiles'))) { - return; - } - - $servers = Hm_IMAP_List::dump(); - $scheduled_msg_count = 0; - - foreach (array_keys($servers) as $server_id) { - $cache = Hm_IMAP_List::get_cache($this->cache, $server_id); - $imap = Hm_IMAP_List::connect($server_id, $cache); - - if (imap_authed($imap)) { - $folder = 'Scheduled'; - $ret = $imap->get_mailbox_page($folder, 'DATE', false, 'ALL'); - - foreach ($ret[1] as $msg) { - $msg_headers = $imap->get_message_headers($msg['uid']); - - try { - if (!empty($msg_headers['X-Schedule'])) { - $scheduled_msg_count++; - } else { - continue; - } - - if (send_scheduled_message($this, $imap, $msg, $server_id)) { - $scheduled_msg_count--; - } - } catch (Exception $e) { - Hm_Debug::add(sprintf('ERRCannot send message: %s', $msg_headers['subject'])); - } - } - } - } - - $this->out('scheduled_msg_count', $scheduled_msg_count); -}} - -/** - * Changes the schedule of the message - * @subpackage smtp/handler - */ -class Hm_Handler_re_schedule_message_sending extends Hm_Handler_Module { - public function process() { - if (!($this->module_is_supported('imap') || $this->module_is_supported('profiles'))) { - return; - } - list($success, $form) = $this->process_form(array('schedule_date', 'scheduled_msg_ids')); - if (!$success) { - return; - } - $scheduled_msg_count = 0; - $new_schedule_date = $form['schedule_date']; - if ($form['schedule_date'] != 'now') { - $new_schedule_date = get_scheduled_date($form['schedule_date']); - } - $ids = explode(',', $form['scheduled_msg_ids']); - foreach ($ids as $msg_part) { - list($imap_server_id, $msg_id, $folder) = explode('_', $msg_part); - $cache = Hm_IMAP_List::get_cache($this->cache, $imap_server_id); - $imap = Hm_IMAP_List::connect($imap_server_id, $cache); - if (imap_authed($imap)) { - $folder = hex2bin($folder); - if (reschedule_message_sending($this, $imap, $msg_id, $folder, $new_schedule_date, $imap_server_id)) { - $scheduled_msg_count++; - } - } - } - $this->out('scheduled_msg_count', $scheduled_msg_count); - if ($scheduled_msg_count == count($ids)) { - $msg = 'Operation successful'; - } elseif ($scheduled_msg_count > 0) { - $msg = 'Some messages have been scheduled for sending'; - } else { - $msg = 'ERRFailed to schedule sending for messages'; - } - Hm_Msgs::add($msg); - $this->save_hm_msgs(); - } -} - /** * @subpackage keyboard_shortcuts/output */ @@ -1347,6 +1253,7 @@ protected function output() { } } } + $res .= ''. smtp_server_dropdown($this->module_output(), $this, $recip, $selected_id). '
@@ -1356,6 +1263,7 @@ protected function output() { '. schedule_dropdown($this). '
'; + if ($this->get('list_path') && ($reply_type == 'reply' || $reply_type == 'reply_all')) { $res .= ''; } @@ -1652,6 +1560,97 @@ protected function output() { } } +/** + * Send scheduled messages + * @subpackage smtp/handler + */ +class Hm_Handler_send_scheduled_messages extends Hm_Handler_Module { + /** + * Send delayed messages + * This should use cron + */ + public function process() { + if (!($this->module_is_supported('imap') || $this->module_is_supported('profiles'))) { + return; + } + + $servers = Hm_IMAP_List::dump(); + $scheduled_msg_count = 0; + + foreach (array_keys($servers) as $server_id) { + $cache = Hm_IMAP_List::get_cache($this->cache, $server_id); + $imap = Hm_IMAP_List::connect($server_id, $cache); + + if (imap_authed($imap)) { + $folder = 'Scheduled'; + $ret = $imap->get_mailbox_page($folder, 'DATE', false, 'ALL'); + + foreach ($ret[1] as $msg) { + $msg_headers = $imap->get_message_headers($msg['uid']); + + try { + if (!empty($msg_headers['X-Schedule'])) { + $scheduled_msg_count++; + } else { + continue; + } + + if (send_scheduled_message($this, $imap, $msg, $server_id)) { + $scheduled_msg_count--; + } + } catch (Exception $e) { + Hm_Debug::add(sprintf('ERRCannot send message: %s', $msg_headers['subject'])); + } + } + } + } + + $this->out('scheduled_msg_count', $scheduled_msg_count); +}} + +/** + * Changes the schedule of the message + * @subpackage smtp/handler + */ +class Hm_Handler_re_schedule_message_sending extends Hm_Handler_Module { + public function process() { + if (!($this->module_is_supported('imap') || $this->module_is_supported('profiles'))) { + return; + } + list($success, $form) = $this->process_form(array('schedule_date', 'scheduled_msg_ids')); + if (!$success) { + return; + } + $scheduled_msg_count = 0; + $new_schedule_date = $form['schedule_date']; + if ($form['schedule_date'] != 'now') { + $new_schedule_date = get_scheduled_date($form['schedule_date']); + } + $ids = explode(',', $form['scheduled_msg_ids']); + foreach ($ids as $msg_part) { + list($imap_server_id, $msg_id, $folder) = explode('_', $msg_part); + + $mailbox = new Hm_Mailbox($imap_server_id, $this->user_config, $this->session, $this->config); + if ($mailbox->connect()) { + $folder = hex2bin($folder); + if (reschedule_message_sending($this, $mailbox, $msg_id, $folder, $new_schedule_date, $imap_server_id)) { + $scheduled_msg_count++; + } + } + } + $this->out('scheduled_msg_count', $scheduled_msg_count); + if ($scheduled_msg_count == count($ids)) { + $msg = 'Operation successful'; + } elseif ($scheduled_msg_count > 0) { + $msg = 'Some messages have been scheduled for sending'; + } else { + $msg = 'ERRFailed to schedule sending for messages'; + } + Hm_Msgs::add($msg); + $this->save_hm_msgs(); + } +} + /** * Add scheduled send to the message list controls * @subpackage imap/output @@ -2033,11 +2032,13 @@ function save_imap_draft($atts, $id, $session, $mod, $mod_cache, $uploaded_files $from = false; $name = ''; $uploaded_files = get_uploaded_files_from_array($uploaded_files); + if ($profile && $profile['type'] == 'imap' && $mod->module_is_supported('imap')) { $from = $profile['replyto']; $name = $profile['name']; $imap_profile = Hm_IMAP_List::fetch($profile['user'], $profile['server']); } + if (!$imap_profile || empty($imap_profile)) { $imap_profile = find_imap_by_smtp( $mod->user_config->get('imap_servers'), @@ -2057,19 +2058,11 @@ function save_imap_draft($atts, $id, $session, $mod, $mod_cache, $uploaded_files Hm_Msgs::add('ERRThere is no draft directory configured for this account.'); return -1; } - $cache = Hm_IMAP_List::get_cache($mod_cache, $imap_profile['id']); - $imap = Hm_IMAP_List::connect($imap_profile['id'], $cache); - - if (!empty($atts['schedule'])) { - $folder ='Scheduled'; - if (!count($imap->get_mailbox_status($folder))) { - $imap->create_mailbox($folder); - } - $atts['schedule'] = get_scheduled_date($atts['schedule']); - } else { - $folder = $specials['draft']; + $mailbox = new Hm_Mailbox($imap_profile['id'], $mod->user_config, $session, $mod->config); + if (! $mailbox || ! $mailbox->connect()) { + return -1; } - + if (!empty($atts['schedule'])) { $folder ='Scheduled'; if (!count($mailbox->get_folder_status($folder))) { @@ -2080,32 +2073,28 @@ function save_imap_draft($atts, $id, $session, $mod, $mod_cache, $uploaded_files $folder = $specials['draft']; } - $mime = prepare_draft_mime($atts, $uploaded_files, $from, $name, $profile['id']); $res = $mime->process_attachments(); - + $msg = str_replace("\r\n", "\n", $mime->get_mime_msg()); $msg = str_replace("\n", "\r\n", $msg); $msg = rtrim($msg)."\r\n"; - if ($imap->append_start($folder, mb_strlen($msg), false, true)) { - $imap->append_feed($msg."\r\n"); - if (!$imap->append_end()) { - Hm_Msgs::add('ERRAn error occurred saving the draft message'); - return -1; - } + if ($mailbox->store_message($folder, $msg, false, true)) { + Hm_Msgs::add('ERRAn error occurred saving the draft message'); + return -1; } - $mailbox_page = $imap->get_mailbox_page($folder, 'ARRIVAL', true, 'DRAFT', 0, 10); + $messages = $mailbox->get_messages($folder, 'ARRIVAL', true, 'DRAFT', 0, 10); // Remove old version from the mailbox if ($id) { - $mailbox->message_action($specials['draft'], 'DELETE', array($id)); - $mailbox->message_action($specials['draft'], 'EXPUNGE', array($id)); + $mailbox->message_action($folder, 'DELETE', array($id)); + $mailbox->message_action($folder, 'EXPUNGE', array($id)); } - if (!empty($messages[1])) { - foreach ($messages[1] as $mail) { - $msg_header = $mailbox->get_message_headers($specials['draft'], $mail['uid']); + + foreach ($messages[1] as $mail) { + $msg_header = $mailbox->get_message_headers($folder, $mail['uid']); // Convert all header keys to lowercase $msg_header_lower = array_change_key_case($msg_header, CASE_LOWER); $mime_headers_lower = array_change_key_case($mime->get_headers(), CASE_LOWER); @@ -2119,7 +2108,8 @@ function save_imap_draft($atts, $id, $session, $mod, $mod_cache, $uploaded_files return $mail['uid']; } } - }} + } + return -1; }} /** @@ -2376,4 +2366,4 @@ function recip_count_check($headers, $omod) { if ($recip_count > MAX_RECIPIENT_WARNING) { Hm_Msgs::add('ERRMessage contains more than the maximum number of recipients, proceed with caution'); } -}} \ No newline at end of file +}} From 37b4dc4d1b154b443dd70244802ea5aaaa7ae632 Mon Sep 17 00:00:00 2001 From: Josaphat Imani Date: Wed, 22 Jan 2025 12:29:59 +0200 Subject: [PATCH 07/20] Send scheduled messages via ajax --- modules/core/handler_modules.php | 2 +- modules/imap/hm-imap.php | 4 +- modules/imap/js_modules/route_handlers.js | 2 - modules/profiles/functions.php | 4 +- modules/smtp/js_modules/route_handlers.js | 46 ++++++++++++++++------- modules/smtp/modules.php | 10 ++--- modules/smtp/site.js | 14 ++----- 7 files changed, 44 insertions(+), 38 deletions(-) diff --git a/modules/core/handler_modules.php b/modules/core/handler_modules.php index 6fab55ab51..25270cc12d 100644 --- a/modules/core/handler_modules.php +++ b/modules/core/handler_modules.php @@ -1159,7 +1159,7 @@ public function process() { return; } - add_profile($profileName, $profileSignature, $profileReplyTo, $profileIsDefault, $email, $imapAddress, $email, $this->smtp_server_id, $this->imap_server_id, $this); + add_profile($profileName, $profileSignature, $profileReplyTo, $profileIsDefault, $email, $onlyJmap? $jmapAddress: $imapAddress, $email, $this->smtp_server_id, $this->imap_server_id, $this); } if ($this->module_is_supported('imap_folders')) { diff --git a/modules/imap/hm-imap.php b/modules/imap/hm-imap.php index 676a58adbd..c73d53dda1 100644 --- a/modules/imap/hm-imap.php +++ b/modules/imap/hm-imap.php @@ -970,7 +970,7 @@ public function get_message_list($uids, $raw=false, $include_content_body = fals if ($this->is_supported( 'X-GM-EXT-1' )) { $command .= 'X-GM-MSGID X-GM-THRID X-GM-LABELS '; } - $command .= "BODY.PEEK[HEADER.FIELDS (SUBJECT X-AUTO-BCC FROM DATE CONTENT-TYPE X-PRIORITY TO LIST-ARCHIVE REFERENCES MESSAGE-ID X-SNOOZED X-SCHEDULE X-PROFILE-ID X-DELIVERY)])"; + $command .= "BODY.PEEK[HEADER.FIELDS (SUBJECT X-AUTO-BCC FROM DATE CONTENT-TYPE X-PRIORITY TO LIST-ARCHIVE REFERENCES MESSAGE-ID X-SNOOZED X-SCHEDULE X-PROFILE-ID X-DELIVERY)]"; if ($include_content_body) { $command .= " BODY.PEEK[0.1]"; } @@ -985,7 +985,7 @@ public function get_message_list($uids, $raw=false, $include_content_body = fals $status = $this->check_response($res, true); $tags = array('X-GM-MSGID' => 'google_msg_id', 'X-GM-THRID' => 'google_thread_id', 'X-GM-LABELS' => 'google_labels', 'UID' => 'uid', 'FLAGS' => 'flags', 'RFC822.SIZE' => 'size', 'INTERNALDATE' => 'internal_date'); $junk = array('X-AUTO-BCC', 'MESSAGE-ID', 'REFERENCES', 'X-SNOOZED', 'X-SCHEDULE', 'X-PROFILE-ID', 'X-DELIVERY', 'LIST-ARCHIVE', 'SUBJECT', 'FROM', 'CONTENT-TYPE', 'TO', '(', ')', ']', 'X-PRIORITY', 'DATE'); - $flds = array('x-auto-bcc' => 'x_auto_bcc', 'message-id' => 'message_id', 'references' => 'references', 'x-snoozed' => 'x_snoozed', 'x-schedule' => 'x_schedule', 'x-profile-id' => 'x_profile_id', 'x-delivery' => 'x_delivery', 'list-archive' => 'list_archive', 'date' => 'date', 'from' => 'from', 'to' => 'to', 'subject' => 'subject', 'content-type' => 'content_type', 'x-priority' => 'x_priority'); + $flds = array('x-auto-bcc' => 'x_auto_bcc', 'message-id' => 'message_id', 'references' => 'references', 'x-snoozed' => 'x_snoozed', 'x-schedule' => 'x_schedule', 'x-profile-id' => 'x_profile_id', 'x-delivery' => 'x_delivery', 'list-archive' => 'list_archive', 'date' => 'date', 'from' => 'from', 'to' => 'to', 'subject' => 'subject', 'content-type' => 'content_type', 'x-priority' => 'x_priority', 'body' => 'content_body'); $headers = array(); foreach ($res as $n => $vals) { diff --git a/modules/imap/js_modules/route_handlers.js b/modules/imap/js_modules/route_handlers.js index 9dd9c1f424..9e3b00edd1 100644 --- a/modules/imap/js_modules/route_handlers.js +++ b/modules/imap/js_modules/route_handlers.js @@ -4,8 +4,6 @@ function applyImapMessageListPageHandlers(routeParams) { imap_setup_snooze(); imap_setup_tags(); - Hm_Message_List.set_row_events(); - processNextActionDate(); if (window.inlineMessageMessageListAndSearchPageHandler) inlineMessageMessageListAndSearchPageHandler(routeParams); diff --git a/modules/profiles/functions.php b/modules/profiles/functions.php index d75922ab6a..dcfc100056 100644 --- a/modules/profiles/functions.php +++ b/modules/profiles/functions.php @@ -12,8 +12,8 @@ function add_profile($name, $signature, $reply_to, $is_default, $email, $server, 'replyto' => $reply_to, 'default' => $is_default, 'address' => $email, - 'server' => $imap_server_id, - 'user' => $email, + 'server' => $server, + 'user' => $user, 'type' => 'imap' ); $id = Hm_Profiles::add($profile); diff --git a/modules/smtp/js_modules/route_handlers.js b/modules/smtp/js_modules/route_handlers.js index f6f08b75ea..7ba4d2ae41 100644 --- a/modules/smtp/js_modules/route_handlers.js +++ b/modules/smtp/js_modules/route_handlers.js @@ -2,14 +2,8 @@ function applySmtpComposePageHandlers() { init_resumable_upload() - let isScheduledMode = null; - setupActionSchedule(function () { - let schedule = $('.nexter_input').val(); $('.smtp_send_placeholder').trigger('click'); - - save_compose_state(false, true, schedule); - isScheduledMode = schedule; }); if (window.HTMLEditor) { @@ -119,16 +113,21 @@ function applySmtpComposePageHandlers() { } async function handleSendAnyway() { + // if ($('.compose_draft_id').val() == '0') { + // Hm_Notices.show([hm_trans('Please wait, sending message...')]); + // await waitForValueChange('.compose_draft_id', '0'); + // } - if ($('.compose_draft_id').val() == '0') { - Hm_Notices.show([hm_trans('Please wait, sending message...')]); - await waitForValueChange('.compose_draft_id', '0'); - } - - - if (handleMissingAttachment()) { - if (isScheduledMode == null) { + if ($('.nexter_input').val()) { + save_compose_state(false, true, $('.nexter_input').val(), function(res) { + if (!res.router_user_msgs[0].startsWith('ERR')) { + reset_smtp_form(false); + Hm_Folders.reload_folders(true); + Hm_Utils.redirect(); + } + }); + } else { document.getElementsByClassName("smtp_send")[0].click(); } } else { @@ -259,4 +258,23 @@ function applySmtpComposePageHandlers() { if (window.pgpComposePageHandler) pgpComposePageHandler(); if (window.profilesComposePageHandler) profilesComposePageHandler(); + + var scheduled_msg_count = 0; + var sendScheduledMessages = function() { + Hm_Ajax.request( + [{'name': 'hm_ajax_hook', 'value': 'ajax_send_scheduled_messages'}], + function(res) { + scheduled_msg_count = res.scheduled_msg_count; + }, + ); + } + + // sendScheduledMessages(); + // setInterval(sendScheduledMessages, 60000); + // window.onbeforeunload = () => { + // if (scheduled_msg_count == 0) { + // return; + // } + // return sprintf(hm_trans("You have %d scheduled messages that won\'t be executed if you quit"), scheduled_msg_count); + // }; } \ No newline at end of file diff --git a/modules/smtp/modules.php b/modules/smtp/modules.php index a37b4ca3ca..9180677371 100644 --- a/modules/smtp/modules.php +++ b/modules/smtp/modules.php @@ -318,11 +318,7 @@ public function process() { $draft_notice = array_key_exists('draft_notice', $this->request->post) ? $this->request->post['draft_notice'] : false; $uploaded_files = array_key_exists('uploaded_files', $this->request->post) ? $this->request->post['uploaded_files'] : false; $delivery_receipt = array_key_exists('compose_delivery_receipt', $this->request->post) ? $this->request->post['compose_delivery_receipt'] : false; - $schedule = array_key_exists('schedule', $this->request->post) ? $this->request->post['schedule'] : ''; - if ($schedule == "undefined") { - $schedule = ""; - } if (array_key_exists('delete_uploaded_files', $this->request->post) && $this->request->post['delete_uploaded_files']) { delete_uploaded_files($this->session, $draft_id); @@ -2037,8 +2033,8 @@ function save_imap_draft($atts, $id, $session, $mod, $mod_cache, $uploaded_files $from = $profile['replyto']; $name = $profile['name']; $imap_profile = Hm_IMAP_List::fetch($profile['user'], $profile['server']); + $imap_profile = Hm_IMAP_List::dump($imap_profile['id'], true); // Change this later } - if (!$imap_profile || empty($imap_profile)) { $imap_profile = find_imap_by_smtp( $mod->user_config->get('imap_servers'), @@ -2048,7 +2044,7 @@ function save_imap_draft($atts, $id, $session, $mod, $mod_cache, $uploaded_files $from = $mod->user_config->get('smtp_servers')[$atts['draft_smtp']]['user']; } } - if (!$imap_profile || empty($imap_profile)) { + if (empty($imap_profile)) { return -1; } @@ -2058,7 +2054,7 @@ function save_imap_draft($atts, $id, $session, $mod, $mod_cache, $uploaded_files Hm_Msgs::add('ERRThere is no draft directory configured for this account.'); return -1; } - $mailbox = new Hm_Mailbox($imap_profile['id'], $mod->user_config, $session, $mod->config); + $mailbox = new Hm_Mailbox($imap_profile['id'], $mod->user_config, $session, $imap_profile); if (! $mailbox || ! $mailbox->connect()) { return -1; } diff --git a/modules/smtp/site.js b/modules/smtp/site.js index aec9ab1819..6ff6b4248f 100644 --- a/modules/smtp/site.js +++ b/modules/smtp/site.js @@ -94,8 +94,7 @@ var save_compose_state = function(no_files, notice, schedule, callback) { return; } $('.compose_draft_id').val(0) - Hm_Ajax.request( - [{'name': 'hm_ajax_hook', 'value': 'ajax_smtp_save_draft'}, + Hm_Ajax.request([{'name': 'hm_ajax_hook', 'value': 'ajax_smtp_save_draft'}, {'name': 'draft_body', 'value': body}, {'name': 'draft_id', 'value': draft_id}, {'name': 'draft_smtp', 'value': smtp}, @@ -106,9 +105,9 @@ var save_compose_state = function(no_files, notice, schedule, callback) { {'name': 'draft_in_reply_to', 'value': inreplyto}, {'name': 'delete_uploaded_files', 'value': no_files}, {'name': 'draft_to', 'value': to}, - {'name': 'schedule', 'value': schedule}, - {'name': 'compose_delivery_receipt', 'value': delivery_receipt}, - {'name': 'uploaded_files', 'value': uploaded_files}], + {'name': 'uploaded_files', 'value': uploaded_files}, + {'name': 'schedule', 'value': schedule ?? ''}, + {'name': 'compose_delivery_receipt', 'value': delivery_receipt ?? ''}], function(res) { if (res.draft_id) { $('.compose_draft_id').val(res.draft_id); @@ -117,11 +116,6 @@ var save_compose_state = function(no_files, notice, schedule, callback) { $('.draft_list .draft_'+draft_id+' a').text(res.draft_subject); } - if (schedule) { - $(".compose_form")[0].reset(); - return; - } - if (callback) { callback(res); } From 115fb913a3bfc6542b7f739f29bfb2bb556637c0 Mon Sep 17 00:00:00 2001 From: Josaphat Imani Date: Thu, 23 Jan 2025 10:37:11 +0200 Subject: [PATCH 08/20] Make reschedule sending in a working state --- lib/servers.php | 10 +++ modules/core/hm-mailbox.php | 4 + modules/imap/functions.php | 11 +-- modules/smtp/functions.php | 99 ++++++++++------------- modules/smtp/js_modules/route_handlers.js | 2 +- modules/smtp/modules.php | 28 ++++--- 6 files changed, 76 insertions(+), 78 deletions(-) diff --git a/lib/servers.php b/lib/servers.php index 9e37fdb967..4b4ead1769 100644 --- a/lib/servers.php +++ b/lib/servers.php @@ -273,4 +273,14 @@ private static function match($server, $user, $name) { } return false; } + + public static function getForMailbox($id) { + $server = self::get($id, true); + if ($server) { + $server['password'] = $server['pass']; + $server['username'] = $server['user']; + return $server; + } + return false; + } } diff --git a/modules/core/hm-mailbox.php b/modules/core/hm-mailbox.php index 819733d3da..672c582af6 100644 --- a/modules/core/hm-mailbox.php +++ b/modules/core/hm-mailbox.php @@ -586,4 +586,8 @@ public function select_folder($folder) { } return true; } + + public function get_config() { + return $this->config; + } } diff --git a/modules/imap/functions.php b/modules/imap/functions.php index ad08c0566c..a2719b14e6 100644 --- a/modules/imap/functions.php +++ b/modules/imap/functions.php @@ -1702,16 +1702,13 @@ function save_sent_msg($handler, $imap_id, $imap, $imap_details, $msg, $msg_id, $uid = null; if ($sent_folder) { Hm_Debug::add(sprintf("Attempting to save sent message for IMAP server %s in folder %s", $imap_details['server'], $sent_folder)); - if ($imap->append_start($sent_folder, strlen($msg), true)) { - $imap->append_feed($msg."\r\n"); - if (!$imap->append_end() && $show_errors) { - Hm_Msgs::add('ERRAn error occurred saving the sent message'); - } + if (! $imap->store_message($sent_folder, $msg)) { + Hm_Msgs::add('ERRAn error occurred saving the sent message'); } - $mailbox_page = $imap->get_mailbox_page($sent_folder, 'ARRIVAL', true, 'ALL', 0, 10); + $mailbox_page = $imap->get_messages($sent_folder, 'ARRIVAL', true, 'ALL', 0, 10); foreach ($mailbox_page[1] as $mail) { - $msg_header = $imap->get_message_headers($mail['uid']); + $msg_header = $imap->get_message_headers($sent_folder, $mail['uid']); if ($msg_header['Message-Id'] === $msg_id) { $uid = $mail['uid']; break; diff --git a/modules/smtp/functions.php b/modules/smtp/functions.php index 742068cc49..579f92cf09 100644 --- a/modules/smtp/functions.php +++ b/modules/smtp/functions.php @@ -66,66 +66,55 @@ function get_reply_type($request) { * @subpackage smtp/functions */ if (!hm_exists('send_scheduled_message')) { -function send_scheduled_message($handler, $imap, $msg_id, $server_id, $send_now = false) { - $msg_headers = $imap->get_message_headers($msg_id); - $imap_details = Hm_IMAP_List::dump($server_id); - if (empty($imap_details)) { +function send_scheduled_message($handler, $imap, $folder, $msg_id, $server_id, $send_now = false) { + $msg_headers = $imap->get_message_headers($folder, $msg_id); + $imap_details = $imap->get_config(); + + try { + if (empty($msg_headers['X-Schedule'])) { return false; } - - try { - if (empty($msg_headers['X-Schedule'])) { - return false; - } - if (new DateTime($msg_headers['X-Schedule']) <= new DateTime() || $send_now) { - $profile = Hm_Profiles::get($msg_headers['X-Profile-ID']); - if (!$profile) { - $profiles = Hm_Profiles::search('server', $imap_details['server']); + if (new DateTime($msg_headers['X-Schedule']) <= new DateTime() || $send_now) { + $profile = Hm_Profiles::get($msg_headers['X-Profile-ID']); + if (!$profile) { + $profiles = Hm_Profiles::search('server', $imap_details['server']); - if (!$profiles) { - Hm_Debug::add(sprintf('ERRCannot find profiles corresponding with IMAP server: %s', $imap_details['server'])); - return false; - } - $profile = $profiles[0]; + if (!$profiles) { + Hm_Debug::add(sprintf('ERRCannot find profiles corresponding with IMAP server: %s', $imap_details['server'])); + return false; } + $profile = $profiles[0]; + } - $smtp = Hm_SMTP_List::connect($profile['smtp_id'], false); + $smtp = Hm_SMTP_List::connect($profile['smtp_id'], false); - if (smtp_authed($smtp)) { - if (isset($msg_headers['X-Delivery'])) { - $from_params = 'RET=HDRS'; - $recipients_params = 'NOTIFY=SUCCESS,FAILURE'; - } else { - $from_params = ''; - $recipients_params = ''; - } + if ($smtp) { + $delivery_receipt = isset($msg_headers['X-Delivery']); - $recipients = []; - foreach (['To', 'Cc', 'Bcc'] as $fld) { - if (array_key_exists($fld, $msg_headers)) { - $recipients = array_merge($recipients, Hm_MIME_Msg::find_addresses($msg_headers[$fld])); - } + $recipients = []; + foreach (['To', 'Cc', 'Bcc'] as $fld) { + if (array_key_exists($fld, $msg_headers)) { + $recipients = array_merge($recipients, Hm_MIME_Msg::find_addresses($msg_headers[$fld])); } + } - $msg_content = $imap->get_message_content($msg_id, 0); - $from = process_address_fld($msg_headers['From']); + $msg_content = $imap->get_message_content($folder, $msg_id, 0); + $from = process_address_fld($msg_headers['From']); - $err_msg = $smtp->send_message($from[0]['email'], $recipients, $msg_content, $from_params, $recipients_params); + $err_msg = $smtp->send_message($from[0]['email'], $recipients, $msg_content, $delivery_receipt); - if (!$err_msg) { - if ($imap->message_action('DELETE', [$msg_id])) { - $imap->message_action('EXPUNGE', [$msg_id]); - } - save_sent_msg($handler, $server_id, $imap, $imap_details, $msg_content, $msg_id, false); - return true; - } + if (!$err_msg) { + $imap->delete_message($folder, $msg_id, false); + save_sent_msg($handler, $server_id, $imap, $imap_details, $msg_content, $msg_id, false); + return true; } } - } catch (Exception $e) { - Hm_Debug::add(sprintf('ERRCannot send message: %s', $msg_headers['subject'])); } - return false; + } catch (Exception $e) { + Hm_Debug::add(sprintf('ERRCannot send message: %s', $msg_headers['subject'])); + } + return false; }} /** @@ -133,15 +122,13 @@ function send_scheduled_message($handler, $imap, $msg_id, $server_id, $send_now */ if (!hm_exists('reschedule_message_sending')) { function reschedule_message_sending($handler, $imap, $msg_id, $folder, $new_date, $server_id) { - if (!$imap->select_mailbox($folder)) { - return; - } if ($new_date == 'now') { - return send_scheduled_message($handler, $imap, $msg_id, $server_id, true); + return send_scheduled_message($handler, $imap, $folder, $msg_id, $server_id, true); } - $msg = $imap->get_message_content($msg_id, 0); + $msg = $imap->get_message_content($folder, $msg_id, 0); $new_date = get_scheduled_date($new_date); preg_match("/^X-Schedule:.*(\r?\n[ \t]+.*)*\r?\n?/im", $msg, $matches); + if (count($matches)) { $msg = str_replace($matches[0], "X-Schedule: {$new_date}\n", $msg); } else { @@ -152,17 +139,13 @@ function reschedule_message_sending($handler, $imap, $msg_id, $folder, $new_date $msg = rtrim($msg)."\r\n"; $schedule_folder = 'Scheduled'; - if (!count($imap->get_mailbox_status($schedule_folder))) { + if (!count($imap->get_folder_status($schedule_folder))) { return; } $res = false; - if ($imap->select_mailbox($schedule_folder) && $imap->append_start($schedule_folder, strlen($msg))) { - $imap->append_feed($msg."\r\n"); - if ($imap->append_end()) { - if ($imap->select_mailbox($folder) && $imap->message_action('DELETE', array($msg_id))) { - $imap->message_action('EXPUNGE', array($msg_id)); - $res = true; - } + if ($imap->store_message($schedule_folder, $msg)) { + if ($imap->delete_message($folder, $msg_id, false)) { + $res = true; } } return $res; diff --git a/modules/smtp/js_modules/route_handlers.js b/modules/smtp/js_modules/route_handlers.js index 7ba4d2ae41..736481cc34 100644 --- a/modules/smtp/js_modules/route_handlers.js +++ b/modules/smtp/js_modules/route_handlers.js @@ -121,7 +121,7 @@ function applySmtpComposePageHandlers() { if (handleMissingAttachment()) { if ($('.nexter_input').val()) { save_compose_state(false, true, $('.nexter_input').val(), function(res) { - if (!res.router_user_msgs[0].startsWith('ERR')) { + if (res.draft_id) { reset_smtp_form(false); Hm_Folders.reload_folders(true); Hm_Utils.redirect(); diff --git a/modules/smtp/modules.php b/modules/smtp/modules.php index 9180677371..71c420c6ea 100644 --- a/modules/smtp/modules.php +++ b/modules/smtp/modules.php @@ -557,8 +557,8 @@ public function process() { } $imap_profile = Hm_IMAP_List::fetch($profile['user'], $profile['server']); $specials = get_special_folders($this, $imap_profile['id']); - if (!array_key_exists('sent', $specials) || !$specials['sent']) { - Hm_Msgs::add('ERRPlease configure a sent folder for this IMAP account'); + if ($imap_profile && (!array_key_exists('sent', $specials) || !$specials['sent'])) { + Hm_Msgs::add('ERRPlease configure a sent folder for account ' . $imap_profile['name']); } } } @@ -1609,7 +1609,7 @@ public function process() { * @subpackage smtp/handler */ class Hm_Handler_re_schedule_message_sending extends Hm_Handler_Module { - public function process() { + public function process() { if (!($this->module_is_supported('imap') || $this->module_is_supported('profiles'))) { return; } @@ -1625,9 +1625,10 @@ public function process() { $ids = explode(',', $form['scheduled_msg_ids']); foreach ($ids as $msg_part) { list($imap_server_id, $msg_id, $folder) = explode('_', $msg_part); + $imap_server = Hm_IMAP_List::getForMailbox($imap_server_id); - $mailbox = new Hm_Mailbox($imap_server_id, $this->user_config, $this->session, $this->config); - if ($mailbox->connect()) { + $mailbox = new Hm_Mailbox($imap_server_id, $this->user_config, $this->session, $imap_server); + if ($mailbox && $mailbox->connect()) { $folder = hex2bin($folder); if (reschedule_message_sending($this, $mailbox, $msg_id, $folder, $new_schedule_date, $imap_server_id)) { $scheduled_msg_count++; @@ -1889,19 +1890,17 @@ function delete_draft($id, $cache, $imap_server_id, $folder) { */ if (!hm_exists('find_imap_by_smtp')) { function find_imap_by_smtp($imap_profiles, $smtp_profile) { - $id = 0; foreach ($imap_profiles as $profile) { if ($smtp_profile['user'] == $profile['user']) { - return array_merge(['id' => $id], $profile); + return $profile; } if (explode('@', $smtp_profile['user'])[0] == explode('@', $profile['user'])[0]) { - return array_merge(['id' => $id], $profile); + return $profile; } if ($smtp_profile['user'] == $profile['name']) { - return array_merge(['id' => $id], $profile); + return $profile; } - $id++; } }} @@ -2033,7 +2032,6 @@ function save_imap_draft($atts, $id, $session, $mod, $mod_cache, $uploaded_files $from = $profile['replyto']; $name = $profile['name']; $imap_profile = Hm_IMAP_List::fetch($profile['user'], $profile['server']); - $imap_profile = Hm_IMAP_List::dump($imap_profile['id'], true); // Change this later } if (!$imap_profile || empty($imap_profile)) { $imap_profile = find_imap_by_smtp( @@ -2048,6 +2046,7 @@ function save_imap_draft($atts, $id, $session, $mod, $mod_cache, $uploaded_files return -1; } + $imap_profile = Hm_IMAP_List::getForMailbox($imap_profile['id']); $specials = get_special_folders($mod, $imap_profile['id']); if ((!array_key_exists('draft', $specials) || !$specials['draft']) && !array_key_exists('schedule', $atts)) { @@ -2072,11 +2071,16 @@ function save_imap_draft($atts, $id, $session, $mod, $mod_cache, $uploaded_files $mime = prepare_draft_mime($atts, $uploaded_files, $from, $name, $profile['id']); $res = $mime->process_attachments(); + if (! empty($atts['schedule']) && empty($mime->get_recipient_addresses())) { + Hm_Msgs::add("ERRNo valid recipients found"); + return -1; + } + $msg = str_replace("\r\n", "\n", $mime->get_mime_msg()); $msg = str_replace("\n", "\r\n", $msg); $msg = rtrim($msg)."\r\n"; - if ($mailbox->store_message($folder, $msg, false, true)) { + if (! $mailbox->store_message($folder, $msg, false, true)) { Hm_Msgs::add('ERRAn error occurred saving the draft message'); return -1; } From aa317932cb8edf1190e8660bd91c3a3e4914afe4 Mon Sep 17 00:00:00 2001 From: Josaphat Imani Date: Thu, 23 Jan 2025 11:54:53 +0200 Subject: [PATCH 09/20] Make background sending scheduled messages work --- modules/smtp/functions.php | 10 ++++---- modules/smtp/js_modules/route_handlers.js | 19 -------------- modules/smtp/modules.php | 31 +++++++++-------------- modules/smtp/site.js | 22 +++++++++++++++- 4 files changed, 38 insertions(+), 44 deletions(-) diff --git a/modules/smtp/functions.php b/modules/smtp/functions.php index 579f92cf09..c4719a54de 100644 --- a/modules/smtp/functions.php +++ b/modules/smtp/functions.php @@ -66,7 +66,7 @@ function get_reply_type($request) { * @subpackage smtp/functions */ if (!hm_exists('send_scheduled_message')) { -function send_scheduled_message($handler, $imap, $folder, $msg_id, $server_id, $send_now = false) { +function send_scheduled_message($handler, $imap, $folder, $msg_id, $send_now = false) { $msg_headers = $imap->get_message_headers($folder, $msg_id); $imap_details = $imap->get_config(); @@ -89,7 +89,7 @@ function send_scheduled_message($handler, $imap, $folder, $msg_id, $server_id, $ $smtp = Hm_SMTP_List::connect($profile['smtp_id'], false); - if ($smtp) { + if ($smtp && $smtp->authed()) { $delivery_receipt = isset($msg_headers['X-Delivery']); $recipients = []; @@ -106,7 +106,7 @@ function send_scheduled_message($handler, $imap, $folder, $msg_id, $server_id, $ if (!$err_msg) { $imap->delete_message($folder, $msg_id, false); - save_sent_msg($handler, $server_id, $imap, $imap_details, $msg_content, $msg_id, false); + save_sent_msg($handler, $imap->get_config()['id'], $imap, $imap_details, $msg_content, $msg_id, false); return true; } } @@ -121,9 +121,9 @@ function send_scheduled_message($handler, $imap, $folder, $msg_id, $server_id, $ * @subpackage smtp/functions */ if (!hm_exists('reschedule_message_sending')) { -function reschedule_message_sending($handler, $imap, $msg_id, $folder, $new_date, $server_id) { +function reschedule_message_sending($handler, $imap, $msg_id, $folder, $new_date) { if ($new_date == 'now') { - return send_scheduled_message($handler, $imap, $folder, $msg_id, $server_id, true); + return send_scheduled_message($handler, $imap, $folder, $msg_id, true); } $msg = $imap->get_message_content($folder, $msg_id, 0); $new_date = get_scheduled_date($new_date); diff --git a/modules/smtp/js_modules/route_handlers.js b/modules/smtp/js_modules/route_handlers.js index 736481cc34..14c7e70159 100644 --- a/modules/smtp/js_modules/route_handlers.js +++ b/modules/smtp/js_modules/route_handlers.js @@ -258,23 +258,4 @@ function applySmtpComposePageHandlers() { if (window.pgpComposePageHandler) pgpComposePageHandler(); if (window.profilesComposePageHandler) profilesComposePageHandler(); - - var scheduled_msg_count = 0; - var sendScheduledMessages = function() { - Hm_Ajax.request( - [{'name': 'hm_ajax_hook', 'value': 'ajax_send_scheduled_messages'}], - function(res) { - scheduled_msg_count = res.scheduled_msg_count; - }, - ); - } - - // sendScheduledMessages(); - // setInterval(sendScheduledMessages, 60000); - // window.onbeforeunload = () => { - // if (scheduled_msg_count == 0) { - // return; - // } - // return sprintf(hm_trans("You have %d scheduled messages that won\'t be executed if you quit"), scheduled_msg_count); - // }; } \ No newline at end of file diff --git a/modules/smtp/modules.php b/modules/smtp/modules.php index 71c420c6ea..785afddb15 100644 --- a/modules/smtp/modules.php +++ b/modules/smtp/modules.php @@ -1576,26 +1576,19 @@ public function process() { foreach (array_keys($servers) as $server_id) { $cache = Hm_IMAP_List::get_cache($this->cache, $server_id); $imap = Hm_IMAP_List::connect($server_id, $cache); - - if (imap_authed($imap)) { + + if ($imap->authed()) { $folder = 'Scheduled'; - $ret = $imap->get_mailbox_page($folder, 'DATE', false, 'ALL'); - + $ret = $imap->get_messages($folder, 'DATE', false, 'ALL'); foreach ($ret[1] as $msg) { - $msg_headers = $imap->get_message_headers($msg['uid']); - - try { - if (!empty($msg_headers['X-Schedule'])) { - $scheduled_msg_count++; - } else { - continue; - } - - if (send_scheduled_message($this, $imap, $msg, $server_id)) { - $scheduled_msg_count--; - } - } catch (Exception $e) { - Hm_Debug::add(sprintf('ERRCannot send message: %s', $msg_headers['subject'])); + $msg_headers = $imap->get_message_headers($folder, $msg['uid']); + if (! empty($msg_headers['X-Schedule'])) { + $scheduled_msg_count++; + } else { + continue; + } + if (send_scheduled_message($this, $imap, $folder, $msg['uid'])) { + $scheduled_msg_count++; } } } @@ -1630,7 +1623,7 @@ public function process() { $mailbox = new Hm_Mailbox($imap_server_id, $this->user_config, $this->session, $imap_server); if ($mailbox && $mailbox->connect()) { $folder = hex2bin($folder); - if (reschedule_message_sending($this, $mailbox, $msg_id, $folder, $new_schedule_date, $imap_server_id)) { + if (reschedule_message_sending($this, $mailbox, $msg_id, $folder, $new_schedule_date)) { $scheduled_msg_count++; } } diff --git a/modules/smtp/site.js b/modules/smtp/site.js index 6ff6b4248f..bf36d05725 100644 --- a/modules/smtp/site.js +++ b/modules/smtp/site.js @@ -383,4 +383,24 @@ function smtpSettingsPageHandler() { [] ); }); -} \ No newline at end of file +} + +$(function() { + var scheduled_msg_count = 0; + var sendScheduledMessages = function() { + Hm_Ajax.request( + [{'name': 'hm_ajax_hook', 'value': 'ajax_send_scheduled_messages'}], + function(res) { + scheduled_msg_count = res.scheduled_msg_count; + }, + ); + } + + sendScheduledMessages(); + setInterval(sendScheduledMessages, 60000); + window.onbeforeunload = (e) => { + if (scheduled_msg_count > 0 && e.currentTarget.location.hostname !== document.location.hostname) { + return sprintf(hm_trans("You have %d scheduled messages that won\'t be executed if you quit"), scheduled_msg_count); + } + }; +}); \ No newline at end of file From 32215934c5d35f55f0ea2b4590dd75d4a98134b9 Mon Sep 17 00:00:00 2001 From: Josaphat Imani Date: Thu, 23 Jan 2025 13:09:38 +0200 Subject: [PATCH 10/20] Fix showing wait message while no draft is saving --- modules/smtp/js_modules/route_handlers.js | 8 ++++---- modules/smtp/modules.php | 1 + modules/smtp/site.js | 9 +++++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/modules/smtp/js_modules/route_handlers.js b/modules/smtp/js_modules/route_handlers.js index 14c7e70159..72a824784f 100644 --- a/modules/smtp/js_modules/route_handlers.js +++ b/modules/smtp/js_modules/route_handlers.js @@ -113,10 +113,10 @@ function applySmtpComposePageHandlers() { } async function handleSendAnyway() { - // if ($('.compose_draft_id').val() == '0') { - // Hm_Notices.show([hm_trans('Please wait, sending message...')]); - // await waitForValueChange('.compose_draft_id', '0'); - // } + if ($('.saving_draft').val() !== '0') { + Hm_Notices.show([hm_trans('Please wait, sending message...')]); + await waitForValueChange('.saving_draft', '0'); + } if (handleMissingAttachment()) { if ($('.nexter_input').val()) { diff --git a/modules/smtp/modules.php b/modules/smtp/modules.php index 785afddb15..7aed806278 100644 --- a/modules/smtp/modules.php +++ b/modules/smtp/modules.php @@ -1159,6 +1159,7 @@ protected function output() { ''. ''. ''. + ''. ''. '
'. diff --git a/modules/smtp/site.js b/modules/smtp/site.js index bf36d05725..e4b21a02be 100644 --- a/modules/smtp/site.js +++ b/modules/smtp/site.js @@ -93,7 +93,7 @@ var save_compose_state = function(no_files, notice, schedule, callback) { if (!body && !subject && !to && !cc && !bcc) { return; } - $('.compose_draft_id').val(0) + $('.saving_draft').val(1); Hm_Ajax.request([{'name': 'hm_ajax_hook', 'value': 'ajax_smtp_save_draft'}, {'name': 'draft_body', 'value': body}, {'name': 'draft_id', 'value': draft_id}, @@ -109,6 +109,7 @@ var save_compose_state = function(no_files, notice, schedule, callback) { {'name': 'schedule', 'value': schedule ?? ''}, {'name': 'compose_delivery_receipt', 'value': delivery_receipt ?? ''}], function(res) { + $('.saving_draft').val(0); if (res.draft_id) { $('.compose_draft_id').val(res.draft_id); } @@ -121,7 +122,11 @@ var save_compose_state = function(no_files, notice, schedule, callback) { } }, [], - no_icon + no_icon, + false, + function () { + $('.saving_draft').val(0); + } ); }; From b10344e8fa4c588ca7e3c3db075202a9b7b43357 Mon Sep 17 00:00:00 2001 From: Josaphat Imani Date: Thu, 23 Jan 2025 16:56:26 +0200 Subject: [PATCH 11/20] Fix using Hm_IMAP_List instead of Hm_Mailbox --- lib/servers.php | 19 +++++++++++-- modules/imap/handler_modules.php | 3 +- modules/smtp/functions.php | 28 +++++++++--------- modules/smtp/modules.php | 49 ++++++++++++++++---------------- 4 files changed, 56 insertions(+), 43 deletions(-) diff --git a/lib/servers.php b/lib/servers.php index 4b4ead1769..af06ec307a 100644 --- a/lib/servers.php +++ b/lib/servers.php @@ -274,13 +274,26 @@ private static function match($server, $user, $name) { return false; } + private static function appendPpasswordAndUsername(array $server) { + $server['password'] = $server['pass']; + $server['username'] = $server['user']; + return $server; + } + public static function getForMailbox($id) { $server = self::get($id, true); if ($server) { - $server['password'] = $server['pass']; - $server['username'] = $server['user']; - return $server; + return self::appendPpasswordAndUsername($server); } return false; } + + public static function dumpForMailbox($id = false) { + $list = self::dump($id, true); + foreach ($list as $index => $server) { + $server = self::appendPpasswordAndUsername($server); + $list[$index] = $server; + } + return $list; + } } diff --git a/modules/imap/handler_modules.php b/modules/imap/handler_modules.php index c311e02b21..4a45b8087a 100644 --- a/modules/imap/handler_modules.php +++ b/modules/imap/handler_modules.php @@ -1723,7 +1723,8 @@ public function process() { 'port' => $auth_server['port'], 'tls' => $auth_server['tls'], 'user' => $auth_server['username'], - 'pass' => $auth_server['password'] + 'pass' => $auth_server['password'], + 'type' => 'imap', ); if (! empty($auth_server['sieve_config_host'])) { $imap_details['sieve_config_host'] = $auth_server['sieve_config_host']; diff --git a/modules/smtp/functions.php b/modules/smtp/functions.php index c4719a54de..a86b0665ab 100644 --- a/modules/smtp/functions.php +++ b/modules/smtp/functions.php @@ -66,9 +66,9 @@ function get_reply_type($request) { * @subpackage smtp/functions */ if (!hm_exists('send_scheduled_message')) { -function send_scheduled_message($handler, $imap, $folder, $msg_id, $send_now = false) { - $msg_headers = $imap->get_message_headers($folder, $msg_id); - $imap_details = $imap->get_config(); +function send_scheduled_message($handler, $mailbox, $folder, $msg_id, $send_now = false) { + $msg_headers = $mailbox->get_message_headers($folder, $msg_id); + $mailbox_details = $mailbox->get_config(); try { if (empty($msg_headers['X-Schedule'])) { @@ -78,10 +78,10 @@ function send_scheduled_message($handler, $imap, $folder, $msg_id, $send_now = f if (new DateTime($msg_headers['X-Schedule']) <= new DateTime() || $send_now) { $profile = Hm_Profiles::get($msg_headers['X-Profile-ID']); if (!$profile) { - $profiles = Hm_Profiles::search('server', $imap_details['server']); + $profiles = Hm_Profiles::search('server', $mailbox_details['server']); if (!$profiles) { - Hm_Debug::add(sprintf('ERRCannot find profiles corresponding with IMAP server: %s', $imap_details['server'])); + Hm_Debug::add(sprintf('ERRCannot find profiles corresponding with MAILBOX server: %s', $mailbox_details['server'])); return false; } $profile = $profiles[0]; @@ -99,14 +99,14 @@ function send_scheduled_message($handler, $imap, $folder, $msg_id, $send_now = f } } - $msg_content = $imap->get_message_content($folder, $msg_id, 0); + $msg_content = $mailbox->get_message_content($folder, $msg_id, 0); $from = process_address_fld($msg_headers['From']); $err_msg = $smtp->send_message($from[0]['email'], $recipients, $msg_content, $delivery_receipt); if (!$err_msg) { - $imap->delete_message($folder, $msg_id, false); - save_sent_msg($handler, $imap->get_config()['id'], $imap, $imap_details, $msg_content, $msg_id, false); + $mailbox->delete_message($folder, $msg_id, false); + save_sent_msg($handler, $mailbox->get_config()['id'], $mailbox, $mailbox_details, $msg_content, $msg_id, false); return true; } } @@ -121,11 +121,11 @@ function send_scheduled_message($handler, $imap, $folder, $msg_id, $send_now = f * @subpackage smtp/functions */ if (!hm_exists('reschedule_message_sending')) { -function reschedule_message_sending($handler, $imap, $msg_id, $folder, $new_date) { +function reschedule_message_sending($handler, $mailbox, $msg_id, $folder, $new_date) { if ($new_date == 'now') { - return send_scheduled_message($handler, $imap, $folder, $msg_id, true); + return send_scheduled_message($handler, $mailbox, $folder, $msg_id, true); } - $msg = $imap->get_message_content($folder, $msg_id, 0); + $msg = $mailbox->get_message_content($folder, $msg_id, 0); $new_date = get_scheduled_date($new_date); preg_match("/^X-Schedule:.*(\r?\n[ \t]+.*)*\r?\n?/im", $msg, $matches); @@ -139,12 +139,12 @@ function reschedule_message_sending($handler, $imap, $msg_id, $folder, $new_date $msg = rtrim($msg)."\r\n"; $schedule_folder = 'Scheduled'; - if (!count($imap->get_folder_status($schedule_folder))) { + if (!count($mailbox->get_folder_status($schedule_folder))) { return; } $res = false; - if ($imap->store_message($schedule_folder, $msg)) { - if ($imap->delete_message($folder, $msg_id, false)) { + if ($mailbox->store_message($schedule_folder, $msg)) { + if ($mailbox->delete_message($folder, $msg_id, false)) { $res = true; } } diff --git a/modules/smtp/modules.php b/modules/smtp/modules.php index 7aed806278..85ca72bfa7 100644 --- a/modules/smtp/modules.php +++ b/modules/smtp/modules.php @@ -1566,37 +1566,36 @@ class Hm_Handler_send_scheduled_messages extends Hm_Handler_Module { * Send delayed messages * This should use cron */ - public function process() { - if (!($this->module_is_supported('imap') || $this->module_is_supported('profiles'))) { - return; - } + public function process() { + if (!($this->module_is_supported('imap') || $this->module_is_supported('profiles'))) { + return; + } - $servers = Hm_IMAP_List::dump(); - $scheduled_msg_count = 0; + $servers = Hm_IMAP_List::dumpForMailbox(); + $scheduled_msg_count = 0; - foreach (array_keys($servers) as $server_id) { - $cache = Hm_IMAP_List::get_cache($this->cache, $server_id); - $imap = Hm_IMAP_List::connect($server_id, $cache); - - if ($imap->authed()) { - $folder = 'Scheduled'; - $ret = $imap->get_messages($folder, 'DATE', false, 'ALL'); - foreach ($ret[1] as $msg) { - $msg_headers = $imap->get_message_headers($folder, $msg['uid']); - if (! empty($msg_headers['X-Schedule'])) { - $scheduled_msg_count++; - } else { - continue; - } - if (send_scheduled_message($this, $imap, $folder, $msg['uid'])) { - $scheduled_msg_count++; + foreach ($servers as $server_id => $config) { + $mailbox = new Hm_Mailbox($server_id, $this->user_config, $this->session, $config); + if ($mailbox && $mailbox->connect()) { + $folder = 'Scheduled'; + $ret = $mailbox->get_messages($folder, 'DATE', false, 'ALL'); + foreach ($ret[1] as $msg) { + $msg_headers = $mailbox->get_message_headers($folder, $msg['uid']); + if (! empty($msg_headers['X-Schedule'])) { + $scheduled_msg_count++; + } else { + continue; + } + if (send_scheduled_message($this, $mailbox, $folder, $msg['uid'])) { + $scheduled_msg_count++; + } } } } - } - $this->out('scheduled_msg_count', $scheduled_msg_count); -}} + $this->out('scheduled_msg_count', $scheduled_msg_count); + } +} /** * Changes the schedule of the message From 34fcf4b35d2e11d8826b0e238cb8e19e502f355f Mon Sep 17 00:00:00 2001 From: Josaphat Imani Date: Thu, 23 Jan 2025 17:31:49 +0200 Subject: [PATCH 12/20] Fix selenium tests --- modules/smtp/site.js | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/modules/smtp/site.js b/modules/smtp/site.js index e4b21a02be..16585ac7bf 100644 --- a/modules/smtp/site.js +++ b/modules/smtp/site.js @@ -391,21 +391,23 @@ function smtpSettingsPageHandler() { } $(function() { - var scheduled_msg_count = 0; - var sendScheduledMessages = function() { - Hm_Ajax.request( - [{'name': 'hm_ajax_hook', 'value': 'ajax_send_scheduled_messages'}], - function(res) { - scheduled_msg_count = res.scheduled_msg_count; - }, - ); - } - - sendScheduledMessages(); - setInterval(sendScheduledMessages, 60000); - window.onbeforeunload = (e) => { - if (scheduled_msg_count > 0 && e.currentTarget.location.hostname !== document.location.hostname) { - return sprintf(hm_trans("You have %d scheduled messages that won\'t be executed if you quit"), scheduled_msg_count); + if (hm_is_logged()) { + var scheduled_msg_count = 0; + var sendScheduledMessages = function() { + Hm_Ajax.request( + [{'name': 'hm_ajax_hook', 'value': 'ajax_send_scheduled_messages'}], + function(res) { + scheduled_msg_count = res.scheduled_msg_count; + }, + ); } - }; + + sendScheduledMessages(); + setInterval(sendScheduledMessages, 60000); + window.onbeforeunload = (e) => { + if (scheduled_msg_count > 0 && e.currentTarget.location.hostname !== document.location.hostname) { + return sprintf(hm_trans("You have %d scheduled messages that won\'t be executed if you quit"), scheduled_msg_count); + } + }; + } }); \ No newline at end of file From f62d980d2d821b8195b6cc58266e63d1a7ad8455 Mon Sep 17 00:00:00 2001 From: Amani Nyumu Date: Wed, 29 Jan 2025 16:56:32 +0100 Subject: [PATCH 13/20] feat(backend): Removing SMTP connections using the Mailbox class --- modules/imap/functions.php | 12 ++++++------ modules/smtp/functions.php | 8 ++++---- modules/smtp/modules.php | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/modules/imap/functions.php b/modules/imap/functions.php index a2719b14e6..665ec66a96 100644 --- a/modules/imap/functions.php +++ b/modules/imap/functions.php @@ -1683,32 +1683,32 @@ function flattenMessagesLists($messagesLists, $listSize) { } if (!hm_exists('save_sent_msg')) { -function save_sent_msg($handler, $imap_id, $imap, $imap_details, $msg, $msg_id, $show_errors = true) { +function save_sent_msg($handler, $imap_id, $mailbox, $imap_details, $msg, $msg_id, $show_errors = true) { $specials = get_special_folders($handler, $imap_id); if (array_key_exists('sent', $specials) && $specials['sent']) { $sent_folder = $specials['sent']; } if (!$sent_folder) { - $auto_sent = $imap->get_special_use_mailboxes('sent'); + $auto_sent = $mailbox->get_special_use_mailboxes('sent'); if (!array_key_exists('sent', $auto_sent)) { return; } $sent_folder = $auto_sent['sent']; } if (!$sent_folder) { - Hm_Debug::add(sprintf("Unable to save sent message, no sent folder for IMAP %s", $imap_details['server'])); + Hm_Debug::add(sprintf("Unable to save sent message, no sent folder for server %s %s", $mailbox->server_type(), $imap_details['server'])); } $uid = null; if ($sent_folder) { - Hm_Debug::add(sprintf("Attempting to save sent message for IMAP server %s in folder %s", $imap_details['server'], $sent_folder)); - if (! $imap->store_message($sent_folder, $msg)) { + Hm_Debug::add(sprintf("Attempting to save sent message for server %s in folder %s", $mailbox->server_type(), $imap_details['server'], $sent_folder)); + if (! $mailbox->store_message($sent_folder, $msg)) { Hm_Msgs::add('ERRAn error occurred saving the sent message'); } $mailbox_page = $imap->get_messages($sent_folder, 'ARRIVAL', true, 'ALL', 0, 10); foreach ($mailbox_page[1] as $mail) { - $msg_header = $imap->get_message_headers($sent_folder, $mail['uid']); + $msg_header = $mailbox->get_message_headers($sent_folder, $mail['uid']); if ($msg_header['Message-Id'] === $msg_id) { $uid = $mail['uid']; break; diff --git a/modules/smtp/functions.php b/modules/smtp/functions.php index a86b0665ab..b49173cbea 100644 --- a/modules/smtp/functions.php +++ b/modules/smtp/functions.php @@ -68,8 +68,8 @@ function get_reply_type($request) { if (!hm_exists('send_scheduled_message')) { function send_scheduled_message($handler, $mailbox, $folder, $msg_id, $send_now = false) { $msg_headers = $mailbox->get_message_headers($folder, $msg_id); - $mailbox_details = $mailbox->get_config(); - + $mailbox_details = $mailbox->get_config(); + $imap_profile = Hm_IMAP_List::fetch($mailbox_details['user'], $mailbox_details['server']); try { if (empty($msg_headers['X-Schedule'])) { return false; @@ -87,9 +87,9 @@ function send_scheduled_message($handler, $mailbox, $folder, $msg_id, $send_now $profile = $profiles[0]; } - $smtp = Hm_SMTP_List::connect($profile['smtp_id'], false); + $smtp = new Hm_Mailbox($imap_profile['id'], $handler->user_config, $handler->session, $imap_profile); - if ($smtp && $smtp->authed()) { + if ($smtp && $smtp->connect()) { $delivery_receipt = isset($msg_headers['X-Delivery']); $recipients = []; diff --git a/modules/smtp/modules.php b/modules/smtp/modules.php index 85ca72bfa7..041bd6b71d 100644 --- a/modules/smtp/modules.php +++ b/modules/smtp/modules.php @@ -1576,7 +1576,7 @@ public function process() { foreach ($servers as $server_id => $config) { $mailbox = new Hm_Mailbox($server_id, $this->user_config, $this->session, $config); - if ($mailbox && $mailbox->connect()) { + if ($mailbox->get_connection() != null && $mailbox->connect()) { $folder = 'Scheduled'; $ret = $mailbox->get_messages($folder, 'DATE', false, 'ALL'); foreach ($ret[1] as $msg) { @@ -2050,7 +2050,7 @@ function save_imap_draft($atts, $id, $session, $mod, $mod_cache, $uploaded_files if (! $mailbox || ! $mailbox->connect()) { return -1; } - + if (!empty($atts['schedule'])) { $folder ='Scheduled'; if (!count($mailbox->get_folder_status($folder))) { From 4ad32cc7d1f78873fdd75906583458a7c232993b Mon Sep 17 00:00:00 2001 From: Amani Nyumu Date: Wed, 29 Jan 2025 17:55:18 +0100 Subject: [PATCH 14/20] feat(backend): update hm-ews to support these headers --- modules/imap/hm-ews.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/imap/hm-ews.php b/modules/imap/hm-ews.php index bae8f093b3..80b815066e 100644 --- a/modules/imap/hm-ews.php +++ b/modules/imap/hm-ews.php @@ -541,9 +541,12 @@ public function get_message_list($itemIds, $include_preview = false) { 'message_id' => $message->get('internetMessageId'), 'x_auto_bcc' => null, 'x_snoozed' => null, + 'x_schedule' => null, + 'x_profile_id' => null, + 'x_delivery' => null, ]; foreach ($message->get('internetMessageHeaders')->InternetMessageHeader as $header) { - foreach (['x-gm-msgid' => 'google_msg_id', 'x-gm-thrid' => 'google_thread_id', 'x-gm-labels' => 'google_labels', 'x-auto-bcc' => 'x_auto_bcc', 'message-id' => 'message_id', 'references' => 'references', 'x-snoozed' => 'x_snoozed', 'list-archive' => 'list_archive', 'content-type' => 'content-type', 'x-priority' => 'x-priority'] as $hname => $key) { + foreach (['x-gm-msgid' => 'google_msg_id', 'x-gm-thrid' => 'google_thread_id', 'x-gm-labels' => 'google_labels', 'x-auto-bcc' => 'x_auto_bcc', 'message-id' => 'message_id', 'references' => 'references', 'x-snoozed' => 'x_snoozed', 'x-schedule' => 'x_schedule', 'x-profile-id' => 'x_profile_id', 'x-delivery' => 'x_delivery', 'list-archive' => 'list_archive', 'content-type' => 'content-type', 'x-priority' => 'x-priority'] as $hname => $key) { if (strtolower($header->get('headerName')) == $hname) { $msg[$key] = (string) $header; } From 9ad1d594f35e314c46c6ffd1111a104ccb3c8905 Mon Sep 17 00:00:00 2001 From: Amani Nyumu Date: Wed, 29 Jan 2025 22:25:17 +0100 Subject: [PATCH 15/20] feat(backend): refactor code based on review feedback Fixed typo with double 'p'. Used switch statement in 'get_scheduled_date' and other --- lib/servers.php | 6 +- modules/core/functions.php | 77 +++++++++++++++---------- modules/core/message_list_functions.php | 6 +- modules/core/site.css | 2 +- 4 files changed, 52 insertions(+), 39 deletions(-) diff --git a/lib/servers.php b/lib/servers.php index af06ec307a..c486deb1cd 100644 --- a/lib/servers.php +++ b/lib/servers.php @@ -274,7 +274,7 @@ private static function match($server, $user, $name) { return false; } - private static function appendPpasswordAndUsername(array $server) { + private static function appendPasswordAndUsername(array $server) { $server['password'] = $server['pass']; $server['username'] = $server['user']; return $server; @@ -283,7 +283,7 @@ private static function appendPpasswordAndUsername(array $server) { public static function getForMailbox($id) { $server = self::get($id, true); if ($server) { - return self::appendPpasswordAndUsername($server); + return self::appendPasswordAndUsername($server); } return false; } @@ -291,7 +291,7 @@ public static function getForMailbox($id) { public static function dumpForMailbox($id = false) { $list = self::dump($id, true); foreach ($list as $index => $server) { - $server = self::appendPpasswordAndUsername($server); + $server = self::appendPasswordAndUsername($server); $list[$index] = $server; } return $list; diff --git a/modules/core/functions.php b/modules/core/functions.php index a422a0858b..11c37f56ff 100644 --- a/modules/core/functions.php +++ b/modules/core/functions.php @@ -639,32 +639,44 @@ function privacy_setting_callback($val, $key, $mod) { } if (!hm_exists('get_scheduled_date')) { -function get_scheduled_date($format, $only_label = false) { - if ($format == 'later_in_day') { - $date_string = 'today 18:00'; - $label = 'Later in the day'; - } elseif ($format == 'tomorrow') { - $date_string = '+1 day 08:00'; - $label = 'Tomorrow'; - } elseif ($format == 'next_weekend') { - $date_string = 'next Saturday 08:00'; - $label = 'Next weekend'; - } elseif ($format == 'next_week') { - $date_string = 'next week 08:00'; - $label = 'Next week'; - } elseif ($format == 'next_month') { - $date_string = 'next month 08:00'; - $label = 'Next month'; - } else { - $date_string = $format; - $label = 'Certain date'; - } - $time = strtotime($date_string); - if ($only_label) { - return [$label, date('D, H:i', $time)]; - } - return date('D, d M Y H:i', $time); -}} + function get_scheduled_date($format, $only_label = false) { + switch ($format) { + case 'later_in_day': + $date_string = 'today 18:00'; + $label = 'Later in the day'; + break; + case 'tomorrow': + $date_string = '+1 day 08:00'; + $label = 'Tomorrow'; + break; + case 'next_weekend': + $date_string = 'next Saturday 08:00'; + $label = 'Next weekend'; + break; + case 'next_week': + $date_string = 'next week 08:00'; + $label = 'Next week'; + break; + case 'next_month': + $date_string = 'next month 08:00'; + $label = 'Next month'; + break; + default: + $date_string = $format; + $label = 'Certain date'; + break; + } + + $time = strtotime($date_string); + + if ($only_label) { + return [$label, date('D, H:i', $time)]; + } + + return date('D, d M Y H:i', $time); + } +} + /** * @subpackage imap/functions @@ -721,15 +733,16 @@ function parse_nexter_header($header, $name) { $header = str_replace("$name: ", '', $header); $result = []; - foreach (explode(';', $header) as $kv) + foreach (explode(';', $header) as $keyValue) { - $kv = trim($kv); - $spacePos = strpos($kv, ' '); + $keyValue = trim($keyValue); + $spacePos = strpos($keyValue, ' '); if ($spacePos > 0) { - $result[rtrim(substr($kv, 0, $spacePos), ':')] = trim(substr($kv, $spacePos+1)); + $result[rtrim(substr($keyValue, 0, $spacePos), ':')] = trim(substr($keyValue, $spacePos+1)); } else { - $result[$kv] = true; + $result[$keyValue] = true; } } return $result; - }} + } +} \ No newline at end of file diff --git a/modules/core/message_list_functions.php b/modules/core/message_list_functions.php index 6ee3d62162..7950c36860 100644 --- a/modules/core/message_list_functions.php +++ b/modules/core/message_list_functions.php @@ -336,11 +336,11 @@ function subject_callback($vals, $style, $output_mod) { */ if (!hm_exists('date_callback')) { function date_callback($vals, $style, $output_mod) { - $nexter_class = isset($vals[2]) && $vals[2]? ' nexter_date': ''; + $delayed_class = isset($vals[2]) && $vals[2]? ' delayed_date': ''; if ($style == 'news') { - return sprintf('
%s
', $nexter_class, $output_mod->html_safe($vals[0]), $output_mod->html_safe($vals[1])); + return sprintf('
%s
', $delayed_class, $output_mod->html_safe($vals[0]), $output_mod->html_safe($vals[1])); } - return sprintf('%s', $nexter_class, $output_mod->html_safe(date('r', $vals[1])), $output_mod->html_safe($vals[0]), $output_mod->html_safe($vals[1])); + return sprintf('%s', $delayed_class, $output_mod->html_safe(date('r', $vals[1])), $output_mod->html_safe($vals[0]), $output_mod->html_safe($vals[1])); }} function dates_holders_callback($vals) { diff --git a/modules/core/site.css b/modules/core/site.css index e33ce8b88b..612b492aa4 100644 --- a/modules/core/site.css +++ b/modules/core/site.css @@ -1276,7 +1276,7 @@ div.unseen, .mobile .imap_sort { width: 100%; } -.nexter_date { +.delayed_date { color: var(--bs-primary) !important; } From aed9a951d95f5d00e2e51eebc4b3e17c617511a4 Mon Sep 17 00:00:00 2001 From: Amani Nyumu Date: Mon, 10 Feb 2025 09:35:35 +0100 Subject: [PATCH 16/20] =?UTF-8?q?feat=20(backend):=20refactor=20event=20ha?= =?UTF-8?q?ndling=20and=20variable=20declarations=20-=20Event=20handlers?= =?UTF-8?q?=20attached=20directly=20to=20target=20elements=20to=20avoid=20?= =?UTF-8?q?multiple=20bindings.=20-=20Rename=20parse=5Fnexter=5Fheader=20t?= =?UTF-8?q?o=20parse=5Fdelayed=5Fheader=20and=20Reschedule=20sending=20to?= =?UTF-8?q?=20Rechedule=20-=20Replacement=20of=20=E2=80=9Cvar=E2=80=9D=20b?= =?UTF-8?q?y=20=E2=80=9Clet=E2=80=9D.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/core/functions.php | 6 +++--- modules/imap/functions.php | 8 ++++---- modules/imap/handler_modules.php | 2 +- modules/imap/output_modules.php | 2 +- modules/smtp/functions.php | 14 +++++++------- modules/smtp/js_modules/route_handlers.js | 3 +-- modules/smtp/modules.php | 2 +- modules/smtp/site.js | 4 ++-- 8 files changed, 20 insertions(+), 21 deletions(-) diff --git a/modules/core/functions.php b/modules/core/functions.php index 11c37f56ff..dcec99e355 100644 --- a/modules/core/functions.php +++ b/modules/core/functions.php @@ -702,7 +702,7 @@ function schedule_dropdown($output, $send_now = false) { $txt = ''; if ($send_now) { $txt .= '` }; +var hm_spinner_text = function(text, id = 'spinner-text') { + return `
+ ${text} + +
`; +}; var fillImapData = function(details) { $('#srv_setup_stepper_imap_address').val(details.server); $('#srv_setup_stepper_imap_port').val(details.port); @@ -1811,7 +1807,6 @@ var hasLeadingOrTrailingSpaces = function(str) { var Hm_Message_List = new Message_List(); function sortHandlerForMessageListAndSearchPage() { - $('.combined_sort').on("change", function() { Hm_Message_List.sort($(this).val()); }); $('.source_link').on("click", function() { $('.list_sources').toggle(); $('#list_controls_menu').hide(); return false; }); if (getListPathParam() == 'unread' && $('.menu_unread > a').css('font-weight') == 'bold') { $('.menu_unread > a').css('font-weight', 'normal'); diff --git a/modules/imap/functions.php b/modules/imap/functions.php index ee5ae703d0..cecd7d4536 100644 --- a/modules/imap/functions.php +++ b/modules/imap/functions.php @@ -320,7 +320,7 @@ function format_imap_message_list($msg_list, $output_module, $parent_list=false, array('subject_callback', $subject, $url, $flags, $icon, $preview_msg), array('safe_output_callback', 'source', $source), array('safe_output_callback', 'from'.$nofrom, $from, null, str_replace(array($from, '<', '>'), '', $msg['from'])), - array('date_callback', $date, $timestamp), + array('date_callback', $date, $timestamp, $is_snoozed || $is_scheduled), array('dates_holders_callback', $msg['internal_date'], $msg['date']), ), $id, @@ -336,7 +336,8 @@ function format_imap_message_list($msg_list, $output_module, $parent_list=false, array('safe_output_callback', 'from'.$nofrom, $from, null, str_replace(array($from, '<', '>'), '', $msg['from'])), array('subject_callback', $subject, $url, $flags, null, $preview_msg), array('date_callback', $date, $timestamp, $is_snoozed || $is_scheduled), - array('icon_callback', $flags) + array('icon_callback', $flags), + array('dates_holders_callback', $msg['internal_date'], $msg['date']), ), $id, $style, @@ -1608,9 +1609,34 @@ function connect_to_imap_server($address, $name, $port, $user, $pass, $tls, $ima } } -function getCombinedMessagesLists($sources, $cache, $searchTerms, $listPage, $limit, $offsets = [], $defaultOffset = 0, $filter = 'ALL') { +/** + * @param array $sources + * @param object $cache + * @param array $search + */ +function getCombinedMessagesLists($sources, $cache, $search) { + $defaultSearch = [ + 'filter' => 'ALL', + 'sort' => 'ARRIVAL', + 'reverse' => true, + 'terms' => [], + 'limit' => 10, + 'offsets' => [], + 'defaultOffset' => 0, + 'listPage' => 1 + ]; + $search = array_merge($defaultSearch, $search); + + $filter = $search['filter']; + $sort = $search['sort']; + $reverse = $search['reverse']; + $searchTerms = $search['terms']; + $limit = $search['limit']; + $offsets = $search['offsets']; + $listPage = $search['listPage']; + $totalMessages = 0; - $offset = $defaultOffset; + $offset = $search['defaultOffset']; $messagesLists = []; $status = []; foreach ($sources as $index => $dataSource) { @@ -1623,14 +1649,27 @@ function getCombinedMessagesLists($sources, $cache, $searchTerms, $listPage, $li $mailbox = Hm_IMAP_List::get_connected_mailbox($dataSource['id'], $cache); if ($mailbox && $mailbox->authed()) { + $connection = $mailbox->get_connection(); + $folder = $dataSource['folder']; - $state = $mailbox->get_connection()->get_mailbox_status(hex2bin($folder)); + $mailbox->select_folder(hex2bin($folder)); + $state = $connection->get_mailbox_status(hex2bin($folder)); $status['imap_'.$dataSource['id'].'_'.$folder] = $state; - $uids = $mailbox->search(hex2bin($folder), $filter, false, $searchTerms); + if ($mailbox->is_imap()) { + if ($connection->is_supported( 'SORT' )) { + $sortedUids = $connection->get_message_sort_order($sort, $reverse, $filter); + } else { + $sortedUids = $connection->sort_by_fetch($sort, $reverse, $filter); + } + + $uids = $mailbox->search(hex2bin($folder), $filter, $sortedUids, $searchTerms); + } else { + // EWS + $uids = $connection->search($folder, $sort, $reverse, $filter, 0, $limit, $searchTerms); + } + $total = count($uids); - // most recent messages at the top - $uids = array_reverse($uids); $uids = array_slice($uids, $offset, $limit); $headers = $mailbox->get_message_list(hex2bin($folder), $uids); @@ -1682,6 +1721,18 @@ function flattenMessagesLists($messagesLists, $listSize) { return ['messages' => $endList, 'offsets' => $sizesTaken]; } +function sortCombinedMessages($list, $sort) { + usort($list, function($a, $b) use ($sort) { + $sortField = str_replace(['arrival', '-'], ['internal_date', ''], $sort); + if (strpos($sort, '-') === 0) { + return strtotime($a[$sortField]) - strtotime($b[$sortField]); + } + return strtotime($b[$sortField]) - strtotime($a[$sortField]); + }); + + return $list; +} + if (!hm_exists('save_sent_msg')) { function save_sent_msg($handler, $imap_id, $mailbox, $imap_details, $msg, $msg_id, $show_errors = true) { $specials = get_special_folders($handler, $imap_id); diff --git a/modules/smtp/modules.php b/modules/smtp/modules.php index 46e9e69e72..a3b1dc40d4 100644 --- a/modules/smtp/modules.php +++ b/modules/smtp/modules.php @@ -1576,7 +1576,7 @@ public function process() { foreach ($servers as $server_id => $config) { $mailbox = new Hm_Mailbox($server_id, $this->user_config, $this->session, $config); - if ($mailbox->get_connection() != null && $mailbox->connect()) { + if ($mailbox->authed()) { $folder = 'Scheduled'; $ret = $mailbox->get_messages($folder, 'DATE', false, 'ALL'); foreach ($ret[1] as $msg) { @@ -1621,7 +1621,7 @@ public function process() { $imap_server = Hm_IMAP_List::getForMailbox($imap_server_id); $mailbox = new Hm_Mailbox($imap_server_id, $this->user_config, $this->session, $imap_server); - if ($mailbox->get_connection() != null && $mailbox->connect()) { + if ($mailbox->authed()) { $folder = hex2bin($folder); if (reschedule_message_sending($this, $mailbox, $msg_id, $folder, $new_schedule_date)) { $scheduled_msg_count++; @@ -2047,7 +2047,7 @@ function save_imap_draft($atts, $id, $session, $mod, $mod_cache, $uploaded_files return -1; } $mailbox = new Hm_Mailbox($imap_profile['id'], $mod->user_config, $session, $imap_profile); - if (! $mailbox || ! $mailbox->connect()) { + if (! $mailbox->authed()) { return -1; } From cfab9e29fef31413a83ec6872ee7ee28f2f59f98 Mon Sep 17 00:00:00 2001 From: Josaphat Imani Date: Tue, 11 Feb 2025 11:13:31 +0200 Subject: [PATCH 18/20] Use Hm_Mailbox in place of Hm_SMTP_List --- lib/servers.php | 3 +++ modules/imap/site.css | 3 ++- modules/smtp/functions.php | 45 +++++++++++++++++++------------------- 3 files changed, 28 insertions(+), 23 deletions(-) diff --git a/lib/servers.php b/lib/servers.php index c486deb1cd..a1537b470e 100644 --- a/lib/servers.php +++ b/lib/servers.php @@ -290,6 +290,9 @@ public static function getForMailbox($id) { public static function dumpForMailbox($id = false) { $list = self::dump($id, true); + if ($id !== false) { + return self::appendPasswordAndUsername($list); + } foreach ($list as $index => $server) { $server = self::appendPasswordAndUsername($server); $list[$index] = $server; diff --git a/modules/imap/site.css b/modules/imap/site.css index db8576ec53..d4a3c5ead1 100644 --- a/modules/imap/site.css +++ b/modules/imap/site.css @@ -280,7 +280,8 @@ } .jmap_section, .sent_setting, -.imap_section { +.imap_section, +.ews_server_config_section { display: none; } .imap_server_setup .content_title { diff --git a/modules/smtp/functions.php b/modules/smtp/functions.php index a5013a6620..7a3350cfa0 100644 --- a/modules/smtp/functions.php +++ b/modules/smtp/functions.php @@ -66,9 +66,9 @@ function get_reply_type($request) { * @subpackage smtp/functions */ if (!hm_exists('send_scheduled_message')) { -function send_scheduled_message($handler, $mailbox, $folder, $msg_id, $send_now = false) { - $msg_headers = $mailbox->get_message_headers($folder, $msg_id); - $mailbox_details = $mailbox->get_config(); +function send_scheduled_message($handler, $imapMailbox, $folder, $msg_id, $send_now = false) { + $msg_headers = $imapMailbox->get_message_headers($folder, $msg_id); + $mailbox_details = $imapMailbox->get_config(); try { if (empty($msg_headers['X-Schedule'])) { return false; @@ -85,31 +85,32 @@ function send_scheduled_message($handler, $mailbox, $folder, $msg_id, $send_now } $profile = $profiles[0]; } - $smtp = Hm_SMTP_List::connect($profile['smtp_id'], false); - - if (! $smtp || ! $smtp->authed()) { + $smtpConfig = Hm_SMTP_List::dumpForMailbox($profile['smtp_id']); + $smtpMailbox = new Hm_Mailbox($profile['smtp_id'], $handler->user_config, $handler->session, $smtpConfig); + + if (! $smtpMailbox->authed()) { Hm_Msgs::add("ERRFailed to authenticate to the SMTP server"); return; } - - $delivery_receipt = isset($msg_headers['X-Delivery']); - - $recipients = []; - foreach (['To', 'Cc', 'Bcc'] as $fld) { - if (array_key_exists($fld, $msg_headers)) { - $recipients = array_merge($recipients, Hm_MIME_Msg::find_addresses($msg_headers[$fld])); - } - } - $msg_content = $mailbox->get_message_content($folder, $msg_id, 0); - $from = process_address_fld($msg_headers['From']); + $delivery_receipt = isset($msg_headers['X-Delivery']); - $err_msg = $smtp->send_message($from[0]['email'], $recipients, $msg_content, $delivery_receipt); - if (!$err_msg) { - $mailbox->delete_message($folder, $msg_id, false); - save_sent_msg($handler, $mailbox->get_config()['id'], $mailbox, $mailbox_details, $msg_content, $msg_id, false); - return true; + $recipients = []; + foreach (['To', 'Cc', 'Bcc'] as $fld) { + if (array_key_exists($fld, $msg_headers)) { + $recipients = array_merge($recipients, Hm_MIME_Msg::find_addresses($msg_headers[$fld])); } + } + + $msg_content = $imapMailbox->get_message_content($folder, $msg_id, 0); + $from = process_address_fld($msg_headers['From']); + + $err_msg = $smtpMailbox->send_message($from[0]['email'], $recipients, $msg_content, $delivery_receipt); + if (!$err_msg) { + $imapMailbox->delete_message($folder, $msg_id, false); + save_sent_msg($handler, $imapMailbox->get_config()['id'], $imapMailbox, $mailbox_details, $msg_content, $msg_id, false); + return true; + } } } catch (Exception $e) { Hm_Debug::add(sprintf('ERRCannot send message: %s', $msg_headers['subject'])); From dfa683948f27123073e90dec4f4e96ae1ed57e58 Mon Sep 17 00:00:00 2001 From: Josaphat Imani Date: Tue, 11 Feb 2025 12:04:53 +0200 Subject: [PATCH 19/20] Save UTC time to fix delayed scheduled message sending --- modules/core/functions.php | 2 +- modules/core/site.js | 10 ++++++---- modules/smtp/functions.php | 4 +--- modules/smtp/modules.php | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/modules/core/functions.php b/modules/core/functions.php index dcec99e355..d9dfcf197d 100644 --- a/modules/core/functions.php +++ b/modules/core/functions.php @@ -673,7 +673,7 @@ function get_scheduled_date($format, $only_label = false) { return [$label, date('D, H:i', $time)]; } - return date('D, d M Y H:i', $time); + return date('D, d M Y H:i T', $time); } } diff --git a/modules/core/site.js b/modules/core/site.js index bd26bb7ffd..9bdd0948c2 100644 --- a/modules/core/site.js +++ b/modules/core/site.js @@ -2425,8 +2425,9 @@ function setupActionSchedule(callback) { } }); $(document).on('change', '.nexter_input_date', function (e) { - if ($(this).val() && new Date().getTime() < new Date($(this).val()).getTime()) { - $('.nexter_input').val($(this).val()).trigger('change'); + const selectedDate = new Date($(this).val()); + if ($(this).val() && new Date().getTime() < selectedDate.getTime()) { + $('.nexter_input').val(selectedDate.toISOString()).trigger('change'); } }); $(document).on('change', '.nexter_input', callback); @@ -2451,8 +2452,9 @@ function setupActionSnooze(callback) { } }); $(document).on('change', '.nexter_input_date_snooze', function (e) { - if ($(this).val() && new Date().getTime() < new Date($(this).val()).getTime()) { - $('.nexter_input_snooze').val($(this).val()).trigger('change'); + const selectedDate = new Date($(this).val()); + if ($(this).val() && new Date().getTime() < selectedDate.getTime()) { + $('.nexter_input_snooze').val(selectedDate.toISOString()).trigger('change'); } }); $(document).on('change', '.nexter_input_snooze', callback); diff --git a/modules/smtp/functions.php b/modules/smtp/functions.php index 7a3350cfa0..d3752e6e31 100644 --- a/modules/smtp/functions.php +++ b/modules/smtp/functions.php @@ -85,9 +85,7 @@ function send_scheduled_message($handler, $imapMailbox, $folder, $msg_id, $send_ } $profile = $profiles[0]; } - $smtpConfig = Hm_SMTP_List::dumpForMailbox($profile['smtp_id']); - $smtpMailbox = new Hm_Mailbox($profile['smtp_id'], $handler->user_config, $handler->session, $smtpConfig); - + $smtpMailbox = Hm_SMTP_List::connect($profile['smtp_id'], false); if (! $smtpMailbox->authed()) { Hm_Msgs::add("ERRFailed to authenticate to the SMTP server"); return; diff --git a/modules/smtp/modules.php b/modules/smtp/modules.php index a3b1dc40d4..62b1411e8c 100644 --- a/modules/smtp/modules.php +++ b/modules/smtp/modules.php @@ -1576,7 +1576,7 @@ public function process() { foreach ($servers as $server_id => $config) { $mailbox = new Hm_Mailbox($server_id, $this->user_config, $this->session, $config); - if ($mailbox->authed()) { + if ($mailbox->connect()) { $folder = 'Scheduled'; $ret = $mailbox->get_messages($folder, 'DATE', false, 'ALL'); foreach ($ret[1] as $msg) { @@ -1621,7 +1621,7 @@ public function process() { $imap_server = Hm_IMAP_List::getForMailbox($imap_server_id); $mailbox = new Hm_Mailbox($imap_server_id, $this->user_config, $this->session, $imap_server); - if ($mailbox->authed()) { + if ($mailbox->connect()) { $folder = hex2bin($folder); if (reschedule_message_sending($this, $mailbox, $msg_id, $folder, $new_schedule_date)) { $scheduled_msg_count++; @@ -2047,7 +2047,7 @@ function save_imap_draft($atts, $id, $session, $mod, $mod_cache, $uploaded_files return -1; } $mailbox = new Hm_Mailbox($imap_profile['id'], $mod->user_config, $session, $imap_profile); - if (! $mailbox->authed()) { + if (! $mailbox->connect()) { return -1; } From 5beec678df5a6adfc9727d231a8198e7f684c0e1 Mon Sep 17 00:00:00 2001 From: Josaphat Imani Date: Thu, 13 Feb 2025 09:33:08 +0200 Subject: [PATCH 20/20] Change the way of getting new saved message uid --- modules/imap/functions.php | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/modules/imap/functions.php b/modules/imap/functions.php index fbdc71f09e..5c600ba5e3 100644 --- a/modules/imap/functions.php +++ b/modules/imap/functions.php @@ -1753,18 +1753,10 @@ function save_sent_msg($handler, $imap_id, $mailbox, $imap_details, $msg, $msg_i $uid = null; if ($sent_folder) { Hm_Debug::add(sprintf("Attempting to save sent message for server %s in folder %s", $mailbox->server_type(), $imap_details['server'], $sent_folder)); - if (! $mailbox->store_message($sent_folder, $msg)) { + $uid = $mailbox->store_message($sent_folder, $msg); + if (! $uid) { Hm_Msgs::add('ERRAn error occurred saving the sent message'); } - - $mailbox_page = $mailbox->get_messages($sent_folder, 'ARRIVAL', true, 'ALL', 0, 10); - foreach ($mailbox_page[1] as $mail) { - $msg_header = $mailbox->get_message_headers($sent_folder, $mail['uid']); - if ($msg_header['Message-Id'] === $msg_id) { - $uid = $mail['uid']; - break; - } - } } return $uid; }}