Skip to content

Commit 62e9174

Browse files
authored
Merge pull request cypht-org#1091 from amaninyumu1/Cypht-delay-send-later-scheduled-sending
feat(backend): add scheduled send functionality
2 parents fbb70f9 + 5beec67 commit 62e9174

38 files changed

+715
-113
lines changed

language/az.php

+1
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,7 @@
621621
'You must provide a name for your script' => false,
622622
'Empty script' => false,
623623
'Please create a profile for saving sent messages option' => false,
624+
'You have %d scheduled messages that won\'t be executed if you quit' => false,
624625
'Attachment storage unavailable, please contact your site administrator' => false,
625626
'Your subject is empty!' => false,
626627
'Your body is empty!' => false,

language/de.php

+1
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,7 @@
618618
'You must provide a name for your script' => false,
619619
'Empty script' => false,
620620
'Please create a profile for saving sent messages option' => false,
621+
'You have %d scheduled messages that won\'t be executed if you quit' => false,
621622
'Attachment storage unavailable, please contact your site administrator' => false,
622623
'Your subject is empty!' => false,
623624
'Your body is empty!' => false,

language/en.php

+1
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,7 @@
636636
'You must provide a name for your script' => false,
637637
'Empty script' => false,
638638
'Please create a profile for saving sent messages option' => false,
639+
'You have %d scheduled messages that won\'t be executed if you quit' => false,
639640
'Attachment storage unavailable, please contact your site administrator' => false,
640641
'Your subject is empty!' => false,
641642
'Your body is empty!' => false,

language/es.php

+1
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,7 @@
618618
'You must provide a name for your script' => false,
619619
'Empty script' => false,
620620
'Please create a profile for saving sent messages option' => false,
621+
'You have %d scheduled messages that won\'t be executed if you quit' => false,
621622
'Attachment storage unavailable, please contact your site administrator' => false,
622623
'Your subject is empty!' => false,
623624
'Your body is empty!' => false,

language/et.php

+1
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,7 @@
626626
'You must provide a name for your script' => false,
627627
'Empty script' => false,
628628
'Please create a profile for saving sent messages option' => false,
629+
'You have %d scheduled messages that won\'t be executed if you quit' => false,
629630
'Attachment storage unavailable, please contact your site administrator' => false,
630631
'Your subject is empty!' => false,
631632
'Your body is empty!' => false,

language/fa.php

+1
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,7 @@
670670
'You must provide a name for your script' => false,
671671
'Empty script' => false,
672672
'Please create a profile for saving sent messages option' => false,
673+
'You have %d scheduled messages that won\'t be executed if you quit' => false,
673674
'Attachment storage unavailable, please contact your site administrator' => false,
674675
'Your subject is empty!' => false,
675676
'Your body is empty!' => false,

language/fr.php

+1
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,7 @@
617617
'You must provide a name for your script' => false,
618618
'Empty script' => false,
619619
'Please create a profile for saving sent messages option' => false,
620+
'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',
620621
'Attachment storage unavailable, please contact your site administrator' => false,
621622
'Your subject is empty!' => false,
622623
'Your body is empty!' => false,

language/hu.php

+1
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,7 @@
618618
'You must provide a name for your script' => false,
619619
'Empty script' => false,
620620
'Please create a profile for saving sent messages option' => false,
621+
'You have %d scheduled messages that won\'t be executed if you quit' => false,
621622
'Attachment storage unavailable, please contact your site administrator' => false,
622623
'Your subject is empty!' => false,
623624
'Your body is empty!' => false,

language/id.php

+1
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,7 @@
625625
'You must provide a name for your script' => false,
626626
'Empty script' => false,
627627
'Please create a profile for saving sent messages option' => false,
628+
'You have %d scheduled messages that won\'t be executed if you quit' => false,
628629
'Attachment storage unavailable, please contact your site administrator' => false,
629630
'Your subject is empty!' => false,
630631
'Your body is empty!' => false,

language/it.php

+1
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,7 @@
618618
'You must provide a name for your script' => false,
619619
'Empty script' => false,
620620
'Please create a profile for saving sent messages option' => false,
621+
'You have %d scheduled messages that won\'t be executed if you quit' => false,
621622
'Attachment storage unavailable, please contact your site administrator' => false,
622623
'Your subject is empty!' => false,
623624
'Your body is empty!' => false,

language/ja.php

+1
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,7 @@
618618
'You must provide a name for your script' => false,
619619
'Empty script' => false,
620620
'Please create a profile for saving sent messages option' => false,
621+
'You have %d scheduled messages that won\'t be executed if you quit' => false,
621622
'Attachment storage unavailable, please contact your site administrator' => false,
622623
'Your subject is empty!' => false,
623624
'Your body is empty!' => false,

language/nl.php

