Skip to content

Commit

Permalink
Added snooze functionnality to messages
Browse files Browse the repository at this point in the history
  • Loading branch information
josaphatim committed Dec 11, 2022
1 parent 1695f99 commit 8cc9ce5
Show file tree
Hide file tree
Showing 6 changed files with 350 additions and 5 deletions.
147 changes: 147 additions & 0 deletions modules/imap/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -1144,3 +1144,150 @@ function get_personal_ns($imap) {
);
}}

/**
* @subpackage imap/functions
*/
if (!hm_exists('snooze_message')) {
function snooze_message($imap, $msg_id, $folder, $snooze_tag) {
if (!$imap->select_mailbox($folder)) {
return false;
}
if (!$snooze_tag) {
$imap->message_action('UNREAD', array($msg_id));
}
$msg = $imap->get_message_content($msg_id, 0);
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'];
}
if ($snooze_tag) {
$from = $old_folder ?? $folder;
$msg = "$snooze_tag;\n \tfrom $from\n".$msg;
}
$msg = str_replace("\r\n", "\n", $msg);
$msg = str_replace("\n", "\r\n", $msg);
$msg = rtrim($msg)."\r\n";

$res = false;
$snooze_folder = 'Snoozed';
if ($snooze_tag) {
if (!count($imap->get_mailbox_status($snooze_folder))) {
$imap->create_mailbox($snooze_folder);
}
if ($imap->select_mailbox($snooze_folder) && $imap->append_start($snooze_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;
}
}
}
} else {
$snooze_headers = parse_snooze_header($matches[0]);
$original_folder = $snooze_headers['from'];
if ($imap->select_mailbox($original_folder) && $imap->append_start($original_folder, strlen($msg))) {
$imap->append_feed($msg."\r\n");
if ($imap->append_end()) {
if ($imap->select_mailbox($snooze_folder) && $imap->message_action('DELETE', array($msg_id))) {
$imap->message_action('EXPUNGE', array($msg_id));
$res = true;
}
}
}
}
return $res;
}}

/**
* @subpackage imap/functions
*/
if (!hm_exists('parse_snooze_header')) {
function parse_snooze_header($snooze_header)
{
$snooze_header = str_replace('X-Snoozed: ', '', $snooze_header);
$result = [];
foreach (explode(';', str_replace("\r\n", " ", $snooze_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;
}}

/**
* @subpackage imap/functions
*/
if (!hm_exists('get_snooze_date')) {
function get_snooze_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('snooze_formats')) {
function snooze_formats() {
return array(
'tomorrow',
'next_weekend',
'next_week',
'next_month'
);
}}

/**
* @subpackage imap/functions
*/
if (!hm_exists('snooze_dropdown')) {
function snooze_dropdown($output, $unsnooze = false) {
if (date('H') <= 16) {
$values = array_merge(['later_in_day'], snooze_formats());
}
$txt = '<div style="display: inline-block;">';
$txt .= '<a class="snooze_link hlink" id="snooze_message" href="#">'.$output->trans('Snooze').'</a>';
$txt .= '<div class="snooze_dropdown" style="display:none;">';
foreach ($values as $format) {
$labels = get_snooze_date($format, true);
$txt .= '<a href="#" class="snooze_helper" data-value="'.$format.'">'.$output->trans($labels[0]).' <span>'.$labels[1].'</span></a>';
}
$txt .= '<label for="snooze_input_date" class="snooze_date_picker">'.$output->trans('Pick a date').'</label>';
$txt .= '<input id="snooze_input_date" type="datetime-local" min="'.date('Y-m-d\Th:m').'" class="snooze_input_date" style="visibility: hidden; position: absolute; height: 0;">';
$txt .= '<input class="snooze_input" style="display:none;">';
if ($unsnooze) {
$txt .= '<a href="#" data-value="unsnooze" class="unsnooze snooze_helper">'.$output->trans('Unsnooze').'</a>';
}
$txt .= '</div></div>';

return $txt;
}}

80 changes: 80 additions & 0 deletions modules/imap/handler_modules.php
Original file line number Diff line number Diff line change
Expand Up @@ -907,6 +907,86 @@ public function process() {
}
}

/**
* Snooze message
* @subpackage imap/handler
*/
class Hm_Handler_imap_snooze_message extends Hm_Handler_Module {
/**
* Use IMAP to snooze the selected message uid
*/
public function process() {
list($success, $form) = $this->process_form(array('imap_snooze_ids', 'imap_snooze_until'));
if (!$success) {
return;
}
$snoozed_messages = 0;
$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']);
$snooze_tag = "X-Snoozed: at $at;\n \tuntil $until";
}
$ids = explode(',', $form['imap_snooze_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 (snooze_message($imap, $msg_id, $folder, $snooze_tag)) {
$snoozed_messages++;
}
}
}
$this->out('snoozed_messages', $snoozed_messages);
if ($snoozed_messages == count($ids)) {
$msg = 'Messages snoozed';
} elseif ($snoozed_messages > 0) {
$msg = 'Some messages have been snoozed';
} else {
$msg = 'ERRFailed to snooze selected messages';
}
Hm_Msgs::add($msg);
$msgs = Hm_Msgs::get();
Hm_Msgs::flush();
$this->session->secure_cookie($this->request, 'hm_msgs', base64_encode(json_encode($msgs)));
}
}

