Skip to content

Commit

Permalink
Merge pull request #1091 from amaninyumu1/Cypht-delay-send-later-sche…
Browse files Browse the repository at this point in the history
…duled-sending

feat(backend): add scheduled send functionality
  • Loading branch information
kroky authored Feb 13, 2025
2 parents fbb70f9 + 5beec67 commit 62e9174
Show file tree
Hide file tree
Showing 38 changed files with 715 additions and 113 deletions.
1 change: 1 addition & 0 deletions language/az.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions language/de.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions language/en.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions language/es.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions language/et.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions language/fa.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions language/fr.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions language/hu.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions language/id.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions language/it.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions language/ja.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions language/nl.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions language/pt-BR.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions language/ro.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions language/ru.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions language/zh-Hans.php
Original file line number Diff line number Diff line change
Expand Up @@ -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!' => '内容为空!',
Expand Down
26 changes: 26 additions & 0 deletions lib/servers.php
Original file line number Diff line number Diff line change
Expand Up @@ -273,4 +273,30 @@ private static function match($server, $user, $name) {
}
return false;
}

private static function appendPasswordAndUsername(array $server) {
$server['password'] = $server['pass'];
$server['username'] = $server['user'];
return $server;
}

public static function getForMailbox($id) {
$server = self::get($id, true);
if ($server) {
return self::appendPasswordAndUsername($server);
}
return false;
}

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;
}
return $list;
}
}
109 changes: 109 additions & 0 deletions modules/core/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -637,3 +637,112 @@ function privacy_setting_callback($val, $key, $mod) {
}
return $val;
}

if (!hm_exists('get_scheduled_date')) {
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 T', $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 .= '<div class="dropdown d-inline-block">
<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>';
}
$txt .= '<ul class="dropdown-menu nexter_dropdown schedule_dropdown" aria-labelledby="dropdownMenuNexterDate">';
foreach ($values as $format) {
$labels = get_scheduled_date($format, true);
$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>';
}
$txt .= '<li><hr class="dropdown-divider"></li>';
$txt .= '<li><label for="nexter_input_date" class="nexter_date_picker dropdown-item cursor-pointer">'.$output->trans('Pick a date').'</label>';
$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;">';
$txt .= '<input class="nexter_input" style="display:none;"></li>';
if ($send_now) {
$txt .= '<li><hr class="dropdown-divider"></li>';
$txt .= '<li><a href="#" data-value="now" class="nexter_date_helper dropdown-item"">'.$output->trans('Send now').'</a></li>';
}
$txt .= '</ul>';
if ($send_now) {
$txt .= '</div>';
}

return $txt;
}}

/**
* @subpackage imap/functions
*/
if (!hm_exists('parse_delayed_header')) {
function parse_delayed_header($header, $name)
{
$header = str_replace("$name: ", '', $header);
$result = [];
foreach (explode(';', $header) as $keyValue)
{
$keyValue = trim($keyValue);
$spacePos = strpos($keyValue, ' ');
if ($spacePos > 0) {
$result[rtrim(substr($keyValue, 0, $spacePos), ':')] = trim(substr($keyValue, $spacePos+1));
} else {
$result[$keyValue] = true;
}
}
return $result;
}
}
9 changes: 9 additions & 0 deletions modules/core/handler_modules.php
Original file line number Diff line number Diff line change
Expand Up @@ -1168,6 +1168,15 @@ public function process() {
};
}

if($isSender && $isReceiver && $createProfile && isset($this->imap_server_id) && isset($this->smtp_server_id) && ! ($smtpServerId || $imapServerId)) {
if (!$this->module_is_supported('profiles')) {
Hm_Msgs::add("Profiles module is not enabled", "danger");
return;
}

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')) {
$this->out('imap_server_id', $this->imap_server_id);
$this->out('imap_service_name', $provider);
Expand Down
7 changes: 7 additions & 0 deletions modules/core/hm-mailbox.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ public function server_type() {
}

public function authed() {
if (! $this->connection) {
return false;
}
if ($this->is_imap()) {
return $this->connection->get_state() == 'authenticated' || $this->connection->get_state() == 'selected';
} elseif ($this->is_smtp()) {
Expand Down Expand Up @@ -586,4 +589,8 @@ public function select_folder($folder) {
}
return true;
}

public function get_config() {
return $this->config;
}
}
6 changes: 3 additions & 3 deletions modules/core/message_list_functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -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': '';
$delayed_class = isset($vals[2]) && $vals[2]? ' delayed_date': '';
if ($style == 'news') {
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]));
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]));
}
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]));
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]));
}}

function dates_holders_callback($vals) {
Expand Down
2 changes: 1 addition & 1 deletion modules/core/site.css
Original file line number Diff line number Diff line change
Expand Up @@ -1276,7 +1276,7 @@ div.unseen,
.mobile .imap_sort {
width: 100%;
}
.snoozed_date {
.delayed_date {
color: var(--bs-primary) !important;
}

Expand Down
54 changes: 54 additions & 0 deletions modules/core/site.js
Original file line number Diff line number Diff line change
Expand Up @@ -2435,3 +2435,57 @@ 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) {
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);
}

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) {
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);
}
Loading

0 comments on commit 62e9174

Please sign in to comment.