+1
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,7 @@
618618
'You must provide a name for your script' => false,
619619
'Empty script' => false,
620620
'Please create a profile for saving sent messages option' => false,
621+
'You have %d scheduled messages that won\'t be executed if you quit' => false,
621622
'Attachment storage unavailable, please contact your site administrator' => false,
622623
'Your subject is empty!' => false,
623624
'Your body is empty!' => false,

language/pt-BR.php

+1
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,7 @@
617617
'You must provide a name for your script' => false,
618618
'Empty script' => false,
619619
'Please create a profile for saving sent messages option' => false,
620+
'You have %d scheduled messages that won\'t be executed if you quit' => false,
620621
'Attachment storage unavailable, please contact your site administrator' => false,
621622
'Your subject is empty!' => false,
622623
'Your body is empty!' => false,

language/ro.php

+1
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,7 @@
617617
'You must provide a name for your script' => false,
618618
'Empty script' => false,
619619
'Please create a profile for saving sent messages option' => false,
620+
'You have %d scheduled messages that won\'t be executed if you quit' => false,
620621
'Attachment storage unavailable, please contact your site administrator' => false,
621622
'Your subject is empty!' => false,
622623
'Your body is empty!' => false,

language/ru.php

+1
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,7 @@
619619
'You must provide a name for your script' => false,
620620
'Empty script' => false,
621621
'Please create a profile for saving sent messages option' => false,
622+
'You have %d scheduled messages that won\'t be executed if you quit' => false,
622623
'Attachment storage unavailable, please contact your site administrator' => false,
623624
'Your subject is empty!' => false,
624625
'Your body is empty!' => false,

language/zh-Hans.php

+1
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,7 @@
639639
'You must provide a name for your script' => '请提供脚本名称',
640640
'Empty script' => '空脚本',
641641
'Please create a profile for saving sent messages option' => '请创建用于保存已发送信息选项的配置文件',
642+
'You have %d scheduled messages that won\'t be executed if you quit' => false,
642643
'Attachment storage unavailable, please contact your site administrator' => '附件存储不可用,请联系您的网站管理员',
643644
'Your subject is empty!' => '主题为空!',
644645
'Your body is empty!' => '内容为空!',

lib/servers.php

+26
Original file line numberDiff line numberDiff line change
@@ -273,4 +273,30 @@ private static function match($server, $user, $name) {
273273
}
274274
return false;
275275
}
276+
277+
private static function appendPasswordAndUsername(array $server) {
278+
$server['password'] = $server['pass'];
279+
$server['username'] = $server['user'];
280+
return $server;
281+
}
282+
283+
public static function getForMailbox($id) {
284+
$server = self::get($id, true);
285+
if ($server) {
286+
return self::appendPasswordAndUsername($server);
287+
}
288+
return false;
289+
}
290+
291+
public static function dumpForMailbox($id = false) {
292+
$list = self::dump($id, true);
293+
if ($id !== false) {
294+
return self::appendPasswordAndUsername($list);
295+
}
296+
foreach ($list as $index => $server) {
297+
$server = self::appendPasswordAndUsername($server);
298+
$list[$index] = $server;
299+
}
300+
return $list;
301+
}
276302
}

modules/core/functions.php