/**
* Unsnooze messages
* @subpackage imap/handler
*/
class Hm_Handler_imap_unsnooze_message extends Hm_Handler_Module {
/**
* Use IMAP unsnooze messages in snoozed directory
* This should use cron
*/
public function process() {
$servers = Hm_IMAP_List::dump();
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 = 'Snoozed';
$ret = $imap->get_mailbox_page($folder, 'DATE', false, 'ALL');
foreach ($ret[1] as $msg) {
$msg_headers = $imap->get_message_headers($msg['uid']);
try {
$snooze_headers = parse_snooze_header($msg_headers['X-Snoozed']);
if (new DateTime($snooze_headers['until']) <= new DateTime()) {
snooze_message($imap, $msg['uid'], $folder, null);
}
} catch (Exception $e) {
Hm_Debug::add(sprintf('ERRCannot unsnooze message: %s', $msg_headers['subject']));
}
}
}
}
}
}

/**
* Perform an IMAP message action
* @subpackage imap/handler
Expand Down
18 changes: 16 additions & 2 deletions modules/imap/output_modules.php
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,8 @@ protected function output() {
$txt .= ' | <a class="delete_link hlink" id="delete_message" href="#">'.$this->trans('Delete').'</a>';
$txt .= ' | <a class="hlink" id="copy_message" href="#">'.$this->trans('Copy').'</a>';
$txt .= ' | <a class="hlink" id="move_message" href="#">'.$this->trans('Move').'</a>';
$txt .= ' | <a class="archive_link hlink" id="archive_message" href="#">'.$this->trans('Archive').'</a>';
$txt .= ' | <a class="archive_link hlink" id="archive_message" href="#">'.$this->trans('Archive').'</a>';
$txt .= ' | ' . snooze_dropdown($this, isset($headers['X-Snoozed']));

if ($this->get('sieve_filters_enabled')) {
$imap_server = Hm_IMAP_List::get($this->get('msg_server_id'), false);
Expand Down Expand Up @@ -1041,4 +1042,17 @@ protected function output() {
$this->trans('Archive to the original folder').'</label></td>'.
'<td><input type="checkbox" '.$checked.' id="original_folder" name="original_folder" value="1" />'.$reset.'</td></tr>';
}
}
}

/**
* Add snooze dialog to the message list controls
* @subpackage imap/output
*/
class Hm_Output_snooze_msg_control extends Hm_Output_Module {
protected function output() {
$parts = explode('_', $this->get('list_path'));
$unsnooze = $parts[0] == 'imap' && hex2bin($parts[2]) == 'Snoozed';
$res = snooze_dropdown($this, $unsnooze);
$this->concat('msg_controls_extra', $res);
}
}
26 changes: 24 additions & 2 deletions modules/imap/setup.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
add_handler('message_list', 'imap_message_list_type', true, 'imap', 'message_list_type', 'after');
add_output('message_list', 'imap_custom_controls', true, 'imap', 'message_list_heading', 'before');
add_output('message_list', 'move_copy_controls', true, 'imap', 'message_list_heading', 'before');
add_output('message_list', 'snooze_msg_control', true, 'imap', 'imap_custom_controls', 'after');

/* message view page */
add_handler('message', 'imap_download_message', true, 'imap', 'message_list_type', 'after');
Expand Down Expand Up @@ -272,6 +273,22 @@
add_handler('ajax_update_server_pw', 'load_imap_servers_from_config', true, 'imap', 'load_user_data', 'after');
add_handler('ajax_update_server_pw', 'save_imap_servers', true, 'imap', 'save_user_data', 'before');

