Skip to content

Commit fa5966d

Browse files
authored
Merge branch 'master' into mta-sts-implementation
2 parents d09124e + e6b53a8 commit fa5966d

File tree

20 files changed

+239
-64
lines changed

20 files changed

+239
-64
lines changed

modules/calendar/js_modules/route_handlers.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
function applyCalendarPageHandlers() {
2-
$('.event_delete').on("click", function() {
2+
$('.event_delete a').on("click", function() {
33
if (hm_delete_prompt()) {
4-
$(this).parent().submit();
4+
$(this).closest('form').submit();
55
}
66
});
77
$('.cal_title').on("click", function(e) {

modules/calendar/site.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,7 @@
44
.calendar_week td { width: 14%; height: 600px; text-align: left; padding: 10px; border-bottom: solid 1px #eee; vertical-align: top; border-right: solid 1px #eee; }
55
.event_details { cursor: pointer; z-index: 100; display: none; position: absolute; width: 300px; border: solid 1px #ddd; background-color: #fff; padding: 10px; }
66
.event_details td { padding: 5px; border: none; height: auto; }
7+
.event_detail {
8+
max-height: 35vh;
9+
overflow: auto;
10+
}

modules/core/js_modules/Hm_MessagesStore.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ class Hm_MessagesStore {
312312
} else {
313313
if (this.path == 'tag') {
314314
config.push({ name: "hm_ajax_hook", value: 'ajax_imap_tag_data' });
315-
config.push({ name: "folder", value: getParam('tag_id') });
315+
config.push({ name: "folder", value: getParam('filter') });
316316
configs.push(config);
317317
} else {
318318
let sources = hm_data_sources();

modules/core/navigation/routes.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ const modulesRoutes = [
8181
{
8282
page: 'shortcuts',
8383
handler: 'applyShortcutsPageHandlers'
84+
},
85+
{
86+
page: 'tags',
87+
handler: 'applyTagsPageHandlers'
8488
}
8589
]
8690

modules/core/site.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ var Hm_Ajax_Request = function() { return {
246246
res = Hm_Utils.json_decode(Hm_Crypt.decrypt(res.payload));
247247
}
248248
if ((res.status && res.status == 'not callable') || !res.router_login_state) {
249-
this.fail(xhr, true, !res.router_login_state);
249+
this.fail(xhr, true);
250250
return;
251251
}
252252
if (Hm_Ajax.err_condition) {
@@ -2587,3 +2587,21 @@ function setupActionSnooze(callback) {
25872587
});
25882588
$(document).on('change', '.nexter_input_snooze', callback);
25892589
}
2590+
2591+
document.addEventListener("show.bs.dropdown", function (event) {
2592+
const currentToggle = event.target;
2593+
2594+
// If it's nested inside a dropdown menu, skip
2595+
if (currentToggle.closest(".dropdown-menu")) {
2596+
return;
2597+
}
2598+
2599+
// Close other top-level dropdowns
2600+
document.querySelectorAll(".dropdown-toggle.show").forEach((openBtn) => {
2601+
if (openBtn !== currentToggle && !openBtn.closest(".dropdown-menu")) {
2602+
const dropdownInstance = bootstrap.Dropdown.getInstance(openBtn);
2603+
if (dropdownInstance) dropdownInstance.hide();
2604+
}
2605+
});
2606+
});
2607+

modules/imap/functions.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1512,16 +1512,18 @@ function snooze_dropdown($output, $unsnooze = false) {
15121512
}}
15131513

15141514
if (!hm_exists('tags_dropdown')) {
1515-
function tags_dropdown($context, $headers) {
1515+
function tags_dropdown($context) {
1516+
$msgUid = $context->get('msg_text_uid');
1517+
$msgTags = Hm_Tags::getTagIdsWithMessage($msgUid);
1518+
15161519
$folders = $context->get('tags', array());
15171520
$txt = '<div class="dropdown d-inline-block">
1518-
<a class="hlink text-decoration-none btn btn-sm btn-outline-secondary dropdown-toggle" id="dropdownMenuTag" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="true">'.$context->trans('Tags').'</a>
1521+
<a class="hlink text-decoration-none btn btn-sm btn-outline-secondary dropdown-toggle" id="dropdownMenuTag" data-bs-toggle="dropdown" aria-haspopup="true" href="#" aria-expanded="true">'.$context->trans('Tags').'</a>
15191522
<ul class="dropdown-menu" aria-labelledby="dropdownMenuTag">';
15201523

1521-
$tags = !empty($headers['X-Cypht-Tags']) ? explode(',', $headers['X-Cypht-Tags']) : array();
15221524
foreach ($folders as $folder) {
15231525
$tag = $folder['name'];
1524-
$is_checked = in_array($folder['id'], array_map('trim', $tags));
1526+
$is_checked = in_array($folder['id'], $msgTags);
15251527
$txt .= '<li class="d-flex dropdown-item gap-2">';
15261528
$txt .= '<input class="form-check-input me-1 label-checkbox" type="checkbox" value="" aria-label="..." data-id="'.$folder['id'].'" '.($is_checked ? 'checked' : '').'>';
15271529
$txt .= '<span>'.$context->trans($tag).'</span>';

modules/imap/hm-ews.php

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -565,12 +565,21 @@ public function get_message_list($itemIds, $include_preview = false) {
565565
$result = $this->ews->GetItem($request);
566566
$messages = [];
567567

568-
foreach ($result as $message) {
569-
$flags = $this->extract_flags($message);
570-
$uid = bin2hex($message->getItemId()->getId());
571-
$msg = $this->getMessageProperties($message, $uid, $flags, $include_preview);
572-
$messages[$uid] = $msg;
568+
// For a mailbox with a single item, EWS returns the item directly instead of an array
569+
if (! is_array($result)) {
570+
$uid = bin2hex($result->getItemId()->getId());
571+
$flags = $this->extract_flags($result);
572+
$messages[$uid] = $this->getMessageProperties($result, $uid, $flags, $include_preview);
573+
return $messages;
574+
} else {
575+
foreach ($result as $message) {
576+
$flags = $this->extract_flags($message);
577+
$uid = bin2hex($message->getItemId()->getId());
578+
$msg = $this->getMessageProperties($message, $uid, $flags, $include_preview);
579+
$messages[$uid] = $msg;
580+
}
573581
}
582+
574583
return $messages;
575584
}
576585

modules/imap/output_modules.php

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -193,17 +193,17 @@ protected function output() {
193193
if (!array_key_exists('subject', lc_headers($headers)) || !trim(lc_headers($headers)['subject'])) {
194194
$headers['subject'] = $this->trans('[No Subject]');
195195
}
196-
196+
197197
// Start Bootstrap container
198198
$txt .= '<div class="container-fluid p-0 ml-0 border-bottom border-secondary-subtle text-muted">';
199-
199+
200200
foreach ($small_headers as $fld) {
201201
foreach ($headers as $name => $value) {
202202
if ($fld == mb_strtolower($name)) {
203203
if ($fld == 'subject') {
204204
$txt .= '<div class="row g-0 py-0 py-sm-1 small_header d-flex">';
205205
$txt .= '<div class="col-12">';
206-
if (isset($headers['Flags']) && mb_stristr($headers['Flags'], 'flagged')) {
206+
if (array_key_exists('flags', lc_headers($headers)) && mb_stristr(lc_headers($headers)['flags'], 'flagged')) {
207207
$txt .= '<i class="bi bi-star-half account_icon"></i> ';
208208
}
209209
$txt .= '<span class="fs-5 fw-normal text-dark js-header_subject">' . $this->html_safe($value) . '</span>';
@@ -271,10 +271,10 @@ protected function output() {
271271
$txt .= '</div></div></div></div></div>';
272272
}
273273
elseif ($fld == 'reply-to') {
274-
$from = addr_parse($headers['From']);
274+
$from = addr_parse(lc_headers($headers)['from'] ?? '');
275275
$replyEmails = array_map(function ($addr) {
276276
return $addr['email'];
277-
}, process_address_fld($headers['Reply-To']));
277+
}, process_address_fld(lc_headers($headers)['reply-to'] ?? ''));
278278

279279
if (count($replyEmails) === 1 && ($replyEmails[0] === $from['email'])) {
280280
$txt .= '<div class="row g-0 py-1 long_header">';
@@ -307,7 +307,7 @@ protected function output() {
307307
}
308308
}
309309
}
310-
$is_draft = isset($headers['Flags']) && mb_stristr($headers['Flags'], 'draft');
310+
$is_draft = array_key_exists('flags', lc_headers($headers)) && mb_stristr(lc_headers($headers)['flags'], 'draft');
311311
if($is_draft) {
312312
$txt .= '<div class="row g-0 py-2"><div class="col-12 text-center text-md-start"><a class="btn btn-primary" href="?page=compose'.$reply_args.'&imap_draft=1"><i class="bi bi-pencil"></i> '.$this->trans('Edit Draft').'</a></div></div>';
313313
}
@@ -328,11 +328,11 @@ protected function output() {
328328
}
329329
}
330330
}
331-
331+
332332
if ($this->get('list_headers')) {
333333
$txt .= format_list_headers($this);
334334
}
335-
335+
336336
$lc_headers = lc_headers($headers);
337337
if (array_key_exists('to', $lc_headers)) {
338338
$addr_list = process_address_fld($lc_headers['to']);
@@ -362,9 +362,9 @@ protected function output() {
362362
$txt .= '<a href="#" class="hlink all_headers text-decoration-none btn btn-sm btn-outline-secondary">'.$this->trans('All headers').'</a>';
363363
$txt .= '<a class="hlink small_headers text-decoration-none btn btn-sm btn-outline-secondary" href="#">'.$this->trans('Small headers').'</a>';
364364
$txt .= '</div>';
365-
365+
366366
$txt .= '<div class="d-flex flex-wrap gap-2">';
367-
if (!isset($headers['Flags']) || !mb_stristr($headers['Flags'], 'draft')) {
367+
if (!array_key_exists('flags', $lc_headers) || !mb_stristr($lc_headers['flags'], 'draft')) {
368368
$txt .= '<a class="reply_link hlink text-decoration-none btn btn-sm btn-outline-secondary" href="?page=compose&amp;reply=1'.$reply_args.'">'.$this->trans('Reply').'</a>';
369369
if ($size > 1) {
370370
$txt .= '<a class="reply_all_link hlink text-decoration-none btn btn-sm btn-outline-secondary" href="?page=compose&amp;reply_all=1'.$reply_args.'">'.$this->trans('Reply-all').'</a>';
@@ -373,7 +373,7 @@ protected function output() {
373373
}
374374
$txt .= forward_dropdown($this, $reply_args);
375375
}
376-
if (isset($headers['Flags']) && mb_stristr($headers['Flags'], 'flagged')) {
376+
if (array_key_exists('flags', $lc_headers) && mb_stristr($lc_headers['flags'], 'flagged')) {
377377
$txt .= '<a id="flag_msg" class="flagged_link hlink text-decoration-none btn btn-sm btn-outline-secondary hide" data-state="unflagged" href="#">'.$this->trans('Flag').'</a>';
378378
$txt .= '<a id="unflag_msg" class="unflagged_link hlink text-decoration-none btn btn-sm btn-outline-secondary" data-state="flagged" href="#">'.$this->trans('Unflag').'</a>';
379379
} else {
@@ -388,17 +388,17 @@ protected function output() {
388388
if (!$this->get('is_archive_folder')) {
389389
$txt .= '<a class="archive_link hlink text-decoration-none btn btn-sm btn-outline-secondary" id="archive_message" href="#">'.$this->trans('Archive').'</a>';
390390
}
391-
391+
392392
if($this->get('tags')){
393-
$txt .= tags_dropdown($this, $headers);
393+
$txt .= tags_dropdown($this);
394394
}
395-
if (isset($headers['X-Schedule'])) {
395+
if (isset($lc_headers['x-schedule'])) {
396396
$txt .= schedule_dropdown($this, true);
397397
}
398398

399399
$settings = $this->get('user_settings', array());
400400
if(array_key_exists('enable_snooze_setting', $settings) && $settings['enable_snooze_setting']) {
401-
$txt .= snooze_dropdown($this, isset($headers['X-Snoozed']));
401+
$txt .= snooze_dropdown($this, isset($lc_headers['x-snoozed']));
402402
}
403403
if ($this->get('sieve_filters_enabled') && !$is_draft) {
404404
$server_id = $this->get('msg_server_id');
@@ -407,7 +407,7 @@ protected function output() {
407407
$user_config = $this->get('user_config');
408408
$contact_list = $user_config->get('contacts', []);
409409
$existing_emails = array_column($contact_list, 'email_address');
410-
$sender = addr_parse($headers['From'])['email'];
410+
$sender = addr_parse($lc_headers['from'] ?? '')['email'];
411411
$domain = '*@'.get_domain($sender);
412412
$blocked_senders = get_blocked_senders_array($imap_server, $this->get('site_config'), $this->get('user_config'));
413413
$sender_blocked = in_array($sender, $blocked_senders);
@@ -742,7 +742,7 @@ protected function output() {
742742
}
743743
$res .= '</tr>';
744744
}
745-
}
745+
}
746746
return $res;
747747
}
748748
}
@@ -1299,7 +1299,7 @@ protected function output() {
12991299
if (!$enable_snooze) {
13001300
return;
13011301
}
1302-
1302+
13031303
$parts = explode('_', $this->get('list_path'));
13041304
$unsnooze = $parts[0] == 'imap' && hex2bin($parts[2]) == 'Snoozed';
13051305
$res = snooze_dropdown($this, $unsnooze);

modules/imap/site.js

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ async function select_imap_folder(path, page = 1, reload, processInTheBackground
413413
const tableRow = Hm_Utils.tbody().find(`tr[data-uid="${rowUid}"]`);
414414
if (!tableRow.length) {
415415
const index = messages.rows.map(r => $(r['0']).data('uid')).indexOf(rowUid);
416-
if (Hm_Utils.rows().length >= index) {
416+
if ((Hm_Utils.rows().length - 1) >= index) {
417417
Hm_Utils.rows().eq(index).before(row);
418418
} else {
419419
Hm_Utils.tbody().append(row);
@@ -500,18 +500,8 @@ $(document).on('submit', '#imap_filter_form', async function(event) {
500500
event.preventDefault();
501501
const url = new URL(location.href);
502502
url.search = $(this).serialize();
503-
history.pushState(history.state, "", url.toString());
504-
location.next = url.search;
505-
try {
506-
const messages = new Hm_MessagesStore(getListPathParam(), Hm_Utils.get_url_page_number(), `${getParam('keyword')}_${getParam('filter')}`, getParam('sort'));
507-
await messages.load(!messages.hasLocalData(), false, false, () => {
508-
display_imap_mailbox(messages.rows, messages.list, messages);
509-
showPagination(messages.pages);
510-
});
511-
} catch (error) {
512-
console.log(error);
513-
// Show error message. TODO: No message utility yet, implement it later.
514-
}
503+
url.searchParams.set('list_page', '1');
504+
navigate(url.toString());
515505
});
516506

517507
var display_imap_mailbox = function(rows, id, store, checkEmptyState = true) {
@@ -670,15 +660,16 @@ var get_message_content = function(msg_part, uid, list_path, listParent, detail,
670660
$('.prev, .next').hide();
671661
}
672662
globals.auto_advance_email_enabled = Boolean(res.auto_advance_email_enabled);
663+
664+
if (callback) {
665+
callback(res)
666+
}
673667
};
674668

675669
if (!msg_part) {
676670
var msgContent = get_local_message_content(uid, list_path);
677671
if (msgContent) {
678672
onSuccess(msgContent);
679-
if (callback) {
680-
callback(msgContent)
681-
}
682673
}
683674
}
684675

@@ -712,8 +703,7 @@ var get_message_content = function(msg_part, uid, list_path, listParent, detail,
712703
}
713704
},
714705
[],
715-
false,
716-
callback
706+
false
717707
);
718708
}
719709
}
@@ -1153,7 +1143,20 @@ function imap_setup_tags() {
11531143
Hm_Ajax.request(
11541144
[{'name': 'hm_ajax_hook', 'value': 'ajax_imap_tag'},
11551145
{'name': 'tag_id', 'value': folder_id},
1156-
{'name': 'list_path', 'value': ids}]
1146+
{'name': 'list_path', 'value': ids},
1147+
{'name': 'untag', 'value': !$(this).is(':checked')},
1148+
{'name': 'tag', 'value': $(this).is(':checked')}],
1149+
function() {
1150+
if (getPageNameParam() == 'message_list') {
1151+
// remove cached message content so that tag changes are reflected
1152+
ids.forEach((id) => {
1153+
const uid = id.split('_')[1];
1154+
Hm_Utils.remove_from_local_storage(getMessageStorageKey(uid));
1155+
});
1156+
} else if (getPageNameParam() == 'message') {
1157+
set_message_content();
1158+
}
1159+
}
11571160
);
11581161
});
11591162
}

modules/smtp/hm-smtp.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ class Hm_SMTP {
8989
private $scramAuthenticator;
9090
private $supports_tls;
9191
private $supports_auth;
92+
private $supports_dsn;
9293
private $max_message_size;
9394

9495
function __construct($conf) {
@@ -250,6 +251,9 @@ function capabilities($ehlo_response) {
250251
$auth_mecs = array_slice($line[1], 1);
251252
$this->supports_auth = array_map(function($v) { return mb_strtolower($v); }, $auth_mecs);
252253
break;
254+
case 'dsn': // supports delivery status notifications
255+
$this->supports_dsn = true;
256+
break;
253257
case 'size': // advisary maximum message size
254258
if(isset($line[1][1]) && is_numeric($line[1][1])) {
255259
$this->max_message_size = $line[1][1];
@@ -634,6 +638,10 @@ function send_message($from, $recipients, $message, $from_params = '', $recipien
634638
return $result;
635639
}
636640

641+
function supports_dsn() {
642+
return $this->supports_dsn;
643+
}
644+
637645
function puke() {
638646
return
639647
print_r($this->debug, true).

0 commit comments

Comments
 (0)