+109
Original file line numberDiff line numberDiff line change
@@ -637,3 +637,112 @@ function privacy_setting_callback($val, $key, $mod) {
637637
}
638638
return $val;
639639
}
640+
641+
if (!hm_exists('get_scheduled_date')) {
642+
function get_scheduled_date($format, $only_label = false) {
643+
switch ($format) {
644+
case 'later_in_day':
645+
$date_string = 'today 18:00';
646+
$label = 'Later in the day';
647+
break;
648+
case 'tomorrow':
649+
$date_string = '+1 day 08:00';
650+
$label = 'Tomorrow';
651+
break;
652+
case 'next_weekend':
653+
$date_string = 'next Saturday 08:00';
654+
$label = 'Next weekend';
655+
break;
656+
case 'next_week':
657+
$date_string = 'next week 08:00';
658+
$label = 'Next week';
659+
break;
660+
case 'next_month':
661+
$date_string = 'next month 08:00';
662+
$label = 'Next month';
663+
break;
664+
default:
665+
$date_string = $format;
666+
$label = 'Certain date';
667+
break;
668+
}
669+
670+
$time = strtotime($date_string);
671+
672+
if ($only_label) {
673+
return [$label, date('D, H:i', $time)];
674+
}
675+
676+
return date('D, d M Y H:i T', $time);
677+
}
678+
}
679+
680+
681+
/**
682+
* @subpackage imap/functions
683+
*/
684+
if (!hm_exists('nexter_formats')) {
685+
function nexter_formats() {
686+
$values = array(
687+
'tomorrow',
688+
'next_weekend',
689+
'next_week',
690+
'next_month'
691+
);
692+
if (date('H') <= 16) {
693+
array_push($values, 'later_in_day');
694+
}
695+
return $values;
696+
}}
697+
698+
if (!hm_exists('schedule_dropdown')) {
699+
function schedule_dropdown($output, $send_now = false) {
700+
$values = nexter_formats();
701+
702+
$txt = '';
703+
if ($send_now) {
704+
$txt .= '<div class="dropdown d-inline-block">
705+
<button type="button" class="btn btn-light btn-sm dropdown-toggle" id="dropdownMenuNexterDate" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="true">'.$output->trans('Reschedule').'</button>';
706+
}
707+
$txt .= '<ul class="dropdown-menu nexter_dropdown schedule_dropdown" aria-labelledby="dropdownMenuNexterDate">';
708+
foreach ($values as $format) {
709+
$labels = get_scheduled_date($format, true);
710+
$txt .= '<li><a href="#" class="nexter_date_helper dropdown-item d-flex justify-content-between gap-5" data-value="'.$format.'"><span>'.$output->trans($labels[0]).'</span> <span class="text-end">'.$labels[1].'</span></a></li>';
711+
}
712+
$txt .= '<li><hr class="dropdown-divider"></li>';
713+
$txt .= '<li><label for="nexter_input_date" class="nexter_date_picker dropdown-item cursor-pointer">'.$output->trans('Pick a date').'</label>';
714+
$txt .= '<input id="nexter_input_date" type="datetime-local" min="'.date('Y-m-d\Th:m').'" class="nexter_input_date" style="visibility: hidden; position: absolute; height: 0;">';
715+
$txt .= '<input class="nexter_input" style="display:none;"></li>';
716+
if ($send_now) {
717+
$txt .= '<li><hr class="dropdown-divider"></li>';
718+
$txt .= '<li><a href="#" data-value="now" class="nexter_date_helper dropdown-item"">'.$output->trans('Send now').'</a></li>';
719+
}
720+
$txt .= '</ul>';
721+
if ($send_now) {
722+
$txt .= '</div>';
723+
}
724+
725+
return $txt;
726+
}}
727+
728+
/**
729+
* @subpackage imap/functions
730+
*/
731+
if (!hm_exists('parse_delayed_header')) {
732+
function parse_delayed_header($header, $name)
733+
{
734+
$header = str_replace("$name: ", '', $header);
735+
$result = [];
736+
foreach (explode(';', $header) as $keyValue)
737+
{
738+
$keyValue = trim($keyValue);
739+
$spacePos = strpos($keyValue, ' ');
740+
if ($spacePos > 0) {
741+
$result[rtrim(substr($keyValue, 0, $spacePos), ':')] = trim(substr($keyValue, $spacePos+1));
742+
} else {
743+
$result[$keyValue] = true;
744+
}
745+
}
746+
return $result;
747+
}
748+
}

modules/core/handler_modules.php

+9
Original file line numberDiff line numberDiff line change
@@ -1168,6 +1168,15 @@ public function process() {
11681168
};
11691169
}
11701170