/* snooze email */
setup_base_ajax_page('ajax_imap_snooze', 'core');
add_handler('ajax_imap_snooze', 'load_imap_servers_from_config', true);
add_handler('ajax_imap_snooze', 'imap_oauth2_token_check', true);
add_handler('ajax_imap_snooze', 'close_session_early', true, 'core');
add_handler('ajax_imap_snooze', 'save_imap_cache', true);
add_handler('ajax_imap_snooze', 'imap_snooze_message', true, 'core');

/* unsnooze emails in snoozed folders */
setup_base_ajax_page('ajax_imap_unsnooze', 'core');
add_handler('ajax_imap_unsnooze', 'load_imap_servers_from_config', true);
add_handler('ajax_imap_unsnooze', 'imap_oauth2_token_check', true);
add_handler('ajax_imap_unsnooze', 'close_session_early', true, 'core');
add_handler('ajax_imap_unsnooze', 'save_imap_cache', true);
add_handler('ajax_imap_unsnooze', 'imap_unsnooze_message', true, 'core');

/* allowed input */
return array(
'allowed_pages' => array(
Expand All @@ -295,6 +312,8 @@
'ajax_imap_mark_as_read',
'ajax_imap_move_copy_action',
'ajax_imap_folder_status',
'ajax_imap_snooze',
'ajax_imap_unsnooze',
),

'allowed_output' => array(
Expand All @@ -312,7 +331,8 @@
'combined_inbox_server_ids' => array(FILTER_SANITIZE_FULL_SPECIAL_CHARS, false),
'imap_delete_error' => array(FILTER_VALIDATE_BOOLEAN, false),
'move_count' => array(FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_REQUIRE_ARRAY),
'show_pagination_links' => array(FILTER_VALIDATE_BOOLEAN, false)
'show_pagination_links' => array(FILTER_VALIDATE_BOOLEAN, false),
'snoozed_messages' => array(FILTER_VALIDATE_INT, false),
),

'allowed_get' => array(
Expand Down Expand Up @@ -370,7 +390,9 @@
'imap_move_page' => FILTER_SANITIZE_FULL_SPECIAL_CHARS,
'compose_unflag_send' => FILTER_VALIDATE_BOOLEAN,
'imap_per_page' => FILTER_VALIDATE_INT,
'original_folder' => FILTER_VALIDATE_BOOLEAN
'original_folder' => FILTER_VALIDATE_BOOLEAN,
'imap_snooze_ids' => FILTER_SANITIZE_FULL_SPECIAL_CHARS,
'imap_snooze_until' => FILTER_SANITIZE_FULL_SPECIAL_CHARS
)
);

Expand Down
10 changes: 9 additions & 1 deletion modules/imap/site.css
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,12 @@
#archive_val { padding-left: 20px; }

.attached_image { margin-right: 20px; margin-bottom: 20px; height: 200px; }
.attached_image_box { display: flex; flex-wrap: wrap; border-top: solid 1px #ddd; padding-top: 20px; padding-left: 20px; width: 100%; padding-bottom: 40px; }
.attached_image_box { display: flex; flex-wrap: wrap; border-top: solid 1px #ddd; padding-top: 20px; padding-left: 20px; width: 100%; padding-bottom: 40px; }

.snooze_date_picker, .snooze_helper { color: #333; text-transform: capitalize; }
.snooze_date_picker { border-top: 1px solid #ddd; }
.snooze_dropdown { position: absolute; margin-top: 15px; background-color: #fff; border: 1px solid #ddd; box-shadow: 3px 3px 3px #ddd; font-variant: none; min-width: 250px; }
.snooze_date_picker:hover, .snooze_helper:hover { background-color: #eee; }
.header_links a.snooze_date_picker, .header_links a.snooze_helper, .msg_controls a.snooze_date_picker, .msg_controls a.snooze_helper { padding: 8px; padding-left: 15px !important; padding-right: 15px !important; white-space: nowrap; font-size: 1rem; display: block !important; margin-right: 0; border: 0; }
.snooze_helper span { float: right; }
.snooze_date_picker { display: block; padding: 8px 15px; color: #333; font-size: 1rem; cursor: pointer; }
Loading

0 comments on commit 8cc9ce5

Please sign in to comment.