1171+
if($isSender && $isReceiver && $createProfile && isset($this->imap_server_id) && isset($this->smtp_server_id) && ! ($smtpServerId || $imapServerId)) {
1172+
if (!$this->module_is_supported('profiles')) {
1173+
Hm_Msgs::add("Profiles module is not enabled", "danger");
1174+
return;
1175+
}
1176+
1177+
add_profile($profileName, $profileSignature, $profileReplyTo, $profileIsDefault, $email, $onlyJmap? $jmapAddress: $imapAddress, $email, $this->smtp_server_id, $this->imap_server_id, $this);
1178+
}
1179+
11711180
if ($this->module_is_supported('imap_folders')) {
11721181
$this->out('imap_server_id', $this->imap_server_id);
11731182
$this->out('imap_service_name', $provider);

modules/core/hm-mailbox.php

+7
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ public function server_type() {
7474
}
7575

7676
public function authed() {
77+
if (! $this->connection) {
78+
return false;
79+
}
7780
if ($this->is_imap()) {
7881
return $this->connection->get_state() == 'authenticated' || $this->connection->get_state() == 'selected';
7982
} elseif ($this->is_smtp()) {
@@ -586,4 +589,8 @@ public function select_folder($folder) {
586589
}
587590
return true;
588591
}
592+
593+
public function get_config() {
594+
return $this->config;
595+
}
589596
}

modules/core/message_list_functions.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -336,11 +336,11 @@ function subject_callback($vals, $style, $output_mod) {
336336
*/
337337
if (!hm_exists('date_callback')) {
338338
function date_callback($vals, $style, $output_mod) {
339-
$snooze_class = isset($vals[2]) && $vals[2]? ' snoozed_date': '';
339+
$delayed_class = isset($vals[2]) && $vals[2]? ' delayed_date': '';
340340
if ($style == 'news') {
341-
return sprintf('<div class="msg_date%s">%s<input type="hidden" class="msg_timestamp" value="%s" /></div>', $snooze_class, $output_mod->html_safe($vals[0]), $output_mod->html_safe($vals[1]));
341+
return sprintf('<div class="msg_date%s">%s<input type="hidden" class="msg_timestamp" value="%s" /></div>', $delayed_class, $output_mod->html_safe($vals[0]), $output_mod->html_safe($vals[1]));
342342
}
343-
return sprintf('<td class="msg_date%s" title="%s">%s<input type="hidden" class="msg_timestamp" value="%s" /></td>', $snooze_class, $output_mod->html_safe(date('r', $vals[1])), $output_mod->html_safe($vals[0]), $output_mod->html_safe($vals[1]));
343+
return sprintf('<td class="msg_date%s" title="%s">%s<input type="hidden" class="msg_timestamp" value="%s" /></td>', $delayed_class, $output_mod->html_safe(date('r', $vals[1])), $output_mod->html_safe($vals[0]), $output_mod->html_safe($vals[1]));
344344
}}
345345

346346
function dates_holders_callback($vals) {

modules/core/site.css

+1-1
Original file line numberDiff line numberDiff line change
@@ -1276,7 +1276,7 @@ div.unseen,
12761276
.mobile .imap_sort {
12771277
width: 100%;
12781278
}
1279-
.snoozed_date {
1279+
.delayed_date {
12801280
color: var(--bs-primary) !important;
12811281
}
12821282

modules/core/site.js

+54
Original file line numberDiff line numberDiff line change
@@ -2435,3 +2435,57 @@ const observeMessageTextMutationAndHandleExternalResources = (inline) => {
24352435
});
24362436
}
24372437
};
2438+
2439+
function setupActionSchedule(callback) {
2440+
$(document).on('click', '.nexter_date_picker', function (e) {
2441+
document.querySelector('.nexter_input_date').showPicker();
2442+
});
2443+
$(document).on('click', '.nexter_date_helper', function (e) {
2444+
e.preventDefault();
2445+
$('.nexter_input').val($(this).attr('data-value')).trigger('change');
2446+
});
2447+
$(document).on('input', '.nexter_input_date', function (e) {
2448+
var now = new Date();
2449+
now.setMinutes(now.getMinutes() + 1);
2450+
$(this).attr('min', now.toJSON().slice(0, 16));
2451+
if (new Date($(this).val()).getTime() <= now.getTime()) {
2452+
$('.nexter_date_picker').css('border', '1px solid red');
2453+
} else {
2454+
$('.nexter_date_picker').css({ 'border': 'unset', 'border-top': '1px solid #ddd' });
2455+
}
2456+
});
2457+
$(document).on('change', '.nexter_input_date', function (e) {
2458+
const selectedDate = new Date($(this).val());
2459+
if ($(this).val() && new Date().getTime() < selectedDate.getTime()) {
2460+
$('.nexter_input').val(selectedDate.toISOString()).trigger('change');
2461+
}
2462+
});
2463+
$(document).on('change', '.nexter_input', callback);
2464+
}
2465+
2466+
function setupActionSnooze(callback) {
2467+
$(document).on('click', '.nexter_date_picker_snooze', function (e) {
2468+
document.querySelector('.nexter_input_date_snooze').showPicker();
2469+
});
2470+
$(document).on('click', '.nexter_date_helper_snooze', function (e) {
2471+
e.preventDefault();
2472+
$('.nexter_input_snooze').val($(this).attr('data-value')).trigger('change');
2473+
});
2474+
$(document).on('input', '.nexter_input_date_snooze', function (e) {
2475+
var now = new Date();
2476+
now.setMinutes(now.getMinutes() + 1);
2477+
$(this).attr('min', now.toJSON().slice(0, 16));
2478+
if (new Date($(this).val()).getTime() <= now.getTime()) {
2479+
$('.nexter_date_picker_snooze').css('border', '1px solid red');
2480+
} else {
2481+
$('.nexter_date_picker_snooze').css({ 'border': 'unset', 'border-top': '1px solid #ddd' });
2482+
}
2483+
});
2484+
$(document).on('change', '.nexter_input_date_snooze', function (e) {
2485+
const selectedDate = new Date($(this).val());
2486+
if ($(this).val() && new Date().getTime() < selectedDate.getTime()) {
2487+
$('.nexter_input_snooze').val(selectedDate.toISOString()).trigger('change');
2488+
}
2489+
});
2490+
$(document).on('change', '.nexter_input_snooze', callback);
2491+
}

0 commit comments

Comments
 (0)